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