Mercurial > dotfiles
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 |