Skip to content

Commit 7cb9a56

Browse files
committed
Improve handling of lookup in previous lines
- Handle jedi-vim call signatures when looking for colon and function def in previous line. This adds `jedi\S` to s:skip_special_chars and improves the algorithm to find the colon in the previous line in `s:indent_like_previous_line`. - Add `s:match_expr_on_line` and use it for `nothing_after_opening_paren`. - Handle comments after opening paren (#32). NOTE: This handles older Vim versions, where `synconcealed` behaves different (returns an empty list for non-concealed) and `getcurpos` is not available (Vim 7.3.429 (used on Travis)). Fixes: #32
1 parent 668edf8 commit 7cb9a56

File tree

2 files changed

+90
-9
lines changed

2 files changed

+90
-9
lines changed

indent/python.vim

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,25 @@ let s:paren_pairs = ['()', '{}', '[]']
4343
let s:control_statement = '^\s*\(class\|def\|if\|while\|with\|for\|except\)\>'
4444
let s:stop_statement = '^\s*\(break\|continue\|raise\|return\|pass\)\>'
4545

46-
" Skip strings and comments
46+
" Skip strings and comments. Return 1 for chars to skip.
47+
" jedi* refers to syntax definitions from jedi-vim for call signatures, which
48+
" are inserted temporarily into the buffer.
4749
let s:skip_special_chars = 'synIDattr(synID(line("."), col("."), 0), "name") ' .
48-
\ '=~? "string\\|comment"'
50+
\ '=~? "\\vstring|comment|jedi\\S"'
51+
52+
let s:skip_after_opening_paren = 'synIDattr(synID(line("."), col("."), 0), "name") ' .
53+
\ '=~? "\\vcomment|jedi\\S"'
54+
55+
" Also ignore anything concealed.
56+
" Wrapper around synconcealed for older Vim (7.3.429, used on Travis CI).
57+
function! s:is_concealed(line, col)
58+
let concealed = synconcealed(a:line, a:col)
59+
return len(concealed) && concealed[0]
60+
endfunction
61+
if has('conceal')
62+
let s:skip_special_chars .= '|| s:is_concealed(line("."), col("."))'
63+
endif
64+
4965

5066
let s:skip_search = 'synIDattr(synID(line("."), col("."), 0), "name") ' .
5167
\ '=~? "comment"'
@@ -134,6 +150,27 @@ function! s:find_start_of_block(lnum, types)
134150
return 0
135151
endfunction
136152

153+
" Is "expr" true for every position in "lnum", beginning at "start"?
154+
" (optionally up to a:1 / 4th argument)
155+
function! s:match_expr_on_line(expr, lnum, start, ...)
156+
let text = getline(a:lnum)
157+
let end = a:0 ? a:1 : len(text)
158+
if a:start > end
159+
return 1
160+
endif
161+
let save_pos = getpos('.')
162+
let r = 1
163+
for i in range(a:start, end)
164+
call cursor(a:lnum, i)
165+
if !(eval(a:expr) || text[i-1] =~ '\s')
166+
let r = 0
167+
break
168+
endif
169+
endfor
170+
call setpos('.', save_pos)
171+
return r
172+
endfunction
173+
137174
" Line up with open parenthesis/bracket/brace.
138175
function! s:indent_like_opening_paren(lnum)
139176
let [paren_lnum, paren_col] = s:find_opening_paren(a:lnum)
@@ -143,7 +180,8 @@ function! s:indent_like_opening_paren(lnum)
143180
let text = getline(paren_lnum)
144181
let base = indent(paren_lnum)
145182

146-
let nothing_after_opening_paren = text =~ '\%'.(paren_col + 1).'c\s*$'
183+
let nothing_after_opening_paren = s:match_expr_on_line(
184+
\ s:skip_after_opening_paren, paren_lnum, paren_col+1)
147185
let starts_with_closing_paren = getline(a:lnum) =~ '^\s*[])}]'
148186

149187
if nothing_after_opening_paren
@@ -204,13 +242,18 @@ function! s:indent_like_previous_line(lnum)
204242
call cursor(lnum, len(text))
205243
let ignore_last_char = eval(s:skip_special_chars)
206244

207-
" Search for final colon that is not inside a string or comment.
208-
while search(':\s*\%(#.*\)\?$', 'bcW', lnum)
209-
if eval(s:skip_special_chars)
245+
" Search for final colon that is not inside something to be ignored.
246+
while search(':', 'bcW', lnum)
247+
let curpos = getpos(".")[2]
248+
if curpos == 1 | break | endif
249+
if eval(s:skip_special_chars)
250+
normal! h
251+
continue
252+
endif
253+
if !s:match_expr_on_line(s:skip_special_chars, lnum, curpos)
254+
return base + s:sw()
255+
endif
210256
normal! h
211-
else
212-
return base + s:sw()
213-
endif
214257
endwhile
215258

216259
if text =~ '\\$' && !ignore_last_char

spec/indent/indent_spec.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@
7676
end
7777
end
7878

79+
describe "when after an '{' that is followed by a comment" do
80+
before { vim.feedkeys 'imydict = { # comment\<CR>' }
81+
82+
it "indent by one level" do
83+
indent.should == shiftwidth
84+
vim.feedkeys '1: 1,\<CR>'
85+
indent.should == shiftwidth
86+
end
87+
88+
it "lines up the closing parenthesis" do
89+
vim.feedkeys '}'
90+
indent.should == 0
91+
end
92+
end
93+
7994
describe "when using gq to reindent a '(' that is" do
8095
before { vim.feedkeys 'itest(' }
8196
it "something and has a string without spaces at the end" do
@@ -129,6 +144,15 @@
129144
end
130145
end
131146

147+
describe "when the previous line has a colon in a string" do
148+
before { vim.feedkeys 'itest(":".join(["1","2"]))\<CR>' }
149+
it "does not indent" do
150+
vim.feedkeys 'if True:'
151+
indent.should == 0
152+
proposed_indent.should == 0
153+
end
154+
end
155+
132156
describe "when after an '(' that is followed by an unfinished string" do
133157
before { vim.feedkeys 'itest("""' }
134158

@@ -374,6 +398,20 @@
374398
end
375399
end
376400

401+
describe "when jedi-vim call signatures are used" do
402+
before { vim.command 'syn match jediFunction "JEDI_CALL_SIGNATURE" keepend extend' }
403+
404+
it "ignores the call signature after a colon" do
405+
vim.feedkeys 'iif True: JEDI_CALL_SIGNATURE\<CR>'
406+
indent.should == shiftwidth
407+
end
408+
409+
it "ignores the call signature after a function" do
410+
vim.feedkeys 'idef f( JEDI_CALL_SIGNATURE\<CR>'
411+
indent.should == shiftwidth * 2
412+
end
413+
end
414+
377415
def shiftwidth
378416
@shiftwidth ||= vim.echo("exists('*shiftwidth') ? shiftwidth() : &sw").to_i
379417
end

0 commit comments

Comments
 (0)