php.vim 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. " Vim indent file
  2. " Language: PHP
  3. " Author: John Wellesz <John.wellesz (AT) gmail (DOT) com>
  4. " URL: https://www.2072productions.com/vim/indent/php.vim
  5. " Home: https://github.com/2072/PHP-Indenting-for-VIm
  6. " Last Change: 2023 August 18th
  7. " Version: 1.75
  8. "
  9. "
  10. " Type :help php-indent for available options
  11. "
  12. " A fully commented version of this file is available on github
  13. "
  14. "
  15. " If you find a bug, please open a ticket on github.com
  16. " ( https://github.com/2072/PHP-Indenting-for-VIm/issues ) with an example of
  17. " code that breaks the algorithm.
  18. "
  19. " NOTE: This script must be used with PHP syntax ON and with the php syntax
  20. " script by Lutz Eymers (http://www.isp.de/data/php.vim ) or with the
  21. " script by Peter Hodge (https://www.vim.org/scripts/script.php?script_id=1571 )
  22. " the later is bunbdled by default with Vim 7.
  23. "
  24. "
  25. " In the case you have syntax errors in your script such as HereDoc end
  26. " identifiers not at col 1 you'll have to indent your file 2 times (This
  27. " script will automatically put HereDoc end identifiers at col 1 if
  28. " they are followed by a ';').
  29. "
  30. " NOTE: If you are editing files in Unix file format and that (by accident)
  31. " there are '\r' before new lines, this script won't be able to proceed
  32. " correctly and will make many mistakes because it won't be able to match
  33. " '\s*$' correctly.
  34. " So you have to remove those useless characters first with a command like:
  35. "
  36. " :%s /\r$//g
  37. "
  38. " or simply 'let' the option PHP_removeCRwhenUnix to 1 and the script will
  39. " silently remove them when VIM load this script (at each bufread).
  40. if exists("b:did_indent")
  41. finish
  42. endif
  43. let b:did_indent = 1
  44. let g:php_sync_method = 0
  45. if exists("PHP_default_indenting")
  46. let b:PHP_default_indenting = PHP_default_indenting * shiftwidth()
  47. else
  48. let b:PHP_default_indenting = 0
  49. endif
  50. if exists("PHP_outdentSLComments")
  51. let b:PHP_outdentSLComments = PHP_outdentSLComments * shiftwidth()
  52. else
  53. let b:PHP_outdentSLComments = 0
  54. endif
  55. if exists("PHP_BracesAtCodeLevel")
  56. let b:PHP_BracesAtCodeLevel = PHP_BracesAtCodeLevel
  57. else
  58. let b:PHP_BracesAtCodeLevel = 0
  59. endif
  60. if exists("PHP_autoformatcomment")
  61. let b:PHP_autoformatcomment = PHP_autoformatcomment
  62. else
  63. let b:PHP_autoformatcomment = 1
  64. endif
  65. if exists("PHP_outdentphpescape")
  66. let b:PHP_outdentphpescape = PHP_outdentphpescape
  67. else
  68. let b:PHP_outdentphpescape = 1
  69. endif
  70. if exists("PHP_noArrowMatching")
  71. let b:PHP_noArrowMatching = PHP_noArrowMatching
  72. else
  73. let b:PHP_noArrowMatching = 0
  74. endif
  75. if exists("PHP_vintage_case_default_indent") && PHP_vintage_case_default_indent
  76. let b:PHP_vintage_case_default_indent = 1
  77. else
  78. let b:PHP_vintage_case_default_indent = 0
  79. endif
  80. if exists("PHP_IndentFunctionCallParameters")
  81. let b:PHP_IndentFunctionCallParameters = PHP_IndentFunctionCallParameters
  82. else
  83. let b:PHP_IndentFunctionCallParameters = 0
  84. endif
  85. if exists("PHP_IndentFunctionDeclarationParameters")
  86. let b:PHP_IndentFunctionDeclarationParameters = PHP_IndentFunctionDeclarationParameters
  87. else
  88. let b:PHP_IndentFunctionDeclarationParameters = 0
  89. endif
  90. let b:PHP_lastindented = 0
  91. let b:PHP_indentbeforelast = 0
  92. let b:PHP_indentinghuge = 0
  93. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  94. let b:PHP_LastIndentedWasComment = 0
  95. let b:PHP_InsideMultilineComment = 0
  96. let b:InPHPcode = 0
  97. let b:InPHPcode_checked = 0
  98. let b:InPHPcode_and_script = 0
  99. let b:InPHPcode_tofind = ""
  100. let b:PHP_oldchangetick = b:changedtick
  101. let b:UserIsTypingComment = 0
  102. let b:optionsset = 0
  103. setlocal nosmartindent
  104. setlocal noautoindent
  105. setlocal nocindent
  106. setlocal nolisp
  107. setlocal indentexpr=GetPhpIndent()
  108. setlocal indentkeys=0{,0},0),0],:,!^F,o,O,e,*<Return>,=?>,=<?,=*/
  109. let b:undo_indent = "setl ai< cin< inde< indk< lisp< si<"
  110. let s:searchpairflags = 'bWr'
  111. if &fileformat == "unix" && exists("PHP_removeCRwhenUnix") && PHP_removeCRwhenUnix
  112. silent! %s/\r$//g
  113. endif
  114. if exists("*GetPhpIndent")
  115. call ResetPhpOptions()
  116. finish " XXX -- comment this line for easy dev
  117. endif
  118. let s:endline = '\s*\%(//.*\|#\[\@!.*\|/\*.*\*/\s*\)\=$'
  119. let s:PHP_validVariable = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
  120. let s:notPhpHereDoc = '\<\%(break\|return\|continue\|exit\|die\|true\|false\|elseif\|else\|end\%(if\|while\|for\|foreach\|match\|switch\)\)\>'
  121. let s:blockstart = '\%(\%(\%(}\s*\)\=else\%(\s\+\)\=\)\=if\>\|\%(}\s*\)\?else\>\|do\>\|while\>\|match\>\|switch\>\|case\>\|default\>\|for\%(each\)\=\>\|declare\>\|class\>\|trait\>\|\%()\s*\)\=use\>\|interface\>\|abstract\>\|final\>\|try\>\|\%(}\s*\)\=catch\>\|\%(}\s*\)\=finally\>\)'
  122. let s:functionDeclPrefix = '\<function\>\%(\s\+&\='.s:PHP_validVariable.'\)\=\s*('
  123. let s:functionDecl = s:functionDeclPrefix.'.*'
  124. let s:multilineFunctionDecl = s:functionDeclPrefix.s:endline
  125. let s:arrayDecl = '\<array\>\s*(.*'
  126. let s:multilineFunctionCall = s:PHP_validVariable.'\s*('.s:endline
  127. let s:unstated = '\%(^\s*'.s:blockstart.'.*)\|\%(//.*\)\@<!\<e'.'lse\>\)'.s:endline
  128. let s:terminated = '\%(\%(;\%(\s*\%(?>\|}\)\)\=\|<<<\s*[''"]\=\a\w*[''"]\=$\|^\s*}\|^\s*'.s:PHP_validVariable.':\)'.s:endline.'\)'
  129. let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!'
  130. let s:matchStart = 'match\s*(\s*\$\?'.s:PHP_validVariable.'\s*)\s*{'. s:endline
  131. let s:structureHead = '^\s*\%(' . s:blockstart . '\)\|'. s:functionDecl . s:endline . '\|\<new\s\+class\>\|' . s:matchStart
  132. let s:escapeDebugStops = 0
  133. function! DebugPrintReturn(scriptLine)
  134. if ! s:escapeDebugStops
  135. echo "debug:" . a:scriptLine
  136. let c = getchar()
  137. if c == "\<Del>"
  138. let s:escapeDebugStops = 1
  139. end
  140. endif
  141. endfunction
  142. function! GetLastRealCodeLNum(startline) " {{{
  143. let lnum = a:startline
  144. if b:GetLastRealCodeLNum_ADD && b:GetLastRealCodeLNum_ADD == lnum + 1
  145. let lnum = b:GetLastRealCodeLNum_ADD
  146. endif
  147. while lnum > 1
  148. let lnum = prevnonblank(lnum)
  149. let lastline = getline(lnum)
  150. if b:InPHPcode_and_script && lastline =~ '?>\s*$'
  151. let lnum = lnum - 1
  152. elseif lastline =~ '^\s*?>.*<?\%(php\)\=\s*$'
  153. let lnum = lnum - 1
  154. elseif lastline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
  155. let lnum = lnum - 1
  156. elseif lastline =~ '\*/\s*$'
  157. call cursor(lnum, 1)
  158. if lastline !~ '^\*/'
  159. call search('\*/', 'W')
  160. endif
  161. let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
  162. let lastline = getline(lnum)
  163. if lastline =~ '^\s*/\*'
  164. let lnum = lnum - 1
  165. else
  166. break
  167. endif
  168. elseif lastline =~? '\%(//\s*\|?>.*\)\@<!<?\%(php\)\=\s*$\|^\s*<script\>'
  169. while lastline !~ '\(<?.*\)\@<!?>' && lnum > 1
  170. let lnum = lnum - 1
  171. let lastline = getline(lnum)
  172. endwhile
  173. if lastline =~ '^\s*?>'
  174. let lnum = lnum - 1
  175. else
  176. break
  177. endif
  178. elseif lastline =~? '^\a\w*;\=$' && lastline !~? s:notPhpHereDoc
  179. let tofind=substitute( lastline, '\(\a\w*\);\=', '<<<\\s*[''"]\\=\1[''"]\\=$', '')
  180. while getline(lnum) !~? tofind && lnum > 1
  181. let lnum = lnum - 1
  182. endwhile
  183. elseif lastline =~ '^\s*[''"`][;,]'.s:endline || (lastline =~ '^[^''"`]*[''"`][;,]'.s:endline && IslinePHP(lnum, "") == "SpecStringEntrails")
  184. let tofind=substitute( lastline, '^.*\([''"`]\)[;,].*$', '^[^\1]\\+[\1]$\\|^[^\1]\\+[=([]\\s*[\1]', '')
  185. let trylnum = lnum
  186. while getline(trylnum) !~? tofind && trylnum > 1
  187. let trylnum = trylnum - 1
  188. endwhile
  189. if trylnum == 1
  190. break
  191. else
  192. if lastline =~ ';'.s:endline
  193. while getline(trylnum) !~? s:terminated && getline(trylnum) !~? '{'.s:endline && trylnum > 1
  194. let trylnum = prevnonblank(trylnum - 1)
  195. endwhile
  196. if trylnum == 1
  197. break
  198. end
  199. end
  200. let lnum = trylnum
  201. end
  202. else
  203. break
  204. endif
  205. endwhile
  206. if lnum==1 && getline(lnum) !~ '<?'
  207. let lnum=0
  208. endif
  209. if b:InPHPcode_and_script && 1 > b:InPHPcode
  210. let b:InPHPcode_and_script = 0
  211. endif
  212. return lnum
  213. endfunction " }}}
  214. function! Skippmatch2()
  215. let line = getline(".")
  216. if line =~ "\\([\"']\\).*/\\*.*\\1" || line =~ '\%(//\|#\[\@!\).*/\*'
  217. return 1
  218. else
  219. return 0
  220. endif
  221. endfun
  222. function! Skippmatch() " {{{
  223. let synname = synIDattr(synID(line("."), col("."), 0), "name")
  224. if synname ==? "Delimiter" || synname ==? "phpRegionDelimiter" || synname =~? "^phpParent" || synname ==? "phpArrayParens" || synname =~? '^php\%(Block\|Brace\)' || synname ==? "javaScriptBraces" || synname =~? '^php\%(Doc\)\?Comment' && b:UserIsTypingComment
  225. return 0
  226. else
  227. return 1
  228. endif
  229. endfun " }}}
  230. function! FindOpenBracket(lnum, blockStarter) " {{{
  231. call cursor(a:lnum, 1)
  232. let line = searchpair('{', '', '}', 'bW', 'Skippmatch()')
  233. if a:blockStarter == 1
  234. while line > 1
  235. let linec = getline(line)
  236. if linec =~ s:terminated || linec =~ s:structureHead
  237. break
  238. endif
  239. let line = GetLastRealCodeLNum(line - 1)
  240. endwhile
  241. endif
  242. return line
  243. endfun " }}}
  244. let s:blockChars = {'{':1, '[': 1, '(': 1, ')':-1, ']':-1, '}':-1}
  245. let s:blockCharsLUT = {'{':'{', '}':'{', '[':'[', ']':'[', '(':'(', ')':'('}
  246. function! BalanceDirection (str)
  247. let balance = {'{':0, '[': 0, '(': 0, 'none':0}
  248. let director = 'none'
  249. for c in split(a:str, '\zs')
  250. if has_key(s:blockChars, c)
  251. let balance[s:blockCharsLUT[c]] += s:blockChars[c]
  252. if balance[s:blockCharsLUT[c]]
  253. let director = s:blockCharsLUT[c]
  254. endif
  255. endif
  256. endfor
  257. return balance[director]
  258. endfun
  259. function! StripEndlineComments (line)
  260. let cleaned = substitute(a:line,'\v(//|#\[\@!)((([^"'']*(["''])[^"'']*\5)+[^"'']*$)|([^"'']*$))','','')
  261. if cleaned != a:line
  262. endif
  263. return cleaned
  264. endfun
  265. function! FindArrowIndent (lnum) " {{{
  266. let parentArrowPos = -1
  267. let cursorPos = -1
  268. let lnum = a:lnum
  269. while lnum > 1
  270. let last_line = getline(lnum)
  271. if last_line =~ '^\s*->'
  272. let parentArrowPos = indent(a:lnum)
  273. break
  274. else
  275. if b:PHP_noArrowMatching
  276. break
  277. endif
  278. let cleanedLnum = StripEndlineComments(last_line)
  279. if cleanedLnum =~ ')'.s:endline
  280. if BalanceDirection(cleanedLnum) <= 0
  281. call cursor(lnum, 1)
  282. call searchpos(')'.s:endline, 'cW', lnum)
  283. let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()')
  284. let cursorPos = col(".")
  285. if openedparent != lnum
  286. let lnum = openedparent
  287. continue
  288. else
  289. endif
  290. else
  291. let parentArrowPos = -1
  292. break
  293. end
  294. endif
  295. if cleanedLnum =~ '->'
  296. call cursor(lnum, cursorPos == -1 ? strwidth(cleanedLnum) : cursorPos)
  297. let parentArrowPos = searchpos('->', 'cWb', lnum)[1] - 1
  298. break
  299. else
  300. let parentArrowPos = -1
  301. break
  302. endif
  303. endif
  304. endwhile
  305. if parentArrowPos == -1
  306. let parentArrowPos = indent(lnum) + shiftwidth()
  307. end
  308. return parentArrowPos
  309. endfun "}}}
  310. function! FindTheIfOfAnElse (lnum, StopAfterFirstPrevElse) " {{{
  311. if getline(a:lnum) =~# '^\s*}\s*else\%(if\)\=\>'
  312. let beforeelse = a:lnum
  313. else
  314. let beforeelse = GetLastRealCodeLNum(a:lnum - 1)
  315. endif
  316. if !s:level
  317. let s:iftoskip = 0
  318. endif
  319. if getline(beforeelse) =~# '^\s*\%(}\s*\)\=else\%(\s*if\)\@!\>'
  320. let s:iftoskip = s:iftoskip + 1
  321. endif
  322. if getline(beforeelse) =~ '^\s*}'
  323. let beforeelse = FindOpenBracket(beforeelse, 0)
  324. if getline(beforeelse) =~ '^\s*{'
  325. let beforeelse = GetLastRealCodeLNum(beforeelse - 1)
  326. endif
  327. endif
  328. if !s:iftoskip && a:StopAfterFirstPrevElse && getline(beforeelse) =~# '^\s*\%([}]\s*\)\=else\%(if\)\=\>'
  329. return beforeelse
  330. endif
  331. if getline(beforeelse) !~# '^\s*if\>' && beforeelse>1 || s:iftoskip && beforeelse>1
  332. if s:iftoskip && getline(beforeelse) =~# '^\s*if\>'
  333. let s:iftoskip = s:iftoskip - 1
  334. endif
  335. let s:level = s:level + 1
  336. let beforeelse = FindTheIfOfAnElse(beforeelse, a:StopAfterFirstPrevElse)
  337. endif
  338. return beforeelse
  339. endfunction " }}}
  340. let s:defaultORcase = '^\s*\%(default\|case\).*:'
  341. function! FindTheSwitchIndent (lnum) " {{{
  342. let test = GetLastRealCodeLNum(a:lnum - 1)
  343. if test <= 1
  344. return indent(1) - shiftwidth() * b:PHP_vintage_case_default_indent
  345. end
  346. while getline(test) =~ '^\s*}' && test > 1
  347. let test = GetLastRealCodeLNum(FindOpenBracket(test, 0) - 1)
  348. if getline(test) =~ '^\s*switch\>'
  349. let test = GetLastRealCodeLNum(test - 1)
  350. endif
  351. endwhile
  352. if getline(test) =~# '^\s*switch\>'
  353. return indent(test)
  354. elseif getline(test) =~# s:defaultORcase
  355. return indent(test) - shiftwidth() * b:PHP_vintage_case_default_indent
  356. else
  357. return FindTheSwitchIndent(test)
  358. endif
  359. endfunction "}}}
  360. let s:SynPHPMatchGroups = {'phpparent':1, 'delimiter':1, 'define':1, 'storageclass':1, 'structure':1, 'exception':1}
  361. function! IslinePHP (lnum, tofind) " {{{
  362. let cline = getline(a:lnum)
  363. if a:tofind==""
  364. let tofind = "^\\s*[\"'`]*\\s*\\zs\\S"
  365. else
  366. let tofind = a:tofind
  367. endif
  368. let tofind = tofind . '\c'
  369. let coltotest = match (cline, tofind) + 1
  370. let synname = synIDattr(synID(a:lnum, coltotest, 0), "name")
  371. if synname ==? 'phpStringSingle' || synname ==? 'phpStringDouble' || synname ==? 'phpBacktick'
  372. if cline !~ '^\s*[''"`]' " ??? XXX
  373. return "SpecStringEntrails"
  374. else
  375. return synname
  376. end
  377. end
  378. if get(s:SynPHPMatchGroups, tolower(synname)) || synname =~ '^php' || synname =~? '^javaScript'
  379. return synname
  380. else
  381. return ""
  382. endif
  383. endfunction " }}}
  384. let s:autoresetoptions = 0
  385. if ! s:autoresetoptions
  386. let s:autoresetoptions = 1
  387. endif
  388. function! ResetPhpOptions()
  389. if ! b:optionsset && &filetype =~ "php"
  390. if b:PHP_autoformatcomment
  391. setlocal comments=s1:/*,mb:*,ex:*/,://,f:#[,:#
  392. setlocal formatoptions-=t
  393. setlocal formatoptions+=q
  394. setlocal formatoptions+=r
  395. setlocal formatoptions+=o
  396. setlocal formatoptions+=c
  397. setlocal formatoptions+=b
  398. endif
  399. let b:optionsset = 1
  400. endif
  401. endfunc
  402. call ResetPhpOptions()
  403. function! GetPhpIndentVersion()
  404. return "1.75"
  405. endfun
  406. function! GetPhpIndent()
  407. let b:GetLastRealCodeLNum_ADD = 0
  408. let UserIsEditing=0
  409. if b:PHP_oldchangetick != b:changedtick
  410. let b:PHP_oldchangetick = b:changedtick
  411. let UserIsEditing=1
  412. endif
  413. if b:PHP_default_indenting
  414. let b:PHP_default_indenting = g:PHP_default_indenting * shiftwidth()
  415. endif
  416. let cline = getline(v:lnum)
  417. if !b:PHP_indentinghuge && b:PHP_lastindented > b:PHP_indentbeforelast
  418. if b:PHP_indentbeforelast
  419. let b:PHP_indentinghuge = 1
  420. endif
  421. let b:PHP_indentbeforelast = b:PHP_lastindented
  422. endif
  423. if b:InPHPcode_checked && prevnonblank(v:lnum - 1) != b:PHP_lastindented
  424. if b:PHP_indentinghuge
  425. let b:PHP_indentinghuge = 0
  426. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  427. endif
  428. let real_PHP_lastindented = v:lnum
  429. let b:PHP_LastIndentedWasComment=0
  430. let b:PHP_InsideMultilineComment=0
  431. let b:PHP_indentbeforelast = 0
  432. let b:InPHPcode = 0
  433. let b:InPHPcode_checked = 0
  434. let b:InPHPcode_and_script = 0
  435. let b:InPHPcode_tofind = ""
  436. elseif v:lnum > b:PHP_lastindented
  437. let real_PHP_lastindented = b:PHP_lastindented
  438. else
  439. let real_PHP_lastindented = v:lnum
  440. endif
  441. let b:PHP_lastindented = v:lnum
  442. if !b:InPHPcode_checked " {{{ One time check
  443. let b:InPHPcode_checked = 1
  444. let b:UserIsTypingComment = 0
  445. let synname = ""
  446. if cline !~ '<?.*?>'
  447. let synname = IslinePHP (prevnonblank(v:lnum), "")
  448. endif
  449. if synname!=""
  450. if synname ==? "SpecStringEntrails"
  451. let b:InPHPcode = -1 " thumb down
  452. let b:InPHPcode_tofind = ""
  453. elseif synname !=? "phpHereDoc" && synname !=? "phpHereDocDelimiter"
  454. let b:InPHPcode = 1
  455. let b:InPHPcode_tofind = ""
  456. if synname =~? '^php\%(Doc\)\?Comment'
  457. let b:UserIsTypingComment = 1
  458. let b:InPHPcode_checked = 0
  459. endif
  460. if synname =~? '^javaScript'
  461. let b:InPHPcode_and_script = 1
  462. endif
  463. else
  464. let b:InPHPcode = 0
  465. let lnum = v:lnum - 1
  466. while getline(lnum) !~? '<<<\s*[''"]\=\a\w*[''"]\=$' && lnum > 1
  467. let lnum = lnum - 1
  468. endwhile
  469. let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<\s*[''"]\=\(\a\w*\)[''"]\=$', '^\\s*\1;\\=$', '')
  470. endif
  471. else
  472. let b:InPHPcode = 0
  473. let b:InPHPcode_tofind = s:PHP_startindenttag
  474. endif
  475. endif "!b:InPHPcode_checked }}}
  476. " Test if we are indenting PHP code {{{
  477. let lnum = prevnonblank(v:lnum - 1)
  478. let last_line = getline(lnum)
  479. let endline= s:endline
  480. if b:InPHPcode_tofind!=""
  481. if cline =~? b:InPHPcode_tofind
  482. let b:InPHPcode_tofind = ""
  483. let b:UserIsTypingComment = 0
  484. if b:InPHPcode == -1
  485. let b:InPHPcode = 1
  486. return -1
  487. end
  488. let b:InPHPcode = 1
  489. if cline =~ '\*/'
  490. call cursor(v:lnum, 1)
  491. if cline !~ '^\*/'
  492. call search('\*/', 'W')
  493. endif
  494. let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
  495. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  496. let b:PHP_LastIndentedWasComment = 0
  497. if cline =~ '^\s*\*/'
  498. return indent(lnum) + 1
  499. else
  500. return indent(lnum)
  501. endif
  502. elseif cline =~? '<script\>'
  503. let b:InPHPcode_and_script = 1
  504. let b:GetLastRealCodeLNum_ADD = v:lnum
  505. endif
  506. endif
  507. endif
  508. if 1 == b:InPHPcode
  509. if !b:InPHPcode_and_script && last_line =~ '\%(<?.*\)\@<!?>\%(.*<?\)\@!' && IslinePHP(lnum, '?>')=~?"Delimiter"
  510. if cline !~? s:PHP_startindenttag
  511. let b:InPHPcode = 0
  512. let b:InPHPcode_tofind = s:PHP_startindenttag
  513. elseif cline =~? '<script\>'
  514. let b:InPHPcode_and_script = 1
  515. endif
  516. elseif last_line =~ '^[^''"`]\+[''"`]$' && last_line !~ '^\s*\%(//\|#\[\@!\|/\*.*\*/\s*$\)' " a string identifier with nothing after it and no other string identifier before
  517. let b:InPHPcode = -1
  518. let b:InPHPcode_tofind = substitute( last_line, '^.*\([''"`]\).*$', '^[^\1]*\1[;,]$', '')
  519. elseif last_line =~? '<<<\s*[''"]\=\a\w*[''"]\=$'
  520. let b:InPHPcode = 0
  521. let b:InPHPcode_tofind = substitute( last_line, '^.*<<<\s*[''"]\=\(\a\w*\)[''"]\=$', '^\\s*\1;\\=$', '')
  522. elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' && getline(v:lnum + 1) !~ '^\s*\*'
  523. let b:InPHPcode = 0
  524. let b:InPHPcode_tofind = '\*/'
  525. elseif cline =~? '^\s*</script>'
  526. let b:InPHPcode = 0
  527. let b:InPHPcode_tofind = s:PHP_startindenttag
  528. endif
  529. endif " }}}
  530. if 1 > b:InPHPcode && !b:InPHPcode_and_script
  531. return -1
  532. endif
  533. " Indent successive // or # comment the same way the first is {{{
  534. let addSpecial = 0
  535. if cline =~ '^\s*\%(//\|#\[\@!\|/\*.*\*/\s*$\)'
  536. let addSpecial = b:PHP_outdentSLComments
  537. if b:PHP_LastIndentedWasComment == 1
  538. return indent(real_PHP_lastindented)
  539. endif
  540. let b:PHP_LastIndentedWasComment = 1
  541. else
  542. let b:PHP_LastIndentedWasComment = 0
  543. endif " }}}
  544. " Indent multiline /* comments correctly {{{
  545. if b:PHP_InsideMultilineComment || b:UserIsTypingComment
  546. if cline =~ '^\s*\*\%(\/\)\@!'
  547. if last_line =~ '^\s*/\*'
  548. return indent(lnum) + 1
  549. else
  550. return indent(lnum)
  551. endif
  552. else
  553. let b:PHP_InsideMultilineComment = 0
  554. endif
  555. endif
  556. if !b:PHP_InsideMultilineComment && cline =~ '^\s*/\*\%(.*\*/\)\@!'
  557. if getline(v:lnum + 1) !~ '^\s*\*'
  558. return -1
  559. endif
  560. let b:PHP_InsideMultilineComment = 1
  561. endif " }}}
  562. " Things always indented at col 1 (PHP delimiter: <?, ?>, Heredoc end) {{{
  563. if cline =~# '^\s*<?' && cline !~ '?>' && b:PHP_outdentphpescape
  564. return 0
  565. endif
  566. if cline =~ '^\s*?>' && cline !~# '<?' && b:PHP_outdentphpescape
  567. return 0
  568. endif
  569. if (cline =~? '^\s*\a\w*;$\|^\a\w*$' || (cline =~? '^\s*[''"`][;,]' && IslinePHP(v:lnum, '[;,]') !~? '^\(phpString[SD]\|phpBacktick\)') ) && cline !~? s:notPhpHereDoc
  570. return 0
  571. endif " }}}
  572. let s:level = 0
  573. let lnum = GetLastRealCodeLNum(v:lnum - 1)
  574. let last_line = getline(lnum)
  575. let ind = indent(lnum)
  576. if ind==0 && b:PHP_default_indenting
  577. let ind = b:PHP_default_indenting
  578. endif
  579. if lnum == 0
  580. return b:PHP_default_indenting + addSpecial
  581. endif
  582. if cline =~ '^\s*}\%(}}\)\@!'
  583. let ind = indent(FindOpenBracket(v:lnum, 1))
  584. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  585. if b:PHP_BracesAtCodeLevel
  586. let ind = ind + shiftwidth()
  587. endif
  588. return ind
  589. endif
  590. if cline =~ '^\s*\*/'
  591. call cursor(v:lnum, 1)
  592. if cline !~ '^\*/'
  593. call search('\*/', 'W')
  594. endif
  595. let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
  596. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  597. if cline =~ '^\s*\*/'
  598. return indent(lnum) + 1
  599. else
  600. return indent(lnum)
  601. endif
  602. endif
  603. if last_line =~ '[;}]'.endline && last_line !~ '^[)\]]' && last_line !~# s:defaultORcase && last_line !~ '^\s*[''"`][;,]'
  604. if ind==b:PHP_default_indenting
  605. return b:PHP_default_indenting + addSpecial
  606. elseif b:PHP_indentinghuge && ind==b:PHP_CurrentIndentLevel && cline !~# '^\s*\%(else\|\%(case\|default\).*:\|[})];\=\)' && last_line !~# '^\s*\%(\%(}\s*\)\=else\)\|^\(\s*\S\+\s*\)\+}'.endline && getline(GetLastRealCodeLNum(lnum - 1))=~';'.endline
  607. return b:PHP_CurrentIndentLevel + addSpecial
  608. endif
  609. endif
  610. let LastLineClosed = 0
  611. let terminated = s:terminated
  612. let unstated = s:unstated
  613. if ind != b:PHP_default_indenting && cline =~# '^\s*else\%(if\)\=\>'
  614. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  615. return indent(FindTheIfOfAnElse(v:lnum, 1))
  616. elseif cline =~# s:defaultORcase
  617. return FindTheSwitchIndent(v:lnum) + shiftwidth() * b:PHP_vintage_case_default_indent
  618. elseif cline =~ '^\s*)\=\s*{'
  619. let previous_line = last_line
  620. let last_line_num = lnum
  621. while last_line_num > 1
  622. if previous_line =~ terminated || previous_line =~ s:structureHead
  623. let ind = indent(last_line_num)
  624. if b:PHP_BracesAtCodeLevel
  625. let ind = ind + shiftwidth()
  626. endif
  627. return ind
  628. endif
  629. let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
  630. let previous_line = getline(last_line_num)
  631. endwhile
  632. elseif cline =~ '^\s*->'
  633. return FindArrowIndent(lnum)
  634. elseif last_line =~# unstated && cline !~ '^\s*);\='.endline
  635. let ind = ind + shiftwidth() " we indent one level further when the preceding line is not stated
  636. return ind + addSpecial
  637. elseif (ind != b:PHP_default_indenting || last_line =~ '^[)\]]' ) && last_line =~ terminated
  638. let previous_line = last_line
  639. let last_line_num = lnum
  640. let LastLineClosed = 1
  641. let isSingleLineBlock = 0
  642. while 1
  643. if ! isSingleLineBlock && previous_line =~ '^\s*}\|;\s*}'.endline
  644. call cursor(last_line_num, 1)
  645. if previous_line !~ '^}'
  646. call search('}\|;\s*}'.endline, 'W')
  647. end
  648. let oldLastLine = last_line_num
  649. let last_line_num = searchpair('{', '', '}', 'bW', 'Skippmatch()')
  650. if getline(last_line_num) =~ '^\s*{'
  651. let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
  652. elseif oldLastLine == last_line_num
  653. let isSingleLineBlock = 1
  654. continue
  655. endif
  656. let previous_line = getline(last_line_num)
  657. continue
  658. else
  659. let isSingleLineBlock = 0
  660. if getline(last_line_num) =~# '^\s*else\%(if\)\=\>'
  661. let last_line_num = FindTheIfOfAnElse(last_line_num, 0)
  662. continue
  663. endif
  664. let last_match = last_line_num
  665. let one_ahead_indent = indent(last_line_num)
  666. let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
  667. let two_ahead_indent = indent(last_line_num)
  668. let after_previous_line = previous_line
  669. let previous_line = getline(last_line_num)
  670. if previous_line =~# s:defaultORcase.'\|{'.endline
  671. break
  672. endif
  673. if after_previous_line=~# '^\s*'.s:blockstart.'.*)'.endline && previous_line =~# '[;}]'.endline
  674. break
  675. endif
  676. if one_ahead_indent == two_ahead_indent || last_line_num < 1
  677. if previous_line =~# '\%(;\|^\s*}\)'.endline || last_line_num < 1
  678. break
  679. endif
  680. endif
  681. endif
  682. endwhile
  683. if indent(last_match) != ind
  684. let ind = indent(last_match)
  685. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  686. return ind + addSpecial
  687. endif
  688. endif
  689. if (last_line !~ '^\s*}\%(}}\)\@!')
  690. let plinnum = GetLastRealCodeLNum(lnum - 1)
  691. else
  692. let plinnum = GetLastRealCodeLNum(FindOpenBracket(lnum, 1) - 1)
  693. endif
  694. let AntepenultimateLine = getline(plinnum)
  695. let last_line = StripEndlineComments(last_line)
  696. if ind == b:PHP_default_indenting
  697. if last_line =~ terminated && last_line !~# s:defaultORcase
  698. let LastLineClosed = 1
  699. endif
  700. endif
  701. if !LastLineClosed
  702. let openedparent = -1
  703. if last_line =~# '[{(\[]'.endline || last_line =~? '\h\w*\s*(.*,$' && AntepenultimateLine !~ '[,(\[]'.endline && BalanceDirection(last_line) > 0
  704. let dontIndent = 0
  705. if last_line =~ '\S\+\s*{'.endline && last_line !~ '^\s*[)\]]\+\(\s*:\s*'.s:PHP_validVariable.'\)\=\s*{'.endline && last_line !~ s:structureHead
  706. let dontIndent = 1
  707. endif
  708. if !dontIndent && (!b:PHP_BracesAtCodeLevel || last_line !~# '^\s*{')
  709. let ind = ind + shiftwidth()
  710. endif
  711. if b:PHP_IndentFunctionCallParameters && last_line =~ s:multilineFunctionCall && last_line !~ s:structureHead && last_line !~ s:arrayDecl
  712. let ind = ind + b:PHP_IndentFunctionCallParameters * shiftwidth()
  713. endif
  714. if b:PHP_IndentFunctionDeclarationParameters && last_line =~ s:multilineFunctionDecl
  715. let ind = ind + b:PHP_IndentFunctionDeclarationParameters * shiftwidth()
  716. endif
  717. if b:PHP_BracesAtCodeLevel || b:PHP_vintage_case_default_indent == 1
  718. let b:PHP_CurrentIndentLevel = ind
  719. endif
  720. elseif last_line =~ '),'.endline && BalanceDirection(last_line) < 0
  721. call cursor(lnum, 1)
  722. call searchpos('),'.endline, 'cW')
  723. let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()')
  724. if openedparent != lnum
  725. let ind = indent(openedparent)
  726. endif
  727. elseif last_line =~ s:structureHead
  728. let ind = ind + shiftwidth()
  729. elseif AntepenultimateLine =~ '{'.endline && AntepenultimateLine !~? '^\s*use\>' && AntepenultimateLine !~? s:matchStart || AntepenultimateLine =~ terminated || AntepenultimateLine =~# s:defaultORcase
  730. let ind = ind + shiftwidth()
  731. endif
  732. if openedparent >= 0
  733. let last_line = StripEndlineComments(getline(openedparent))
  734. endif
  735. endif
  736. if cline =~ '^\s*[)\]];\='
  737. call cursor(v:lnum, 1)
  738. call searchpos('[)\]]', 'cW')
  739. let matchedBlockChar = cline[col('.')-1]
  740. let openedparent = searchpair('\M'.s:blockCharsLUT[matchedBlockChar], '', '\M'.matchedBlockChar, 'bW', 'Skippmatch()')
  741. if openedparent != v:lnum
  742. let ind = indent(openedparent)
  743. endif
  744. elseif last_line =~ '^\s*->' && last_line !~? s:structureHead && BalanceDirection(last_line) <= 0
  745. let ind = ind - shiftwidth()
  746. endif
  747. let b:PHP_CurrentIndentLevel = ind
  748. return ind + addSpecial
  749. endfunction