diff options
author | Adam Stankiewicz <sheerun@sher.pl> | 2013-09-12 16:33:12 +0200 |
---|---|---|
committer | Adam Stankiewicz <sheerun@sher.pl> | 2013-09-12 16:33:12 +0200 |
commit | d96dc724d442bbc9788815ab3da09d9fff0555a9 (patch) | |
tree | f4b1859aa45a28cd420c7285006a4398726ff18a /indent/php.vim | |
parent | d4eca3f2eb6c8496261ed252596d6813b9eb8a04 (diff) | |
download | vim-polyglot-d96dc724d442bbc9788815ab3da09d9fff0555a9.tar.gz vim-polyglot-d96dc724d442bbc9788815ab3da09d9fff0555a9.zip |
Add extended php support
Diffstat (limited to 'indent/php.vim')
-rw-r--r-- | indent/php.vim | 1269 |
1 files changed, 1269 insertions, 0 deletions
diff --git a/indent/php.vim b/indent/php.vim new file mode 100644 index 00000000..6286c6b3 --- /dev/null +++ b/indent/php.vim @@ -0,0 +1,1269 @@ +" 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: |