php.vim 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  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: 2020 Mar 05
  7. " Version: 1.70
  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 s:searchpairflags = 'bWr'
  110. if &fileformat == "unix" && exists("PHP_removeCRwhenUnix") && PHP_removeCRwhenUnix
  111. silent! %s/\r$//g
  112. endif
  113. if exists("*GetPhpIndent")
  114. call ResetPhpOptions()
  115. finish " XXX -- comment this line for easy dev
  116. endif
  117. let s:endline = '\s*\%(//.*\|#.*\|/\*.*\*/\s*\)\=$'
  118. let s:PHP_validVariable = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
  119. let s:notPhpHereDoc = '\%(break\|return\|continue\|exit\|die\|else\|end\%(if\|while\|for\|foreach\|switch\)\)'
  120. let s:blockstart = '\%(\%(\%(}\s*\)\=else\%(\s\+\)\=\)\=if\>\|\%(}\s*\)\?else\>\|do\>\|while\>\|switch\>\|case\>\|default\>\|for\%(each\)\=\>\|declare\>\|class\>\|trait\>\|\%()\s*\)\=use\>\|interface\>\|abstract\>\|final\>\|try\>\|\%(}\s*\)\=catch\>\|\%(}\s*\)\=finally\>\)'
  121. let s:functionDeclPrefix = '\<function\>\%(\s\+&\='.s:PHP_validVariable.'\)\=\s*('
  122. let s:functionDecl = s:functionDeclPrefix.'.*'
  123. let s:multilineFunctionDecl = s:functionDeclPrefix.s:endline
  124. let s:arrayDecl = '\<array\>\s*(.*'
  125. let s:multilineFunctionCall = s:PHP_validVariable.'\s*('.s:endline
  126. let s:unstated = '\%(^\s*'.s:blockstart.'.*)\|\%(//.*\)\@<!\<e'.'lse\>\)'.s:endline
  127. let s:terminated = '\%(\%(;\%(\s*\%(?>\|}\)\)\=\|<<<\s*[''"]\=\a\w*[''"]\=$\|^\s*}\|^\s*'.s:PHP_validVariable.':\)'.s:endline.'\)'
  128. let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!'
  129. let s:structureHead = '^\s*\%(' . s:blockstart . '\)\|'. s:functionDecl . s:endline . '\|\<new\s\+class\>'
  130. let s:escapeDebugStops = 0
  131. function! DebugPrintReturn(scriptLine)
  132. if ! s:escapeDebugStops
  133. echo "debug:" . a:scriptLine
  134. let c = getchar()
  135. if c == "\<Del>"
  136. let s:escapeDebugStops = 1
  137. end
  138. endif
  139. endfunction
  140. function! GetLastRealCodeLNum(startline) " {{{
  141. let lnum = a:startline
  142. if b:GetLastRealCodeLNum_ADD && b:GetLastRealCodeLNum_ADD == lnum + 1
  143. let lnum = b:GetLastRealCodeLNum_ADD
  144. endif
  145. while lnum > 1
  146. let lnum = prevnonblank(lnum)
  147. let lastline = getline(lnum)
  148. if b:InPHPcode_and_script && lastline =~ '?>\s*$'
  149. let lnum = lnum - 1
  150. elseif lastline =~ '^\s*?>.*<?\%(php\)\=\s*$'
  151. let lnum = lnum - 1
  152. elseif lastline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
  153. let lnum = lnum - 1
  154. elseif lastline =~ '\*/\s*$'
  155. call cursor(lnum, 1)
  156. if lastline !~ '^\*/'
  157. call search('\*/', 'W')
  158. endif
  159. let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
  160. let lastline = getline(lnum)
  161. if lastline =~ '^\s*/\*'
  162. let lnum = lnum - 1
  163. else
  164. break
  165. endif
  166. elseif lastline =~? '\%(//\s*\|?>.*\)\@<!<?\%(php\)\=\s*$\|^\s*<script\>'
  167. while lastline !~ '\(<?.*\)\@<!?>' && lnum > 1
  168. let lnum = lnum - 1
  169. let lastline = getline(lnum)
  170. endwhile
  171. if lastline =~ '^\s*?>'
  172. let lnum = lnum - 1
  173. else
  174. break
  175. endif
  176. elseif lastline =~? '^\a\w*;\=$' && lastline !~? s:notPhpHereDoc
  177. let tofind=substitute( lastline, '\(\a\w*\);\=', '<<<\\s*[''"]\\=\1[''"]\\=$', '')
  178. while getline(lnum) !~? tofind && lnum > 1
  179. let lnum = lnum - 1
  180. endwhile
  181. elseif lastline =~ '^\s*[''"`][;,]' || (lastline =~ '^[^''"`]*[''"`][;,]'.s:endline && IslinePHP(lnum, "") == "SpecStringEntrails")
  182. let tofind=substitute( lastline, '^.*\([''"`]\)[;,].*$', '^[^\1]\\+[\1]$\\|^[^\1]\\+[=([]\\s*[\1]', '')
  183. let trylnum = lnum
  184. while getline(trylnum) !~? tofind && trylnum > 1
  185. let trylnum = trylnum - 1
  186. endwhile
  187. if trylnum == 1
  188. break
  189. else
  190. if lastline =~ ';'.s:endline
  191. while getline(trylnum) !~? s:terminated && getline(trylnum) !~? '{'.s:endline && trylnum > 1
  192. let trylnum = prevnonblank(trylnum - 1)
  193. endwhile
  194. if trylnum == 1
  195. break
  196. end
  197. end
  198. let lnum = trylnum
  199. end
  200. else
  201. break
  202. endif
  203. endwhile
  204. if lnum==1 && getline(lnum) !~ '<?'
  205. let lnum=0
  206. endif
  207. if b:InPHPcode_and_script && 1 > b:InPHPcode
  208. let b:InPHPcode_and_script = 0
  209. endif
  210. return lnum
  211. endfunction " }}}
  212. function! Skippmatch2()
  213. let line = getline(".")
  214. if line =~ "\\([\"']\\).*/\\*.*\\1" || line =~ '\%(//\|#\).*/\*'
  215. return 1
  216. else
  217. return 0
  218. endif
  219. endfun
  220. function! Skippmatch() " {{{
  221. let synname = synIDattr(synID(line("."), col("."), 0), "name")
  222. if synname ==? "Delimiter" || synname ==? "phpRegionDelimiter" || synname =~? "^phpParent" || synname ==? "phpArrayParens" || synname =~? '^php\%(Block\|Brace\)' || synname ==? "javaScriptBraces" || synname =~? '^php\%(Doc\)\?Comment' && b:UserIsTypingComment
  223. return 0
  224. else
  225. return 1
  226. endif
  227. endfun " }}}
  228. function! FindOpenBracket(lnum, blockStarter) " {{{
  229. call cursor(a:lnum, 1)
  230. let line = searchpair('{', '', '}', 'bW', 'Skippmatch()')
  231. if a:blockStarter == 1
  232. while line > 1
  233. let linec = getline(line)
  234. if linec =~ s:terminated || linec =~ s:structureHead
  235. break
  236. endif
  237. let line = GetLastRealCodeLNum(line - 1)
  238. endwhile
  239. endif
  240. return line
  241. endfun " }}}
  242. let s:blockChars = {'{':1, '[': 1, '(': 1, ')':-1, ']':-1, '}':-1}
  243. let s:blockCharsLUT = {'{':'{', '}':'{', '[':'[', ']':'[', '(':'(', ')':'('}
  244. function! BalanceDirection (str)
  245. let balance = {'{':0, '[': 0, '(': 0, 'none':0}
  246. let director = 'none'
  247. for c in split(a:str, '\zs')
  248. if has_key(s:blockChars, c)
  249. let balance[s:blockCharsLUT[c]] += s:blockChars[c]
  250. if balance[s:blockCharsLUT[c]]
  251. let director = s:blockCharsLUT[c]
  252. endif
  253. endif
  254. endfor
  255. return balance[director]
  256. endfun
  257. function! StripEndlineComments (line)
  258. return substitute(a:line,"\\(//\\|#\\)\\(\\(\\([^\"']*\\([\"']\\)[^\"']*\\5\\)\\+[^\"']*$\\)\\|\\([^\"']*$\\)\\)",'','')
  259. endfun
  260. function! FindArrowIndent (lnum) " {{{
  261. let parrentArrowPos = -1
  262. let cursorPos = -1
  263. let lnum = a:lnum
  264. while lnum > 1
  265. let last_line = getline(lnum)
  266. if last_line =~ '^\s*->'
  267. let parrentArrowPos = indent(a:lnum)
  268. break
  269. else
  270. if b:PHP_noArrowMatching
  271. break
  272. endif
  273. let cleanedLnum = StripEndlineComments(last_line)
  274. if cleanedLnum =~ ')'.s:endline
  275. if BalanceDirection(cleanedLnum) <= 0
  276. call cursor(lnum, 1)
  277. call searchpos(')'.s:endline, 'cW', lnum)
  278. let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()')
  279. let cursorPos = col(".")
  280. if openedparent != lnum
  281. let lnum = openedparent
  282. continue
  283. else
  284. endif
  285. else
  286. let parrentArrowPos = -1
  287. break
  288. end
  289. endif
  290. if cleanedLnum =~ '->'
  291. call cursor(lnum, cursorPos == -1 ? strwidth(cleanedLnum) : cursorPos)
  292. let parrentArrowPos = searchpos('->', 'cWb', lnum)[1] - 1
  293. break
  294. else
  295. let parrentArrowPos = -1
  296. break
  297. endif
  298. endif
  299. endwhile
  300. if parrentArrowPos == -1
  301. let parrentArrowPos = indent(lnum) + shiftwidth()
  302. end
  303. return parrentArrowPos
  304. endfun "}}}
  305. function! FindTheIfOfAnElse (lnum, StopAfterFirstPrevElse) " {{{
  306. if getline(a:lnum) =~# '^\s*}\s*else\%(if\)\=\>'
  307. let beforeelse = a:lnum
  308. else
  309. let beforeelse = GetLastRealCodeLNum(a:lnum - 1)
  310. endif
  311. if !s:level
  312. let s:iftoskip = 0
  313. endif
  314. if getline(beforeelse) =~# '^\s*\%(}\s*\)\=else\%(\s*if\)\@!\>'
  315. let s:iftoskip = s:iftoskip + 1
  316. endif
  317. if getline(beforeelse) =~ '^\s*}'
  318. let beforeelse = FindOpenBracket(beforeelse, 0)
  319. if getline(beforeelse) =~ '^\s*{'
  320. let beforeelse = GetLastRealCodeLNum(beforeelse - 1)
  321. endif
  322. endif
  323. if !s:iftoskip && a:StopAfterFirstPrevElse && getline(beforeelse) =~# '^\s*\%([}]\s*\)\=else\%(if\)\=\>'
  324. return beforeelse
  325. endif
  326. if getline(beforeelse) !~# '^\s*if\>' && beforeelse>1 || s:iftoskip && beforeelse>1
  327. if s:iftoskip && getline(beforeelse) =~# '^\s*if\>'
  328. let s:iftoskip = s:iftoskip - 1
  329. endif
  330. let s:level = s:level + 1
  331. let beforeelse = FindTheIfOfAnElse(beforeelse, a:StopAfterFirstPrevElse)
  332. endif
  333. return beforeelse
  334. endfunction " }}}
  335. let s:defaultORcase = '^\s*\%(default\|case\).*:'
  336. function! FindTheSwitchIndent (lnum) " {{{
  337. let test = GetLastRealCodeLNum(a:lnum - 1)
  338. if test <= 1
  339. return indent(1) - shiftwidth() * b:PHP_vintage_case_default_indent
  340. end
  341. while getline(test) =~ '^\s*}' && test > 1
  342. let test = GetLastRealCodeLNum(FindOpenBracket(test, 0) - 1)
  343. if getline(test) =~ '^\s*switch\>'
  344. let test = GetLastRealCodeLNum(test - 1)
  345. endif
  346. endwhile
  347. if getline(test) =~# '^\s*switch\>'
  348. return indent(test)
  349. elseif getline(test) =~# s:defaultORcase
  350. return indent(test) - shiftwidth() * b:PHP_vintage_case_default_indent
  351. else
  352. return FindTheSwitchIndent(test)
  353. endif
  354. endfunction "}}}
  355. let s:SynPHPMatchGroups = {'phpparent':1, 'delimiter':1, 'define':1, 'storageclass':1, 'structure':1, 'exception':1}
  356. function! IslinePHP (lnum, tofind) " {{{
  357. let cline = getline(a:lnum)
  358. if a:tofind==""
  359. let tofind = "^\\s*[\"'`]*\\s*\\zs\\S"
  360. else
  361. let tofind = a:tofind
  362. endif
  363. let tofind = tofind . '\c'
  364. let coltotest = match (cline, tofind) + 1
  365. let synname = synIDattr(synID(a:lnum, coltotest, 0), "name")
  366. if synname ==? 'phpStringSingle' || synname ==? 'phpStringDouble' || synname ==? 'phpBacktick'
  367. if cline !~ '^\s*[''"`]' " ??? XXX
  368. return "SpecStringEntrails"
  369. else
  370. return synname
  371. end
  372. end
  373. if get(s:SynPHPMatchGroups, tolower(synname)) || synname =~ '^php' || synname =~? '^javaScript'
  374. return synname
  375. else
  376. return ""
  377. endif
  378. endfunction " }}}
  379. let s:autoresetoptions = 0
  380. if ! s:autoresetoptions
  381. let s:autoresetoptions = 1
  382. endif
  383. function! ResetPhpOptions()
  384. if ! b:optionsset && &filetype =~ "php"
  385. if b:PHP_autoformatcomment
  386. setlocal comments=s1:/*,mb:*,ex:*/,://,:#
  387. setlocal formatoptions-=t
  388. setlocal formatoptions+=q
  389. setlocal formatoptions+=r
  390. setlocal formatoptions+=o
  391. setlocal formatoptions+=c
  392. setlocal formatoptions+=b
  393. endif
  394. let b:optionsset = 1
  395. endif
  396. endfunc
  397. call ResetPhpOptions()
  398. function! GetPhpIndentVersion()
  399. return "1.70-bundle"
  400. endfun
  401. function! GetPhpIndent()
  402. let b:GetLastRealCodeLNum_ADD = 0
  403. let UserIsEditing=0
  404. if b:PHP_oldchangetick != b:changedtick
  405. let b:PHP_oldchangetick = b:changedtick
  406. let UserIsEditing=1
  407. endif
  408. if b:PHP_default_indenting
  409. let b:PHP_default_indenting = g:PHP_default_indenting * shiftwidth()
  410. endif
  411. let cline = getline(v:lnum)
  412. if !b:PHP_indentinghuge && b:PHP_lastindented > b:PHP_indentbeforelast
  413. if b:PHP_indentbeforelast
  414. let b:PHP_indentinghuge = 1
  415. endif
  416. let b:PHP_indentbeforelast = b:PHP_lastindented
  417. endif
  418. if b:InPHPcode_checked && prevnonblank(v:lnum - 1) != b:PHP_lastindented
  419. if b:PHP_indentinghuge
  420. let b:PHP_indentinghuge = 0
  421. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  422. endif
  423. let real_PHP_lastindented = v:lnum
  424. let b:PHP_LastIndentedWasComment=0
  425. let b:PHP_InsideMultilineComment=0
  426. let b:PHP_indentbeforelast = 0
  427. let b:InPHPcode = 0
  428. let b:InPHPcode_checked = 0
  429. let b:InPHPcode_and_script = 0
  430. let b:InPHPcode_tofind = ""
  431. elseif v:lnum > b:PHP_lastindented
  432. let real_PHP_lastindented = b:PHP_lastindented
  433. else
  434. let real_PHP_lastindented = v:lnum
  435. endif
  436. let b:PHP_lastindented = v:lnum
  437. if !b:InPHPcode_checked " {{{ One time check
  438. let b:InPHPcode_checked = 1
  439. let b:UserIsTypingComment = 0
  440. let synname = ""
  441. if cline !~ '<?.*?>'
  442. let synname = IslinePHP (prevnonblank(v:lnum), "")
  443. endif
  444. if synname!=""
  445. if synname ==? "SpecStringEntrails"
  446. let b:InPHPcode = -1 " thumb down
  447. let b:InPHPcode_tofind = ""
  448. elseif synname !=? "phpHereDoc" && synname !=? "phpHereDocDelimiter"
  449. let b:InPHPcode = 1
  450. let b:InPHPcode_tofind = ""
  451. if synname =~? '^php\%(Doc\)\?Comment'
  452. let b:UserIsTypingComment = 1
  453. let b:InPHPcode_checked = 0
  454. endif
  455. if synname =~? '^javaScript'
  456. let b:InPHPcode_and_script = 1
  457. endif
  458. else
  459. let b:InPHPcode = 0
  460. let lnum = v:lnum - 1
  461. while getline(lnum) !~? '<<<\s*[''"]\=\a\w*[''"]\=$' && lnum > 1
  462. let lnum = lnum - 1
  463. endwhile
  464. let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<\s*[''"]\=\(\a\w*\)[''"]\=$', '^\\s*\1;\\=$', '')
  465. endif
  466. else
  467. let b:InPHPcode = 0
  468. let b:InPHPcode_tofind = s:PHP_startindenttag
  469. endif
  470. endif "!b:InPHPcode_checked }}}
  471. " Test if we are indenting PHP code {{{
  472. let lnum = prevnonblank(v:lnum - 1)
  473. let last_line = getline(lnum)
  474. let endline= s:endline
  475. if b:InPHPcode_tofind!=""
  476. if cline =~? b:InPHPcode_tofind
  477. let b:InPHPcode_tofind = ""
  478. let b:UserIsTypingComment = 0
  479. if b:InPHPcode == -1
  480. let b:InPHPcode = 1
  481. return -1
  482. end
  483. let b:InPHPcode = 1
  484. if cline =~ '\*/'
  485. call cursor(v:lnum, 1)
  486. if cline !~ '^\*/'
  487. call search('\*/', 'W')
  488. endif
  489. let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
  490. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  491. let b:PHP_LastIndentedWasComment = 0
  492. if cline =~ '^\s*\*/'
  493. return indent(lnum) + 1
  494. else
  495. return indent(lnum)
  496. endif
  497. elseif cline =~? '<script\>'
  498. let b:InPHPcode_and_script = 1
  499. let b:GetLastRealCodeLNum_ADD = v:lnum
  500. endif
  501. endif
  502. endif
  503. if 1 == b:InPHPcode
  504. if !b:InPHPcode_and_script && last_line =~ '\%(<?.*\)\@<!?>\%(.*<?\)\@!' && IslinePHP(lnum, '?>')=~?"Delimiter"
  505. if cline !~? s:PHP_startindenttag
  506. let b:InPHPcode = 0
  507. let b:InPHPcode_tofind = s:PHP_startindenttag
  508. elseif cline =~? '<script\>'
  509. let b:InPHPcode_and_script = 1
  510. endif
  511. elseif last_line =~ '^[^''"`]\+[''"`]$' && last_line !~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)' " a string identifier with nothing after it and no other string identifier before
  512. let b:InPHPcode = -1
  513. let b:InPHPcode_tofind = substitute( last_line, '^.*\([''"`]\).*$', '^[^\1]*\1[;,]$', '')
  514. elseif last_line =~? '<<<\s*[''"]\=\a\w*[''"]\=$'
  515. let b:InPHPcode = 0
  516. let b:InPHPcode_tofind = substitute( last_line, '^.*<<<\s*[''"]\=\(\a\w*\)[''"]\=$', '^\\s*\1;\\=$', '')
  517. elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' && getline(v:lnum + 1) !~ '^\s*\*'
  518. let b:InPHPcode = 0
  519. let b:InPHPcode_tofind = '\*/'
  520. elseif cline =~? '^\s*</script>'
  521. let b:InPHPcode = 0
  522. let b:InPHPcode_tofind = s:PHP_startindenttag
  523. endif
  524. endif " }}}
  525. if 1 > b:InPHPcode && !b:InPHPcode_and_script
  526. return -1
  527. endif
  528. " Indent successive // or # comment the same way the first is {{{
  529. let addSpecial = 0
  530. if cline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
  531. let addSpecial = b:PHP_outdentSLComments
  532. if b:PHP_LastIndentedWasComment == 1
  533. return indent(real_PHP_lastindented)
  534. endif
  535. let b:PHP_LastIndentedWasComment = 1
  536. else
  537. let b:PHP_LastIndentedWasComment = 0
  538. endif " }}}
  539. " Indent multiline /* comments correctly {{{
  540. if b:PHP_InsideMultilineComment || b:UserIsTypingComment
  541. if cline =~ '^\s*\*\%(\/\)\@!'
  542. if last_line =~ '^\s*/\*'
  543. return indent(lnum) + 1
  544. else
  545. return indent(lnum)
  546. endif
  547. else
  548. let b:PHP_InsideMultilineComment = 0
  549. endif
  550. endif
  551. if !b:PHP_InsideMultilineComment && cline =~ '^\s*/\*\%(.*\*/\)\@!'
  552. if getline(v:lnum + 1) !~ '^\s*\*'
  553. return -1
  554. endif
  555. let b:PHP_InsideMultilineComment = 1
  556. endif " }}}
  557. " Things always indented at col 1 (PHP delimiter: <?, ?>, Heredoc end) {{{
  558. if cline =~# '^\s*<?' && cline !~ '?>' && b:PHP_outdentphpescape
  559. return 0
  560. endif
  561. if cline =~ '^\s*?>' && cline !~# '<?' && b:PHP_outdentphpescape
  562. return 0
  563. endif
  564. if cline =~? '^\s*\a\w*;$\|^\a\w*$\|^\s*[''"`][;,]' && cline !~? s:notPhpHereDoc
  565. return 0
  566. endif " }}}
  567. let s:level = 0
  568. let lnum = GetLastRealCodeLNum(v:lnum - 1)
  569. let last_line = getline(lnum)
  570. let ind = indent(lnum)
  571. if ind==0 && b:PHP_default_indenting
  572. let ind = b:PHP_default_indenting
  573. endif
  574. if lnum == 0
  575. return b:PHP_default_indenting + addSpecial
  576. endif
  577. if cline =~ '^\s*}\%(}}\)\@!'
  578. let ind = indent(FindOpenBracket(v:lnum, 1))
  579. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  580. return ind
  581. endif
  582. if cline =~ '^\s*\*/'
  583. call cursor(v:lnum, 1)
  584. if cline !~ '^\*/'
  585. call search('\*/', 'W')
  586. endif
  587. let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
  588. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  589. if cline =~ '^\s*\*/'
  590. return indent(lnum) + 1
  591. else
  592. return indent(lnum)
  593. endif
  594. endif
  595. if last_line =~ '[;}]'.endline && last_line !~ '^[)\]]' && last_line !~# s:defaultORcase && last_line !~ '^\s*[''"`][;,]'
  596. if ind==b:PHP_default_indenting
  597. return b:PHP_default_indenting + addSpecial
  598. elseif b:PHP_indentinghuge && ind==b:PHP_CurrentIndentLevel && cline !~# '^\s*\%(else\|\%(case\|default\).*:\|[})];\=\)' && last_line !~# '^\s*\%(\%(}\s*\)\=else\)' && getline(GetLastRealCodeLNum(lnum - 1))=~';'.endline
  599. return b:PHP_CurrentIndentLevel + addSpecial
  600. endif
  601. endif
  602. let LastLineClosed = 0
  603. let terminated = s:terminated
  604. let unstated = s:unstated
  605. if ind != b:PHP_default_indenting && cline =~# '^\s*else\%(if\)\=\>'
  606. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  607. return indent(FindTheIfOfAnElse(v:lnum, 1))
  608. elseif cline =~# s:defaultORcase
  609. return FindTheSwitchIndent(v:lnum) + shiftwidth() * b:PHP_vintage_case_default_indent
  610. elseif cline =~ '^\s*)\=\s*{'
  611. let previous_line = last_line
  612. let last_line_num = lnum
  613. while last_line_num > 1
  614. if previous_line =~ terminated || previous_line =~ s:structureHead
  615. let ind = indent(last_line_num)
  616. if b:PHP_BracesAtCodeLevel
  617. let ind = ind + shiftwidth()
  618. endif
  619. return ind
  620. endif
  621. let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
  622. let previous_line = getline(last_line_num)
  623. endwhile
  624. elseif cline =~ '^\s*->'
  625. return FindArrowIndent(lnum)
  626. elseif last_line =~# unstated && cline !~ '^\s*);\='.endline
  627. let ind = ind + shiftwidth() " we indent one level further when the preceding line is not stated
  628. return ind + addSpecial
  629. elseif (ind != b:PHP_default_indenting || last_line =~ '^[)\]]' ) && last_line =~ terminated
  630. let previous_line = last_line
  631. let last_line_num = lnum
  632. let LastLineClosed = 1
  633. let isSingleLineBlock = 0
  634. while 1
  635. if ! isSingleLineBlock && previous_line =~ '^\s*}\|;\s*}'.endline
  636. call cursor(last_line_num, 1)
  637. if previous_line !~ '^}'
  638. call search('}\|;\s*}'.endline, 'W')
  639. end
  640. let oldLastLine = last_line_num
  641. let last_line_num = searchpair('{', '', '}', 'bW', 'Skippmatch()')
  642. if getline(last_line_num) =~ '^\s*{'
  643. let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
  644. elseif oldLastLine == last_line_num
  645. let isSingleLineBlock = 1
  646. continue
  647. endif
  648. let previous_line = getline(last_line_num)
  649. continue
  650. else
  651. let isSingleLineBlock = 0
  652. if getline(last_line_num) =~# '^\s*else\%(if\)\=\>'
  653. let last_line_num = FindTheIfOfAnElse(last_line_num, 0)
  654. continue
  655. endif
  656. let last_match = last_line_num
  657. let one_ahead_indent = indent(last_line_num)
  658. let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
  659. let two_ahead_indent = indent(last_line_num)
  660. let after_previous_line = previous_line
  661. let previous_line = getline(last_line_num)
  662. if previous_line =~# s:defaultORcase.'\|{'.endline
  663. break
  664. endif
  665. if after_previous_line=~# '^\s*'.s:blockstart.'.*)'.endline && previous_line =~# '[;}]'.endline
  666. break
  667. endif
  668. if one_ahead_indent == two_ahead_indent || last_line_num < 1
  669. if previous_line =~# '\%(;\|^\s*}\)'.endline || last_line_num < 1
  670. break
  671. endif
  672. endif
  673. endif
  674. endwhile
  675. if indent(last_match) != ind
  676. let ind = indent(last_match)
  677. let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
  678. return ind + addSpecial
  679. endif
  680. endif
  681. if (last_line !~ '^\s*}\%(}}\)\@!')
  682. let plinnum = GetLastRealCodeLNum(lnum - 1)
  683. else
  684. let plinnum = GetLastRealCodeLNum(FindOpenBracket(lnum, 1) - 1)
  685. endif
  686. let AntepenultimateLine = getline(plinnum)
  687. let last_line = StripEndlineComments(last_line)
  688. if ind == b:PHP_default_indenting
  689. if last_line =~ terminated && last_line !~# s:defaultORcase
  690. let LastLineClosed = 1
  691. endif
  692. endif
  693. if !LastLineClosed
  694. let openedparent = -1
  695. if last_line =~# '[{(\[]'.endline || last_line =~? '\h\w*\s*(.*,$' && AntepenultimateLine !~ '[,(\[]'.endline && BalanceDirection(last_line) > 0
  696. let dontIndent = 0
  697. if last_line =~ '\S\+\s*{'.endline && last_line !~ '^\s*[)\]]\+\(\s*:\s*'.s:PHP_validVariable.'\)\=\s*{'.endline && last_line !~ s:structureHead
  698. let dontIndent = 1
  699. endif
  700. if !dontIndent && (!b:PHP_BracesAtCodeLevel || last_line !~# '^\s*{')
  701. let ind = ind + shiftwidth()
  702. endif
  703. if b:PHP_IndentFunctionCallParameters && last_line =~ s:multilineFunctionCall && last_line !~ s:structureHead && last_line !~ s:arrayDecl
  704. let ind = ind + b:PHP_IndentFunctionCallParameters * shiftwidth()
  705. endif
  706. if b:PHP_IndentFunctionDeclarationParameters && last_line =~ s:multilineFunctionDecl
  707. let ind = ind + b:PHP_IndentFunctionDeclarationParameters * shiftwidth()
  708. endif
  709. if b:PHP_BracesAtCodeLevel || b:PHP_vintage_case_default_indent == 1
  710. let b:PHP_CurrentIndentLevel = ind
  711. endif
  712. elseif last_line =~ '),'.endline && BalanceDirection(last_line) < 0
  713. call cursor(lnum, 1)
  714. call searchpos('),'.endline, 'cW')
  715. let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()')
  716. if openedparent != lnum
  717. let ind = indent(openedparent)
  718. endif
  719. elseif last_line =~ s:structureHead
  720. let ind = ind + shiftwidth()
  721. elseif AntepenultimateLine =~ '{'.endline && AntepenultimateLine !~? '^\s*use\>' || AntepenultimateLine =~ terminated || AntepenultimateLine =~# s:defaultORcase
  722. let ind = ind + shiftwidth()
  723. endif
  724. if openedparent >= 0
  725. let last_line = StripEndlineComments(getline(openedparent))
  726. endif
  727. endif
  728. if cline =~ '^\s*[)\]];\='
  729. call cursor(v:lnum, 1)
  730. call searchpos('[)\]]', 'cW')
  731. let matchedBlockChar = cline[col('.')-1]
  732. let openedparent = searchpair('\M'.s:blockCharsLUT[matchedBlockChar], '', '\M'.matchedBlockChar, 'bW', 'Skippmatch()')
  733. if openedparent != v:lnum
  734. let ind = indent(openedparent)
  735. endif
  736. elseif last_line =~ '^\s*->' && last_line !~? s:structureHead && BalanceDirection(last_line) <= 0
  737. let ind = ind - shiftwidth()
  738. endif
  739. let b:PHP_CurrentIndentLevel = ind
  740. return ind + addSpecial
  741. endfunction