@@ -27,6 +27,7 @@ setlocal expandtab
27
27
setlocal nolisp
28
28
setlocal autoindent
29
29
setlocal indentexpr = GetPythonPEPIndent (v: lnum )
30
+ setlocal formatexpr = GetPythonPEPFormat (v: lnum ,v: count )
30
31
setlocal indentkeys = ! ^F,o ,O,<:> ,0 ),0 ],0 },= elif,= except
31
32
setlocal tabstop = 4
32
33
setlocal softtabstop = 4
@@ -59,6 +60,8 @@ let s:stop_statement = '^\s*\(break\|continue\|raise\|return\|pass\)\>'
59
60
let s: skip_special_chars = ' synIDattr(synID(line("."), col("."), 0), "name") ' .
60
61
\ ' =~? "\\vstring|comment|jedi\\S"'
61
62
63
+ let s: skip_string = ' synIDattr(synID(line("."), col("."), 0), "name") ' .
64
+ \ ' =~? "String"'
62
65
let s: skip_after_opening_paren = ' synIDattr(synID(line("."), col("."), 0), "name") ' .
63
66
\ ' =~? "\\vcomment|jedi\\S"'
64
67
@@ -421,3 +424,123 @@ function! GetPythonPEPIndent(lnum)
421
424
422
425
return s: indent_like_previous_line (a: lnum )
423
426
endfunction
427
+
428
+ function s: SearchPosWithSkip (pattern, flags, skip , stopline)
429
+ "
430
+ " Returns true if a match is found for {pattern}, but ignores matches
431
+ " where {skip} evaluates to false. This allows you to do nifty things
432
+ " like, say, only matching outside comments, only on odd-numbered lines,
433
+ " or whatever else you like.
434
+ "
435
+ " Mimics the built-in search() function, but adds a {skip} expression
436
+ " like that available in searchpair() and searchpairpos().
437
+ " (See the Vim help on search() for details of the other parameters.)
438
+ "
439
+ " Note the current position, so that if there are no unskipped
440
+ " matches, the cursor can be restored to this location.
441
+ "
442
+ let l: flags = a: flags
443
+ let l: movepos = getpos (' .' )
444
+ let l: firstmatch = []
445
+ let l: pos = [0 , 0 , 0 , 0 ]
446
+
447
+ " Loop as long as {pattern} continues to be found.
448
+ "
449
+ while search (a: pattern , l: flags , a: stopline ) > 0
450
+ if l: firstmatch == []
451
+ let l: firstmatch = getpos (' .' )
452
+ let l: flags = substitute (l: flags , ' c' , ' ' , ' ' )
453
+ elseif l: firstmatch == getpos (' .' )
454
+ break
455
+ endif
456
+
457
+ " If {skip} is true, ignore this match and continue searching.
458
+ "
459
+ if eval (a: skip )
460
+ continue
461
+ endif
462
+
463
+ " If we get here, {pattern} was found and {skip} is false,
464
+ " so this is a match we don't want to ignore. Update the
465
+ " match position and stop searching.
466
+ "
467
+ let l: pos = getpos (' .' )
468
+ let l: movepos = getpos (' .' )
469
+ break
470
+
471
+ endwhile
472
+
473
+ " Jump to the position of the unskipped match, or to the original
474
+ " position if there wasn't one.
475
+ "
476
+
477
+ call setpos (' .' , l: movepos )
478
+ return [l: pos [1 ], l: pos [2 ]]
479
+
480
+ endfunction
481
+
482
+ function s: IsInComment (lnum, col )
483
+ echom synIDattr (synID (a: lnum , a: col , 1 ), ' name' )
484
+ return synIDattr (synID (a: lnum , a: col , 1 ), ' name' ) = ~? ' comment'
485
+ endfunction
486
+
487
+ function ! GetPythonPEPFormat (lnum, count )
488
+ let l: tw = &textwidth ? &textwidth : 79
489
+
490
+ let l: winview = winsaveview ()
491
+
492
+ let l: count = a: count
493
+ let l: first_char = indent (a: lnum ) + 1
494
+
495
+ if mode () == ? ' i' " gq was not pressed, but tw was set
496
+ return 1
497
+ endif
498
+
499
+ " This gq is only meant to do code with strings, not comments.
500
+ if s: IsInComment (a: lnum , l: first_char )
501
+ return 1
502
+ endif
503
+
504
+ if len (getline (a: lnum )) <= l: tw && l: count == 1 " No need for gq
505
+ return 1
506
+ endif
507
+
508
+ " Put all the lines on one line and do normal splitting after that.
509
+ if l: count > 1
510
+ while l: count > 1
511
+ let l: count -= 1
512
+ normal ! J
513
+ endwhile
514
+ endif
515
+
516
+ call cursor (a: lnum , l: tw + 1 )
517
+ let l: orig_breakpoint = searchpos (' ' , ' bcW' , a: lnum )
518
+ call cursor (a: lnum , l: tw + 1 )
519
+ call cursor (a: lnum , l: tw + 1 )
520
+ let l: breakpoint = s: SearchPosWithSkip (' ' , ' bcW' , s: skip_string , a: lnum )
521
+
522
+ echom ' normal'
523
+ echom l: orig_breakpoint [1 ]
524
+ echom ' new'
525
+ echom l: breakpoint [1 ]
526
+ " No need for special treatment, normal gq handles edgecases better
527
+ if l: breakpoint [1 ] == l: orig_breakpoint [1 ]
528
+ call winrestview (l: winview )
529
+ return 1
530
+ endif
531
+
532
+ " If the match is at the indent level try breaking after string as last
533
+ " resort
534
+ if l: breakpoint [1 ] <= indent (a: lnum )
535
+ call cursor (a: lnum , l: tw + 1 )
536
+ let l: breakpoint = s: SearchPosWithSkip (' ' , ' cW' , s: skip_special_chars , a: lnum )
537
+ endif
538
+
539
+
540
+ if l: breakpoint [1 ] == 0
541
+ call winrestview (l: winview )
542
+ else
543
+ call feedkeys (" r\<CR> " )
544
+ call feedkeys (' gqq' )
545
+ endif
546
+ endfunction
0 commit comments