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