"============================================================================ " File: pythontextobj.vim " Author: Nat Williams " License: I dunno " Description: Adds text objects for Python classes and functions. " Credits: Most code pretty much copied straight out of Alfredo Deza's " chapa.vim (https://github.com/alfredodeza/chapa.vim) " Also Austin Taylor's indentobj " (https://github.com/austintaylor/vim-indentobject) "============================================================================ if (exists("g:loaded_pythontextobj") && g:loaded_pythontextobj) finish endif let g:loaded_pythontextobj = 1 onoremap af :call FunctionTextObject(0) onoremap if :call FunctionTextObject(1) vnoremap af :call FunctionTextObject(0)gv vnoremap if :call FunctionTextObject(1)gv onoremap ac :call ClassTextObject(0) onoremap ic :call ClassTextObject(1) vnoremap ac :call ClassTextObject(0)gv vnoremap ic :call ClassTextObject(1)gv " Select an object ("class"/"function") function! s:PythonSelectObject(obj, inner) " find definition line let start_line = s:FindPythonObjectStart(a:obj) if (! start_line) return endif " get end (w/ or w/out whitespace) let until = s:ObjectEnd(start_line, a:inner) " include decorators if (! a:inner) let start_line = s:StartDecorators(start_line) endif " select range let line_moves = until - start_line exec start_line if line_moves > 0 execute "normal V" . line_moves . "j" else execute "normal VG" endif endfunction function! s:ObjectEnd(start, inner) let objend = s:NextIndent(a:start) if a:inner let objend = prevnonblank(objend) endif return objend endfunction function! s:NextIndent(start) let line = a:start let lastline = line('$') let indent = indent(line) while (line > 0 && line <= lastline) let line = line + 1 if (indent(line) <= indent && getline(line) !~ '^\s*$') return line - 1 endif endwhile return lastline endfunction function! s:StartDecorators(start) " Returns the line of the first decorator line above the starting line, " counting only decorators with the same level. exec a:start normal ^ let def_indent = indent(line(".")) normal k while (indent(line(".") == def_indent) && getline(".") =~ '\v^\s*\@') normal k endwhile return line(".") + 1 endfunction function! s:FindPythonObjectStart(obj) " TODO: don't match definitions at equal or greater indent unless it matches " at cursor position let cursor_start_pos = line(".") " Empty lines have the indentation of the previous non-empty line in python, " so we skip backwards until we find one that is not empty while (getline(".") =~ '^\s*$') && (line(".") > 1) exec line(".") - 1 endwhile let cursor_indent = indent(line(".")) 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 found = 0 while (! found) normal $ let result = search(objregexp, "Wbcn") if (! result) return endif if indent(result) < cursor_indent || (indent(result) == cursor_indent && result == cursor_start_pos) let found = 1 else exec line(".") - 1 endif endwhile exec cursor_start_pos return result endfunction function! FunctionTextObject(inner) call s:PythonSelectObject('function', a:inner) endfunction function! ClassTextObject(inner) call s:PythonSelectObject('class', a:inner) endfunction