Skip to content

Commit bb87bed

Browse files
committed
Add a start of a gq implementation that does not split strings halfway through
1 parent 7dfebca commit bb87bed

File tree

1 file changed

+123
-0
lines changed

1 file changed

+123
-0
lines changed

indent/python.vim

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ setlocal expandtab
2727
setlocal nolisp
2828
setlocal autoindent
2929
setlocal indentexpr=GetPythonPEPIndent(v:lnum)
30+
setlocal formatexpr=GetPythonPEPFormat(v:lnum,v:count)
3031
setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif,=except
3132
setlocal tabstop=4
3233
setlocal softtabstop=4
@@ -59,6 +60,8 @@ let s:stop_statement = '^\s*\(break\|continue\|raise\|return\|pass\)\>'
5960
let 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"'
6265
let 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)
423426
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

Comments
 (0)