+ return base
+endfunction
+
+" If this line is dedented and the number of indent spaces is valid
+" (multiple of the indentation size), trust the user.
+function! s:is_dedented_already(current, base)
+ let dedent_size = a:current - a:base
+ return (dedent_size < 0 && a:current % s:sw() == 0) ? 1 : 0
+endfunction
+
+" Is the syntax at lnum (and optionally cnum) a python string?
+function! s:is_python_string(lnum, ...)
+ let line = getline(a:lnum)
+ if a:0
+ let cols = type(a:1) != type([]) ? [a:1] : a:1
+ else
+ let cols = range(1, max([1, len(line)]))
+ endif
+ for cnum in cols
+ if match(map(synstack(a:lnum, cnum),
+ \ "synIDattr(v:val, 'name')"), 'python\S*String') == -1
+ return 0
+ end
+ endfor
+ return 1
+endfunction
+
+function! GetPythonPEPIndent(lnum)
+ " First line has indent 0
+ if a:lnum == 1
+ return 0
+ endif
+
+ let line = getline(a:lnum)
+ let prevline = getline(a:lnum-1)
+
+ " Multilinestrings: continous, docstring or starting.
+ if s:is_python_string(a:lnum-1, max([1, len(prevline)]))
+ \ && (s:is_python_string(a:lnum, 1)
+ \ || match(line, '^\%("""\|''''''\)') != -1)
+
+ " Indent closing quotes as the line with the opening ones.
+ let match_quotes = match(line, '^\s*\zs\%("""\|''''''\)')
+ if match_quotes != -1
+ " closing multiline string
+ let quotes = line[match_quotes:(match_quotes+2)]
+ let pairpos = searchpairpos(quotes, '', quotes, 'b')
+ if pairpos[0] != 0
+ return indent(pairpos[0])
+ else
+ " TODO: test to cover this!
+ endif
+ endif
+
+ if s:is_python_string(a:lnum-1)
+ " Previous line is (completely) a string: keep current indent.
+ return -1
+ endif
+
+ if match(prevline, '^\s*\%("""\|''''''\)') != -1
+ " docstring.
+ return indent(a:lnum-1)
+ endif
+
+ let indent_multi = get(b:, 'python_pep8_indent_multiline_string',
+ \ get(g:, 'python_pep8_indent_multiline_string', 0))
+ if match(prevline, '\v%("""|'''''')$') != -1
+ " Opening multiline string, started in previous line.
+ if (&autoindent && indent(a:lnum) == indent(a:lnum-1))
+ \ || match(line, '\v^\s+$') != -1
+ " <CR> with empty line or to split up 'foo("""bar' into
+ " 'foo("""' and 'bar'.
+ if indent_multi == -2
+ return indent(a:lnum-1) + s:sw()
+ endif
+ return indent_multi
+ endif
+ endif
+
+ " Keep existing indent.
+ if match(line, '\v^\s*\S') != -1
+ return -1
+ endif
+
+ if indent_multi != -2
+ return indent_multi
+ endif
+
+ return s:indent_like_opening_paren(a:lnum)
+ endif
+
+ " Parens: If we can find an open parenthesis/bracket/brace, line up with it.
+ let indent = s:indent_like_opening_paren(a:lnum)
+ if indent >= -1
+ return indent
+ endif
+
+ " Blocks: Match indent of first block of this type.
+ let indent = s:indent_like_block(a:lnum)
+ if indent >= -1
+ return indent
+ endif
+
+ return s:indent_like_previous_line(a:lnum)