Skip to content

Commit 62189f5

Browse files
authored
Merge pull request #118 from blueyed/searchpair-timeout
Improve performance
2 parents 22cdb82 + c2e53f8 commit 62189f5

File tree

2 files changed

+72
-29
lines changed

2 files changed

+72
-29
lines changed

indent/python.vim

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ if !exists('g:python_pep8_indent_hang_closing')
3838
let g:python_pep8_indent_hang_closing = 0
3939
endif
4040

41+
" TODO: check required patch for timeout argument, likely lower than 7.3.429 though.
42+
if !exists('g:python_pep8_indent_searchpair_timeout')
43+
if has('patch-8.0.1483')
44+
let g:python_pep8_indent_searchpair_timeout = 150
45+
else
46+
let g:python_pep8_indent_searchpair_timeout = 0
47+
endif
48+
endif
49+
4150
let s:block_rules = {
4251
\ '^\s*elif\>': ['if', 'elif'],
4352
\ '^\s*except\>': ['try', 'except'],
@@ -57,25 +66,34 @@ else
5766
endif
5867
let s:stop_statement = '^\s*\(break\|continue\|raise\|return\|pass\)\>'
5968

60-
" Skip strings and comments. Return 1 for chars to skip.
61-
" jedi* refers to syntax definitions from jedi-vim for call signatures, which
62-
" are inserted temporarily into the buffer.
63-
let s:skip_special_chars = 'synIDattr(synID(line("."), col("."), 0), "name") ' .
64-
\ '=~? "\\vstring|comment|^pythonbytes%(contents)=$|jedi\\S"'
65-
6669
let s:skip_after_opening_paren = 'synIDattr(synID(line("."), col("."), 0), "name") ' .
6770
\ '=~? "\\vcomment|jedi\\S"'
6871

69-
" Also ignore anything concealed.
70-
" Wrapper around synconcealed for older Vim (7.3.429, used on Travis CI).
71-
function! s:is_concealed(line, col)
72-
let concealed = synconcealed(a:line, a:col)
73-
return len(concealed) && concealed[0]
74-
endfunction
75-
if has('conceal')
76-
let s:skip_special_chars .= '|| s:is_concealed(line("."), col("."))'
77-
endif
72+
if !get(g:, 'python_pep8_indent_skip_concealed', 0) || !has('conceal')
73+
" Skip strings and comments. Return 1 for chars to skip.
74+
" jedi* refers to syntax definitions from jedi-vim for call signatures, which
75+
" are inserted temporarily into the buffer.
76+
function! s:_skip_special_chars(line, col)
77+
return synIDattr(synID(a:line, a:col, 0), 'name')
78+
\ =~? "\\vstring|comment|^pythonbytes%(contents)=$|jedi\\S"
79+
endfunction
80+
else
81+
" Also ignore anything concealed.
82+
" TODO: doc; likely only necessary with jedi-vim, where a better version is
83+
" planned (https://github.com/Vimjas/vim-python-pep8-indent/pull/98).
84+
85+
" Wrapper around synconcealed for older Vim (7.3.429, used on Travis CI).
86+
function! s:is_concealed(line, col)
87+
let concealed = synconcealed(a:line, a:col)
88+
return len(concealed) && concealed[0]
89+
endfunction
7890

91+
function! s:_skip_special_chars(line, col)
92+
return synIDattr(synID(a:line, a:col, 0), 'name')
93+
\ =~? "\\vstring|comment|^pythonbytes%(contents)=$|jedi\\S"
94+
\ || s:is_concealed(a:line, a:col)
95+
endfunction
96+
endif
7997

8098
" Use 'shiftwidth()' instead of '&sw'.
8199
" (Since Vim patch 7.3.629, 'shiftwidth' can be set to 0 to follow 'tabstop').
@@ -99,10 +117,12 @@ function! s:find_opening_paren(lnum, col)
99117
call cursor(a:lnum, a:col)
100118

101119
let nearest = [0, 0]
120+
let timeout = g:python_pep8_indent_searchpair_timeout
121+
let skip_special_chars = 's:_skip_special_chars(line("."), col("."))'
102122
for [p, maxoff] in items(s:paren_pairs)
103123
let stopline = max([0, line('.') - maxoff, nearest[0]])
104124
let next = searchpairpos(
105-
\ '\V'.p[0], '', '\V'.p[1], 'bnW', s:skip_special_chars, stopline)
125+
\ '\V'.p[0], '', '\V'.p[1], 'bnW', skip_special_chars, stopline, timeout)
106126
if next[0] && (next[0] > nearest[0] || (next[0] == nearest[0] && next[1] > nearest[1]))
107127
let nearest = next
108128
endif
@@ -257,24 +277,23 @@ function! s:indent_like_previous_line(lnum)
257277
let base = indent(start)
258278
let current = indent(a:lnum)
259279

