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

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