diff --git a/README b/README old mode 100644 new mode 100755 index 50fe96d..c279188 --- a/README +++ b/README @@ -1,15 +1,7 @@ -This is a mirror of http://www.vim.org/scripts/script.php?script_id=30 +Modified edition of http://www.vim.org/scripts/script.php?script_id=30 Written by Mikael Berthe. +Modified by Bruce Yang, Menus are removed and bindings changed. -This script can be useful when editing Python scripts. It provides the following menus: -- Select a block of lines with the same indentation -- Select a function -- Select a class -- Go to previous/next class/function -- Go to the beginning/end of a block -- Comment/uncomment the selection -- Jump to the last/next line with the same indent -- Shift a block (left/right) -- Creates list of classes and functions for easy code navigation +This script can be useful when editing Python scripts. Version 1.8+ requires Vim 7, earlier versions require Vim 6 diff --git a/ftplugin/python.vim b/ftplugin/python.vim new file mode 100755 index 0000000..fd1c321 --- /dev/null +++ b/ftplugin/python.vim @@ -0,0 +1,169 @@ +" -*- vim -*- +" FILE: python_fn.vim +" LAST MODIFICATION: 2014-11-15 09:11 +" (C) Copyright 2001-2005 Mikael Berthe +" Maintained by Jon Franklin +" Modified by Bruce Yang +" Version: 1.14 + +" This script has been modified by Bruce for key binding compatibility +" to unimpaired vim plugin. Menus are removed for clearly. + +" USAGE: +" +" Save this file to $VIMFILES/ftplugin/python.vim. You can have multiple +" python ftplugins by creating $VIMFILES/ftplugin/python and saving your +" ftplugins in that directory. If saving this to the global ftplugin +" directory, this is the recommended method, since vim ships with an +" ftplugin/python.vim file already. +" +" REQUIREMENTS: +" vim (>= 7) +" +" Shortcuts: +" [k -- Jump to beginning of block +" ]k -- Jump to end of block +" vik -- Select (Visual Line Mode) block +" [c -- Jump to previous class +" ]c -- Jump to next class +" [f -- Jump to previous function +" ]f -- Jump to next function +" vic -- Select current/previous class +" vif -- Select current/previous function + +" Only do this when not done yet for this buffer +if exists("b:loaded_py_ftplugin") + finish +endif +let b:loaded_py_ftplugin = 1 + +map [k :PBoB +vmap [k :PBoBm'gv`` +map ]k :PEoB +vmap ]k :PEoBm'gv`` + +vmap ik [kV]k + +omap ac :call PythonSelectObject("class") +omap af :call PythonSelectObject("function") + +" jump to previous class +map [c :call PythonDec("class", -1) +vmap [c :call PythonDec("class", -1) + +" jump to next class +map ]c :call PythonDec("class", 1) +vmap ]c :call PythonDec("class", 1) + +" jump to previous function +map [f :call PythonDec("function", -1) +vmap [f :call PythonDec("function", -1) + +" jump to next function +map ]f :call PythonDec("function", 1) +vmap ]f :call PythonDec("function", 1) + + +:com! PBoB execute "normal ".PythonBoB(line('.'), -1, 1)."G" +:com! PEoB execute "normal ".PythonBoB(line('.'), 1, 1)."G" + +" Go to a block boundary (-1: previous, 1: next) +" If force_sel_comments is true, 'g:py_select_trailing_comments' is ignored +function! PythonBoB(line, direction, force_sel_comments) + let ln = a:line + let ind = indent(ln) + let mark = ln + let indent_valid = strlen(getline(ln)) + let ln = ln + a:direction + if (a:direction == 1) && (!a:force_sel_comments) && + \ exists("g:py_select_trailing_comments") && + \ (!g:py_select_trailing_comments) + let sel_comments = 0 + else + let sel_comments = 1 + endif + + while((ln >= 1) && (ln <= line('$'))) + if (sel_comments) || (match(getline(ln), "^\\s*#") == -1) + if (!indent_valid) + let indent_valid = strlen(getline(ln)) + let ind = indent(ln) + let mark = ln + else + if (strlen(getline(ln))) + if (indent(ln) < ind) + break + endif + let mark = ln + endif + endif + endif + let ln = ln + a:direction + endwhile + + return mark +endfunction + + +" Go to previous (-1) or next (1) class/function definition +function! PythonDec(obj, direction) + if (a:obj == "class") + let objregexp = "^\\s*class\\s\\+[a-zA-Z0-9_]\\+" + \ . "\\s*\\((\\([a-zA-Z0-9_,. \\t\\n]\\)*)\\)\\=\\s*:" + else + let objregexp = "^\\s*def\\s\\+[a-zA-Z0-9_]\\+\\s*(\\_[^:#]*)\\s*:" + endif + let flag = "W" + if (a:direction == -1) + let flag = flag."b" + endif + let res = search(objregexp, flag) +endfunction + + +" Select an object ("class"/"function") +function! PythonSelectObject(obj) + " Go to the object declaration + normal $ + call PythonDec(a:obj, -1) + let beg = line('.') + + if !exists("g:py_select_leading_comments") || (g:py_select_leading_comments) + let decind = indent(beg) + let cl = beg + while (cl>1) + let cl = cl - 1 + if (indent(cl) == decind) && (getline(cl)[decind] == "#") + let beg = cl + else + break + endif + endwhile + endif + + if (a:obj == "class") + let eod = "\\(^\\s*class\\s\\+[a-zA-Z0-9_]\\+\\s*" + \ . "\\((\\([a-zA-Z0-9_,. \\t\\n]\\)*)\\)\\=\\s*\\)\\@<=:" + else + let eod = "\\(^\\s*def\\s\\+[a-zA-Z0-9_]\\+\\s*(\\_[^:#]*)\\s*\\)\\@<=:" + endif + " Look for the end of the declaration (not always the same line!) + call search(eod, "") + + " Is it a one-line definition? + if match(getline('.'), "^\\s*\\(#.*\\)\\=$", col('.')) == -1 + let cl = line('.') + execute ":".beg + execute "normal V".cl."G" + else + " Select the whole block + execute "normal \" + let cl = line('.') + execute ":".beg + execute "normal V".PythonBoB(cl, 1, 0)."G" + endif +endfunction + + +" vim:set et sts=2 sw=2: + diff --git a/plugin/python_fn.vim b/plugin/python_fn.vim deleted file mode 100644 index 7c7cf21..0000000 --- a/plugin/python_fn.vim +++ /dev/null @@ -1,446 +0,0 @@ -" -*- vim -*- -" FILE: python_fn.vim -" LAST MODIFICATION: 2008-08-28 8:19pm -" (C) Copyright 2001-2005 Mikael Berthe -" Maintained by Jon Franklin -" Version: 1.13 - -" USAGE: -" -" Save this file to $VIMFILES/ftplugin/python.vim. You can have multiple -" python ftplugins by creating $VIMFILES/ftplugin/python and saving your -" ftplugins in that directory. If saving this to the global ftplugin -" directory, this is the recommended method, since vim ships with an -" ftplugin/python.vim file already. -" You can set the global variable "g:py_select_leading_comments" to 0 -" if you don't want to select comments preceding a declaration (these -" are usually the description of the function/class). -" You can set the global variable "g:py_select_trailing_comments" to 0 -" if you don't want to select comments at the end of a function/class. -" If these variables are not defined, both leading and trailing comments -" are selected. -" Example: (in your .vimrc) "let g:py_select_leading_comments = 0" -" You may want to take a look at the 'shiftwidth' option for the -" shift commands... -" -" REQUIREMENTS: -" vim (>= 7) -" -" Shortcuts: -" ]t -- Jump to beginning of block -" ]e -- Jump to end of block -" ]v -- Select (Visual Line Mode) block -" ]< -- Shift block to left -" ]> -- Shift block to right -" ]# -- Comment selection -" ]u -- Uncomment selection -" ]c -- Select current/previous class -" ]d -- Select current/previous function -" ] -- Jump to previous line with the same/lower indentation -" ] -- Jump to next line with the same/lower indentation - -" Only do this when not done yet for this buffer -if exists("b:loaded_py_ftplugin") - finish -endif -let b:loaded_py_ftplugin = 1 - -map ]t :PBoB -vmap ]t :PBOBm'gv`` -map ]e :PEoB -vmap ]e :PEoBm'gv`` - -map ]v ]tV]e -map ]< ]tV]e< -vmap ]< < -map ]> ]tV]e> -vmap ]> > - -map ]# :call PythonCommentSelection() -vmap ]# :call PythonCommentSelection() -map ]u :call PythonUncommentSelection() -vmap ]u :call PythonUncommentSelection() - -map ]c :call PythonSelectObject("class") -map ]d :call PythonSelectObject("function") - -map ] :call PythonNextLine(-1) -map ] :call PythonNextLine(1) -" You may prefer use and ... :-) - -" jump to previous class -map ]J :call PythonDec("class", -1) -vmap ]J :call PythonDec("class", -1) - -" jump to next class -map ]j :call PythonDec("class", 1) -vmap ]j :call PythonDec("class", 1) - -" jump to previous function -map ]F :call PythonDec("function", -1) -vmap ]F :call PythonDec("function", -1) - -" jump to next function -map ]f :call PythonDec("function", 1) -vmap ]f :call PythonDec("function", 1) - - - -" Menu entries -nmenu &Python.Update\ IM-Python\ Menu - \:call UpdateMenu() -nmenu &Python.-Sep1- : -nmenu &Python.Beginning\ of\ Block[t - \]t -nmenu &Python.End\ of\ Block]e - \]e -nmenu &Python.-Sep2- : -nmenu &Python.Shift\ Block\ Left]< - \]< -vmenu &Python.Shift\ Block\ Left]< - \]< -nmenu &Python.Shift\ Block\ Right]> - \]> -vmenu &Python.Shift\ Block\ Right]> - \]> -nmenu &Python.-Sep3- : -vmenu &Python.Comment\ Selection]# - \]# -nmenu &Python.Comment\ Selection]# - \]# -vmenu &Python.Uncomment\ Selection]u - \]u -nmenu &Python.Uncomment\ Selection]u - \]u -nmenu &Python.-Sep4- : -nmenu &Python.Previous\ Class]J - \]J -nmenu &Python.Next\ Class]j - \]j -nmenu &Python.Previous\ Function]F - \]F -nmenu &Python.Next\ Function]f - \]f -nmenu &Python.-Sep5- : -nmenu &Python.Select\ Block]v - \]v -nmenu &Python.Select\ Function]d - \]d -nmenu &Python.Select\ Class]c - \]c -nmenu &Python.-Sep6- : -nmenu &Python.Previous\ Line\ wrt\ indent] - \] -nmenu &Python.Next\ Line\ wrt\ indent] - \] - -:com! PBoB execute "normal ".PythonBoB(line('.'), -1, 1)."G" -:com! PEoB execute "normal ".PythonBoB(line('.'), 1, 1)."G" -:com! UpdateMenu call UpdateMenu() - - -" Go to a block boundary (-1: previous, 1: next) -" If force_sel_comments is true, 'g:py_select_trailing_comments' is ignored -function! PythonBoB(line, direction, force_sel_comments) - let ln = a:line - let ind = indent(ln) - let mark = ln - let indent_valid = strlen(getline(ln)) - let ln = ln + a:direction - if (a:direction == 1) && (!a:force_sel_comments) && - \ exists("g:py_select_trailing_comments") && - \ (!g:py_select_trailing_comments) - let sel_comments = 0 - else - let sel_comments = 1 - endif - - while((ln >= 1) && (ln <= line('$'))) - if (sel_comments) || (match(getline(ln), "^\\s*#") == -1) - if (!indent_valid) - let indent_valid = strlen(getline(ln)) - let ind = indent(ln) - let mark = ln - else - if (strlen(getline(ln))) - if (indent(ln) < ind) - break - endif - let mark = ln - endif - endif - endif - let ln = ln + a:direction - endwhile - - return mark -endfunction - - -" Go to previous (-1) or next (1) class/function definition -function! PythonDec(obj, direction) - if (a:obj == "class") - let objregexp = "^\\s*class\\s\\+[a-zA-Z0-9_]\\+" - \ . "\\s*\\((\\([a-zA-Z0-9_,. \\t\\n]\\)*)\\)\\=\\s*:" - else - let objregexp = "^\\s*def\\s\\+[a-zA-Z0-9_]\\+\\s*(\\_[^:#]*)\\s*:" - endif - let flag = "W" - if (a:direction == -1) - let flag = flag."b" - endif - let res = search(objregexp, flag) -endfunction - - -" Comment out selected lines -" commentString is inserted in non-empty lines, and should be aligned with -" the block -function! PythonCommentSelection() range - let commentString = "#" - let cl = a:firstline - let ind = 1000 " I hope nobody use so long lines! :) - - " Look for smallest indent - while (cl <= a:lastline) - if strlen(getline(cl)) - let cind = indent(cl) - let ind = ((ind < cind) ? ind : cind) - endif - let cl = cl + 1 - endwhile - if (ind == 1000) - let ind = 1 - else - let ind = ind + 1 - endif - - let cl = a:firstline - execute ":".cl - " Insert commentString in each non-empty line, in column ind - while (cl <= a:lastline) - if strlen(getline(cl)) - execute "normal ".ind."|i".commentString - endif - execute "normal \" - let cl = cl + 1 - endwhile -endfunction - -" Uncomment selected lines -function! PythonUncommentSelection() range - " commentString could be different than the one from CommentSelection() - " For example, this could be "# \\=" - let commentString = "#" - let cl = a:firstline - while (cl <= a:lastline) - let ul = substitute(getline(cl), - \"\\(\\s*\\)".commentString."\\(.*\\)$", "\\1\\2", "") - call setline(cl, ul) - let cl = cl + 1 - endwhile -endfunction - - -" Select an object ("class"/"function") -function! PythonSelectObject(obj) - " Go to the object declaration - normal $ - call PythonDec(a:obj, -1) - let beg = line('.') - - if !exists("g:py_select_leading_comments") || (g:py_select_leading_comments) - let decind = indent(beg) - let cl = beg - while (cl>1) - let cl = cl - 1 - if (indent(cl) == decind) && (getline(cl)[decind] == "#") - let beg = cl - else - break - endif - endwhile - endif - - if (a:obj == "class") - let eod = "\\(^\\s*class\\s\\+[a-zA-Z0-9_]\\+\\s*" - \ . "\\((\\([a-zA-Z0-9_,. \\t\\n]\\)*)\\)\\=\\s*\\)\\@<=:" - else - let eod = "\\(^\\s*def\\s\\+[a-zA-Z0-9_]\\+\\s*(\\_[^:#]*)\\s*\\)\\@<=:" - endif - " Look for the end of the declaration (not always the same line!) - call search(eod, "") - - " Is it a one-line definition? - if match(getline('.'), "^\\s*\\(#.*\\)\\=$", col('.')) == -1 - let cl = line('.') - execute ":".beg - execute "normal V".cl."G" - else - " Select the whole block - execute "normal \" - let cl = line('.') - execute ":".beg - execute "normal V".PythonBoB(cl, 1, 0)."G" - endif -endfunction - - -" Jump to the next line with the same (or lower) indentation -" Useful for moving between "if" and "else", for example. -function! PythonNextLine(direction) - let ln = line('.') - let ind = indent(ln) - let indent_valid = strlen(getline(ln)) - let ln = ln + a:direction - - while((ln >= 1) && (ln <= line('$'))) - if (!indent_valid) && strlen(getline(ln)) - break - else - if (strlen(getline(ln))) - if (indent(ln) <= ind) - break - endif - endif - endif - let ln = ln + a:direction - endwhile - - execute "normal ".ln."G" -endfunction - -function! UpdateMenu() - " delete menu if it already exists, then rebuild it. - " this is necessary in case you've got multiple buffers open - " a future enhancement to this would be to make the menu aware of - " all buffers currently open, and group classes and functions by buffer - if exists("g:menuran") - aunmenu IM-Python - endif - let restore_fe = &foldenable - set nofoldenable - " preserve disposition of window and cursor - let cline=line('.') - let ccol=col('.') - 1 - norm H - let hline=line('.') - " create the menu - call MenuBuilder() - " restore disposition of window and cursor - exe "norm ".hline."Gzt" - let dnscroll=cline-hline - exe "norm ".dnscroll."j".ccol."l" - let &foldenable = restore_fe -endfunction - -function! MenuBuilder() - norm gg0 - let currentclass = -1 - let classlist = [] - let parentclass = "" - while line(".") < line("$") - " search for a class or function - if match ( getline("."), '^\s*class\s\+[_a-zA-Z].*\|^\s*def\s\+[_a-zA-Z].*' ) != -1 - norm ^ - let linenum = line('.') - let indentcol = col('.') - norm "nye - let classordef=@n - norm w"nywge - let objname=@n - let parentclass = FindParentClass(classlist, indentcol) - if classordef == "class" - call AddClass(objname, linenum, parentclass) - else " this is a function - call AddFunction(objname, linenum, parentclass) - endif - " We actually created a menu, so lets set the global variable - let g:menuran=1 - call RebuildClassList(classlist, [objname, indentcol], classordef) - endif " line matched - norm j - endwhile -endfunction - -" classlist contains the list of nested classes we are in. -" in most cases it will be empty or contain a single class -" but where a class is nested within another, it will contain 2 or more -" this function adds or removes classes from the list based on indentation -function! RebuildClassList(classlist, newclass, classordef) - let i = len(a:classlist) - 1 - while i > -1 - if a:newclass[1] <= a:classlist[i][1] - call remove(a:classlist, i) - endif - let i = i - 1 - endwhile - if a:classordef == "class" - call add(a:classlist, a:newclass) - endif -endfunction - -" we found a class or function, determine its parent class based on -" indentation and what's contained in classlist -function! FindParentClass(classlist, indentcol) - let i = 0 - let parentclass = "" - while i < len(a:classlist) - if a:indentcol <= a:classlist[i][1] - break - else - if len(parentclass) == 0 - let parentclass = a:classlist[i][0] - else - let parentclass = parentclass.'\.'.a:classlist[i][0] - endif - endif - let i = i + 1 - endwhile - return parentclass -endfunction - -" add a class to the menu -function! AddClass(classname, lineno, parentclass) - if len(a:parentclass) > 0 - let classstring = a:parentclass.'\.'.a:classname - else - let classstring = a:classname - endif - exe 'menu IM-Python.classes.'.classstring.' :call JumpToAndUnfold('.a:lineno.')' -endfunction - -" add a function to the menu, grouped by member class -function! AddFunction(functionname, lineno, parentclass) - if len(a:parentclass) > 0 - let funcstring = a:parentclass.'.'.a:functionname - else - let funcstring = a:functionname - endif - exe 'menu IM-Python.functions.'.funcstring.' :call JumpToAndUnfold('.a:lineno.')' -endfunction - - -function! s:JumpToAndUnfold(line) - " Go to the right line - execute 'normal '.a:line.'gg' - " Check to see if we are in a fold - let lvl = foldlevel(a:line) - if lvl != 0 - " and if so, then expand the fold out, other wise, ignore this part. - execute 'normal 15zo' - endif -endfunction - -"" This one will work only on vim 6.2 because of the try/catch expressions. -" function! s:JumpToAndUnfoldWithExceptions(line) -" try -" execute 'normal '.a:line.'gg15zo' -" catch /^Vim\((\a\+)\)\=:E490:/ -" " Do nothing, just consume the error -" endtry -"endfunction - - -" vim:set et sts=2 sw=2: -