]> git.madduck.net Git - etc/vim.git/blob - indent/python.vim

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Unindent 'finally' after 'else' too
[etc/vim.git] / indent / python.vim
1 " PEP8 compatible Python indent file
2 " Language:         Python
3 " Maintainer:       Hynek Schlawack <hs@ox.cx>
4 " Prev Maintainer:  Eric Mc Sween <em@tomcom.de> (address invalid)
5 " Original Author:  David Bustos <bustos@caltech.edu> (address invalid)
6 " License:          Public Domain
7
8 " Only load this indent file when no other was loaded.
9 if exists("b:did_indent")
10     finish
11 endif
12 let b:did_indent = 1
13
14 setlocal expandtab
15 setlocal nolisp
16 setlocal autoindent
17 setlocal indentexpr=GetPythonPEPIndent(v:lnum)
18 setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif,=except
19
20 let s:maxoff = 50
21
22 " Find backwards the closest open parenthesis/bracket/brace.
23 function! s:SearchParensPair()
24     let line = line('.')
25     let col = col('.')
26
27     " Skip strings and comments and don't look too far
28     let skip = "line('.') < " . (line - s:maxoff) . " ? dummy :" .
29                 \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? ' .
30                 \ '"string\\|comment"'
31
32     " Search for parentheses
33     call cursor(line, col)
34     let parlnum = searchpair('(', '', ')', 'bW', skip)
35     let parcol = col('.')
36
37     " Search for brackets
38     call cursor(line, col)
39     let par2lnum = searchpair('\[', '', '\]', 'bW', skip)
40     let par2col = col('.')
41
42     " Search for braces
43     call cursor(line, col)
44     let par3lnum = searchpair('{', '', '}', 'bW', skip)
45     let par3col = col('.')
46
47     " Get the closest match
48     if par2lnum > parlnum || (par2lnum == parlnum && par2col > parcol)
49         let parlnum = par2lnum
50         let parcol = par2col
51     endif
52     if par3lnum > parlnum || (par3lnum == parlnum && par3col > parcol)
53         let parlnum = par3lnum
54         let parcol = par3col
55     endif
56
57     " Put the cursor on the match
58     if parlnum > 0
59         call cursor(parlnum, parcol)
60     endif
61     return parlnum
62 endfunction
63
64 " Find the start of a multi-line statement
65 function! s:StatementStart(lnum)
66     let lnum = a:lnum
67     while 1
68         if getline(lnum - 1) =~ '\\$'
69             let lnum = lnum - 1
70         else
71             call cursor(lnum, 1)
72             let maybe_lnum = s:SearchParensPair()
73             if maybe_lnum < 1
74                 return lnum
75             else
76                 let lnum = maybe_lnum
77             endif
78         endif
79     endwhile
80 endfunction
81
82 " Find the block starter that matches the current line
83 function! s:BlockStarter(lnum, block_start_re)
84     let lnum = a:lnum
85     let maxindent = 10000       " whatever
86     while lnum > 1
87         let lnum = prevnonblank(lnum - 1)
88         if indent(lnum) < maxindent
89             if getline(lnum) =~ a:block_start_re
90                 return lnum
91             else
92                 let maxindent = indent(lnum)
93                 " It's not worth going further if we reached the top level
94                 if maxindent == 0
95                     return -1
96                 endif
97             endif
98         endif
99     endwhile
100     return -1
101 endfunction
102
103 function! GetPythonPEPIndent(lnum)
104     " First line has indent 0
105     if a:lnum == 1
106         return 0
107     endif
108
109     " If we can find an open parenthesis/bracket/brace, line up with it.
110     call cursor(a:lnum, 1)
111     let parlnum = s:SearchParensPair()
112     if parlnum > 0
113         let parcol = col('.')
114         let closing_paren = match(getline(a:lnum), '^\s*[])}]') != -1
115         if match(getline(parlnum), '[([{]\s*$', parcol - 1) != -1
116             if closing_paren
117                 return indent(parlnum)
118             else
119                 return indent(parlnum) + &shiftwidth
120             endif
121         else
122             return parcol
123         endif
124     endif
125
126     " Examine this line
127     let thisline = getline(a:lnum)
128     let thisindent = indent(a:lnum)
129
130     " If the line starts with 'elif', line up with 'if' or 'elif'
131     if thisline =~ '^\s*elif\>'
132         let bslnum = s:BlockStarter(a:lnum, '^\s*\(if\|elif\)\>')
133         if bslnum > 0
134             return indent(bslnum)
135         else
136             return -1
137         endif
138     endif
139
140     " If the line starts with 'except' line up with 'try' or 'except'.
141     if thisline =~ '^\s*except\>'
142         let bslnum = s:BlockStarter(a:lnum, '^\s*\(try\|except\)\>')
143         if bslnum > 0
144             return indent(bslnum)
145         else
146             return -1
147         endif
148     endif
149
150     " If the line starts with 'finally', line up with 'try', 'else', or
151     " 'except'.
152     if thisline =~ '^\s*finally\>'
153         let bslnum = s:BlockStarter(a:lnum, '^\s*\(try\|except\|else\)\>')
154         if bslnum > 0
155             return indent(bslnum)
156         else
157             return -1
158         endif
159     endif
160
161     " If the line starts with 'else', line it up with 'try', 'except', 'for',
162     " 'if', or 'elif'.
163     if thisline =~ '^\s*else\>'
164         :echom thisline
165         let bslnum = s:BlockStarter(a:lnum, '^\s*\(try\|except\|if\|elif\|for\)\>')
166         if bslnum > 0
167             return indent(bslnum)
168         else
169             return -1
170         endif
171     endif
172
173     " Examine previous line
174     let plnum = a:lnum - 1
175     let pline = getline(plnum)
176     let sslnum = s:StatementStart(plnum)
177
178     " If the previous line is blank, keep the same indentation
179     if pline =~ '^\s*$'
180         return -1
181     endif
182
183     " If this line is explicitly joined, find the first indentation that is a
184     " multiple of four and will distinguish itself from next logical line.
185     if pline =~ '\\$'
186         let maybe_indent = indent(sslnum) + &sw
187         let control_structure = '^\s*\(if\|while\|for\s.*\sin\|except\)\s*'
188         if match(getline(sslnum), control_structure) != -1
189             " add extra indent to avoid E125
190             return maybe_indent + &sw
191         else
192             " control structure not found
193             return maybe_indent
194         endif
195     endif
196
197     " If the previous line ended with a colon and is not a comment, indent
198     " relative to statement start.
199     let pline = substitute(pline, '\\\\', '', 'g')
200     if v:version > 703 || (v:version == 703 && has('patch1037'))
201         let pline = substitute(pline, '".\{-}\\\@1<!"\|''.\{-}\\\@1<!''', '', 'g')
202     else
203         let pline = substitute(pline, '".\{-}\\\@<!"\|''.\{-}\\\@<!''', '', 'g')
204     endif
205     if pline =~ '^[^#]*:\s*\(#.*\)\?$'
206         return indent(sslnum) + &sw
207     endif
208
209     " If the previous line was a stop-execution statement or a pass
210     if getline(sslnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>'
211         " See if the user has already dedented
212         if indent(a:lnum) > indent(sslnum) - &sw
213             " If not, recommend one dedent
214             return indent(sslnum) - &sw
215         endif
216         " Otherwise, trust the user
217         return -1
218     endif
219
220     " If this line is dedented and the number of indent spaces is valid
221     " (multiple of the indentation size), trust the user
222     let dedent_size = thisindent - indent(plnum)
223     if dedent_size < 0 && thisindent % &sw == 0
224         return -1
225     endif
226
227     " In all other cases, line up with the start of the previous statement.
228     return indent(sslnum)
229 endfunction