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