python.vim 7.7 KB

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