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

Indent correctly after lines with '#'s in strings
[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
105     " First line has indent 0
106     if a:lnum == 1
107         return 0
108     endif
109
110     " If we can find an open parenthesis/bracket/brace, line up with it.
111     call cursor(a:lnum, 1)
112     let parlnum = s:SearchParensPair()
113     if parlnum > 0
114         let parcol = col('.')
115         let closing_paren = match(getline(a:lnum), '^\s*[])}]') != -1
116         if match(getline(parlnum), '[([{]\s*$', parcol - 1) != -1
117             if closing_paren
118                 return indent(parlnum)
119             else
120                 return indent(parlnum) + &shiftwidth
121             endif
122         else
123             return parcol
124         endif
125     endif
126
127     " Examine this line
128     let thisline = getline(a:lnum)
129     let thisindent = indent(a:lnum)
130
131     " If the line starts with 'elif' or 'else', line up with 'if' or 'elif'
132     if thisline =~ '^\s*\(elif\|else\)\>'
133         let bslnum = s:BlockStarter(a:lnum, '^\s*\(if\|elif\)\>')
134         if bslnum > 0
135             return indent(bslnum)
136         else
137             return -1
138         endif
139     endif
140
141     " If the line starts with 'except' or 'finally', line up with 'try'
142     " or 'except'
143     if thisline =~ '^\s*\(except\|finally\)\>'
144         let bslnum = s:BlockStarter(a:lnum, '^\s*\(try\|except\)\>')
145         if bslnum > 0
146             return indent(bslnum)
147         else
148             return -1
149         endif
150     endif
151
152     " Examine previous line
153     let plnum = a:lnum - 1
154     let pline = getline(plnum)
155     let sslnum = s:StatementStart(plnum)
156
157     " If the previous line is blank, keep the same indentation
158     if pline =~ '^\s*$'
159         return -1
160     endif
161
162     " If this line is explicitly joined, find the first indentation that is a
163     " multiple of four and will distinguish itself from next logical line.
164     if pline =~ '\\$'
165         let maybe_indent = indent(sslnum) + &sw
166         let control_structure = '^\s*\(if\|while\|for\s.*\sin\|except\)\s*'
167         if match(getline(sslnum), control_structure) != -1
168             " add extra indent to avoid E125
169             return maybe_indent + &sw
170         else
171             " control structure not found
172             return maybe_indent
173         endif
174     endif
175
176     " If the previous line ended with a colon and is not a comment, indent
177     " relative to statement start.
178     let pline = substitute(pline, '\\\\', '', 'g')
179     if v:version > 703 || (v:version == 703 && has('patch1037'))
180         let pline = substitute(pline, '".\{-}\\\@1<!"\|''.\{-}\\\@1<!''', '', 'g')
181     else
182         let pline = substitute(pline, '".\{-}\\\@<!"\|''.\{-}\\\@<!''', '', 'g')
183     endif
184     if pline =~ '^[^#]*:\s*\(#.*\)\?$'
185         return indent(sslnum) + &sw
186     endif
187
188     " If the previous line was a stop-execution statement or a pass
189     if getline(sslnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>'
190         " See if the user has already dedented
191         if indent(a:lnum) > indent(sslnum) - &sw
192             " If not, recommend one dedent
193             return indent(sslnum) - &sw
194         endif
195         " Otherwise, trust the user
196         return -1
197     endif
198
199     " If this line is dedented and the number of indent spaces is valid
200     " (multiple of the indentation size), trust the user
201     let dedent_size = thisindent - indent(plnum)
202     if dedent_size < 0 && thisindent % &sw == 0
203         return -1
204     endif
205
206     " In all other cases, line up with the start of the previous statement.
207     return indent(sslnum)
208 endfunction