]> 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:

Un-indent for "else" after "for" and "try/except"
[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', or 'finally', line up with 'try'
141     " or 'except'.
142     if thisline =~ '^\s*\(except\|finally\)\>'
143         let bslnum = s:BlockStarter(a:lnum, '^\s*\(try\|except\)\>')
144         if bslnum > 0
145             return indent(bslnum)
146         else
147             return -1
148         endif
149     endif
150
151     " If the line starts with 'else', line it up with 'try', 'except', 'for',
152     " 'if', or 'elif'.
153     if thisline =~ '^\s*else\>'
154         :echom thisline
155         let bslnum = s:BlockStarter(a:lnum, '^\s*\(try\|except\|if\|elif\|for\)\>')
156         if bslnum > 0
157             return indent(bslnum)
158         else
159             return -1
160         endif
161     endif
162
163     " Examine previous line
164     let plnum = a:lnum - 1
165     let pline = getline(plnum)
166     let sslnum = s:StatementStart(plnum)
167
168     " If the previous line is blank, keep the same indentation
169     if pline =~ '^\s*$'
170         return -1
171     endif
172
173     " If this line is explicitly joined, find the first indentation that is a
174     " multiple of four and will distinguish itself from next logical line.
175     if pline =~ '\\$'
176         let maybe_indent = indent(sslnum) + &sw
177         let control_structure = '^\s*\(if\|while\|for\s.*\sin\|except\)\s*'
178         if match(getline(sslnum), control_structure) != -1
179             " add extra indent to avoid E125
180             return maybe_indent + &sw
181         else
182             " control structure not found
183             return maybe_indent
184         endif
185     endif
186
187     " If the previous line ended with a colon and is not a comment, indent
188     " relative to statement start.
189     let pline = substitute(pline, '\\\\', '', 'g')
190     if v:version > 703 || (v:version == 703 && has('patch1037'))
191         let pline = substitute(pline, '".\{-}\\\@1<!"\|''.\{-}\\\@1<!''', '', 'g')
192     else
193         let pline = substitute(pline, '".\{-}\\\@<!"\|''.\{-}\\\@<!''', '', 'g')
194     endif
195     if pline =~ '^[^#]*:\s*\(#.*\)\?$'
196         return indent(sslnum) + &sw
197     endif
198
199     " If the previous line was a stop-execution statement or a pass
200     if getline(sslnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>'
201         " See if the user has already dedented
202         if indent(a:lnum) > indent(sslnum) - &sw
203             " If not, recommend one dedent
204             return indent(sslnum) - &sw
205         endif
206         " Otherwise, trust the user
207         return -1
208     endif
209
210     " If this line is dedented and the number of indent spaces is valid
211     " (multiple of the indentation size), trust the user
212     let dedent_size = thisindent - indent(plnum)
213     if dedent_size < 0 && thisindent % &sw == 0
214         return -1
215     endif
216
217     " In all other cases, line up with the start of the previous statement.
218     return indent(sslnum)
219 endfunction