]> 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 double "of" in README.rst
[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, try to find an indentation that looks
164     " good.
165     if pline =~ '\\$'
166         let compound_statement = '^\s*\(if\|while\|for\s.*\sin\|except\)\s*'
167         let maybe_indent = matchend(getline(sslnum), compound_statement)
168         if maybe_indent != -1
169             return maybe_indent
170         else
171             return indent(sslnum) + &sw * 2
172         endif
173     endif
174
175     " If the previous line ended with a colon and is not a comment, indent
176     " relative to statement start.
177     if pline =~ '^[^#]*:\s*\(#.*\)\?$'
178         return indent(sslnum) + &sw
179     endif
180
181     " If the previous line was a stop-execution statement or a pass
182     if getline(sslnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>'
183         " See if the user has already dedented
184         if indent(a:lnum) > indent(sslnum) - &sw
185             " If not, recommend one dedent
186             return indent(sslnum) - &sw
187         endif
188         " Otherwise, trust the user
189         return -1
190     endif
191
192     " In all other cases, line up with the start of the previous statement.
193     return indent(sslnum)
194 endfunction