" Vim indent file
" Language:	PHP
" Author:	John Wellesz <John.wellesz (AT) teaser (DOT) fr>
" URL:		http://www.2072productions.com/vim/indent/php.vim
" Last Change:	2010 July 26th
" Newsletter:	http://www.2072productions.com/?to=php-indent-for-vim-newsletter.php
" Version:	1.33
"
"
" Changes: 1.33		- Rewrote Switch(){case:default:} handling from
"			  scratch in a simpler more logical and infallible way...
"			- Removed PHP_ANSI_indenting which is no longer
"		 	  needed.
"
"
" Changes: 1.32b	- Added PHP_ANSI_indenting and PHP_outdentphpescape
"			  options details to VIm documentation (:help php-indent).
"
"
" Changes: 1.32		- Added a new option: PHP_ANSI_indenting
"
"
" Changes: 1.31a	- Added a new option: PHP_outdentphpescape to indent
"			  PHP tags as the surrounding code.
"
" Changes: 1.30		- Fixed empty case/default indentation again :/
"			- The ResetOptions() function will be called each time
"			  the ftplugin calls this script, previously it was
"			  executed on BufWinEnter and Syntax events.
"
"
" Changes: 1.29		- Fixed php file detection for ResetOptions() used for
"			  comments formatting. It now uses the same tests as
"			  filetype.vim. ResetOptions() will be correctly
"			  called for *.phtml, *.ctp and *.inc files.
"
"
" Changes: 1.28		- End HEREDOC delimiters were not considered as such
"			  if they were not followed by a ';'.
"			- Added support for NOWDOC tags ($foo = <<<'bar')
"
"
" Changes: 1.27		- if a "case" was preceded by another "case" on the
"			  previous line, the second "case" was indented incorrectly.
"
" Changes: 1.26		- '/*' character sequences found on a line
"			  starting by a '#' were not dismissed by the indenting algorithm
"			  and could cause indentation problem in some cases.
"
"
" Changes: 1.25		- Fix some indentation errors on multi line conditions
"			  and multi line statements.
"			- Fix when array indenting is broken and a closing
"			');' is placed at the start of the line, following
"			lines will be indented correctly.
"			- New option: PHP_vintage_case_default_indent (default off)
"			- Minor fixes and optimizations.
"
"
" Changes: 1.24		- Added compatibility with the latest version of
"			  php.vim syntax file by Peter Hodge (http://www.vim.org/scripts/script.php?script_id=1571)
"			  This fixes wrong indentation and ultra-slow indenting
"			  on large php files...
"			- Fixed spelling in comments.
"
"
" Changes: 1.23		- <script> html tags are now correctly indented the same
"			  way their content is.
"			- <?.*?> (on a single line) PHP declarations are now
"			  always considered as non-PHP code and let untouched.
"
" Changes: 1.22		- PHPDoc comments are now indented according to the
"			  surrounding code.
"			- This is also true for '/* */' multi-line comments
"			  when the second line begins by a '*'.
"			- Single line '/* */' comments are also indented.
"
"
" Changes: 1.21		- 'try' and 'catch' were not registered as block starters so the '{'
"			  after a 'try' or 'catch' could be wrongly indented...
"			  (thanks to Gert Muller for finding this issue)
"
" Changes: 1.20		- Line beginning by a single or double quote followed
"			  by a space would cause problems... this was related
"			  to the bug correction of version 1.10 - Thanks to
"			  David Fishburn for finding this (he was lucky).
"			- Changed the way this script set the 'formatoptions'
"			  setting, now it uses '-=' and '+='
"			- New option: PHP_autoformatcomment (defaults to 1),
"			  if set to 0 the 'formatoptions' setting will not be
"			  altered.
"			- When PHP_autoformatcomment is not 0, the 'comments'
"			  setting is set to the type of comments that PHP
"			  supports.
"
" Changes: 1.19		- Indentation of '*/' delimiter of '/**/' won't be broken by
"			  strings or '//' comments containing the "/*" character sequence.
"
" Changes: 1.182	- I Forgot to register 'interface' and 'abstract' as block starters so the '{'
"			  after them could be wrongly indented...
"
" Changes: 1.181	- I Forgot to register 'class' as a block starter so the '{'
"			  after a 'class' could be wrongly indented...
"
" Changes: 1.18		- No more problems with Vim 6.3 and UTF-8.
"			- Opening braces "{" are always indented according to their block starter.
"
"				Instead of:
"
"					if( $test
"					    && $test2 )
"					    {
"					    }
"
"				You have:
"
"					if( $test
"					    && $test2 )
"					{
"					}
"
"
" Changes: 1.17		- Now following parts of split lines are indented:
"
"				Instead of:
"
"					$foo=
"					"foo"
"					."foo";
"
"				You have:
"
"					$foo=
"					    "foo"
"					    ."foo";
"
"			- If a "case : break;" was declared on a single line, the
"			  following "case" was not indented correctly.
"			- If a </script> html tag was preceded by a "?>" it wasn't indented.
"			- Some other minor corrections and improvements.
"
"
" Changes: 1.16		- Now starting and ending '*' of multiline '/* */' comments are aligned
"			  on the '*' of the '/*' comment starter.
"			- Some code improvements that make indentation faster.
"
" Changes: 1.15		- Corrected some problems with the indentation of
"			  multiline "array()" declarations.
"
" Changes: 1.14		- Added auto-formatting for comments (using the Vim option formatoptions=qroc).
"			- Added the script option PHP_BracesAtCodeLevel to
"			  indent the '{' and '}' at the same level than the
"			  code they contain.
"
" Changes: 1.13		- Some code cleaning and typo corrections (Thanks to
"			  Emanuele Giaquinta for his patches)
"
" Changes: 1.12		- The bug involving searchpair() and utf-8 encoding in Vim 6.3 will
"			  not make this script to hang but you'll have to be
"			  careful to not write '/* */' comments with other '/*'
"			  inside the comments else the indentation won't be correct.
"			  NOTE: This is true only if you are using utf-8 and vim 6.3.
"
" Changes: 1.11		- If the "case" of a "switch" wasn't alone on its line
"			  and if the "switch" was at col 0 (or at default indenting)
"			  the lines following the "case" were not indented.
"
" Changes: 1.10		- Lines beginning by a single or double quote were
"			  not indented in some cases.
"
" Changes: 1.09		- JavaScript code was not always directly indented.
"
" Changes: 1.08		- End comment tags '*/' are indented like start tags '/*'.
"			- When typing a multiline comment, '}' are indented
"			  according to other commented '{'.
"			- Added a new option 'PHP_removeCRwhenUnix' to
"			  automatically remove CR at end of lines when the file
"			  format is Unix.
"			- Changed the file format of this very file to Unix.
"			- This version seems to correct several issues some people
"			  had with 1.07.
"
" Changes: 1.07		- Added support for "Here document" tags:
"			   - HereDoc end tags are indented properly.
"			   - HereDoc content remains unchanged.
"			- All the code that is outside PHP delimiters remains
"			  unchanged.
"			- New feature: The content of <script.*> html tags is considered as PHP
"			  and indented according to the surrounding PHP code.
"			- "else if" are detected as "elseif".
"			- Multiline /**/ are indented when the user types it but
"			  remain unchanged when indenting from their beginning.
"			- Fixed indenting of // and # comments.
"			- php_sync_method option is set to 0 (fromstart).
"			  This is required for complex PHP scripts else the indent
"			  may fail.
"			- Files with non PHP code at the beginning could alter the indent
"			  of the following PHP code.
"			- Other minor improvements and corrections.
"
" Changes: 1.06:    - Switch block were no longer indented correctly...
"		    - Added an option to use a default indenting instead of 0.
"		      (whereas I still can't find any good reason to use it!)
"		    - A problem with ^\s*);\= lines where ending a non '{}'
"		      structure.
"		    - Changed script local variable to be buffer local
"		      variable instead.
"
" Changes: 1.05:    - Lines containing "<?php ?>" and "?> <?php"
"		      (start and end tag on the same line) are no
"		      longer indented at col 1 but as normal code.
"
" Changes: 1.04:    - Strings containing "//" could break the indenting
"		      algorithm.
"		    - When a '{}' block was at col 1, the second line of the
"		      block was not indented at all (because of a stupid
"		      optimization coupled with a bug).
"
" Changes: 1.03:    - Some indenting problems corrected: end of non '{}'
"		      structures was not detected in some cases. The part of
"		      code concerned have been re-written
"		    - PHP start tags were not indented at col 1
"		    - Wrong comment in the code have been corrected
"
" Changes: 1.02:    - The bug I was talking about in version 1.01 (right below) has
"		      been corrected :)
"		    - Also corrected another bug that could occur in
"		      some special cases.
"		    - I removed the debug mode left in 1.01 that could
"		      cause some Vim messages at loading if other script were
"		      bugged.
"
" Changes: 1.01:    - Some little bug corrections regarding automatic optimized
"		      mode that missed some tests and could break the indenting.
"		    - There is also a problem with complex non bracketed structures, when several
"		      else are following each other, the algorithm do not indent the way it
"		      should.
"		      That will be corrected in the next version.
"
"  If you find a bug, please e-mail me at John.wellesz (AT) teaser (DOT) fr
"  with an example of code that breaks the algorithm.
"
"
"	Thanks a lot for using this script.
"
" NOTE: This script must be used with PHP syntax ON and with the php syntax
"	script by Lutz Eymers (http://www.isp.de/data/php.vim ) or with the
"	script by Peter Hodge (http://www.vim.org/scripts/script.php?script_id=1571 )
"	the later is bundled by default with Vim 7.
"
"
"	In the case you have syntax errors in your script such as HereDoc end
"	identifiers not at col 1 you'll have to indent your file 2 times (This
"	script will automatically put HereDoc end identifiers at col 1 if
"	they are followed by a ';').
"
" NOTE: If you are editing files in Unix file format and that (by accident)
"	there are '\r' before new lines, this script won't be able to proceed
"	correctly and will make many mistakes because it won't be able to match
"	'\s*$' correctly.
"	So you have to remove those useless characters first with a command like:
"
"	:%s /\r$//g
"
"	or simply 'let' the option PHP_removeCRwhenUnix to 1 and the script will
"	silently remove them when VIM load this script (at each bufread).
"
"
" Options: PHP_autoformatcomment = 0 to not enable autoformating of comment by
"		    default, if set to 0, this script will let the 'formatoptions' setting intact.
"
" Options: PHP_default_indenting = # of sw (default is 0), # of sw will be
"		   added to the indent of each line of PHP code.
"
" Options: PHP_removeCRwhenUnix = 1 to make the script automatically remove CR
"		   at end of lines (by default this option is unset), NOTE that you
"		   MUST remove CR when the fileformat is UNIX else the indentation
"		   won't be correct!
"
" Options: PHP_BracesAtCodeLevel = 1 to indent the '{' and '}' at the same
"		   level than the code they contain.
"		   Exemple:
"			Instead of:
"				if ($foo)
"				{
"					foo();
"				}
"
"			You will write:
"				if ($foo)
"					{
"					foo();
"					}
"
"			NOTE: The script will be a bit slower if you use this option because
"			some optimizations won't be available.
"
"
" Options: PHP_outdentphpescape = 0 (defaults to 1) to indent PHP tags as the surrounding code.
"
" Options: PHP_vintage_case_default_indent = 1 (defaults to 0) to add a meaningless indent
"		    befaore 'case:' and 'default":' statement in switch blocks.
"
"
" Remove all the comments from this file:
" :%s /^\s*".*\({{{\|xxx\)\@<!\n\c//g
" }}}
" The 4 following lines prevent this script from being loaded several times per buffer.
" They also prevent the load of different indent scripts for PHP at the same time.
if exists("b:did_indent")
    finish
endif
let b:did_indent = 1
"	This script set the option php_sync_method of PHP syntax script to 0
"	(fromstart indenting method) in order to have an accurate syntax.
"	If you are using very big PHP files (which is a bad idea) you will
"	experience slowings down while editing, if your code contains only PHP
"	code you can comment the line below.
let php_sync_method = 0
" Apply PHP_default_indenting option
if exists("PHP_default_indenting")
    let b:PHP_default_indenting = PHP_default_indenting * &sw
else
    let b:PHP_default_indenting = 0
endif
if exists("PHP_BracesAtCodeLevel")
    let b:PHP_BracesAtCodeLevel = PHP_BracesAtCodeLevel
else
    let b:PHP_BracesAtCodeLevel = 0
endif
if exists("PHP_autoformatcomment")
    let b:PHP_autoformatcomment = PHP_autoformatcomment
else
    let b:PHP_autoformatcomment = 1
endif
if exists("PHP_outdentphpescape")
    let b:PHP_outdentphpescape = PHP_outdentphpescape
else
    let b:PHP_outdentphpescape = 1
endif
if exists("PHP_vintage_case_default_indent") && PHP_vintage_case_default_indent
    let b:PHP_vintage_case_default_indent = 1
else
    let b:PHP_vintage_case_default_indent = 0
endif
let b:PHP_lastindented = 0
let b:PHP_indentbeforelast = 0
let b:PHP_indentinghuge = 0
let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
let b:PHP_LastIndentedWasComment = 0
let b:PHP_InsideMultilineComment = 0
" PHP code detect variables
let b:InPHPcode = 0
let b:InPHPcode_checked = 0
let b:InPHPcode_and_script = 0
let b:InPHPcode_tofind = ""
let b:PHP_oldchangetick = b:changedtick
let b:UserIsTypingComment = 0
let b:optionsset = 0
" The 4 options belows are overridden by indentexpr so they are always off
" anyway...
setlocal nosmartindent
setlocal noautoindent
setlocal nocindent
" autoindent must be on, so the line below is also useless...
setlocal nolisp
setlocal indentexpr=GetPhpIndent()
setlocal indentkeys=0{,0},0),:,!^F,o,O,e,*<Return>,=?>,=<?,=*/
let s:searchpairflags = 'bWr'
" Clean CR when the file is in Unix format
if &fileformat == "unix" && exists("PHP_removeCRwhenUnix") && PHP_removeCRwhenUnix
    silent! %s/\r$//g
endif
" Only define the functions once per Vim session.
if exists("*GetPhpIndent")
    call ResetPhpOptions()
    finish " XXX -- comment this line for easy dev
endif
let s:endline= '\s*\%(//.*\|#.*\|/\*.*\*/\s*\)\=$'
let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!'
"setlocal debug=msg " XXX -- do not comment this line when modifying this file
function! GetLastRealCodeLNum(startline) " {{{
    "Inspired from the function SkipJavaBlanksAndComments by Toby Allsopp for indent/java.vim
    let lnum = a:startline
    " Used to indent <script.*> html tag correctly
    if b:GetLastRealCodeLNum_ADD && b:GetLastRealCodeLNum_ADD == lnum + 1
	let lnum = b:GetLastRealCodeLNum_ADD
    endif
    let old_lnum = lnum
    while lnum > 1
	let lnum = prevnonblank(lnum)
	let lastline = getline(lnum)
	" if we are inside an html <script> we must skip ?> tags to indent
	" everything as php
	if b:InPHPcode_and_script && lastline =~ '?>\s*$'
	    let lnum = lnum - 1
	elseif lastline =~ '^\s*?>.*<?\%(php\)\=\s*$'
	    let lnum = lnum - 1
	elseif lastline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
	    " if line is under comment
	    let lnum = lnum - 1
	elseif lastline =~ '\*/\s*$'
	    " skip multiline comments
	    call cursor(lnum, 1)
	    if lastline !~ '^\*/'
		call search('\*/', 'W')
		" position the cursor on the first */
	    endif
	    let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
	    " find the most outside /*
	    let lastline = getline(lnum)
	    if lastline =~ '^\s*/\*'
		" if line contains nothing but comment
		" do the job again on the line before (a comment can hide another...)
		let lnum = lnum - 1
	    else
		break
	    endif
	elseif lastline =~? '\%(//\s*\|?>.*\)\@<!<?\%(php\)\=\s*$\|^\s*<script\>'
	    " skip non php code
	    while lastline !~ '\(<?.*\)\@<!?>' && lnum > 1
		let lnum = lnum - 1
		let lastline = getline(lnum)
	    endwhile
	    if lastline =~ '^\s*?>'
		" if line contains nothing but end tag
		let lnum = lnum - 1
	    else
		break
		" else there is something important before the ?>
	    endif
	    " Manage "here document" tags
	elseif lastline =~? '^\a\w*;\=$' && lastline !~? s:notPhpHereDoc
	    " match the end of a heredoc
	    let tofind=substitute( lastline, '\(\a\w*\);\=', '<<<''\\=\1''\\=$', '')
	    while getline(lnum) !~? tofind && lnum > 1
		let lnum = lnum - 1
	    endwhile
	else
	    " if none of these were true then we are done
	    break
	endif
    endwhile
    if lnum==1 && getline(lnum) !~ '<?'
	let lnum=0
    endif
    " This is to handle correctly end of script tags; to return the real last php
    " code line else a '?>' could be returned has last_line
    if b:InPHPcode_and_script && !b:InPHPcode
	let b:InPHPcode_and_script = 0
    endif
    "echom lnum
    "call getchar()
    return lnum
endfunction " }}}
function! Skippmatch2()
    let line = getline(".")
   if line =~ '\%(".*\)\@<=/\*\%(.*"\)\@=' || line =~ '\%(\%(//\|#\).*\)\@<=/\*'
       return 1
   else
       return 0
   endif
endfun
function! Skippmatch()	" {{{
    " the slowest instruction of this script, remove it and the script is 3
    " times faster but you may have troubles with '{' inside comments or strings
    " that will break the indent algorithm...
    let synname = synIDattr(synID(line("."), col("."), 0), "name")
    if synname == "Delimiter" || synname == "phpRegionDelimiter" || synname =~# "^phpParent" || synname == "phpArrayParens" || synname =~# '^php\%(Block\|Brace\)' || synname == "javaScriptBraces" || synname =~# "^phpComment" && b:UserIsTypingComment
	return 0
    else
"	echo "\"" . synname . "\"  " . getline(line("."));
"	call getchar()
	return 1
    endif
endfun " }}}
function! FindOpenBracket(lnum) " {{{
    " set the cursor to the start of the lnum line
    call cursor(a:lnum, 1)
    return searchpair('{', '', '}', 'bW', 'Skippmatch()')
endfun " }}}
function! FindTheIfOfAnElse (lnum, StopAfterFirstPrevElse) " {{{
    " A very clever recoursive function created by me (John Wellesz) that find the "if" corresponding to an
    " "else". This function can easily be adapted for other languages :)
    " 2010-07-25 -- Wow! it seems I was very proud of myself, I wouldn't write
    " such a comment nowadays.
    if getline(a:lnum) =~# '^\s*}\s*else\%(if\)\=\>'
	" we do this so we can find the opened bracket to speed up the process
	let beforeelse = a:lnum
    else
	let beforeelse = GetLastRealCodeLNum(a:lnum - 1)
    endif
    if !s:level
	let s:iftoskip = 0
    endif
    " If we've found another "else" then it means we need to skip the next "if"
    " we'll find.
    if getline(beforeelse) =~# '^\s*\%(}\s*\)\=else\%(\s*if\)\@!\>'
	let s:iftoskip = s:iftoskip + 1
    endif
    " A closing bracket? let skip the whole block to save some recursive calls
    if getline(beforeelse) =~ '^\s*}'
	let beforeelse = FindOpenBracket(beforeelse)
	" Put us on the block starter
	if getline(beforeelse) =~ '^\s*{'
	    let beforeelse = GetLastRealCodeLNum(beforeelse - 1)
	endif
    endif
    " sometimes it's not useful to find the very first if of a long if elseif
    " chain. The previous elseif will be enough
    if !s:iftoskip && a:StopAfterFirstPrevElse && getline(beforeelse) =~# '^\s*\%([}]\s*\)\=else\%(if\)\=\>'
	return beforeelse
    endif
    " if there was an else, then there is a if...
    if getline(beforeelse) !~# '^\s*if\>' && beforeelse>1 || s:iftoskip && beforeelse>1
	if s:iftoskip && getline(beforeelse) =~# '^\s*if\>'
	    let s:iftoskip = s:iftoskip - 1
	endif
	let s:level =  s:level + 1
	let beforeelse = FindTheIfOfAnElse(beforeelse, a:StopAfterFirstPrevElse)
    endif
    return beforeelse
endfunction " }}}
let s:defaultORcase = '^\s*\%(default\|case\).*:'
function! FindTheSwitchIndent (lnum) " {{{
    " Yes that's right, another very clever recursive function by the
    " author of the famous FindTheIfOfAnElse()
    let test = GetLastRealCodeLNum(a:lnum - 1)
    if test <= 1
	return indent(1) - &sw * b:PHP_vintage_case_default_indent
    end
    " A closing bracket? let skip the whole block to save some recursive calls
    if getline(test) =~ '^\s*}'
	let test = FindOpenBracket(test)
	" Put us on the line above the block starter since if it's a switch,
	" it's not the one we want.
	if getline(test) =~ '^\s*{'
	    let test = GetLastRealCodeLNum(GetLastRealCodeLNum(test - 1) - 1)
	endif
    endif
    " did we find it?
    if getline(test) =~# '^\s*switch\>'
	return indent(test)
    elseif getline(test) =~# s:defaultORcase
	return indent(test) - &sw * b:PHP_vintage_case_default_indent
    else
	return FindTheSwitchIndent(test)
    endif
endfunction "}}}
function! IslinePHP (lnum, tofind) " {{{
    " This function asks to the syntax if the pattern 'tofind' on the line
    " number 'lnum' is PHP code (very slow...).
    let cline = getline(a:lnum)
    if a:tofind==""
	" This correct the issue where lines beginning by a
	" single or double quote were not indented in some cases.
	let tofind = "^\\s*[\"']*\\s*\\zs\\S"
    else
	let tofind = a:tofind
    endif
    " ignore case
    let tofind = tofind . '\c'
    "find the first non blank char in the current line
    let coltotest = match (cline, tofind) + 1
    " ask to syntax what is its name
    let synname = synIDattr(synID(a:lnum, coltotest, 0), "name")
"	echom synname
    " if matchstr(synname, '^...') == "php" || synname=="Delimiter" || synname =~? '^javaScript'
    if synname =~ '^php' || synname=="Delimiter" || synname =~? '^javaScript'
	return synname
    else
	return ""
    endif
endfunction " }}}
let s:notPhpHereDoc = '\%(break\|return\|continue\|exit\|else\)'
let s:blockstart = '\%(\%(\%(}\s*\)\=else\%(\s\+\)\=\)\=if\>\|else\>\|while\>\|switch\>\|case\>\|default\>\|for\%(each\)\=\>\|declare\>\|class\>\|interface\>\|abstract\>\|try\>\|catch\>\)'
" make sure the options needed for this script to work correctly are set here
" for the last time. They could have been overridden by any 'onevent'
" associated setting file...
let s:autoresetoptions = 0
if ! s:autoresetoptions
    "au BufWinEnter,Syntax	*.php,*.php\d,*.phtml,*.ctp,*.inc	call ResetPhpOptions()
    let s:autoresetoptions = 1
endif
function! ResetPhpOptions()
    if ! b:optionsset && &filetype == "php" 
	if b:PHP_autoformatcomment
	    " Set the comment setting to something correct for PHP
	    setlocal comments=s1:/*,mb:*,ex:*/,://,:#
	    " disable Auto-wrap of text
	    setlocal formatoptions-=t
	    " Allow formatting of comments with "gq"
	    setlocal formatoptions+=q
	    " Insert comment leader after hitting <Enter>
	    setlocal formatoptions+=r
	    " Insert comment leader after hitting o or O in normal mode
	    setlocal formatoptions+=o
	    " Uses trailing white spaces to detect paragraphs
	    setlocal formatoptions+=w
	    " Autowrap comments using textwidth
	    setlocal formatoptions+=c
	    " Do not wrap if you modify a line after textwidth
	    setlocal formatoptions+=b
	endif
	let b:optionsset = 1
    endif
endfunc
call ResetPhpOptions()
function! GetPhpIndent()
    "##############################################
    "########### MAIN INDENT FUNCTION #############
    "##############################################
    " variable added on 2005-01-15 to make <script> tags really indent correctly (not pretty at all :-/ )
    let b:GetLastRealCodeLNum_ADD = 0
    " This detect if the user is currently typing text between each call
    let UserIsEditing=0
    if	b:PHP_oldchangetick != b:changedtick
	let b:PHP_oldchangetick = b:changedtick
	let UserIsEditing=1
    endif
    if b:PHP_default_indenting
	let b:PHP_default_indenting = g:PHP_default_indenting * &sw
    endif
    " current line
    let cline = getline(v:lnum)
    " Let's detect if we are indenting just one line or more than 3 lines
    " in the last case we can slightly optimize our algorithm (by trusting
    " what is above the current line)
    if !b:PHP_indentinghuge && b:PHP_lastindented > b:PHP_indentbeforelast
	if b:PHP_indentbeforelast
	    let b:PHP_indentinghuge = 1
	    echom 'Large indenting detected, speed optimizations engaged (v1.33)'
	endif
	let b:PHP_indentbeforelast = b:PHP_lastindented
    endif
    " If the line we are indenting isn't directly under the previous non-blank
    " line of the file then deactivate the optimization procedures and reset
    " status variable (we restart from scratch)
    if b:InPHPcode_checked && prevnonblank(v:lnum - 1) != b:PHP_lastindented
	if b:PHP_indentinghuge
	    echom 'Large indenting deactivated'
	    let b:PHP_indentinghuge = 0
	    let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
	endif
	let b:PHP_lastindented = v:lnum
	let b:PHP_LastIndentedWasComment=0
	let b:PHP_InsideMultilineComment=0
	let b:PHP_indentbeforelast = 0
	let b:InPHPcode = 0
	let b:InPHPcode_checked = 0
	let b:InPHPcode_and_script = 0
	let b:InPHPcode_tofind = ""
    elseif v:lnum > b:PHP_lastindented
	" we are indenting line in > order (we can rely on the line before)
	let real_PHP_lastindented = b:PHP_lastindented
	let b:PHP_lastindented = v:lnum
    endif
    " We must detect if we are in PHPCODE or not, but one time only, then
    " we will detect php end and start tags, comments /**/ and HereDoc
    " tags
    if !b:InPHPcode_checked " {{{ One time check
	let b:InPHPcode_checked = 1
	let synname = ""
	if cline !~ '<?.*?>'
	    " the line could be blank (if the user presses 'return' so we use
	    " prevnonblank()) We ask to Syntax
	    let synname = IslinePHP (prevnonblank(v:lnum), "")
	endif
	if synname!=""
	    if synname != "phpHereDoc" && synname != "phpHereDocDelimiter"
		let b:InPHPcode = 1
		let b:InPHPcode_tofind = ""
		if synname =~# "^phpComment"
		    let b:UserIsTypingComment = 1
		else
		    let b:UserIsTypingComment = 0
		endif
		if synname =~? '^javaScript'
		    let b:InPHPcode_and_script = 1
		endif
	    else
		"We are inside an "HereDoc"
		let b:InPHPcode = 0
		let b:UserIsTypingComment = 0
		let lnum = v:lnum - 1
		while getline(lnum) !~? '<<<''\=\a\w*''\=$' && lnum > 1
		    let lnum = lnum - 1
		endwhile
		let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<''\=\(\a\w*\)''\=$', '^\\s*\1;\\=$', '')
	    endif
	else
	    " IslinePHP returned "" => we are not in PHP or Javascript
	    let b:InPHPcode = 0
	    let b:UserIsTypingComment = 0
	    " Then we have to find a php start tag...
	    let b:InPHPcode_tofind = '<?\%(.*?>\)\@!\|<script.*>'
	endif
    endif "!b:InPHPcode_checked }}}
    " Now we know where we are so we can verify the line right above the
    " current one to see if we have to stop or restart php indenting
    " Test if we are indenting PHP code {{{
    " Find an executable php code line above the current line.
    let lnum = prevnonblank(v:lnum - 1)
    let last_line = getline(lnum)
    " If we aren't in php code, then there is something we have to find
    if b:InPHPcode_tofind!=""
	if cline =~? b:InPHPcode_tofind
	    let b:InPHPcode = 1
	    let b:InPHPcode_tofind = ""
	    let b:UserIsTypingComment = 0
	    if cline =~ '\*/'
		" End comment tags must be indented like start comment tags
		call cursor(v:lnum, 1)
		if cline !~ '^\*/'
		    call search('\*/', 'W')
		endif
		" find the most outside /*
		let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
		let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
		" prevent a problem if multiline /**/ comment are surrounded by
		" other types of comments
		let b:PHP_LastIndentedWasComment = 0
		if cline =~ '^\s*\*/'
		    return indent(lnum) + 1
		else
		    return indent(lnum)
		endif
	    elseif cline =~? '<script\>'
		" a more accurate test is useless since there isn't any other possibility
		let b:InPHPcode_and_script = 1
		" this will make GetLastRealCodeLNum to add one line to its
		" given argument so it can detect the <script> easily (that is
		" simpler/quicker than using a regex...)
		let b:GetLastRealCodeLNum_ADD = v:lnum
	    endif
	endif
    endif
    " ### If we are in PHP code, we test the line before to see if we have to stop indenting
    if b:InPHPcode
	" Was last line containing a PHP end tag ?
	if !b:InPHPcode_and_script && last_line =~ '\%(<?.*\)\@<!?>\%(.*<?\)\@!' && IslinePHP(lnum, '?>')=~"Delimiter"
	    if cline !~? s:PHP_startindenttag
		let b:InPHPcode = 0
		let b:InPHPcode_tofind = s:PHP_startindenttag
	    elseif cline =~? '<script\>'
		let b:InPHPcode_and_script = 1
	    endif
	    " Was last line the start of a HereDoc ?
	elseif last_line =~? '<<<''\=\a\w*''\=$'
	    let b:InPHPcode = 0
	    let b:InPHPcode_tofind = substitute( last_line, '^.*<<<''\=\(\a\w*\)''\=$', '^\\s*\1;\\=$', '')
	    " Skip /* \n+ */ comments except when the user is currently
	    " writing them or when it is a comment (ie: not a code put in comment)
	elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' && getline(v:lnum + 1) !~ '^\s*\*'
	    let b:InPHPcode = 0
	    let b:InPHPcode_tofind = '\*/'
	    " is current line the end of a HTML script ? (we indent scripts the
	    " same as php code)
	elseif cline =~? '^\s*</script>'
	    let b:InPHPcode = 0
	    let b:InPHPcode_tofind = s:PHP_startindenttag
	    " Note that b:InPHPcode_and_script is still true so that the
	    " </script> can be indented correctly
	endif
    endif " }}}
    " Non PHP code is let as it is
    if !b:InPHPcode && !b:InPHPcode_and_script
	return -1
    endif
    " Align correctly multi // or # lines
    " Indent successive // or # comment the same way the first is {{{
    if cline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
	if b:PHP_LastIndentedWasComment == 1
	    return indent(real_PHP_lastindented)
	endif
	let b:PHP_LastIndentedWasComment = 1
    else
	let b:PHP_LastIndentedWasComment = 0
    endif " }}}
    " Indent multiline /* comments correctly {{{
    "if we are on the start of a MULTI * beginning comment or if the user is
    "currently typing a /* beginning comment.
    if b:PHP_InsideMultilineComment || b:UserIsTypingComment
	if cline =~ '^\s*\*\%(\/\)\@!'
	    " if cline == '*'
	    if last_line =~ '^\s*/\*'
		" if last_line == '/*'
		return indent(lnum) + 1
	    else
		return indent(lnum)
	    endif
	else
	    let b:PHP_InsideMultilineComment = 0
	endif
    endif
    if !b:PHP_InsideMultilineComment && cline =~ '^\s*/\*' && cline !~ '\*/\s*$'
	" if cline == '/*' and doesn't end with '*/'
	if getline(v:lnum + 1) !~ '^\s*\*'
	    return -1
	endif
	let b:PHP_InsideMultilineComment = 1
    endif " }}}
    " Some tags are always indented to col 1
    " Things always indented at col 1 (PHP delimiter: <?, ?>, Heredoc end) {{{
    " PHP start tags are always at col 1, useless to indent unless the end tag
    " is on the same line
    if cline =~# '^\s*<?' && cline !~ '?>' && b:PHP_outdentphpescape
	return 0
    endif
    " PHP end tags are always at col 1, useless to indent unless if it's
    " followed by a start tag on the same line
    if	cline =~ '^\s*?>' && cline !~# '<?' && b:PHP_outdentphpescape
	return 0
    endif
    " put HereDoc end tags at start of lines
    if cline =~? '^\s*\a\w*;$\|^\a\w*$' && cline !~? s:notPhpHereDoc
	return 0
    endif " }}}
    let s:level = 0
    " Find an executable php code line above the current line.
    let lnum = GetLastRealCodeLNum(v:lnum - 1)
    " last line
    let last_line = getline(lnum)
    " by default
    let ind = indent(lnum)
    let endline= s:endline
    if ind==0 && b:PHP_default_indenting
	let ind = b:PHP_default_indenting
    endif
    " Hit the start of the file, use default indent.
    if lnum == 0
	return b:PHP_default_indenting
    endif
    " Search the matching open bracket (with searchpair()) and set the indent of cline
    " to the indent of the matching line. (unless it's a VIm folding end tag)
    if cline =~ '^\s*}\%(}}\)\@!'
	let ind = indent(FindOpenBracket(v:lnum))
	let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
	return ind
    endif
    " Check for end of comment and indent it like its beginning
    if cline =~ '^\s*\*/'
	" End comment tags must be indented like start comment tags
	call cursor(v:lnum, 1)
	if cline !~ '^\*/'
	    call search('\*/', 'W')
	endif
	" find the most outside /*
	let lnum = searchpair('/\*', '', '\*/', s:searchpairflags, 'Skippmatch2()')
	let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
	if cline =~ '^\s*\*/'
	    return indent(lnum) + 1
	else
	    return indent(lnum)
	endif
    endif
    " if the last line is a stated line and it's not indented then why should
    " we indent this one??
    " Do not do this if the last line is a ')' because array indentation can
    " fail... and defaultORcase can be at col 0.
    " if optimized mode is active and nor current or previous line are an 'else'
    " or the end of a possible bracketless thing then indent the same as the previous
    " line
    if last_line =~ '[;}]'.endline && last_line !~ '^)' && last_line !~# s:defaultORcase " Added && last_line !~ '^)' on 2007-12-30
	if ind==b:PHP_default_indenting
	    " if no indentation for the previous line
	    return b:PHP_default_indenting
	elseif b:PHP_indentinghuge && ind==b:PHP_CurrentIndentLevel && cline !~# '^\s*\%(else\|\%(case\|default\).*:\|[})];\=\)' && last_line !~# '^\s*\%(\%(}\s*\)\=else\)' && getline(GetLastRealCodeLNum(lnum - 1))=~';'.endline
	    return b:PHP_CurrentIndentLevel
	endif
    endif
    " used to prevent redundant tests in the last part of the script
    let LastLineClosed = 0
    let terminated = '\%(;\%(\s*?>\)\=\|<<<''\=\a\w*''\=$\|^\s*}\)'.endline
    " What is a terminated line?
    " - a line terminated by a ";" optionally followed by a "?>"
    " - a HEREDOC starter line (the content of such block is never seen by this script)
    " - a "}" not followed by a "{"
    let unstated   = '\%(^\s*'.s:blockstart.'.*)\|\%(//.*\)\@<!\<e'.'lse\>\)'.endline
    " What is an unstated line?
    " - an "else" at the end of line
    " - a  s:blockstart (if while etc...) followed by anything but a ";" at
    "	the end of line
    " if the current line is an 'else' starting line
    " (to match an 'else' preceded by a '}' is irrelevant and futile - see
    " code above)
    if ind != b:PHP_default_indenting && cline =~# '^\s*else\%(if\)\=\>'
	" prevent optimized to work at next call  XXX why ?
	let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
	return indent(FindTheIfOfAnElse(v:lnum, 1))
    elseif cline =~# s:defaultORcase
	" case and default need a special treatment
	return FindTheSwitchIndent(v:lnum) + &sw * b:PHP_vintage_case_default_indent
    elseif cline =~ '^\s*)\=\s*{'
	let previous_line = last_line
	let last_line_num = lnum
	" let's find the indent of the block starter (if, while, for, etc...)
	while last_line_num > 1
	    if previous_line =~ '^\s*\%(' . s:blockstart . '\|\%([a-zA-Z]\s*\)*function\)'
		let ind = indent(last_line_num)
		" If the PHP_BracesAtCodeLevel is set then indent the '{'
		if  b:PHP_BracesAtCodeLevel
		    let ind = ind + &sw
		endif
		return ind
	    endif
	    let last_line_num = last_line_num - 1
	    let previous_line = getline(last_line_num)
	endwhile
    elseif last_line =~# unstated && cline !~ '^\s*);\='.endline
	let ind = ind + &sw " we indent one level further when the preceding line is not stated
	"echo "42"
	"call getchar()
	return ind
	" If the last line is terminated by ';' or if it's a closing '}'
	" We need to check if this isn't the end of a multilevel non '{}'
	" structure such as:
	" Exemple:
	"			if ($truc)
	"				echo 'truc';
	"
	"	OR
	"
	"			if ($truc)
	"				while ($truc) {
	"					lkhlkh();
	"					echo 'infinite loop\n';
	"				}
	"
	"	OR even (ADDED for version 1.17 - no modification required )
	"
	"			$thing =
	"				"something";
    elseif (ind != b:PHP_default_indenting || last_line =~ '^)' ) && last_line =~ terminated " Added || last_line =~ '^)' on 2007-12-30 (array indenting problem broke other things)
	" If we are here it means that the previous line is:
	" - a *;$ line
	" - a [beginning-blanck] } followed by anything but a { $
	let previous_line = last_line
	let last_line_num = lnum
	let LastLineClosed = 1
	" The idea here is to check if the current line is after a non '{}'
	" structure so we can indent it like the top of that structure.
	" The top of that structure is characterized by a if (ff)$ style line
	" preceded by a stated line. If there is no such structure then we
	" just have to find two 'normal' lines following each other with the
	" same indentation and with the first of these two lines terminated by
	" a ; or by a }...
	while 1
	    " let's skip '{}' blocks
	    if previous_line =~ '^\s*}'
		" find the opening '{'
		let last_line_num = FindOpenBracket(last_line_num)
		" if the '{' is alone on the line get the line before
		if getline(last_line_num) =~ '^\s*{'
		    let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
		endif
		let previous_line = getline(last_line_num)
		continue
	    else
		" At this point we know that the previous_line isn't a closing
		" '}' so we can check if we really are in such a structure.
		" it's not a '}' but it could be an else alone...
		if getline(last_line_num) =~# '^\s*else\%(if\)\=\>'
		    let last_line_num = FindTheIfOfAnElse(last_line_num, 0)
		    " re-run the loop (we could find a '}' again)
		    continue
		endif
		" So now it's ok we can check :-)
		" A good quality is to have confidence in oneself so to know
		" if yes or no we are in that struct lets test the indent of
		" last_line_num and of last_line_num - 1!
		" If those are == then we are almost done.
		"
		" That isn't sufficient, we need to test how the first of
		" these 2 lines ends...
		" Remember the 'topest' line we found so far
		let last_match = last_line_num
		let one_ahead_indent = indent(last_line_num)
		let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
		let two_ahead_indent = indent(last_line_num)
		let after_previous_line = previous_line
		let previous_line = getline(last_line_num)
		" If we find a '{' or a case/default then we are inside that block so lets
		" indent properly... Like the line following that block starter
		if previous_line =~# s:defaultORcase.'\|{'.endline
		    break
		endif
		" The 3 lines below are not necessary for the script to work
		" but it makes it work a little more faster in some (rare) cases.
		" We verify if we are at the top of a non '{}' struct.
		if after_previous_line=~# '^\s*'.s:blockstart.'.*)'.endline && previous_line =~# '[;}]'.endline
		    break
		endif
		if one_ahead_indent == two_ahead_indent || last_line_num < 1
		    " So the previous line and the line before are at the same
		    " col. Now we just have to check if the line before is a ;$ or [}]$ ended line
		    " we always check the most ahead line of the 2 lines so
		    " it's useless to match ')$' since the lines couldn't have
		    " the same indent...
		    if previous_line =~# '\%(;\|^\s*}\)'.endline || last_line_num < 1
			break
		    endif
		endif
	    endif
	endwhile
	if indent(last_match) != ind
	    " let's use the indent of the last line matched by the algorithm above
	    let ind = indent(last_match)
	    " line added in version 1.02 to prevent optimized mode
	    " from acting in some special cases
	    let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
	    return ind
	endif
	" if nothing was done lets the old script continue
    endif
    let plinnum = GetLastRealCodeLNum(lnum - 1)
    " previous to last line
    let AntepenultimateLine = getline(plinnum)
    " REMOVE comments at end of line before treatment
    " the first part of the regex removes // from the end of line when they are
    " followed by a number of '"' which is a multiple of 2. The second part
    " removes // that are not followed by any '"'
    " Sorry for this unreadable thing...
    let last_line = substitute(last_line,"\\(//\\|#\\)\\(\\(\\([^\"']*\\([\"']\\)[^\"']*\\5\\)\\+[^\"']*$\\)\\|\\([^\"']*$\\)\\)",'','')
    if ind == b:PHP_default_indenting
	if last_line =~ terminated
	    let LastLineClosed = 1
	endif
    endif
    " Indent blocks enclosed by {} or () (default indenting)
    if !LastLineClosed
	"echo "start"
	"call getchar()
	" the last line isn't a .*; or a }$ line
	" Indent correctly multilevel and multiline '(.*)' things
	" if the last line is a [{(]$ or a multiline function call (or array
	" declaration) with already one parameter on the opening ( line
	if last_line =~# '[{(]'.endline || last_line =~? '\h\w*\s*(.*,$' && AntepenultimateLine !~ '[,(]'.endline
	    if !b:PHP_BracesAtCodeLevel || last_line !~# '^\s*{'
		let ind = ind + &sw
	    endif
	    "	 echo "43"
	    "	 call getchar()
	    if b:PHP_BracesAtCodeLevel || b:PHP_vintage_case_default_indent == 1
		" case and default are not indented inside blocks
		let b:PHP_CurrentIndentLevel = ind
		return ind
	    endif
	    " If the last line isn't empty and ends with a '),' then check if the
	    " ')' was opened on the same line, if not it means it closes a
	    " multiline '(.*)' thing and that the current line need to be
	    " de-indented one time.
	elseif last_line =~ '\S\+\s*),'.endline
	    call cursor(lnum, 1)
	    call search('),'.endline, 'W')
	    let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()')
	    if openedparent != lnum
		let ind = indent(openedparent)
	    endif
	
	    " if the line before starts a block then we need to indent the
	    " current line.
	elseif last_line =~ '^\s*'.s:blockstart
	    let ind = ind + &sw
	    "echo cline. "  --test 5--	 " . ind
	    "call getchar()
	    " In all other cases if the last line isn't terminated indent 1
	    " level higher but only if the last line wasn't already indented
	    " for the same "code event"/reason. IE: if the antepenultimate line is terminated.
	    "
	    " 2nd explanation:
	    "	    - Test if the antepenultimate line is terminated or is
	    "	    a default/case if yes indent else let since it must have
	    "	    been indented correctly already
	"elseif cline !~ '^\s*{' && AntepenultimateLine =~ '\%(;\%(\s*?>\)\=\|<<<\a\w*\|{\|^\s*'.s:blockstart.'.*)\)'.endline.'\|^\s*}\|'.s:defaultORcase
	elseif AntepenultimateLine =~ '\%(;\%(\s*?>\)\=\|<<<''\=\a\w*''\=$\|^\s*}\|{\)'.endline . '\|' . s:defaultORcase
	    let ind = ind + &sw
	    "echo pline. "  --test 2--	 " . ind
	    "call getchar()
	endif
    endif
 
    "echo "end"
    "call getchar()
    " If the current line closes a multiline function call or array def
    if cline =~  '^\s*);\='
	let ind = ind - &sw
    endif
    let b:PHP_CurrentIndentLevel = ind
    return ind
endfunction
" vim: set ts=8 sw=4 sts=4: