python.vim 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. " Support for Python indenting, see runtime/indent/python.vim
  2. let s:keepcpo= &cpo
  3. set cpo&vim
  4. " need to inspect some old g:pyindent_* variables to be backward compatible
  5. let g:python_indent = extend(get(g:, 'python_indent', {}), #{
  6. \ closed_paren_align_last_line: v:true,
  7. \ open_paren: get(g:, 'pyindent_open_paren', 'shiftwidth() * 2'),
  8. \ nested_paren: get(g:, 'pyindent_nested_paren', 'shiftwidth()'),
  9. \ continue: get(g:, 'pyindent_continue', 'shiftwidth() * 2'),
  10. "\ searchpair() can be slow, limit the time to 150 msec or what is put in
  11. "\ g:python_indent.searchpair_timeout
  12. \ searchpair_timeout: get(g:, 'pyindent_searchpair_timeout', 150),
  13. "\ Identing inside parentheses can be very slow, regardless of the searchpair()
  14. "\ timeout, so let the user disable this feature if he doesn't need it
  15. \ disable_parentheses_indenting: get(g:, 'pyindent_disable_parentheses_indenting', v:false),
  16. \ }, 'keep')
  17. let s:maxoff = 50 " maximum number of lines to look backwards for ()
  18. function s:SearchBracket(fromlnum, flags)
  19. return searchpairpos('[[({]', '', '[])}]', a:flags,
  20. \ {-> synstack('.', col('.'))
  21. \ ->map({_, id -> id->synIDattr('name')})
  22. \ ->match('\%(Comment\|Todo\|String\)$') >= 0},
  23. \ [0, a:fromlnum - s:maxoff]->max(), g:python_indent.searchpair_timeout)
  24. endfunction
  25. " See if the specified line is already user-dedented from the expected value.
  26. function s:Dedented(lnum, expected)
  27. return indent(a:lnum) <= a:expected - shiftwidth()
  28. endfunction
  29. " Some other filetypes which embed Python have slightly different indent
  30. " rules (e.g. bitbake). Those filetypes can pass an extra funcref to this
  31. " function which is evaluated below.
  32. function python#GetIndent(lnum, ...)
  33. let ExtraFunc = a:0 > 0 ? a:1 : 0
  34. " If this line is explicitly joined: If the previous line was also joined,
  35. " line it up with that one, otherwise add two 'shiftwidth'
  36. if getline(a:lnum - 1) =~ '\\$'
  37. if a:lnum > 1 && getline(a:lnum - 2) =~ '\\$'
  38. return indent(a:lnum - 1)
  39. endif
  40. return indent(a:lnum - 1) + get(g:, 'pyindent_continue', g:python_indent.continue)->eval()
  41. endif
  42. " If the start of the line is in a string don't change the indent.
  43. if has('syntax_items')
  44. \ && synIDattr(synID(a:lnum, 1, 1), "name") =~ "String$"
  45. return -1
  46. endif
  47. " Search backwards for the previous non-empty line.
  48. let plnum = prevnonblank(v:lnum - 1)
  49. if plnum == 0
  50. " This is the first non-empty line, use zero indent.
  51. return 0
  52. endif
  53. if g:python_indent.disable_parentheses_indenting == 1
  54. let plindent = indent(plnum)
  55. let plnumstart = plnum
  56. else
  57. " Indent inside parens.
  58. " Align with the open paren unless it is at the end of the line.
  59. " E.g.
  60. " open_paren_not_at_EOL(100,
  61. " (200,
  62. " 300),
  63. " 400)
  64. " open_paren_at_EOL(
  65. " 100, 200, 300, 400)
  66. call cursor(a:lnum, 1)
  67. let [parlnum, parcol] = s:SearchBracket(a:lnum, 'nbW')
  68. if parlnum > 0
  69. if parcol != col([parlnum, '$']) - 1
  70. return parcol
  71. elseif getline(a:lnum) =~ '^\s*[])}]' && !g:python_indent.closed_paren_align_last_line
  72. return indent(parlnum)
  73. endif
  74. endif
  75. call cursor(plnum, 1)
  76. " If the previous line is inside parenthesis, use the indent of the starting
  77. " line.
  78. let [parlnum, _] = s:SearchBracket(plnum, 'nbW')
  79. if parlnum > 0
  80. if a:0 > 0 && ExtraFunc(parlnum)
  81. " We may have found the opening brace of a bitbake Python task, e.g. 'python do_task {'
  82. " If so, ignore it here - it will be handled later.
  83. let parlnum = 0
  84. let plindent = indent(plnum)
  85. let plnumstart = plnum
  86. else
  87. let plindent = indent(parlnum)
  88. let plnumstart = parlnum
  89. endif
  90. else
  91. let plindent = indent(plnum)
  92. let plnumstart = plnum
  93. endif
  94. " When inside parenthesis: If at the first line below the parenthesis add
  95. " two 'shiftwidth', otherwise same as previous line.
  96. " i = (a
  97. " + b
  98. " + c)
  99. call cursor(a:lnum, 1)
  100. let [p, _] = s:SearchBracket(a:lnum, 'bW')
  101. if p > 0
  102. if a:0 > 0 && ExtraFunc(p)
  103. " Currently only used by bitbake
  104. " Handle first non-empty line inside a bitbake Python task
  105. if p == plnum
  106. return shiftwidth()
  107. endif
  108. " Handle the user actually trying to close a bitbake Python task
  109. let line = getline(a:lnum)
  110. if line =~ '^\s*}'
  111. return -2
  112. endif
  113. " Otherwise ignore the brace
  114. let p = 0
  115. else
  116. if p == plnum
  117. " When the start is inside parenthesis, only indent one 'shiftwidth'.
  118. let [pp, _] = s:SearchBracket(a:lnum, 'bW')
  119. if pp > 0
  120. return indent(plnum)
  121. \ + get(g:, 'pyindent_nested_paren', g:python_indent.nested_paren)->eval()
  122. endif
  123. return indent(plnum)
  124. \ + get(g:, 'pyindent_open_paren', g:python_indent.open_paren)->eval()
  125. endif
  126. if plnumstart == p
  127. return indent(plnum)
  128. endif
  129. return plindent
  130. endif
  131. endif
  132. endif
  133. " Get the line and remove a trailing comment.
  134. " Use syntax highlighting attributes when possible.
  135. let pline = getline(plnum)
  136. let pline_len = strlen(pline)
  137. if has('syntax_items')
  138. " If the last character in the line is a comment, do a binary search for
  139. " the start of the comment. synID() is slow, a linear search would take
  140. " too long on a long line.
  141. if synstack(plnum, pline_len)
  142. \ ->map({_, id -> id->synIDattr('name')})
  143. \ ->match('\%(Comment\|Todo\)$') >= 0
  144. let min = 1
  145. let max = pline_len
  146. while min < max
  147. let col = (min + max) / 2
  148. if synstack(plnum, col)
  149. \ ->map({_, id -> id->synIDattr('name')})
  150. \ ->match('\%(Comment\|Todo\)$') >= 0
  151. let max = col
  152. else
  153. let min = col + 1
  154. endif
  155. endwhile
  156. let pline = strpart(pline, 0, min - 1)
  157. endif
  158. else
  159. let col = 0
  160. while col < pline_len
  161. if pline[col] == '#'
  162. let pline = strpart(pline, 0, col)
  163. break
  164. endif
  165. let col = col + 1
  166. endwhile
  167. endif
  168. " If the previous line ended with a colon, indent this line
  169. if pline =~ ':\s*$'
  170. return plindent + shiftwidth()
  171. endif
  172. " If the previous line was a stop-execution statement...
  173. if getline(plnum) =~ '^\s*\(break\|continue\|raise\|return\|pass\)\>'
  174. " See if the user has already dedented
  175. if s:Dedented(a:lnum, indent(plnum))
  176. " If so, trust the user
  177. return -1
  178. endif
  179. " If not, recommend one dedent
  180. return indent(plnum) - shiftwidth()
  181. endif
  182. " If the current line begins with a keyword that lines up with "try"
  183. if getline(a:lnum) =~ '^\s*\(except\|finally\)\>'
  184. let lnum = a:lnum - 1
  185. while lnum >= 1
  186. if getline(lnum) =~ '^\s*\(try\|except\)\>'
  187. let ind = indent(lnum)
  188. if ind >= indent(a:lnum)
  189. return -1 " indent is already less than this
  190. endif
  191. return ind " line up with previous try or except
  192. endif
  193. let lnum = lnum - 1
  194. endwhile
  195. return -1 " no matching "try"!
  196. endif
  197. " If the current line begins with a header keyword, dedent
  198. if getline(a:lnum) =~ '^\s*\(elif\|else\)\>'
  199. " Unless the previous line was a one-liner
  200. if getline(plnumstart) =~ '^\s*\(for\|if\|elif\|try\)\>'
  201. return plindent
  202. endif
  203. " Or the user has already dedented
  204. if s:Dedented(a:lnum, plindent)
  205. return -1
  206. endif
  207. return plindent - shiftwidth()
  208. endif
  209. " When after a () construct we probably want to go back to the start line.
  210. " a = (b
  211. " + c)
  212. " here
  213. if parlnum > 0
  214. " ...unless the user has already dedented
  215. if s:Dedented(a:lnum, plindent)
  216. return -1
  217. else
  218. return plindent
  219. endif
  220. endif
  221. return -1
  222. endfunction
  223. let &cpo = s:keepcpo
  224. unlet s:keepcpo