diff --git a/CHANGES.txt b/CHANGES.txt index e0579bf..b094d50 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -7,9 +7,6 @@ Revision 3.3.5 (2013-08-31): - Highlight 'import', 'from' and 'as' as include statements. Patch by pydave at GitHub. - - Added new option 'python_highlight_file_headers_as_comments' (disabled by - default) to highlight shebang and coding file headers as comments. - Proposed by pydave at GitHub. Revision 3.3.4 (2013-08-11): diff --git a/after/indent/python.vim b/after/indent/python.vim new file mode 100644 index 0000000..c1ff445 --- /dev/null +++ b/after/indent/python.vim @@ -0,0 +1,8 @@ +setlocal nolisp +setlocal shiftround +setlocal indentexpr=indent#Indent(v:lnum) +setlocal indentkeys=!^F,o,O,<:>,0),0],0},=elif,=except +setlocal foldmethod=expr +setlocal foldexpr=folding#expr(v:lnum) +setlocal foldtext=folding#text() +map r :w:!python % diff --git a/autoload/folding.vim b/autoload/folding.vim new file mode 100644 index 0000000..99e45e6 --- /dev/null +++ b/autoload/folding.vim @@ -0,0 +1,76 @@ +" Python-mode folding functions + + +let s:blank_regex = '^\s*$' +let s:def_regex = '^\s*\%(class\|def\) \w\+' +let s:decorator_regex = '^\s*@' +let s:doc_begin_regex = '^\s*\%("""\|''''''\)' +let s:doc_end_regex = '\%("""\|''''''\)\s*$' +let s:doc_line_regex = '^\s*\("""\|''''''\).\+\1\s*$' + + +fun! folding#text() " {{{ + let fs = v:foldstart + while getline(fs) =~ '\%(^\s*@\)\|\%(^\s*\%("""\|''''''\)\s*$\)' + let fs = nextnonblank(fs + 1) + endwhile + let line = getline(fs) + + let nucolwidth = &fdc + &number * &numberwidth + let windowwidth = winwidth(0) - nucolwidth - 3 + let foldedlinecount = v:foldend - v:foldstart + + " expand tabs into spaces + let onetab = strpart(' ', 0, &tabstop) + let line = substitute(line, '\t', onetab, 'g') + + let line = strpart(line, 0, windowwidth - 2 -len(foldedlinecount)) + let line = substitute(line, '\%("""\|''''''\)', '', '') + let fillcharcount = windowwidth - len(line) - len(foldedlinecount) + return line . '…' . repeat(" ",fillcharcount) . foldedlinecount . '…' . ' ' +endfunction "}}} + + +fun! folding#expr(lnum) "{{{ + + let line = getline(a:lnum) + let indent = indent(a:lnum) + let prev_line = getline(a:lnum - 1) + + if line =~ s:def_regex || line =~ s:decorator_regex + if prev_line =~ s:decorator_regex + return '=' + else + return ">".(indent / &shiftwidth + 1) + endif + endif + + if line =~ s:doc_begin_regex + \ && line !~ s:doc_line_regex + \ && prev_line =~ s:def_regex + return ">".(indent / &shiftwidth + 1) + endif + + if line =~ s:doc_end_regex + \ && line !~ s:doc_line_regex + return "<".(indent / &shiftwidth + 1) + endif + + if line =~ s:blank_regex + if prev_line =~ s:blank_regex + return -1 + else + return '=' + endif + endif + + if indent == 0 + return 0 + endif + + return '=' + +endfunction "}}} + + +" vim: fdm=marker:fdl=0 diff --git a/autoload/indent.vim b/autoload/indent.vim new file mode 100644 index 0000000..97a7d73 --- /dev/null +++ b/autoload/indent.vim @@ -0,0 +1,184 @@ +" PEP8 compatible Python indent file +" Language: Python +" Maintainer: Hynek Schlawack +" Prev Maintainer: Eric Mc Sween (address invalid) +" Original Author: David Bustos (address invalid) +" Last Change: 2012-06-21 +" License: Public Domainlet + + +function! indent#Indent(lnum) + + " First line has indent 0 + if a:lnum == 1 + return 0 + endif + + " If we can find an open parenthesis/bracket/brace, line up with it. + call cursor(a:lnum, 1) + let parlnum = s:SearchParensPair() + if parlnum > 0 + let parcol = col('.') + let closing_paren = match(getline(a:lnum), '^\s*[])}]') != -1 + if match(getline(parlnum), '[([{]\s*$', parcol - 1) != -1 + if closing_paren + return indent(parlnum) + else + return indent(parlnum) + &shiftwidth + endif + else + return parcol + endif + endif + + " Examine this line + let thisline = getline(a:lnum) + let thisindent = indent(a:lnum) + + " If the line starts with 'elif' or 'else', line up with 'if' or 'elif' + if thisline =~ '^\s*\(elif\|else\)\>' + let bslnum = s:BlockStarter(a:lnum, '^\s*\(if\|elif\)\>') + if bslnum > 0 + return indent(bslnum) + else + return -1 + endif + endif + + " If the line starts with 'except' or 'finally', line up with 'try' + " or 'except' + if thisline =~ '^\s*\(except\|finally\)\>' + let bslnum = s:BlockStarter(a:lnum, '^\s*\(try\|except\)\>') + if bslnum > 0 + return indent(bslnum) + else + return -1 + endif + endif + + " Examine previous line + let plnum = a:lnum - 1 + let pline = getline(plnum) + let sslnum = s:StatementStart(plnum) + + " If the previous line is blank, keep the same indentation + if pline =~ '^\s*$' + return -1 + endif + + " If this line is explicitly joined, try to find an indentation that looks + " good. + if pline =~ '\\$' + let compound_statement = '^\s*\(if\|while\|for\s.*\sin\|except\)\s*' + let maybe_indent = matchend(getline(sslnum), compound_statement) + if maybe_indent != -1 + return maybe_indent + else + return indent(sslnum) + &sw * 2 + endif + endif + + " If the previous line ended with a colon and is not a comment, indent + " relative to statement start. + if pline =~ ':\s*$' && pline !~ '^\s*#' + return indent(sslnum) + &sw + endif + + " If the previous line was a stop-execution statement or a pass + if getline(sslnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>' + " See if the user has already dedented + if indent(a:lnum) > indent(sslnum) - &sw + " If not, recommend one dedent + return indent(sslnum) - &sw + endif + " Otherwise, trust the user + return -1 + endif + + " In all other cases, line up with the start of the previous statement. + return indent(sslnum) +endfunction + + +" Find backwards the closest open parenthesis/bracket/brace. +function! s:SearchParensPair() + let line = line('.') + let col = col('.') + + " Skip strings and comments and don't look too far + let skip = "line('.') < " . (line - 50) . " ? dummy :" . + \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? ' . + \ '"string\\|comment"' + + " Search for parentheses + call cursor(line, col) + let parlnum = searchpair('(', '', ')', 'bW', skip) + let parcol = col('.') + + " Search for brackets + call cursor(line, col) + let par2lnum = searchpair('\[', '', '\]', 'bW', skip) + let par2col = col('.') + + " Search for braces + call cursor(line, col) + let par3lnum = searchpair('{', '', '}', 'bW', skip) + let par3col = col('.') + + " Get the closest match + if par2lnum > parlnum || (par2lnum == parlnum && par2col > parcol) + let parlnum = par2lnum + let parcol = par2col + endif + if par3lnum > parlnum || (par3lnum == parlnum && par3col > parcol) + let parlnum = par3lnum + let parcol = par3col + endif + + " Put the cursor on the match + if parlnum > 0 + call cursor(parlnum, parcol) + endif + return parlnum +endfunction + + +" Find the start of a multi-line statement +function! s:StatementStart(lnum) + let lnum = a:lnum + while 1 + if getline(lnum - 1) =~ '\\$' + let lnum = lnum - 1 + else + call cursor(lnum, 1) + let maybe_lnum = s:SearchParensPair() + if maybe_lnum < 1 + return lnum + else + let lnum = maybe_lnum + endif + endif + endwhile +endfunction + + +" Find the block starter that matches the current line +function! s:BlockStarter(lnum, block_start_re) + let lnum = a:lnum + let maxindent = 10000 " whatever + while lnum > 1 + let lnum = prevnonblank(lnum - 1) + if indent(lnum) < maxindent + if getline(lnum) =~ a:block_start_re + return lnum + else + let maxindent = indent(lnum) + " It's not worth going further if we reached the top level + if maxindent == 0 + return -1 + endif + endif + endif + endwhile + return -1 +endfunction diff --git a/syntax/python.vim b/syntax/python.vim index 8e25a7f..29fb9a0 100644 --- a/syntax/python.vim +++ b/syntax/python.vim @@ -74,9 +74,6 @@ " python_highlight_doctests Highlight doc-tests " python_print_as_function Highlight 'print' statement as " function for Python 2 -" python_highlight_file_headers_as_comments -" Highlight shebang and coding -" headers as comments " " python_highlight_all Enable all the options above " NOTE: This option don't override @@ -151,12 +148,13 @@ syn keyword pythonStatement pass raise syn keyword pythonStatement global assert syn keyword pythonStatement lambda syn keyword pythonStatement with -syn keyword pythonStatement def class nextgroup=pythonFunction skipwhite +"syn keyword pythonStatement def class nextgroup=pythonFunction skipwhite syn keyword pythonRepeat for while syn keyword pythonConditional if elif else syn keyword pythonImport import syn keyword pythonException try except finally syn keyword pythonOperator and in is not or +syn keyword pythonSelf self syn match pythonStatement "\" display syn match pythonImport "\" display @@ -174,6 +172,18 @@ else syn match pythonFunction "\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" display contained endif +" +" Class and method definitions +" +syn region pythonFuncDef start='^\s\{-}def ' end=':' contains=pythonDef,pythonFuncName,pythonSelf +syn keyword pythonDef def contained skipwhite +syn match pythonFuncName "\<[a-zA-Z_][a-zA-Z0-9_]\{-}\>\%((\)\@=" contained + +syn region pythonClassDef start='^\s\{-}class ' end=':' contains=pythonClass,pythonClassName +syn keyword pythonClass class contained skipwhite +syn match pythonClassName "\<[a-zA-Z_][a-zA-Z0-9_]\{-}\>\%((\)\@=" contained + "syn region pythonString start=+[bB]\='+ skip=+\\\\\|\\'\|\\$+ excludenl end=+'+ end=+$+ keepend contains=pythonBytesEscape,pythonBytesEscapeError,pythonUniEscape,pythonUniEscapeError,@Spell + " " Decorators (new in Python 2.4) " @@ -187,10 +197,8 @@ syn match pythonDot "\." display containedin=pythonDottedName " syn match pythonComment "#.*$" display contains=pythonTodo,@Spell -if !s:Enabled("g:python_highlight_file_headers_as_comments") - syn match pythonRun "\%^#!.*$" - syn match pythonCoding "\%^.*\%(\n.*\)\?#.*coding[:=]\s*[0-9A-Za-z-_.]\+.*$" -endif +syn match pythonRun "\%^#!.*$" +syn match pythonCoding "\%^.*\%(\n.*\)\?#.*coding[:=]\s*[0-9A-Za-z-_.]\+.*$" syn keyword pythonTodo TODO FIXME XXX contained " @@ -474,9 +482,9 @@ endif if version >= 508 || !exists("did_python_syn_inits") if version <= 508 let did_python_syn_inits = 1 - command -nargs=+ HiLink hi link + command! -nargs=+ HiLink hi link else - command -nargs=+ HiLink hi def link + command! -nargs=+ HiLink hi def link endif HiLink pythonStatement Statement @@ -486,16 +494,19 @@ if version >= 508 || !exists("did_python_syn_inits") HiLink pythonRepeat Repeat HiLink pythonException Exception HiLink pythonOperator Operator + HiLink pythonSelf Type + HiLink pythonDef RubyDefine + HiLink pythonFuncName Function + HiLink pythonClass Statement + HiLink pythonClassName Type HiLink pythonDecorator Define HiLink pythonDottedName Function HiLink pythonDot Normal HiLink pythonComment Comment - if !s:Enabled("g:python_highlight_file_headers_as_comments") - HiLink pythonCoding Special - HiLink pythonRun Special - endif + HiLink pythonCoding Special + HiLink pythonRun Special HiLink pythonTodo Todo HiLink pythonError Error