260-
" Jump to last character in previous line.
261-
call cursor(lnum, len(text))
262-
let ignore_last_char = eval(s:skip_special_chars)
280+
" Ignore last character in previous line?
281+
let lastcol = len(text)
282+
let col = lastcol
263283

264284
" Search for final colon that is not inside something to be ignored.
265285
while 1
266-
let curpos = getpos('.')[2]
267-
if curpos == 1 | break | endif
268-
if eval(s:skip_special_chars) || text[curpos-1] =~# '\s'
269-
normal! h
286+
if col == 1 | break | endif
287+
if text[col-1] =~# '\s' || s:_skip_special_chars(lnum, col)
288+
let col = col - 1
270289
continue
271-
elseif text[curpos-1] ==# ':'
290+
elseif text[col-1] ==# ':'
272291
return base + s:sw()
273292
endif
274293
break
275294
endwhile
276295

277-
if text =~# '\\$' && !ignore_last_char
296+
if text =~# '\\$' && !s:_skip_special_chars(lnum, lastcol)
278297
" If this line is the continuation of a control statement
279298
" indent further to distinguish the continuation line
280299
" from the next logical line.
@@ -356,11 +375,12 @@ function! GetPythonPEPIndent(lnum)
356375
if match_quotes != -1
357376
" closing multiline string
358377
let quotes = line[match_quotes:(match_quotes+2)]
359-
let pairpos = searchpairpos(quotes, '', quotes, 'b')
378+
call cursor(a:lnum, 1)
379+
let pairpos = searchpairpos(quotes, '', quotes, 'bW', '', 0, g:python_pep8_indent_searchpair_timeout)
360380
if pairpos[0] != 0
361381
return indent(pairpos[0])
362382
else
363-
" TODO: test to cover this!
383+
return -1
364384
endif
365385
endif
366386

spec/indent/indent_spec.rb

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -502,12 +502,27 @@
502502
end
503503

504504
describe "when after a docstring" do
505-
before { vim.feedkeys 'i """' }
506505
it "it does indent the next line to the docstring" do
507-
vim.feedkeys '\<CR>'
506+
vim.feedkeys 'i """\<CR>'
508507
indent.should == 4
509508
proposed_indent.should == 4
510509
end
510+
511+
it "indents the closing docstring quotes" do
512+
vim.feedkeys 'i """\<CR>\<CR>"""'
513+
indent.should == 4
514+
proposed_indent.should == 4
515+
vim.echo('getline(3)').should == ' """'
516+
end
517+
518+
it "indents non-matching docstring quotes" do
519+
vim.feedkeys 'i """\<CR>\<Esc>'
520+
vim.feedkeys "0C'''"
521+
vim.echo('line(".")').should == "4"
522+
vim.echo('getline(".")').should == "'''"
523+
indent.should == 0
524+
proposed_indent.should == -1
525+
end
511526
end
512527

513528
describe "when after a docstring with contents" do
@@ -704,3 +719,11 @@
704719
indent.should == 0
705720
end
706721
end
722+
723+
describe "searchpairpos" do
724+
before { vim.feedkeys '\<ESC>ggdG' }
725+
it "handles nested parenthesis" do
726+
vim.feedkeys 'iif foo.startswith("("):\<CR>'
727+
indent.should == shiftwidth
728+
end
729+
end

0 commit comments

Comments
 (0)