]> git.pld-linux.org Git - packages/vim.git/blob - php.vim
- new
[packages/vim.git] / php.vim
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 " Last Change: 2005 September 17th
6 " Version: 1.181
7 "
8 "
9 " Changes: 1.181    - I Forgot to register 'class' as a block starter so the '{'
10 "                                         after a 'class' could be wrongly indented...
11 "
12 " Changes: 1.18         - No more problems with Vim 6.3 and UTF-8.
13 "                                       - Opening braces "{" are always indented according to their block starter.
14 "
15 "                               Instead of:
16 "                               
17 "                                       if( $test
18 "                                               && $test2 )
19 "                                               {
20 "                       }
21 "
22 "                               You have:
23 "
24 "                                       if( $test
25 "                                               && $test2 )
26 "                                       {
27 "                   }                           
28 "
29 "
30 " Changes: 1.17         - Now following parts of split lines are indented:
31 "                                               Instead of:
32 "                                                       $foo=
33 "                                                       "foo"
34 "                                                       ."foo";
35 "                                                       
36 "                                               You have:
37 "                                                       $foo=
38 "                                                               "foo"
39 "                                                               ."foo";
40 "               
41 "                                       - If a "case : break;" was declared on a single line, the
42 "                                         following "case" was not indented correctly.
43 "                                       - If a </script> html tag was preceded by a "?>" it wasn't indented.
44 "                                       - Some other minor corrections and improvements.
45 "
46 "
47 " Changes: 1.16         - Now starting and ending '*' of multiline '/* */' comments are aligned
48 "                                         on the '*' of the '/*' comment starter.
49 "                                       - Some code improvements that make indentation faster.
50 "
51 " Changes: 1.15         - Corrected some problems with the indentation of
52 "                                         multiline "array()" declarations.
53 "
54 " Changes: 1.14         - Added auto-formatting for comments (using the Vim option formatoptions=qroc).
55 "                                       - Added the script option PHP_BracesAtCodeLevel to
56 "                                         indent the '{' and '}' at the same level than the
57 "                                         code they contain.
58
59 " Changes: 1.13         - Some code cleaning and typo corrections (Thanks to
60 "                                         Emanuele Giaquinta for his patches)
61 "
62 " Changes: 1.12         - The bug involving searchpair() and utf-8 encoding in Vim 6.3 will
63 "                                         not make this script to hang but you'll have to be
64 "                                         careful to not write '/* */' comments with other '/*'
65 "                                         inside the comments else the indentation won't be correct.
66 "                                         NOTE: This is true only if you are using utf-8 and vim 6.3.
67 "
68 " Changes: 1.11         - If the "case" of a "switch" wasn't alone on its line
69 "                                         and if the "switch" was at col 0 (or at default indenting)
70 "                                         the lines following the "case" were not indented.
71 "
72 " Changes: 1.10         - Lines beginning by a single or double quote were
73 "                                         not indented in some cases.
74 "
75 " Changes: 1.09         - JavaScript code was not always directly indented.
76 "
77 " Changes: 1.08         - End comment tags '*/' are indented like start tags '/*'.
78 "                                       - When typing a multiline comment, '}' are indented
79 "                                         according to other commented '{'.
80 "                                       - Added a new option 'PHP_removeCRwhenUnix' to
81 "                                         automatically remove CR at end of lines when the file
82 "                                         format is Unix.
83 "                                       - Changed the file format of this very file to Unix.
84 "                                       - This version seems to correct several issues some people
85 "                                         had with 1.07.
86 "
87 " Changes: 1.07         - Added support for "Here document" tags:
88 "                                          - HereDoc end tags are indented properly.
89 "                                          - HereDoc content remains unchanged.
90 "                                       - All the code that is outside PHP delimiters remains
91 "                                         unchanged.
92 "                                       - New feature: The content of <script.*> html tags is considered as PHP
93 "                                         and indented according to the surrounding PHP code.
94 "                                       - "else if" are detected as "elseif".
95 "                                       - Multiline /**/ are indented when the user types it but
96 "                                         remain unchanged when indenting from their beginning.
97 "                                       - Fixed indenting of // and # comments.
98 "                                       - php_sync_method option is set to 0 (fromstart).
99 "                                         This is required for complex PHP scripts else the indent
100 "                                         may fail.
101 "                                       - Files with non PHP code at the beginning could alter the indent
102 "                                         of the following PHP code.
103 "                                       - Other minor improvements and corrections.
104 "
105 " Changes: 1.06:    - Switch block were no longer indented correctly...
106 "                                       - Added an option to use a default indenting instead of 0.
107 "                                         (whereas I still can't find any good reason to use it!)
108 "                                       - A problem with ^\s*);\= lines where ending a non '{}'
109 "                                         structure.
110 "                                       - Changed script local variable to be buffer local
111 "                                         variable instead.
112 "
113 " Changes: 1.05:    - Lines containing "<?php ?>" and "?> <?php"
114 "                                         (start and end tag on the same line) are no
115 "                                         longer indented at col 1 but as normal code.
116 "
117 " Changes: 1.04:        - Strings containing "//" could break the indenting
118 "                                         algorithm.
119 "                                       - When a '{}' block was at col 1, the second line of the
120 "                                         block was not indented at all (because of a stupid
121 "                                         optimization coupled with a bug).
122 "
123 " Changes: 1.03:        - Some indenting problems corrected: end of non '{}'
124 "                                         structures was not detected in some cases. The part of
125 "                                         code concerned have been re-written
126 "                                       - PHP start tags were not indented at col 1
127 "                                       - Wrong comment in the code have been corrected
128 "
129 " Changes: 1.02:        - The bug I was talking about in version 1.01 (right below) has
130 "                                         been corrected :)
131 "                                       - Also corrected another bug that could occur in
132 "                                         some special cases.
133 "                                       - I removed the debug mode left in 1.01 that could
134 "                                         cause some Vim messages at loading if other script were
135 "                                         bugged.
136 "
137 " Changes: 1.01:        - Some little bug corrections reguarding automatic optimized
138 "                                         mode that missed some tests and could break the indenting.
139 "                                       - There is also a problem with complex non bracked structures, when several
140 "                                         else are following each other, the algorithm do not indent the way it
141 "                                         should.
142 "                                         That will be corrected in the next version.
143
144 "  If you find a bug, please e-mail me at John.wellesz (AT) teaser (DOT) fr
145 "  with an example of code that break the algorithm.
146 "
147 "
148 "       Thanks a lot for using this script.
149 "
150
151 " NOTE: This script must be used with PHP syntax ON and with the php syntax
152 "               script by Lutz Eymers (http://www.isp.de/data/php.vim ) that's the script bundled with Gvim.
153 "
154 "
155 "       In the case you have syntax errors in your script such as end of HereDoc
156 "       tags not at col 1 you'll have to indent your file 2 times (This script 
157 "       will automatically put HereDoc end tags at col 1).
158
159
160 " NOTE: If you are editing file in Unix file format and that (by accident)
161 " there are '\r' before new lines, this script won't be able to proceed
162 " correctly and will make many mistakes because it won't be able to match
163 " '\s*$' correctly.
164 " So you have to remove those useless characters first with a command like:
165 "
166 " :%s /\r$//g
167 "
168 " or simply 'let' the option PHP_removeCRwhenUnix to 1 and the script will
169 " silently remove them when VIM load this script (at each bufread).
170
171 " Options: PHP_default_indenting = # of sw (default is 0), # of sw will be
172 "                  added to the indent of each line of PHP code.
173 "
174 " Options: PHP_removeCRwhenUnix = 1 to make the script automatically remove CR
175 "                  at end of lines (by default this option is unset), NOTE that you
176 "                  MUST remove CR when the fileformat is UNIX else the indentation
177 "                  won't be correct...
178 "
179 " Options: PHP_BracesAtCodeLevel = 1 to indent the '{' and '}' at the same
180 "                  level than the code they contain.
181 "                  Exemple:
182 "                       Instead of:
183 "                               if ($foo)
184 "                               {
185 "                                       foo();
186 "                               }
187 "
188 "                       You will write:
189 "                               if ($foo)
190 "                                       {
191 "                                       foo();
192 "                                       }
193 "
194 "                       NOTE: The script will be a bit slower if you use this option because
195 "                       some optimizations won't be available.
196
197
198 " The 4 following lines prevent this script from being loaded several times per buffer.
199 " They also prevent the load of different indent scripts for PHP at the same time.
200 if exists("b:did_indent")
201         finish
202 endif
203 let b:did_indent = 1
204
205 "       This script set the option php_sync_method of PHP syntax script to 0
206 "       (fromstart indenting method) in order to have an accurate syntax.
207 "       If you are using very big PHP files (which is a bad idea) you will
208 "       experience slowings down while editing, if your code contains only PHP
209 "       code you can comment the line below.
210
211 let php_sync_method = 0
212
213
214 " Apply PHP_default_indenting option
215 if exists("PHP_default_indenting")
216         let b:PHP_default_indenting = PHP_default_indenting * &sw
217 else
218         let b:PHP_default_indenting = 0
219 endif
220
221 if exists("PHP_BracesAtCodeLevel")
222         let b:PHP_BracesAtCodeLevel = PHP_BracesAtCodeLevel
223 else
224         let b:PHP_BracesAtCodeLevel = 0
225 endif
226
227
228 let b:PHP_lastindented = 0
229 let b:PHP_indentbeforelast = 0
230 let b:PHP_indentinghuge = 0
231 let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
232 let b:PHP_LastIndentedWasComment = 0
233 let b:PHP_InsideMultilineComment = 0
234 " PHP code detect variables
235 let b:InPHPcode = 0
236 let b:InPHPcode_checked = 0
237 let b:InPHPcode_and_script = 0
238 let b:InPHPcode_tofind = ""
239 let b:PHP_oldchangetick = b:changedtick
240 let b:UserIsTypingComment = 0
241 let b:optionsset = 0
242
243 " The 4 options belows are overriden by indentexpr so they are always off
244 " anyway...
245 setlocal nosmartindent
246 setlocal noautoindent 
247 setlocal nocindent
248 setlocal nolisp " autoindent must be on, so this line is also useless...
249
250 setlocal indentexpr=GetPhpIndent()
251 setlocal indentkeys=0{,0},0),:,!^F,o,O,e,*<Return>,=?>,=<?,=*/
252
253
254
255 let s:searchpairflags = 'bWr'
256
257 " Clean CR when the file is in Unix format
258 if &fileformat == "unix" && exists("PHP_removeCRwhenUnix") && PHP_removeCRwhenUnix
259         silent! %s/\r$//g
260 endif
261
262 " Only define the functions once per Vim session.
263 if exists("*GetPhpIndent")
264         finish " XXX
265 endif
266
267 let s:endline= '\s*\%(//.*\|#.*\|/\*.*\*/\s*\)\=$'
268 let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!'
269 "setlocal debug=msg " XXX
270
271
272 function! GetLastRealCodeLNum(startline) " {{{
273         "Inspired from the function SkipJavaBlanksAndComments by Toby Allsopp for indent/java.vim 
274         let lnum = a:startline
275         let old_lnum = lnum
276
277         while lnum > 1
278                 let lnum = prevnonblank(lnum)
279                 let lastline = getline(lnum)
280
281                 " if we are inside an html <script> we must skip ?> tags to indent
282                 " everything as php
283                 if b:InPHPcode_and_script && lastline =~ '?>\s*$'
284                         let lnum = lnum - 1
285                 elseif lastline =~ '^\s*?>.*<?\%(php\)\=\s*$'
286                         let lnum = lnum - 1
287                 elseif lastline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)' " if line is under comment
288                         let lnum = lnum - 1
289                 elseif lastline =~ '\*/\s*$' " skip multiline comments
290                         call cursor(lnum, 1)
291                         if lastline !~ '^\*/'
292                                 call search('\*/', 'W') " positition the cursor on the first */
293                         endif
294                         let lnum = searchpair('/\*', '', '\*/', s:searchpairflags) " find the most outside /*
295                         "echo 'lnum skipnonphp= ' . lnum
296                         "call getchar()
297
298                         let lastline = getline(lnum)
299                         if lastline =~ '^\s*/\*' " if line contains nothing but comment
300                                 let lnum = lnum - 1 " do the job again on the line before (a comment can hide another...)
301                         else
302                                 break
303                         endif
304
305                         
306                 elseif lastline =~? '\%(//\s*\|?>.*\)\@<!<?\%(php\)\=\s*$\|^\s*<script\>' " skip non php code
307                 "       call cursor(lnum, 1)
308                 "       call search('<?', 'W')
309                 "       let lnum = searchpair('?>', '', '<?\zs', 'bW', 'getline(".") =~ "<?.*?>"')
310
311                 "       let lastline = getline(lnum)
312                         while lastline !~ '\(<?.*\)\@<!?>' && lnum > 1
313                                 let lnum = lnum - 1
314                                 let lastline = getline(lnum)
315                         endwhile
316                         if lastline =~ '^\s*?>' " if line contains nothing but end tag 
317                                 let lnum = lnum - 1
318                         else
319                                 break " else there is something important before the ?>
320                         endif
321
322
323                         " Manage "here document" tags
324                 elseif lastline =~? '^\a\w*;$' && lastline !~? s:notPhpHereDoc " match the end of a heredoc
325                         let tofind=substitute( lastline, '\([^;]\+\);', '<<<\1$', '')
326                         while getline(lnum) !~? tofind && lnum > 1
327                                 let lnum = lnum - 1
328                         endwhile
329                 else
330                         break " if none of these were true then we are done
331                 endif
332         endwhile
333
334         if lnum==1 && getline(lnum)!~ '<?'
335                 let lnum=0
336         endif
337         
338         " This is to handle correctly end of script tags; to return the real last php
339         " code line else a '?>' could be returned has last_line
340         if b:InPHPcode_and_script && !b:InPHPcode
341                 let b:InPHPcode_and_script = 0
342         endif
343         return lnum
344 endfunction
345 " }}}
346
347 function! Skippmatch()  " {{{
348         " the slowest instruction of this script, remove it and the script is 3
349         " times faster but you may have troubles with '{' inside comments or strings
350         " that will break the indent algorithm...
351         let synname = synIDattr(synID(line("."), col("."), 0), "name")
352         if synname == "Delimiter" || synname == "phpParent" || synname == "javaScriptBraces" || synname == "phpComment" && b:UserIsTypingComment
353                 return 0
354         else
355                 return 1
356         endif
357 endfun
358 " }}}
359
360 function! FindOpenBracket(lnum) " {{{
361         call cursor(a:lnum, 1) " set the cursor to the start of the lnum line
362         return searchpair('{', '', '}', 'bW', 'Skippmatch()')
363 endfun
364 " }}}
365
366 function! FindTheIfOfAnElse (lnum, StopAfterFirstPrevElse) " {{{
367 " A very clever recoursive function created by me (John Wellesz) that find the "if" corresponding to an
368 " "else". This function can easily be adapted for other languages :)
369         
370         if getline(a:lnum) =~# '^\s*}\s*else\%(if\)\=\>'
371                 let beforeelse = a:lnum " we do this so we can find the opened bracket to speed up the process
372         else
373                 let beforeelse = GetLastRealCodeLNum(a:lnum - 1)
374         endif
375
376         if !s:level
377                 let s:iftoskip = 0
378         endif
379
380         " If we found another "else" then it means we need to skip the next "if"
381         " we'll found. (since version 1.02)
382         if getline(beforeelse) =~# '^\s*\%(}\s*\)\=else\%(\s*if\)\@!\>'
383                 let s:iftoskip = s:iftoskip + 1
384         endif
385         
386         " A closing bracket? let skip the whole block to save some recursive calls
387         if getline(beforeelse) =~ '^\s*}'
388                 let beforeelse = FindOpenBracket(beforeelse)
389
390                 " Put us on the block starter
391                 if getline(beforeelse) =~ '^\s*{'
392                         let beforeelse = GetLastRealCodeLNum(beforeelse - 1)
393                 endif
394         endif
395
396
397         if !s:iftoskip && a:StopAfterFirstPrevElse && getline(beforeelse) =~# '^\s*\%([}]\s*\)\=else\%(if\)\=\>'
398                 return beforeelse
399         endif
400
401         " if there was an else, then there is a if...
402         if getline(beforeelse) !~# '^\s*if\>' && beforeelse>1 || s:iftoskip && beforeelse>1
403                 
404                 if  s:iftoskip && getline(beforeelse) =~# '^\s*if\>'
405                         let s:iftoskip = s:iftoskip - 1
406                 endif
407
408                 let s:level =  s:level + 1
409                 let beforeelse = FindTheIfOfAnElse(beforeelse, a:StopAfterFirstPrevElse)
410         endif
411
412         return beforeelse
413
414 endfunction
415 " }}}
416
417 function! IslinePHP (lnum, tofind) " {{{
418         " This function asks to the syntax if the pattern 'tofind' on the line
419         " number 'lnum' is PHP code (very slow...).
420         let cline = getline(a:lnum)
421
422         if a:tofind==""
423                 let tofind = "^\\s*[\"']*\s*\\zs\\S" " This correct the issue where lines beginning by a 
424                 " single or double quote were not indented in some cases.
425         else
426                 let tofind = a:tofind
427         endif
428
429         let tofind = tofind . '\c' " ignorecase
430
431         let coltotest = match (cline, tofind) + 1 "find the first non blank char in the current line
432         
433         let synname = synIDattr(synID(a:lnum, coltotest, 0), "name") " ask to syntax what is its name
434         "echo synname
435
436         " if matchstr(synname, '^...') == "php" || synname=="Delimiter" || synname =~? '^javaScript'
437         if synname =~ '^php' || synname=="Delimiter" || synname =~? '^javaScript'
438                 return synname
439         else
440                 return ""
441         endif
442 endfunction
443 " }}}
444
445 let s:notPhpHereDoc = '\%(break\|return\|continue\|exit\);'
446 let s:blockstart = '\%(\%(\%(}\s*\)\=else\%(\s\+\)\=\)\=if\>\|else\>\|while\>\|switch\>\|for\%(each\)\=\>\|declare\>\|class\>\|[|&]\)'
447
448 " make sure the options needed for this script to work correctly are set here
449 " for the last time. They could have been overriden by any 'onevent'
450 " associated setting file...
451 let s:autorestoptions = 0
452 if ! s:autorestoptions
453         au BufWinEnter,Syntax   *.php,*.php3,*.php4,*.php5      call ResetOptions()
454         let s:autorestoptions = 1
455 endif
456
457 function! ResetOptions()
458         if ! b:optionsset
459                 setlocal formatoptions=qroc
460                 let b:optionsset = 1
461         endif
462 endfunc
463
464 function! GetPhpIndent()
465         "##############################################
466         "########### MAIN INDENT FUNCTION #############
467         "##############################################
468
469         " This detect if the user is currently typing text between each call
470         let UserIsEditing=0
471         if      b:PHP_oldchangetick != b:changedtick
472                 let b:PHP_oldchangetick = b:changedtick
473                 let UserIsEditing=1
474         endif
475
476         if b:PHP_default_indenting
477                 let b:PHP_default_indenting = g:PHP_default_indenting * &sw
478         endif
479
480         let cline = getline(v:lnum) " current line
481
482         " Let's detect if we are indenting just one line or more than 3 lines
483         " in the last case we can slightly optimize our algorithm
484         if !b:PHP_indentinghuge && b:PHP_lastindented > b:PHP_indentbeforelast 
485                 if b:PHP_indentbeforelast
486                         let b:PHP_indentinghuge = 1
487                         echom 'Large indenting detected, speed optimizations engaged'
488                 endif
489                 let b:PHP_indentbeforelast = b:PHP_lastindented
490         endif
491
492         " If the line we are indenting isn't directly under the previous non-blank
493         " line of the file then deactivate the optimization procedures and reset
494         " status variable (we restart for scratch)
495         if b:InPHPcode_checked && prevnonblank(v:lnum - 1) != b:PHP_lastindented
496                 if b:PHP_indentinghuge
497                         echom 'Large indenting deactivated'
498                         let b:PHP_indentinghuge = 0
499                         let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
500                 endif
501                 let b:PHP_lastindented = v:lnum
502                 let b:PHP_LastIndentedWasComment=0
503                 let b:PHP_InsideMultilineComment=0
504                 let b:PHP_indentbeforelast = 0
505                 
506                 let b:InPHPcode = 0
507                 let b:InPHPcode_checked = 0
508                 let b:InPHPcode_and_script = 0
509                 let b:InPHPcode_tofind = ""
510
511         elseif v:lnum > b:PHP_lastindented " we are indenting line in > order (we can rely on the line before)
512                 let real_PHP_lastindented = b:PHP_lastindented
513                 let b:PHP_lastindented = v:lnum
514         endif
515
516         " We must detect if we are in PHPCODE or not, but one time only, then
517         " we will detect php end and start tags, comments /**/ and HereDoc
518         " tags
519
520         if !b:InPHPcode_checked " {{{ One time check
521                 let b:InPHPcode_checked = 1
522
523                 let synname = IslinePHP (prevnonblank(v:lnum), "") " the line could be blank (if the user presses 'return')
524
525                 if synname!=""
526                         if synname != "phpHereDoc"
527                                 let b:InPHPcode = 1
528                                 let b:InPHPcode_tofind = ""
529
530                                 if synname == "phpComment"
531                                         let b:UserIsTypingComment = 1
532                                 else
533                                         let b:UserIsTypingComment = 0
534                                 endif
535
536                                 if synname =~? '^javaScript'
537                                         let b:InPHPcode_and_script = 1
538                                 endif
539
540                         else
541                                 let b:InPHPcode = 0
542                                 let b:UserIsTypingComment = 0
543
544                                 let lnum = v:lnum - 1
545                                 while getline(lnum) !~? '<<<\a\w*$' && lnum > 1
546                                         let lnum = lnum - 1
547                                 endwhile
548
549                                 let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<\(\a\w*\)\c', '^\\s*\1;$', '')
550                         endif
551                 else " IslinePHP returned "" => we are not in PHP or Javascript
552                         let b:InPHPcode = 0
553                         let b:UserIsTypingComment = 0
554                         " Then we have to find a php start tag...
555                         let b:InPHPcode_tofind = '<?\%(.*?>\)\@!\|<script.*>'
556                 endif
557         endif "!b:InPHPcode_checked }}}
558
559         " Now we know where we are so we can verify the line right above the
560         " current one to see if we have to stop or restart php indenting
561
562         " Test if we are indenting PHP code {{{
563         " Find an executable php code line above the current line.
564         let lnum = prevnonblank(v:lnum - 1)
565         let last_line = getline(lnum)
566
567         " If we aren't in php code, then there is something we have to find
568         if b:InPHPcode_tofind!=""
569                 if cline =~? b:InPHPcode_tofind
570                         let     b:InPHPcode = 1
571                         let b:InPHPcode_tofind = ""
572                         let b:UserIsTypingComment = 0
573                         if cline =~ '\*/' " End comment tags must be indented like start comment tags
574                                 call cursor(v:lnum, 1)
575                                 if cline !~ '^\*/'
576                                         call search('\*/', 'W')
577                                 endif
578                                 let lnum = searchpair('/\*', '', '\*/', s:searchpairflags) " find the most outside /*
579
580                                 let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
581                                 let b:PHP_LastIndentedWasComment = 0 " prevent a problem if multiline /**/ comment are surounded by
582                                                                                                          " other types of comments
583                                 
584                                 if cline =~ '^\s*\*/'
585                                         return indent(lnum) + 1
586                                 else
587                                         return indent(lnum)
588                                 endif
589
590                         elseif cline =~? '<script\>' " a more accurate test is useless since there isn't any other possibility
591                                 let b:InPHPcode_and_script = 1
592                         endif
593                 endif
594         endif
595
596         " ### If we are in PHP code, we test the line before to see if we have to stop indenting
597
598         if b:InPHPcode
599
600                 " Was last line containing a PHP end tag ?
601                 if !b:InPHPcode_and_script && last_line =~ '\%(<?.*\)\@<!?>\%(.*<?\)\@!' && IslinePHP(lnum, '?>')=="Delimiter"
602                         if cline !~? s:PHP_startindenttag
603                                 let b:InPHPcode = 0
604                                 let b:InPHPcode_tofind = s:PHP_startindenttag
605                         elseif cline =~? '<script\>'
606                                 let b:InPHPcode_and_script = 1
607                         endif
608
609                         " Was last line the start of a HereDoc ?
610                 elseif last_line =~? '<<<\a\w*$' 
611                         " \&& IslinePHP(lnum, '\a\w*$')=="Delimiter"
612                         let b:InPHPcode = 0
613                         let b:InPHPcode_tofind = substitute( last_line, '^.*<<<\(\a\w*\)\c', '^\\s*\1;$', '')
614
615                         " Skip /* \n+ */ comments execept when the user is currently
616                         " writting them
617                 elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' && getline(v:lnum + 1) !~ '^\s*\*'
618                         let b:InPHPcode = 0
619                         let b:InPHPcode_tofind = '\*/'
620
621                         " is current line the end of a HTML script ? (we indent script the
622                         " same as php code)
623                 elseif cline =~? '^\s*</script>'
624                         let b:InPHPcode = 0
625                         let b:InPHPcode_tofind = s:PHP_startindenttag
626                 endif
627         endif " }}}
628
629         " Non PHP code is let as it is
630         if !b:InPHPcode && !b:InPHPcode_and_script
631                 return -1
632         "elseif !b:InPHPcode
633         "       let b:InPHPcode_and_script = 0 " now it is GetLastRealCodeLNum that resets this variable
634         endif
635
636         " Align correctly multi // or # lines
637
638         " Indent successive // or # comment the same way the first is {{{
639         if cline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
640                 if b:PHP_LastIndentedWasComment == 1
641                         return indent(real_PHP_lastindented) " line replaced in 1.02
642                 endif
643                 let b:PHP_LastIndentedWasComment = 1
644         else
645                 let b:PHP_LastIndentedWasComment = 0
646         endif
647         " }}}
648         
649         " Indent multiline /* comments correctly {{{
650         
651         "if we are on the start of a MULTI * beginning comment or if the user is
652         "currently typing a /* beginning comment.
653
654         if b:PHP_InsideMultilineComment || b:UserIsTypingComment
655                 if cline =~ '^\s*\*\%(\/\)\@!'   " if cline == '*'
656                         if last_line =~ '^\s*/\*' " if last_line == '/*'
657                                 return indent(lnum) + 1
658                         else
659                                 return indent(lnum)
660                         endif
661                 else
662                         let b:PHP_InsideMultilineComment = 0
663                 endif
664         endif
665         
666         if !b:PHP_InsideMultilineComment && cline =~ '^\s*/\*' " if cline == '/*'
667                 let b:PHP_InsideMultilineComment = 1
668                 return -1
669         endif
670         " }}}
671
672         " Some tags are always indented to col 1
673
674         " Things always indented at col 1 (PHP delimiter: <?, ?>, Heredoc end) {{{
675         " PHP start tags are always at col 1, useless to indent unless the end tag
676         " is on the same line
677         if cline =~# '^\s*<?' && cline !~ '?>' " Added the ^\s* part in version 1.03
678                 return 0
679         endif
680
681         " PHP end tags are always at col 1, useless to indent unless if it's
682         " followed by a start tag on the same line
683         if  cline =~ '^\s*?>' && cline !~# '<?'  
684                 return 0
685         endif
686
687         " put HereDoc end tags at start of lines
688         if cline =~? '^\s*\a\w*;$' && cline !~? s:notPhpHereDoc
689                 return 0
690         endif
691         " }}}
692
693         let s:level = 0
694
695         " Find an executable php code line above the current line.
696         let lnum = GetLastRealCodeLNum(v:lnum - 1)
697         let last_line = getline(lnum)    " last line
698         let ind = indent(lnum) " by default
699         let endline= s:endline
700
701         if ind==0 && b:PHP_default_indenting
702                 let ind = b:PHP_default_indenting
703         endif
704
705         " Hit the start of the file, use default indent.
706         if lnum == 0
707                 return b:PHP_default_indenting
708         endif
709
710
711         " Search the matching open bracket (with searchpair()) and set the indent of cline
712         " to the indent of the matching line.
713         if cline =~ '^\s*}\%(}}\)\@!'
714                 let ind = indent(FindOpenBracket(v:lnum))
715                 let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
716                 return ind
717         endif
718
719         " Check for end of comment and indent it like its beginning
720         if cline =~ '^\s*\*/' " End comment tags must be indented like start comment tags
721                 call cursor(v:lnum, 1)
722                 if cline !~ '^\*/'
723                         call search('\*/', 'W')
724                 endif
725                 let lnum = searchpair('/\*', '', '\*/', s:searchpairflags) " find the most outside /*
726                 "echo 'Searchpair returned: ' . lnum
727                 "call getchar()
728
729                 let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
730
731                 if cline =~ '^\s*\*/'
732                         return indent(lnum) + 1
733                 else
734                         return indent(lnum)
735                 endif
736         endif
737
738         let defaultORcase = '^\s*\%(default\|case\).*:'
739
740         " if the last line is a stated line and it's not indented then why should
741         " we indent this one??
742         " if optimized mode is active and nor current or previous line are an 'else'
743         " or the end of a possible bracketless thing then indent the same as the previous
744         " line
745         if last_line =~ '[;}]'.endline && last_line !~# defaultORcase 
746                 if ind==b:PHP_default_indenting " if no indentation for the previous line
747                         return b:PHP_default_indenting
748                 elseif b:PHP_indentinghuge && ind==b:PHP_CurrentIndentLevel && cline !~# '^\s*\%(else\|\%(case\|default\).*:\|[})];\=\)' && last_line !~# '^\s*\%(\%(}\s*\)\=else\)' && getline(GetLastRealCodeLNum(lnum - 1))=~';'.endline
749                         return b:PHP_CurrentIndentLevel
750                 endif
751         endif
752
753         let LastLineClosed = 0 " used to prevent redundant tests in the last part of the script
754
755         let terminated = '\%(;\%(\s*?>\)\=\|<<<\a\w*\|}\)'.endline
756         " What is a terminated line?
757         " - a line terminated by a ";" optionaly followed by a "?>"
758         " - a HEREDOC starter line (the content of such block is never seen by this script)
759         " - a "}" not followed by a "{"
760
761         let unstated   = '\%(^\s*'.s:blockstart.'.*)\|\%(//.*\)\@<!\<e'.'lse\>\)'.endline
762         " What is an unstated line?
763         " - an "else" at the end of line
764         " - a  s:blockstart (if while etc...) followed by anything and a ")" at
765         "   the end of line
766
767         " if the current line is an 'else' starting line
768         " (to match an 'else' preceded by a '}' is irrelevant and futile - see
769         " code above)
770         if ind != b:PHP_default_indenting && cline =~# '^\s*else\%(if\)\=\>'
771                 let b:PHP_CurrentIndentLevel = b:PHP_default_indenting " prevent optimized to work at next call
772                 return indent(FindTheIfOfAnElse(v:lnum, 1))
773         elseif cline =~ '^\s*{'
774                 let previous_line = last_line
775                 let last_line_num = lnum
776                 
777                 while last_line_num > 1
778                         
779                         if previous_line =~ '^\s*\%(' . s:blockstart . '\|\%([a-zA-Z]\s*\)*function\)' && previous_line !~ '^\s*[|&]'
780                                 "echo '{ detected and aligned to ' . last_line_num . '  ('.previous_line.')'
781                                 "call getchar()
782
783                                 let ind = indent(last_line_num)
784
785                                 " If the PHP_BracesAtCodeLevel is set then indent the '{'
786                                 if  b:PHP_BracesAtCodeLevel " XXX mod {
787                                         let ind = ind + &sw
788                                 endif
789                 
790                                 return ind 
791                         endif
792                         
793                         let last_line_num = last_line_num - 1
794                         let previous_line = getline(last_line_num)
795                 endwhile
796
797         elseif last_line =~# unstated && cline !~ '^\s*{\|^\s*);\='.endline
798                 let ind = ind + &sw
799                 return ind
800
801                 " If the last line is terminated by ';' or if it's a closing '}'
802                 " We need to check if this isn't the end of a multilevel non '{}'
803                 " structure such as:
804                 " Exemple: 
805                 "                       if ($truc)
806                 "                               echo 'truc';
807                 "
808                 "       OR
809                 "
810                 "                       if ($truc)
811                 "                               while ($truc) {
812                 "                                       lkhlkh();
813                 "                                       echo 'infinite loop\n';
814                 "                               }
815                 "
816                 "       OR even (ADDED for version 1.17 - no modification required )
817                 "
818                 "                       $thing =
819                 "                               "something";
820         elseif ind != b:PHP_default_indenting && last_line =~ terminated
821                 " If we are here it means that the previous line is:
822                 " - a *;$ line
823                 " - a [beginning-blanck] } followed by anything but a { $
824                 let previous_line = last_line
825                 let last_line_num = lnum
826                 let LastLineClosed = 1
827                 " The idea here is to check if the current line is after a non '{}'
828                 " structure so we can indent it like the top of that structure.
829                 " The top of that structure is caracterized by a if (ff)$ style line
830                 " preceded by a stated line. If there is no such structure then we
831                 " just have to find two 'normal' lines following each other with the
832                 " same indentation and with the first of these two lines terminated by
833                 " a ; or by a }...
834
835                 while 1
836                         " let's skip '{}' blocks
837                         if previous_line =~ '^\s*}'
838                                 " find the openning '{'
839                                 let last_line_num = FindOpenBracket(last_line_num)
840
841                                 " if the '{' is alone on the line get the line before
842                                 if getline(last_line_num) =~ '^\s*{'
843                                         let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
844                                 endif
845
846                                 let previous_line = getline(last_line_num)
847
848                                 continue
849                         else
850                                 " At this point we know that the previous_line isn't a closing
851                                 " '}' so we can check if we really are in such a structure.
852
853                                 " it's not a '}' but it could be an else alone...
854                                 if getline(last_line_num) =~# '^\s*else\%(if\)\=\>'
855                                         let last_line_num = FindTheIfOfAnElse(last_line_num, 0)
856                                         continue " re-run the loop (we could find a '}' again)
857                                 endif
858
859                                 " So now it's ok we can check :-)
860                                 " A good quality is to have confidence in oneself so to know
861                                 " if yes or no we are in that struct lets test the indent of
862                                 " last_line_num and of last_line_num - 1!
863                                 " If those are == then we are almost done.
864                                 "
865                                 " That isn't sufficient, we need to test how the first of the
866                                 " 2 lines is ended...
867
868                                 " Note the indenting of the line we are checking
869
870                                 let last_match = last_line_num " remember the 'topest' line we found so far
871
872                                 let one_ahead_indent = indent(last_line_num)
873                                 let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
874                                 let two_ahead_indent = indent(last_line_num)
875                                 let after_previous_line = previous_line
876                                 let previous_line = getline(last_line_num)
877
878
879                                 " If we find a '{' or a case/default then we are inside that block so lets
880                                 " indent properly... Like the line following that block starter
881                                 if previous_line =~# defaultORcase.'\|{'.endline
882                                         break
883                                 endif
884
885                                 " The 3 lines below are not necessary for the script to work
886                                 " but it makes it work a little more faster in some (rare) cases.
887                                 " We verify if we are at the top of a non '{}' struct.
888                                 if after_previous_line=~# '^\s*'.s:blockstart.'.*)'.endline && previous_line =~# '[;}]'.endline
889                                         break
890                                 endif
891
892                                 if one_ahead_indent == two_ahead_indent || last_line_num < 1 
893                                         " So the previous line and the line before are at the same
894                                         " col. Now we just have to check if the line before is a ;$ or [}]$ ended line
895                                         " we always check the most ahead line of the 2 lines so
896                                         " it's useless to match ')$' since the lines couldn't have
897                                         " the same indent...
898                                         if previous_line =~# '[;}]'.endline || last_line_num < 1
899                                                 break
900                                         endif
901                                 endif
902                         endif
903                 endwhile
904
905                 if indent(last_match) != ind " if nothing was done lets the old script continue
906                         let ind = indent(last_match) " let's use the indent of the last line matched by the alhorithm above
907                         let b:PHP_CurrentIndentLevel = b:PHP_default_indenting " line added in version 1.02 to prevent optimized mode
908                         " from acting in some special cases
909
910                         " case and default are indented 1 level below
911                         if cline =~# defaultORcase
912                                 let ind = ind - &sw
913                         endif
914                         return ind
915                 endif
916         endif
917
918         let plinnum = GetLastRealCodeLNum(lnum - 1)
919         let pline = getline(plinnum) " previous to last line
920
921         " REMOVE comments at end of line before treatment
922         " the first part of the regex removes // from the end of line when they are
923         " followed by a number of '"' which is a multiple of 2. The second part
924         " removes // that are not followed by any '"'
925         " Sorry for this unreadable thing...
926         let last_line = substitute(last_line,"\\(//\\|#\\)\\(\\(\\([^\"']*\\([\"']\\)[^\"']*\\5\\)\\+[^\"']*$\\)\\|\\([^\"']*$\\)\\)",'','')
927
928
929         if ind == b:PHP_default_indenting
930                 if last_line =~ terminated
931                         let LastLineClosed = 1
932                 endif
933         endif
934         
935         " Indent blocks enclosed by {} or () (default indenting)
936         if !LastLineClosed " the last line isn't a .*; or a }$ line
937                 " Indent correctly multilevel and multiline '(.*)' things
938                 
939                 " if the last line is a [{(]$ or a multiline function call (or array
940                 " declaration) with already one parameter on the opening ( line
941                 if last_line =~# '[{(]'.endline || last_line =~? '\h\w*\s*(.*,$' && pline !~ '[,(]'.endline
942
943                         if !b:PHP_BracesAtCodeLevel || last_line !~# '^\s*{' " XXX mod {
944                                 let ind = ind + &sw
945                         endif
946
947                         if b:PHP_BracesAtCodeLevel || cline !~# defaultORcase " XXX mod (2) {
948                                 " case and default are not indented inside blocks
949                                 let b:PHP_CurrentIndentLevel = ind
950                                 return ind
951                         endif
952
953                         " If the last line isn't empty and ends with a '),' then check if the
954                         " ')' was oppened on the same line, if not it means it closes a
955                         " multiline '(.*)' thing and that the current line need to be
956                         " de-indented one time.
957                 elseif last_line =~ '\S\+\s*),'.endline
958                         call cursor(lnum, 1)
959                         call search('),'.endline, 'W')
960                         let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()')
961                         if openedparent != lnum
962                                 let ind = indent(openedparent)
963                         endif
964                         
965                         " In all other cases if the last line isn't terminated indent 1
966                         " level higher but only if the line before the last line wasn't
967                         " indented for the same reason.
968                                 
969                 elseif cline !~ '^\s*{' && pline =~ '\%(;\%(\s*?>\)\=\|<<<\a\w*\|{\|^\s*'.s:blockstart.'\s*(.*)\)'.endline.'\|^\s*}\|'.defaultORcase
970                         
971                         let ind = ind + &sw
972
973                 endif
974
975         elseif last_line =~# defaultORcase
976                 let ind = ind + &sw
977         endif
978
979         " If the current line closes a multiline function call or array def XXX
980         if cline =~  '^\s*);\='
981                 let ind = ind - &sw
982                 " CASE and DEFAULT are indented at the same level than the SWITCH
983         elseif cline =~# defaultORcase
984                 let ind = ind - &sw
985         
986         endif
987
988         let b:PHP_CurrentIndentLevel = ind
989         return ind
990 endfunction
991
992 " vim: set ts=4 sw=4:
993 " vim: set ff=unix:
This page took 0.126634 seconds and 3 git commands to generate.