comparison .vim/ftplugin/python_fn.vim @ 0:c30d68fbd368

Initial import from svn.
author Augie Fackler <durin42@gmail.com>
date Wed, 26 Nov 2008 10:56:09 -0600
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:c30d68fbd368
1 " -*- vim -*-
2 " FILE: python.vim
3 " LAST MODIFICATION: 2008-05-17 6:29pm
4 " (C) Copyright 2001-2005 Mikael Berthe <bmikael@lists.lilotux.net>
5 " Maintained by Jon Franklin <jvfranklin@gmail.com>
6 " Version: 1.12
7
8 " USAGE:
9 "
10 " Save this file to $VIMFILES/ftplugin/python.vim. You can have multiple
11 " python ftplugins by creating $VIMFILES/ftplugin/python and saving your
12 " ftplugins in that directory. If saving this to the global ftplugin
13 " directory, this is the recommended method, since vim ships with an
14 " ftplugin/python.vim file already.
15 " You can set the global variable "g:py_select_leading_comments" to 0
16 " if you don't want to select comments preceding a declaration (these
17 " are usually the description of the function/class).
18 " You can set the global variable "g:py_select_trailing_comments" to 0
19 " if you don't want to select comments at the end of a function/class.
20 " If these variables are not defined, both leading and trailing comments
21 " are selected.
22 " Example: (in your .vimrc) "let g:py_select_leading_comments = 0"
23 " You may want to take a look at the 'shiftwidth' option for the
24 " shift commands...
25 "
26 " REQUIREMENTS:
27 " vim (>= 7)
28 "
29 " Shortcuts:
30 " ]t -- Jump to beginning of block
31 " ]e -- Jump to end of block
32 " ]v -- Select (Visual Line Mode) block
33 " ]< -- Shift block to left
34 " ]> -- Shift block to right
35 " ]# -- Comment selection
36 " ]u -- Uncomment selection
37 " ]c -- Select current/previous class
38 " ]d -- Select current/previous function
39 " ]<up> -- Jump to previous line with the same/lower indentation
40 " ]<down> -- Jump to next line with the same/lower indentation
41
42 " Only do this when not done yet for this buffer
43 if exists("b:loaded_py_ftplugin")
44 finish
45 endif
46 let b:loaded_py_ftplugin = 1
47
48 map ]t :PBoB<CR>
49 vmap ]t :<C-U>PBOB<CR>m'gv``
50 map ]e :PEoB<CR>
51 vmap ]e :<C-U>PEoB<CR>m'gv``
52
53 map ]v ]tV]e
54 map ]< ]tV]e<
55 vmap ]< <
56 map ]> ]tV]e>
57 vmap ]> >
58
59 map ]# :call PythonCommentSelection()<CR>
60 vmap ]# :call PythonCommentSelection()<CR>
61 map ]u :call PythonUncommentSelection()<CR>
62 vmap ]u :call PythonUncommentSelection()<CR>
63
64 map ]c :call PythonSelectObject("class")<CR>
65 map ]d :call PythonSelectObject("function")<CR>
66
67 map ]<up> :call PythonNextLine(-1)<CR>
68 map ]<down> :call PythonNextLine(1)<CR>
69 " You may prefer use <s-up> and <s-down>... :-)
70
71 " jump to previous class
72 map ]J :call PythonDec("class", -1)<CR>
73 vmap ]J :call PythonDec("class", -1)<CR>
74
75 " jump to next class
76 map ]j :call PythonDec("class", 1)<CR>
77 vmap ]j :call PythonDec("class", 1)<CR>
78
79 " jump to previous function
80 map ]F :call PythonDec("function", -1)<CR>
81 vmap ]F :call PythonDec("function", -1)<CR>
82
83 " jump to next function
84 map ]f :call PythonDec("function", 1)<CR>
85 vmap ]f :call PythonDec("function", 1)<CR>
86
87
88
89 " Menu entries
90 nmenu <silent> &Python.Update\ IM-Python\ Menu
91 \:call UpdateMenu()<CR>
92 nmenu &Python.-Sep1- :
93 nmenu <silent> &Python.Beginning\ of\ Block<Tab>[t
94 \]t
95 nmenu <silent> &Python.End\ of\ Block<Tab>]e
96 \]e
97 nmenu &Python.-Sep2- :
98 nmenu <silent> &Python.Shift\ Block\ Left<Tab>]<
99 \]<
100 vmenu <silent> &Python.Shift\ Block\ Left<Tab>]<
101 \]<
102 nmenu <silent> &Python.Shift\ Block\ Right<Tab>]>
103 \]>
104 vmenu <silent> &Python.Shift\ Block\ Right<Tab>]>
105 \]>
106 nmenu &Python.-Sep3- :
107 vmenu <silent> &Python.Comment\ Selection<Tab>]#
108 \]#
109 nmenu <silent> &Python.Comment\ Selection<Tab>]#
110 \]#
111 vmenu <silent> &Python.Uncomment\ Selection<Tab>]u
112 \]u
113 nmenu <silent> &Python.Uncomment\ Selection<Tab>]u
114 \]u
115 nmenu &Python.-Sep4- :
116 nmenu <silent> &Python.Previous\ Class<Tab>]J
117 \]J
118 nmenu <silent> &Python.Next\ Class<Tab>]j
119 \]j
120 nmenu <silent> &Python.Previous\ Function<Tab>]F
121 \]F
122 nmenu <silent> &Python.Next\ Function<Tab>]f
123 \]f
124 nmenu &Python.-Sep5- :
125 nmenu <silent> &Python.Select\ Block<Tab>]v
126 \]v
127 nmenu <silent> &Python.Select\ Function<Tab>]d
128 \]d
129 nmenu <silent> &Python.Select\ Class<Tab>]c
130 \]c
131 nmenu &Python.-Sep6- :
132 nmenu <silent> &Python.Previous\ Line\ wrt\ indent<Tab>]<up>
133 \]<up>
134 nmenu <silent> &Python.Next\ Line\ wrt\ indent<Tab>]<down>
135 \]<down>
136
137 :com! PBoB execute "normal ".PythonBoB(line('.'), -1, 1)."G"
138 :com! PEoB execute "normal ".PythonBoB(line('.'), 1, 1)."G"
139 :com! UpdateMenu call UpdateMenu()
140
141
142 " Go to a block boundary (-1: previous, 1: next)
143 " If force_sel_comments is true, 'g:py_select_trailing_comments' is ignored
144 function! PythonBoB(line, direction, force_sel_comments)
145 let ln = a:line
146 let ind = indent(ln)
147 let mark = ln
148 let indent_valid = strlen(getline(ln))
149 let ln = ln + a:direction
150 if (a:direction == 1) && (!a:force_sel_comments) &&
151 \ exists("g:py_select_trailing_comments") &&
152 \ (!g:py_select_trailing_comments)
153 let sel_comments = 0
154 else
155 let sel_comments = 1
156 endif
157
158 while((ln >= 1) && (ln <= line('$')))
159 if (sel_comments) || (match(getline(ln), "^\\s*#") == -1)
160 if (!indent_valid)
161 let indent_valid = strlen(getline(ln))
162 let ind = indent(ln)
163 let mark = ln
164 else
165 if (strlen(getline(ln)))
166 if (indent(ln) < ind)
167 break
168 endif
169 let mark = ln
170 endif
171 endif
172 endif
173 let ln = ln + a:direction
174 endwhile
175
176 return mark
177 endfunction
178
179
180 " Go to previous (-1) or next (1) class/function definition
181 function! PythonDec(obj, direction)
182 if (a:obj == "class")
183 let objregexp = "^\\s*class\\s\\+[a-zA-Z0-9_]\\+"
184 \ . "\\s*\\((\\([a-zA-Z0-9_,. \\t\\n]\\)*)\\)\\=\\s*:"
185 else
186 let objregexp = "^\\s*def\\s\\+[a-zA-Z0-9_]\\+\\s*(\\_[^:#]*)\\s*:"
187 endif
188 let flag = "W"
189 if (a:direction == -1)
190 let flag = flag."b"
191 endif
192 let res = search(objregexp, flag)
193 endfunction
194
195
196 " Comment out selected lines
197 " commentString is inserted in non-empty lines, and should be aligned with
198 " the block
199 function! PythonCommentSelection() range
200 let commentString = "#"
201 let cl = a:firstline
202 let ind = 1000 " I hope nobody use so long lines! :)
203
204 " Look for smallest indent
205 while (cl <= a:lastline)
206 if strlen(getline(cl))
207 let cind = indent(cl)
208 let ind = ((ind < cind) ? ind : cind)
209 endif
210 let cl = cl + 1
211 endwhile
212 if (ind == 1000)
213 let ind = 1
214 else
215 let ind = ind + 1
216 endif
217
218 let cl = a:firstline
219 execute ":".cl
220 " Insert commentString in each non-empty line, in column ind
221 while (cl <= a:lastline)
222 if strlen(getline(cl))
223 execute "normal ".ind."|i".commentString
224 endif
225 execute "normal \<Down>"
226 let cl = cl + 1
227 endwhile
228 endfunction
229
230 " Uncomment selected lines
231 function! PythonUncommentSelection() range
232 " commentString could be different than the one from CommentSelection()
233 " For example, this could be "# \\="
234 let commentString = "#"
235 let cl = a:firstline
236 while (cl <= a:lastline)
237 let ul = substitute(getline(cl),
238 \"\\(\\s*\\)".commentString."\\(.*\\)$", "\\1\\2", "")
239 call setline(cl, ul)
240 let cl = cl + 1
241 endwhile
242 endfunction
243
244
245 " Select an object ("class"/"function")
246 function! PythonSelectObject(obj)
247 " Go to the object declaration
248 normal $
249 call PythonDec(a:obj, -1)
250 let beg = line('.')
251
252 if !exists("g:py_select_leading_comments") || (g:py_select_leading_comments)
253 let decind = indent(beg)
254 let cl = beg
255 while (cl>1)
256 let cl = cl - 1
257 if (indent(cl) == decind) && (getline(cl)[decind] == "#")
258 let beg = cl
259 else
260 break
261 endif
262 endwhile
263 endif
264
265 if (a:obj == "class")
266 let eod = "\\(^\\s*class\\s\\+[a-zA-Z0-9_]\\+\\s*"
267 \ . "\\((\\([a-zA-Z0-9_,. \\t\\n]\\)*)\\)\\=\\s*\\)\\@<=:"
268 else
269 let eod = "\\(^\\s*def\\s\\+[a-zA-Z0-9_]\\+\\s*(\\_[^:#]*)\\s*\\)\\@<=:"
270 endif
271 " Look for the end of the declaration (not always the same line!)
272 call search(eod, "")
273
274 " Is it a one-line definition?
275 if match(getline('.'), "^\\s*\\(#.*\\)\\=$", col('.')) == -1
276 let cl = line('.')
277 execute ":".beg
278 execute "normal V".cl."G"
279 else
280 " Select the whole block
281 execute "normal \<Down>"
282 let cl = line('.')
283 execute ":".beg
284 execute "normal V".PythonBoB(cl, 1, 0)."G"
285 endif
286 endfunction
287
288
289 " Jump to the next line with the same (or lower) indentation
290 " Useful for moving between "if" and "else", for example.
291 function! PythonNextLine(direction)
292 let ln = line('.')
293 let ind = indent(ln)
294 let indent_valid = strlen(getline(ln))
295 let ln = ln + a:direction
296
297 while((ln >= 1) && (ln <= line('$')))
298 if (!indent_valid) && strlen(getline(ln))
299 break
300 else
301 if (strlen(getline(ln)))
302 if (indent(ln) <= ind)
303 break
304 endif
305 endif
306 endif
307 let ln = ln + a:direction
308 endwhile
309
310 execute "normal ".ln."G"
311 endfunction
312
313 function! UpdateMenu()
314 " delete menu if it already exists, then rebuild it.
315 " this is necessary in case you've got multiple buffers open
316 " a future enhancement to this would be to make the menu aware of
317 " all buffers currently open, and group classes and functions by buffer
318 if exists("g:menuran")
319 aunmenu IM-Python
320 endif
321 let restore_fe = &foldenable
322 set nofoldenable
323 " preserve disposition of window and cursor
324 let cline=line('.')
325 let ccol=col('.') - 1
326 norm H
327 let hline=line('.')
328 " create the menu
329 call MenuBuilder()
330 " restore disposition of window and cursor
331 exe "norm ".hline."Gzt"
332 let dnscroll=cline-hline
333 exe "norm ".dnscroll."j".ccol."l"
334 let &foldenable = restore_fe
335 endfunction
336
337 function! MenuBuilder()
338 norm gg0
339 let currentclass = -1
340 let classlist = []
341 let parentclass = ""
342 while line(".") < line("$")
343 " search for a class or function
344 if match ( getline("."), '^\s*class\s\+[_a-zA-Z].*:\|^\s*def\s\+[_a-zA-Z].*:' ) != -1
345 norm ^
346 let linenum = line('.')
347 let indentcol = col('.')
348 norm "nye
349 let classordef=@n
350 norm w"nywge
351 let objname=@n
352 let parentclass = FindParentClass(classlist, indentcol)
353 if classordef == "class"
354 call AddClass(objname, linenum, parentclass)
355 else " this is a function
356 call AddFunction(objname, linenum, parentclass)
357 endif
358 " We actually created a menu, so lets set the global variable
359 let g:menuran=1
360 call RebuildClassList(classlist, [objname, indentcol], classordef)
361 endif " line matched
362 norm j
363 endwhile
364 endfunction
365
366 " classlist contains the list of nested classes we are in.
367 " in most cases it will be empty or contain a single class
368 " but where a class is nested within another, it will contain 2 or more
369 " this function adds or removes classes from the list based on indentation
370 function! RebuildClassList(classlist, newclass, classordef)
371 let i = len(a:classlist) - 1
372 while i > -1
373 if a:newclass[1] <= a:classlist[i][1]
374 call remove(a:classlist, i)
375 endif
376 let i = i - 1
377 endwhile
378 if a:classordef == "class"
379 call add(a:classlist, a:newclass)
380 endif
381 endfunction
382
383 " we found a class or function, determine its parent class based on
384 " indentation and what's contained in classlist
385 function! FindParentClass(classlist, indentcol)
386 let i = 0
387 let parentclass = ""
388 while i < len(a:classlist)
389 if a:indentcol <= a:classlist[i][1]
390 break
391 else
392 if len(parentclass) == 0
393 let parentclass = a:classlist[i][0]
394 else
395 let parentclass = parentclass.'\.'.a:classlist[i][0]
396 endif
397 endif
398 let i = i + 1
399 endwhile
400 return parentclass
401 endfunction
402
403 " add a class to the menu
404 function! AddClass(classname, lineno, parentclass)
405 if len(a:parentclass) > 0
406 let classstring = a:parentclass.'\.'.a:classname
407 else
408 let classstring = a:classname
409 endif
410 exe 'menu IM-Python.classes.'.classstring.' :call <SID>JumpToAndUnfold('.a:lineno.')<CR>'
411 endfunction
412
413 " add a function to the menu, grouped by member class
414 function! AddFunction(functionname, lineno, parentclass)
415 if len(a:parentclass) > 0
416 let funcstring = a:parentclass.'.'.a:functionname
417 else
418 let funcstring = a:functionname
419 endif
420 exe 'menu IM-Python.functions.'.funcstring.' :call <SID>JumpToAndUnfold('.a:lineno.')<CR>'
421 endfunction
422
423
424 function! s:JumpToAndUnfold(line)
425 " Go to the right line
426 execute 'normal '.a:line.'gg'
427 " Check to see if we are in a fold
428 let lvl = foldlevel(a:line)
429 if lvl != 0
430 " and if so, then expand the fold out, other wise, ignore this part.
431 execute 'normal 15zo'
432 endif
433 endfunction
434
435 "" This one will work only on vim 6.2 because of the try/catch expressions.
436 " function! s:JumpToAndUnfoldWithExceptions(line)
437 " try
438 " execute 'normal '.a:line.'gg15zo'
439 " catch /^Vim\((\a\+)\)\=:E490:/
440 " " Do nothing, just consume the error
441 " endtry
442 "endfunction
443
444
445 " vim:set et sts=2 sw=2:
446