@@ -27,6 +27,7 @@ setlocal expandtab
2727setlocal nolisp
2828setlocal autoindent
2929setlocal indentexpr = GetPythonPEPIndent (v: lnum )
30+ setlocal formatexpr = GetPythonPEPFormat (v: lnum ,v: count )
3031setlocal indentkeys = ! ^F,o ,O,<:> ,0 ),0 ],0 },= elif,= except
3132setlocal tabstop = 4
3233setlocal softtabstop = 4
@@ -59,6 +60,8 @@ let s:stop_statement = '^\s*\(break\|continue\|raise\|return\|pass\)\>'
5960let s: skip_special_chars = ' synIDattr(synID(line("."), col("."), 0), "name") ' .
6061 \ ' =~? "\\vstring|comment|jedi\\S"'
6162
63+ let s: skip_string = ' synIDattr(synID(line("."), col("."), 0), "name") ' .
64+ \ ' =~? "String"'
6265let s: skip_after_opening_paren = ' synIDattr(synID(line("."), col("."), 0), "name") ' .
6366 \ ' =~? "\\vcomment|jedi\\S"'
6467
@@ -421,3 +424,123 @@ function! GetPythonPEPIndent(lnum)
421424
422425 return s: indent_like_previous_line (a: lnum )
423426endfunction
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