From 9d6a9925e545756d0004de33d3ce3e97d0dfdd22 Mon Sep 17 00:00:00 2001 From: "Kenneth Benzie (Benie)" Date: Wed, 27 Mar 2019 22:50:05 +0000 Subject: [PATCH] Copy default GetPythonIndent --- indent/python.vim | 165 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 6 deletions(-) diff --git a/indent/python.vim b/indent/python.vim index 8c19210..d79a6a7 100644 --- a/indent/python.vim +++ b/indent/python.vim @@ -1,13 +1,166 @@ " Override default Python indent file +if exists('b:did_indent') + finish +endif +let b:did_indent = 1 " Make sure lisp indenting doesn't supersede us setlocal nolisp " indentexpr isn't much help otherwise setlocal autoindent - -" TODO: Implement an indentexpr for Python which does not crash on macvim -" 8.1.950 and does not insert 2 tabs when adding a new line in function -" parameters insert mode. Look at GetPythonIndent in: -" /runtime/indent/python.vim -setlocal indentexpr= +" Use custom indent expression +setlocal indentexpr=PythonIndent(v:lnum) +" List of keys that trigger indentexpr in insert mode setlocal indentkeys+=<:>,=elif,=except + +" Derived from the default GetPythonIndent removing deliberate use of invalid +" expressions which show up with 'set debug=msg,throw', also remove the indent +" config options. +function PythonIndent(lnum) + " If this line is explicitly joined: If the previous line was also joined, + " line it up with that one, otherwise add two 'shiftwidth' + if getline(a:lnum - 1) =~# '\\$' + if a:lnum > 1 && getline(a:lnum - 2) =~# '\\$' + return indent(a:lnum - 1) + endif + return indent(a:lnum - 1) + (shiftwidth() * 2) + endif + + " If the start of the line is in a string don't change the indent. + if has('syntax_items') + \ && synIDattr(synID(a:lnum, 1, 1), 'name') =~# 'String$' + return -1 + endif + + " Search backwards for the previous non-empty line. + let plnum = prevnonblank(v:lnum - 1) + + if plnum == 0 + " This is the first non-empty line, use zero indent. + return 0 + endif + + " If the previous line is inside parenthesis, use the indent of the starting + " line. + call cursor(plnum, 1) + let parlnum = searchpair('(\|{\|\[', '', ')\|}\|\]', 'nbW', '', 0, 100) + if parlnum > 0 + let plindent = indent(parlnum) + let plnumstart = parlnum + else + let plindent = indent(plnum) + let plnumstart = plnum + endif + + " When inside parenthesis: If at the first line below the parenthesis add + " 'shiftwidth', otherwise same as previous line. + " i = (a + " + b + " + c) + call cursor(a:lnum, 1) + let p = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', '', 0, 100) + if p > 0 + if p == plnum + " When the start is inside parenthesis, only indent one 'shiftwidth'. + let pp = searchpair('(\|{\|\[', '', ')\|}\|\]', 'bW', '', 0, 100) + return indent(plnum) + shiftwidth() + endif + if plnumstart == p + return indent(plnum) + endif + return plindent + endif + + " Get the line and remove a trailing comment. + " Use syntax highlighting attributes when possible. + let pline = getline(plnum) + let pline_len = strlen(pline) + if has('syntax_items') + " If the last character in the line is a comment, do a binary search for + " the start of the comment. synID() is slow, a linear search would take + " too long on a long line. + if synIDattr(synID(plnum, pline_len, 1), 'name') =~# '\\(Comment\\|Todo\\)$' + let min = 1 + let max = pline_len + while min < max + let col = (min + max) / 2 + if synIDattr(synID(plnum, col, 1), 'name') =~# '\\(Comment\\|Todo\\)$' + let max = col + else + let min = col + 1 + endif + endwhile + let pline = strpart(pline, 0, min - 1) + endif + else + let col = 0 + while col < pline_len + if pline[col] ==# '#' + let pline = strpart(pline, 0, col) + break + endif + let col = col + 1 + endwhile + endif + + " If the previous line ended with a colon, indent this line + if pline =~# ':\s*$' + return plindent + shiftwidth() + endif + + " If the previous line was a stop-execution statement... + if getline(plnum) =~# '^\s*\(break\|continue\|raise\|return\|pass\)\>' + " See if the user has already dedented + if indent(a:lnum) > indent(plnum) - shiftwidth() + " If not, recommend one dedent + return indent(plnum) - shiftwidth() + endif + " Otherwise, trust the user + return -1 + endif + + " If the current line begins with a keyword that lines up with try + if getline(a:lnum) =~# '^\s*\(except\|finally\)\>' + let lnum = a:lnum - 1 + while lnum >= 1 + if getline(lnum) =~# '^\s*\(try\|except\)\>' + let ind = indent(lnum) + if ind >= indent(a:lnum) + " indent is already less than this + return -1 + endif + " line up with previous try or except + return ind + endif + let lnum = lnum - 1 + endwhile + " no matching try! + return -1 + endif + + " If the current line begins with a header keyword, dedent + if getline(a:lnum) =~# '^\s*\(elif\|else\)\>' + + " Unless the previous line was a one-liner + if getline(plnumstart) =~# '^\s*\(for\|if\|try\)\>' + return plindent + endif + + " Or the user has already dedented + if indent(a:lnum) <= plindent - shiftwidth() + return -1 + endif + + return plindent - shiftwidth() + endif + + " When after a () construct we probably want to go back to the start line. + " a = (b + " + c) + " here + if parlnum > 0 + return plindent + endif + + return -1 +endfunction