xref: /vim-8.2.3635/runtime/indent/php.vim (revision b17893aa)
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
20" NOTE: This script must be used with PHP syntax ON and with the php syntax
21"	script by Lutz Eymers (http://www.isp.de/data/php.vim ) or with the
22"	script by Peter Hodge (https://www.vim.org/scripts/script.php?script_id=1571 )
23"	the later is bunbdled by default with Vim 7.
24"
25"
26"	In the case you have syntax errors in your script such as HereDoc end
27"	identifiers not at col 1 you'll have to indent your file 2 times (This
28"	script will automatically put HereDoc end identifiers at col 1 if
29"	they are followed by a ';').
30"
31
32" NOTE: If you are editing files in Unix file format and that (by accident)
33"	there are '\r' before new lines, this script won't be able to proceed
34"	correctly and will make many mistakes because it won't be able to match
35"	'\s*$' correctly.
36"	So you have to remove those useless characters first with a command like:
37"
38"	:%s /\r$//g
39"
40"	or simply 'let' the option PHP_removeCRwhenUnix to 1 and the script will
41"	silently remove them when VIM load this script (at each bufread).
42
43
44if exists("b:did_indent")
45    finish
46endif
47let b:did_indent = 1
48
49
50let g:php_sync_method = 0
51
52
53if exists("PHP_default_indenting")
54    let b:PHP_default_indenting = PHP_default_indenting * shiftwidth()
55else
56    let b:PHP_default_indenting = 0
57endif
58
59if exists("PHP_outdentSLComments")
60    let b:PHP_outdentSLComments = PHP_outdentSLComments * shiftwidth()
61else
62    let b:PHP_outdentSLComments = 0
63endif
64
65if exists("PHP_BracesAtCodeLevel")
66    let b:PHP_BracesAtCodeLevel = PHP_BracesAtCodeLevel
67else
68    let b:PHP_BracesAtCodeLevel = 0
69endif
70
71
72if exists("PHP_autoformatcomment")
73    let b:PHP_autoformatcomment = PHP_autoformatcomment
74else
75    let b:PHP_autoformatcomment = 1
76endif
77
78if exists("PHP_outdentphpescape")
79    let b:PHP_outdentphpescape = PHP_outdentphpescape
80else
81    let b:PHP_outdentphpescape = 1
82endif
83
84if exists("PHP_noArrowMatching")
85    let b:PHP_noArrowMatching = PHP_noArrowMatching
86else
87    let b:PHP_noArrowMatching = 0
88endif
89
90
91if exists("PHP_vintage_case_default_indent") && PHP_vintage_case_default_indent
92    let b:PHP_vintage_case_default_indent = 1
93else
94    let b:PHP_vintage_case_default_indent = 0
95endif
96
97if exists("PHP_IndentFunctionCallParameters")
98    let b:PHP_IndentFunctionCallParameters = PHP_IndentFunctionCallParameters
99else
100    let b:PHP_IndentFunctionCallParameters = 0
101endif
102
103if exists("PHP_IndentFunctionDeclarationParameters")
104    let b:PHP_IndentFunctionDeclarationParameters = PHP_IndentFunctionDeclarationParameters
105else
106    let b:PHP_IndentFunctionDeclarationParameters = 0
107endif
108
109let b:PHP_lastindented = 0
110let b:PHP_indentbeforelast = 0
111let b:PHP_indentinghuge = 0
112let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
113let b:PHP_LastIndentedWasComment = 0
114let b:PHP_InsideMultilineComment = 0
115let b:InPHPcode = 0
116let b:InPHPcode_checked = 0
117let b:InPHPcode_and_script = 0
118let b:InPHPcode_tofind = ""
119let b:PHP_oldchangetick = b:changedtick
120let b:UserIsTypingComment = 0
121let b:optionsset = 0
122
123setlocal nosmartindent
124setlocal noautoindent
125setlocal nocindent
126setlocal nolisp
127
128setlocal indentexpr=GetPhpIndent()
129setlocal indentkeys=0{,0},0),0],:,!^F,o,O,e,*<Return>,=?>,=<?,=*/
130
131
132
133let s:searchpairflags = 'bWr'
134
135if &fileformat == "unix" && exists("PHP_removeCRwhenUnix") && PHP_removeCRwhenUnix
136    silent! %s/\r$//g
137endif
138
139if exists("*GetPhpIndent")
140    call ResetPhpOptions()
141    finish " XXX -- comment this line for easy dev
142endif
143
144
145let s:endline = '\s*\%(//.*\|#.*\|/\*.*\*/\s*\)\=$'
146let s:PHP_validVariable = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
147let s:notPhpHereDoc = '\%(break\|return\|continue\|exit\|die\|else\|end\%(if\|while\|for\|foreach\|switch\)\)'
148let 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\>\)'
149let s:functionDeclPrefix = '\<function\>\%(\s\+&\='.s:PHP_validVariable.'\)\=\s*('
150let s:functionDecl = s:functionDeclPrefix.'.*'
151let s:multilineFunctionDecl = s:functionDeclPrefix.s:endline
152let s:arrayDecl = '\<array\>\s*(.*'
153let s:multilineFunctionCall = s:PHP_validVariable.'\s*('.s:endline
154let s:unstated = '\%(^\s*'.s:blockstart.'.*)\|\%(//.*\)\@<!\<e'.'lse\>\)'.s:endline
155
156
157let s:terminated = '\%(\%(;\%(\s*\%(?>\|}\)\)\=\|<<<\s*[''"]\=\a\w*[''"]\=$\|^\s*}\|^\s*'.s:PHP_validVariable.':\)'.s:endline.'\)'
158let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!'
159let s:structureHead = '^\s*\%(' . s:blockstart . '\)\|'. s:functionDecl . s:endline . '\|\<new\s\+class\>'
160
161
162let s:escapeDebugStops = 0
163function! DebugPrintReturn(scriptLine)
164
165    if ! s:escapeDebugStops
166	echo "debug:" . a:scriptLine
167	let c = getchar()
168	if c == "\<Del>"
169	    let s:escapeDebugStops = 1
170	end
171    endif
172
173endfunction
174
175function! GetLastRealCodeLNum(startline) " {{{
176
177    let lnum = a:startline
178
179    if b:GetLastRealCodeLNum_ADD && b:GetLastRealCodeLNum_ADD == lnum + 1
180	let lnum = b:GetLastRealCodeLNum_ADD
181    endif
182
183    while lnum > 1
184	let lnum = prevnonblank(lnum)
185	let lastline = getline(lnum)
186
187	if b:InPHPcode_and_script && lastline =~ '?>\s*$'
188	    let lnum = lnum - 1
189	elseif lastline =~ '^\s*?>.*<?\%(php\)\=\s*$'
190	    let lnum = lnum - 1
191	elseif lastline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
192	    let lnum = lnum - 1
193	elseif lastline =~ '\*/\s*$'
194	    call cursor(lnum, 1)
195	    if lastline !~ '^\*/'
196		call search('\*/', 'W')
197	    endif
198	    let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
199
200	    let lastline = getline(lnum)
201	    if lastline =~ '^\s*/\*'
202		let lnum = lnum - 1
203	    else
204		break
205	    endif
206
207
208	elseif lastline =~? '\%(//\s*\|?>.*\)\@<!<?\%(php\)\=\s*$\|^\s*<script\>'
209
210	    while lastline !~ '\(<?.*\)\@<!?>' && lnum > 1
211		let lnum = lnum - 1
212		let lastline = getline(lnum)
213	    endwhile
214	    if lastline =~ '^\s*?>'
215		let lnum = lnum - 1
216	    else
217		break
218	    endif
219
220
221	elseif lastline =~? '^\a\w*;\=$' && lastline !~? s:notPhpHereDoc
222	    let tofind=substitute( lastline, '\(\a\w*\);\=', '<<<\\s*[''"]\\=\1[''"]\\=$', '')
223	    while getline(lnum) !~? tofind && lnum > 1
224		let lnum = lnum - 1
225	    endwhile
226	elseif lastline =~ '^\s*[''"`][;,]' || (lastline =~ '^[^''"`]*[''"`][;,]'.s:endline && IslinePHP(lnum, "") == "SpecStringEntrails")
227
228	    let tofind=substitute( lastline, '^.*\([''"`]\)[;,].*$', '^[^\1]\\+[\1]$\\|^[^\1]\\+[=([]\\s*[\1]', '')
229	    let trylnum = lnum
230	    while getline(trylnum) !~? tofind && trylnum > 1
231		let trylnum = trylnum - 1
232	    endwhile
233
234	    if trylnum == 1
235		break
236	    else
237		if lastline =~ ';'.s:endline
238		    while getline(trylnum) !~? s:terminated && getline(trylnum) !~? '{'.s:endline && trylnum > 1
239			let trylnum = prevnonblank(trylnum - 1)
240		    endwhile
241
242
243		    if trylnum == 1
244			break
245		    end
246		end
247		let lnum = trylnum
248	    end
249	else
250	    break
251	endif
252    endwhile
253
254    if lnum==1 && getline(lnum) !~ '<?'
255	let lnum=0
256    endif
257
258    if b:InPHPcode_and_script && 1 > b:InPHPcode
259	let b:InPHPcode_and_script = 0
260    endif
261
262    return lnum
263endfunction " }}}
264
265function! Skippmatch2()
266
267    let line = getline(".")
268
269    if line =~ "\\([\"']\\).*/\\*.*\\1" || line =~ '\%(//\|#\).*/\*'
270	return 1
271    else
272	return 0
273    endif
274endfun
275
276function! Skippmatch()	" {{{
277    let synname = synIDattr(synID(line("."), col("."), 0), "name")
278    if synname ==? "Delimiter" || synname ==? "phpRegionDelimiter" || synname =~? "^phpParent" || synname ==? "phpArrayParens" || synname =~? '^php\%(Block\|Brace\)' || synname ==? "javaScriptBraces" || synname =~? '^php\%(Doc\)\?Comment' && b:UserIsTypingComment
279	return 0
280    else
281	return 1
282    endif
283endfun " }}}
284
285function! FindOpenBracket(lnum, blockStarter) " {{{
286    call cursor(a:lnum, 1)
287    let line = searchpair('{', '', '}', 'bW', 'Skippmatch()')
288
289    if a:blockStarter == 1
290	while line > 1
291	    let linec = getline(line)
292
293	    if linec =~ s:terminated || linec =~ s:structureHead
294		break
295	    endif
296
297	    let line = GetLastRealCodeLNum(line - 1)
298	endwhile
299    endif
300
301    return line
302endfun " }}}
303
304let s:blockChars = {'{':1, '[': 1, '(': 1, ')':-1, ']':-1, '}':-1}
305let s:blockCharsLUT = {'{':'{', '}':'{',   '[':'[', ']':'[',   '(':'(', ')':'('}
306function! BalanceDirection (str)
307
308    let balance = {'{':0, '[': 0, '(': 0, 'none':0}
309    let director = 'none'
310
311    for c in split(a:str, '\zs')
312	if has_key(s:blockChars, c)
313	    let balance[s:blockCharsLUT[c]] += s:blockChars[c]
314
315	    if balance[s:blockCharsLUT[c]]
316		let director = s:blockCharsLUT[c]
317	    endif
318	endif
319    endfor
320
321    return balance[director]
322endfun
323
324function! StripEndlineComments (line)
325    return substitute(a:line,"\\(//\\|#\\)\\(\\(\\([^\"']*\\([\"']\\)[^\"']*\\5\\)\\+[^\"']*$\\)\\|\\([^\"']*$\\)\\)",'','')
326endfun
327
328function! FindArrowIndent (lnum)  " {{{
329
330    let parrentArrowPos = -1
331    let cursorPos = -1
332    let lnum = a:lnum
333    while lnum > 1
334	let last_line = getline(lnum)
335	if last_line =~ '^\s*->'
336	    let parrentArrowPos = indent(a:lnum)
337	    break
338	else
339
340	    if b:PHP_noArrowMatching
341		break
342	    endif
343
344	    let cleanedLnum = StripEndlineComments(last_line)
345
346	    if cleanedLnum =~ ')'.s:endline
347		if BalanceDirection(cleanedLnum) <= 0
348		    call cursor(lnum, 1)
349		    call searchpos(')'.s:endline, 'cW', lnum)
350		    let openedparent =  searchpair('(', '', ')', 'bW', 'Skippmatch()')
351		    let cursorPos = col(".")
352		    if openedparent != lnum
353			let lnum = openedparent
354			continue
355		    else
356		    endif
357		else
358		    let parrentArrowPos = -1
359		    break
360		end
361	    endif
362
363	    if cleanedLnum =~ '->'
364		call cursor(lnum, cursorPos == -1 ? strwidth(cleanedLnum) : cursorPos)
365		let parrentArrowPos = searchpos('->', 'cWb', lnum)[1] - 1
366
367		break
368	    else
369		let parrentArrowPos = -1
370		break
371	    endif
372	endif
373    endwhile
374
375    if parrentArrowPos == -1
376	let parrentArrowPos = indent(lnum) + shiftwidth()
377    end
378
379    return parrentArrowPos
380endfun "}}}
381
382function! FindTheIfOfAnElse (lnum, StopAfterFirstPrevElse) " {{{
383
384    if getline(a:lnum) =~# '^\s*}\s*else\%(if\)\=\>'
385	let beforeelse = a:lnum
386    else
387	let beforeelse = GetLastRealCodeLNum(a:lnum - 1)
388    endif
389
390    if !s:level
391	let s:iftoskip = 0
392    endif
393
394    if getline(beforeelse) =~# '^\s*\%(}\s*\)\=else\%(\s*if\)\@!\>'
395	let s:iftoskip = s:iftoskip + 1
396    endif
397
398    if getline(beforeelse) =~ '^\s*}'
399	let beforeelse = FindOpenBracket(beforeelse, 0)
400
401	if getline(beforeelse) =~ '^\s*{'
402	    let beforeelse = GetLastRealCodeLNum(beforeelse - 1)
403	endif
404    endif
405
406
407    if !s:iftoskip && a:StopAfterFirstPrevElse && getline(beforeelse) =~# '^\s*\%([}]\s*\)\=else\%(if\)\=\>'
408	return beforeelse
409    endif
410
411    if getline(beforeelse) !~# '^\s*if\>' && beforeelse>1 || s:iftoskip && beforeelse>1
412
413	if s:iftoskip && getline(beforeelse) =~# '^\s*if\>'
414	    let s:iftoskip = s:iftoskip - 1
415	endif
416
417	let s:level =  s:level + 1
418	let beforeelse = FindTheIfOfAnElse(beforeelse, a:StopAfterFirstPrevElse)
419    endif
420
421    return beforeelse
422
423endfunction " }}}
424
425let s:defaultORcase = '^\s*\%(default\|case\).*:'
426
427function! FindTheSwitchIndent (lnum) " {{{
428
429    let test = GetLastRealCodeLNum(a:lnum - 1)
430
431    if test <= 1
432	return indent(1) - shiftwidth() * b:PHP_vintage_case_default_indent
433    end
434
435    while getline(test) =~ '^\s*}' && test > 1
436	let test = GetLastRealCodeLNum(FindOpenBracket(test, 0) - 1)
437
438	if getline(test) =~ '^\s*switch\>'
439	    let test = GetLastRealCodeLNum(test - 1)
440	endif
441    endwhile
442
443    if getline(test) =~# '^\s*switch\>'
444	return indent(test)
445    elseif getline(test) =~# s:defaultORcase
446	return indent(test) - shiftwidth() * b:PHP_vintage_case_default_indent
447    else
448	return FindTheSwitchIndent(test)
449    endif
450
451endfunction "}}}
452
453let s:SynPHPMatchGroups = {'phpparent':1, 'delimiter':1, 'define':1, 'storageclass':1, 'structure':1, 'exception':1}
454function! IslinePHP (lnum, tofind) " {{{
455    let cline = getline(a:lnum)
456
457    if a:tofind==""
458	let tofind = "^\\s*[\"'`]*\\s*\\zs\\S"
459    else
460	let tofind = a:tofind
461    endif
462
463    let tofind = tofind . '\c'
464
465    let coltotest = match (cline, tofind) + 1
466
467    let synname = synIDattr(synID(a:lnum, coltotest, 0), "name")
468
469    if synname ==? 'phpStringSingle' || synname ==? 'phpStringDouble' || synname ==? 'phpBacktick'
470	if cline !~ '^\s*[''"`]' " ??? XXX
471	    return "SpecStringEntrails"
472	else
473	    return synname
474	end
475    end
476
477    if get(s:SynPHPMatchGroups, tolower(synname)) || synname =~ '^php' ||  synname =~? '^javaScript'
478	return synname
479    else
480	return ""
481    endif
482endfunction " }}}
483
484let s:autoresetoptions = 0
485if ! s:autoresetoptions
486    let s:autoresetoptions = 1
487endif
488
489function! ResetPhpOptions()
490    if ! b:optionsset && &filetype =~ "php"
491	if b:PHP_autoformatcomment
492
493	    setlocal comments=s1:/*,mb:*,ex:*/,://,:#
494
495	    setlocal formatoptions-=t
496	    setlocal formatoptions+=q
497	    setlocal formatoptions+=r
498	    setlocal formatoptions+=o
499	    setlocal formatoptions+=c
500	    setlocal formatoptions+=b
501	endif
502	let b:optionsset = 1
503    endif
504endfunc
505
506call ResetPhpOptions()
507
508function! GetPhpIndentVersion()
509    return "1.70-bundle"
510endfun
511
512function! GetPhpIndent()
513
514    let b:GetLastRealCodeLNum_ADD = 0
515
516    let UserIsEditing=0
517    if	b:PHP_oldchangetick != b:changedtick
518	let b:PHP_oldchangetick = b:changedtick
519	let UserIsEditing=1
520    endif
521
522    if b:PHP_default_indenting
523	let b:PHP_default_indenting = g:PHP_default_indenting * shiftwidth()
524    endif
525
526    let cline = getline(v:lnum)
527
528    if !b:PHP_indentinghuge && b:PHP_lastindented > b:PHP_indentbeforelast
529	if b:PHP_indentbeforelast
530	    let b:PHP_indentinghuge = 1
531	endif
532	let b:PHP_indentbeforelast = b:PHP_lastindented
533    endif
534
535    if b:InPHPcode_checked && prevnonblank(v:lnum - 1) != b:PHP_lastindented
536	if b:PHP_indentinghuge
537	    let b:PHP_indentinghuge = 0
538	    let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
539	endif
540	let real_PHP_lastindented = v:lnum
541	let b:PHP_LastIndentedWasComment=0
542	let b:PHP_InsideMultilineComment=0
543	let b:PHP_indentbeforelast = 0
544
545	let b:InPHPcode = 0
546	let b:InPHPcode_checked = 0
547	let b:InPHPcode_and_script = 0
548	let b:InPHPcode_tofind = ""
549
550    elseif v:lnum > b:PHP_lastindented
551	let real_PHP_lastindented = b:PHP_lastindented
552    else
553	let real_PHP_lastindented = v:lnum
554    endif
555
556    let b:PHP_lastindented = v:lnum
557
558
559    if !b:InPHPcode_checked " {{{ One time check
560	let b:InPHPcode_checked = 1
561	let b:UserIsTypingComment = 0
562
563	let synname = ""
564	if cline !~ '<?.*?>'
565	    let synname = IslinePHP (prevnonblank(v:lnum), "")
566	endif
567
568	if synname!=""
569	    if synname ==? "SpecStringEntrails"
570		let b:InPHPcode = -1 " thumb down
571		let b:InPHPcode_tofind = ""
572	    elseif synname !=? "phpHereDoc" && synname !=? "phpHereDocDelimiter"
573		let b:InPHPcode = 1
574		let b:InPHPcode_tofind = ""
575
576		if synname =~? '^php\%(Doc\)\?Comment'
577		    let b:UserIsTypingComment = 1
578		    let b:InPHPcode_checked = 0
579		endif
580
581		if synname =~? '^javaScript'
582		    let b:InPHPcode_and_script = 1
583		endif
584
585	    else
586		let b:InPHPcode = 0
587
588		let lnum = v:lnum - 1
589		while getline(lnum) !~? '<<<\s*[''"]\=\a\w*[''"]\=$' && lnum > 1
590		    let lnum = lnum - 1
591		endwhile
592
593		let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<\s*[''"]\=\(\a\w*\)[''"]\=$', '^\\s*\1;\\=$', '')
594	    endif
595	else
596	    let b:InPHPcode = 0
597	    let b:InPHPcode_tofind = s:PHP_startindenttag
598	endif
599    endif "!b:InPHPcode_checked }}}
600
601
602    " Test if we are indenting PHP code {{{
603    let lnum = prevnonblank(v:lnum - 1)
604    let last_line = getline(lnum)
605    let endline= s:endline
606
607    if b:InPHPcode_tofind!=""
608	if cline =~? b:InPHPcode_tofind
609	    let b:InPHPcode_tofind = ""
610	    let b:UserIsTypingComment = 0
611
612	    if b:InPHPcode == -1
613		let b:InPHPcode = 1
614		return -1
615	    end
616
617	    let b:InPHPcode = 1
618
619	    if cline =~ '\*/'
620		call cursor(v:lnum, 1)
621		if cline !~ '^\*/'
622		    call search('\*/', 'W')
623		endif
624		let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
625
626		let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
627
628		let b:PHP_LastIndentedWasComment = 0
629
630		if cline =~ '^\s*\*/'
631		    return indent(lnum) + 1
632		else
633		    return indent(lnum)
634		endif
635
636	    elseif cline =~? '<script\>'
637		let b:InPHPcode_and_script = 1
638		let b:GetLastRealCodeLNum_ADD = v:lnum
639	    endif
640	endif
641    endif
642
643    if 1 == b:InPHPcode
644
645	if !b:InPHPcode_and_script && last_line =~ '\%(<?.*\)\@<!?>\%(.*<?\)\@!' && IslinePHP(lnum, '?>')=~?"Delimiter"
646	    if cline !~? s:PHP_startindenttag
647		let b:InPHPcode = 0
648		let b:InPHPcode_tofind = s:PHP_startindenttag
649	    elseif cline =~? '<script\>'
650		let b:InPHPcode_and_script = 1
651	    endif
652
653	elseif last_line =~ '^[^''"`]\+[''"`]$' && last_line !~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)' " a string identifier with nothing after it and no other string identifier before
654	    let b:InPHPcode = -1
655	    let b:InPHPcode_tofind = substitute( last_line, '^.*\([''"`]\).*$', '^[^\1]*\1[;,]$', '')
656	elseif last_line =~? '<<<\s*[''"]\=\a\w*[''"]\=$'
657	    let b:InPHPcode = 0
658	    let b:InPHPcode_tofind = substitute( last_line, '^.*<<<\s*[''"]\=\(\a\w*\)[''"]\=$', '^\\s*\1;\\=$', '')
659
660	elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' && getline(v:lnum + 1) !~ '^\s*\*'
661	    let b:InPHPcode = 0
662	    let b:InPHPcode_tofind = '\*/'
663
664	elseif cline =~? '^\s*</script>'
665	    let b:InPHPcode = 0
666	    let b:InPHPcode_tofind = s:PHP_startindenttag
667	endif
668    endif " }}}
669
670
671    if 1 > b:InPHPcode && !b:InPHPcode_and_script
672	return -1
673    endif
674
675    " Indent successive // or # comment the same way the first is {{{
676    let addSpecial = 0
677    if cline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
678	let addSpecial = b:PHP_outdentSLComments
679	if b:PHP_LastIndentedWasComment == 1
680	    return indent(real_PHP_lastindented)
681	endif
682	let b:PHP_LastIndentedWasComment = 1
683    else
684	let b:PHP_LastIndentedWasComment = 0
685    endif " }}}
686
687    " Indent multiline /* comments correctly {{{
688
689    if b:PHP_InsideMultilineComment || b:UserIsTypingComment
690	if cline =~ '^\s*\*\%(\/\)\@!'
691	    if last_line =~ '^\s*/\*'
692		return indent(lnum) + 1
693	    else
694		return indent(lnum)
695	    endif
696	else
697	    let b:PHP_InsideMultilineComment = 0
698	endif
699    endif
700
701    if !b:PHP_InsideMultilineComment && cline =~ '^\s*/\*\%(.*\*/\)\@!'
702	if getline(v:lnum + 1) !~ '^\s*\*'
703	    return -1
704	endif
705	let b:PHP_InsideMultilineComment = 1
706    endif " }}}
707
708
709    " Things always indented at col 1 (PHP delimiter: <?, ?>, Heredoc end) {{{
710    if cline =~# '^\s*<?' && cline !~ '?>' && b:PHP_outdentphpescape
711	return 0
712    endif
713
714    if	cline =~ '^\s*?>' && cline !~# '<?' && b:PHP_outdentphpescape
715	return 0
716    endif
717
718    if cline =~? '^\s*\a\w*;$\|^\a\w*$\|^\s*[''"`][;,]' && cline !~? s:notPhpHereDoc
719	return 0
720    endif " }}}
721
722    let s:level = 0
723
724    let lnum = GetLastRealCodeLNum(v:lnum - 1)
725
726    let last_line = getline(lnum)
727    let ind = indent(lnum)
728
729    if ind==0 && b:PHP_default_indenting
730	let ind = b:PHP_default_indenting
731    endif
732
733    if lnum == 0
734	return b:PHP_default_indenting + addSpecial
735    endif
736
737
738    if cline =~ '^\s*}\%(}}\)\@!'
739	let ind = indent(FindOpenBracket(v:lnum, 1))
740	let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
741	return ind
742    endif
743
744    if cline =~ '^\s*\*/'
745	call cursor(v:lnum, 1)
746	if cline !~ '^\*/'
747	    call search('\*/', 'W')
748	endif
749	let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
750
751	let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
752
753	if cline =~ '^\s*\*/'
754	    return indent(lnum) + 1
755	else
756	    return indent(lnum)
757	endif
758    endif
759
760
761    if last_line =~ '[;}]'.endline && last_line !~ '^[)\]]' && last_line !~# s:defaultORcase && last_line !~ '^\s*[''"`][;,]'
762	if ind==b:PHP_default_indenting
763	    return b:PHP_default_indenting + addSpecial
764	elseif b:PHP_indentinghuge && ind==b:PHP_CurrentIndentLevel && cline !~# '^\s*\%(else\|\%(case\|default\).*:\|[})];\=\)' && last_line !~# '^\s*\%(\%(}\s*\)\=else\)' && getline(GetLastRealCodeLNum(lnum - 1))=~';'.endline
765	    return b:PHP_CurrentIndentLevel + addSpecial
766	endif
767    endif
768
769    let LastLineClosed = 0
770
771    let terminated = s:terminated
772
773    let unstated  = s:unstated
774
775
776    if ind != b:PHP_default_indenting && cline =~# '^\s*else\%(if\)\=\>'
777	let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
778	return indent(FindTheIfOfAnElse(v:lnum, 1))
779    elseif cline =~# s:defaultORcase
780	return FindTheSwitchIndent(v:lnum) + shiftwidth() * b:PHP_vintage_case_default_indent
781    elseif cline =~ '^\s*)\=\s*{'
782	let previous_line = last_line
783	let last_line_num = lnum
784
785	while last_line_num > 1
786
787	    if previous_line =~ terminated || previous_line =~ s:structureHead
788
789		let ind = indent(last_line_num)
790
791		if  b:PHP_BracesAtCodeLevel
792		    let ind = ind + shiftwidth()
793		endif
794
795		return ind
796	    endif
797
798	    let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
799	    let previous_line = getline(last_line_num)
800	endwhile
801    elseif cline =~ '^\s*->'
802	return FindArrowIndent(lnum)
803    elseif last_line =~# unstated && cline !~ '^\s*);\='.endline
804	let ind = ind + shiftwidth() " we indent one level further when the preceding line is not stated
805	return ind + addSpecial
806
807    elseif (ind != b:PHP_default_indenting || last_line =~ '^[)\]]' ) && last_line =~ terminated
808	let previous_line = last_line
809	let last_line_num = lnum
810	let LastLineClosed = 1
811
812	let isSingleLineBlock = 0
813	while 1
814	    if ! isSingleLineBlock && previous_line =~ '^\s*}\|;\s*}'.endline
815
816		call cursor(last_line_num, 1)
817		if previous_line !~ '^}'
818		    call search('}\|;\s*}'.endline, 'W')
819		end
820		let oldLastLine = last_line_num
821		let last_line_num = searchpair('{', '', '}', 'bW', 'Skippmatch()')
822
823		if getline(last_line_num) =~ '^\s*{'
824		    let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
825		elseif oldLastLine == last_line_num
826		    let isSingleLineBlock = 1
827		    continue
828		endif
829
830		let previous_line = getline(last_line_num)
831
832		continue
833	    else
834		let isSingleLineBlock = 0
835
836		if getline(last_line_num) =~# '^\s*else\%(if\)\=\>'
837		    let last_line_num = FindTheIfOfAnElse(last_line_num, 0)
838		    continue
839		endif
840
841
842		let last_match = last_line_num
843
844		let one_ahead_indent = indent(last_line_num)
845		let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
846		let two_ahead_indent = indent(last_line_num)
847		let after_previous_line = previous_line
848		let previous_line = getline(last_line_num)
849
850
851		if previous_line =~# s:defaultORcase.'\|{'.endline
852		    break
853		endif
854
855		if after_previous_line=~# '^\s*'.s:blockstart.'.*)'.endline && previous_line =~# '[;}]'.endline
856		    break
857		endif
858
859		if one_ahead_indent == two_ahead_indent || last_line_num < 1
860		    if previous_line =~# '\%(;\|^\s*}\)'.endline || last_line_num < 1
861			break
862		    endif
863		endif
864	    endif
865	endwhile
866
867	if indent(last_match) != ind
868	    let ind = indent(last_match)
869	    let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
870
871	    return ind + addSpecial
872	endif
873    endif
874
875    if (last_line !~ '^\s*}\%(}}\)\@!')
876	let plinnum = GetLastRealCodeLNum(lnum - 1)
877    else
878	let plinnum = GetLastRealCodeLNum(FindOpenBracket(lnum, 1) - 1)
879    endif
880
881    let AntepenultimateLine = getline(plinnum)
882
883    let last_line = StripEndlineComments(last_line)
884
885    if ind == b:PHP_default_indenting
886	if last_line =~ terminated && last_line !~# s:defaultORcase
887	    let LastLineClosed = 1
888	endif
889    endif
890
891    if !LastLineClosed
892
893	let openedparent = -1
894
895
896	if last_line =~# '[{(\[]'.endline || last_line =~? '\h\w*\s*(.*,$' && AntepenultimateLine !~ '[,(\[]'.endline && BalanceDirection(last_line) > 0
897
898	    let dontIndent = 0
899	    if last_line =~ '\S\+\s*{'.endline && last_line !~ '^\s*[)\]]\+\(\s*:\s*'.s:PHP_validVariable.'\)\=\s*{'.endline && last_line !~ s:structureHead
900		let dontIndent = 1
901	    endif
902
903	    if !dontIndent && (!b:PHP_BracesAtCodeLevel || last_line !~# '^\s*{')
904		let ind = ind + shiftwidth()
905	    endif
906
907	    if b:PHP_IndentFunctionCallParameters && last_line =~ s:multilineFunctionCall && last_line !~ s:structureHead && last_line !~ s:arrayDecl
908		let ind = ind + b:PHP_IndentFunctionCallParameters * shiftwidth()
909	    endif
910
911	    if b:PHP_IndentFunctionDeclarationParameters && last_line =~ s:multilineFunctionDecl
912		let ind = ind + b:PHP_IndentFunctionDeclarationParameters * shiftwidth()
913	    endif
914
915	    if b:PHP_BracesAtCodeLevel || b:PHP_vintage_case_default_indent == 1
916		let b:PHP_CurrentIndentLevel = ind
917
918	    endif
919
920	elseif last_line =~ '),'.endline && BalanceDirection(last_line) < 0
921	    call cursor(lnum, 1)
922	    call searchpos('),'.endline, 'cW')
923	    let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()')
924	    if openedparent != lnum
925		let ind = indent(openedparent)
926	    endif
927
928	elseif last_line =~ s:structureHead
929	    let ind = ind + shiftwidth()
930
931
932	elseif AntepenultimateLine =~ '{'.endline && AntepenultimateLine !~? '^\s*use\>' || AntepenultimateLine =~ terminated || AntepenultimateLine =~# s:defaultORcase
933	    let ind = ind + shiftwidth()
934	endif
935
936
937	if openedparent >= 0
938	    let last_line = StripEndlineComments(getline(openedparent))
939	endif
940    endif
941
942    if cline =~ '^\s*[)\]];\='
943	call cursor(v:lnum, 1)
944	call searchpos('[)\]]', 'cW')
945	let matchedBlockChar = cline[col('.')-1]
946	let openedparent = searchpair('\M'.s:blockCharsLUT[matchedBlockChar], '', '\M'.matchedBlockChar, 'bW', 'Skippmatch()')
947	if openedparent != v:lnum
948	    let ind = indent(openedparent)
949	endif
950
951    elseif last_line =~ '^\s*->' && last_line !~? s:structureHead && BalanceDirection(last_line) <= 0
952	let ind = ind - shiftwidth()
953    endif
954
955    let b:PHP_CurrentIndentLevel = ind
956    return ind + addSpecial
957endfunction
958