]>
Commit | Line | Data |
---|---|---|
8fffc476 AM |
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 | |
b2707e99 AM |
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 | " } | |
99df3f68 AM |
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) | |
1d607244 AM |
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. | |
0ae4014d AM |
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. | |
a4c85cb5 AM |
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. | |
8fffc476 AM |
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 | " | |
8fffc476 AM |
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 | " | |
8fffc476 AM |
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. | |
8fffc476 AM |
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 | " | |
8fffc476 AM |
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 | |
a4c85cb5 | 152 | " script by Lutz Eymers (http://www.isp.de/data/php.vim ) that's the script bundled with Gvim. |
8fffc476 | 153 | " |
8fffc476 AM |
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 | ||
a4c85cb5 AM |
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). | |
8fffc476 | 170 | |
a4c85cb5 AM |
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... | |
99df3f68 AM |
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. | |
a4c85cb5 | 196 | |
1d607244 | 197 | |
99df3f68 AM |
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 | |
1d607244 | 202 | endif |
99df3f68 | 203 | let b:did_indent = 1 |
1d607244 | 204 | |
99df3f68 AM |
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. | |
1d607244 | 210 | |
99df3f68 | 211 | let php_sync_method = 0 |
8fffc476 | 212 | |
99df3f68 AM |
213 | |
214 | " Apply PHP_default_indenting option | |
8fffc476 AM |
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 | |
99df3f68 AM |
220 | |
221 | if exists("PHP_BracesAtCodeLevel") | |
222 | let b:PHP_BracesAtCodeLevel = PHP_BracesAtCodeLevel | |
223 | else | |
224 | let b:PHP_BracesAtCodeLevel = 0 | |
225 | endif | |
226 | ||
8fffc476 AM |
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 | |
99df3f68 | 233 | let b:PHP_InsideMultilineComment = 0 |
8fffc476 AM |
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 | |
a4c85cb5 | 240 | let b:UserIsTypingComment = 0 |
99df3f68 | 241 | let b:optionsset = 0 |
8fffc476 | 242 | |
99df3f68 AM |
243 | " The 4 options belows are overriden by indentexpr so they are always off |
244 | " anyway... | |
1d607244 | 245 | setlocal nosmartindent |
99df3f68 | 246 | setlocal noautoindent |
1d607244 | 247 | setlocal nocindent |
99df3f68 | 248 | setlocal nolisp " autoindent must be on, so this line is also useless... |
1d607244 | 249 | |
8fffc476 | 250 | setlocal indentexpr=GetPhpIndent() |
a4c85cb5 | 251 | setlocal indentkeys=0{,0},0),:,!^F,o,O,e,*<Return>,=?>,=<?,=*/ |
8fffc476 | 252 | |
99df3f68 | 253 | |
b2707e99 AM |
254 | |
255 | let s:searchpairflags = 'bWr' | |
99df3f68 AM |
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. | |
8fffc476 | 263 | if exists("*GetPhpIndent") |
1d607244 | 264 | finish " XXX |
8fffc476 AM |
265 | endif |
266 | ||
267 | let s:endline= '\s*\%(//.*\|#.*\|/\*.*\*/\s*\)\=$' | |
268 | let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!' | |
1d607244 | 269 | "setlocal debug=msg " XXX |
8fffc476 AM |
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 | |
99df3f68 AM |
285 | elseif lastline =~ '^\s*?>.*<?\%(php\)\=\s*$' |
286 | let lnum = lnum - 1 | |
8fffc476 AM |
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) | |
b2707e99 AM |
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 /* | |
1d607244 AM |
295 | "echo 'lnum skipnonphp= ' . lnum |
296 | "call getchar() | |
8fffc476 AM |
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) | |
1d607244 AM |
308 | " call search('<?', 'W') |
309 | " let lnum = searchpair('?>', '', '<?\zs', 'bW', 'getline(".") =~ "<?.*?>"') | |
8fffc476 AM |
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 | |
99df3f68 AM |
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 | |
8fffc476 AM |
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") | |
99df3f68 | 352 | if synname == "Delimiter" || synname == "phpParent" || synname == "javaScriptBraces" || synname == "phpComment" && b:UserIsTypingComment |
8fffc476 | 353 | return 0 |
8fffc476 AM |
354 | else |
355 | return 1 | |
8fffc476 AM |
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 | |
99df3f68 | 387 | if getline(beforeelse) =~ '^\s*}' |
8fffc476 AM |
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 | |
a4c85cb5 | 400 | |
8fffc476 AM |
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=="" | |
0ae4014d AM |
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. | |
8fffc476 AM |
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 | |
0ae4014d | 434 | "echo synname |
8fffc476 | 435 | |
0ae4014d AM |
436 | " if matchstr(synname, '^...') == "php" || synname=="Delimiter" || synname =~? '^javaScript' |
437 | if synname =~ '^php' || synname=="Delimiter" || synname =~? '^javaScript' | |
8fffc476 AM |
438 | return synname |
439 | else | |
440 | return "" | |
441 | endif | |
442 | endfunction | |
443 | " }}} | |
444 | ||
445 | let s:notPhpHereDoc = '\%(break\|return\|continue\|exit\);' | |
b2707e99 | 446 | let s:blockstart = '\%(\%(\%(}\s*\)\=else\%(\s\+\)\=\)\=if\>\|else\>\|while\>\|switch\>\|for\%(each\)\=\>\|declare\>\|class\>\|[|&]\)' |
99df3f68 AM |
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 | |
8fffc476 AM |
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 | ||
8fffc476 AM |
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 | |
99df3f68 | 503 | let b:PHP_InsideMultilineComment=0 |
8fffc476 AM |
504 | let b:PHP_indentbeforelast = 0 |
505 | ||
8fffc476 AM |
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 = "" | |
0ae4014d | 529 | |
a4c85cb5 AM |
530 | if synname == "phpComment" |
531 | let b:UserIsTypingComment = 1 | |
532 | else | |
533 | let b:UserIsTypingComment = 0 | |
534 | endif | |
0ae4014d AM |
535 | |
536 | if synname =~? '^javaScript' | |
537 | let b:InPHPcode_and_script = 1 | |
538 | endif | |
539 | ||
8fffc476 AM |
540 | else |
541 | let b:InPHPcode = 0 | |
a4c85cb5 | 542 | let b:UserIsTypingComment = 0 |
8fffc476 AM |
543 | |
544 | let lnum = v:lnum - 1 | |
a4c85cb5 | 545 | while getline(lnum) !~? '<<<\a\w*$' && lnum > 1 |
8fffc476 AM |
546 | let lnum = lnum - 1 |
547 | endwhile | |
548 | ||
549 | let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<\(\a\w*\)\c', '^\\s*\1;$', '') | |
550 | endif | |
0ae4014d | 551 | else " IslinePHP returned "" => we are not in PHP or Javascript |
8fffc476 | 552 | let b:InPHPcode = 0 |
a4c85cb5 | 553 | let b:UserIsTypingComment = 0 |
8fffc476 AM |
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 | |
0ae4014d | 561 | |
8fffc476 AM |
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 = "" | |
a4c85cb5 AM |
572 | let b:UserIsTypingComment = 0 |
573 | if cline =~ '\*/' " End comment tags must be indented like start comment tags | |
574 | call cursor(v:lnum, 1) | |
b2707e99 AM |
575 | if cline !~ '^\*/' |
576 | call search('\*/', 'W') | |
577 | endif | |
578 | let lnum = searchpair('/\*', '', '\*/', s:searchpairflags) " find the most outside /* | |
99df3f68 AM |
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 | ||
8fffc476 AM |
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*$' | |
0ae4014d | 611 | " \&& IslinePHP(lnum, '\a\w*$')=="Delimiter" |
8fffc476 AM |
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 | |
b2707e99 | 617 | elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' && getline(v:lnum + 1) !~ '^\s*\*' |
8fffc476 AM |
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 | |
8fffc476 AM |
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 | |
99df3f68 AM |
632 | "elseif !b:InPHPcode |
633 | " let b:InPHPcode_and_script = 0 " now it is GetLastRealCodeLNum that resets this variable | |
8fffc476 AM |
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 | " }}} | |
99df3f68 AM |
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 | " }}} | |
8fffc476 AM |
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 | ||
8fffc476 AM |
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 | ||
99df3f68 AM |
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 | |
a4c85cb5 | 721 | call cursor(v:lnum, 1) |
b2707e99 AM |
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() | |
99df3f68 AM |
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 | |
a4c85cb5 | 751 | endif |
0ae4014d | 752 | |
8fffc476 AM |
753 | let LastLineClosed = 0 " used to prevent redundant tests in the last part of the script |
754 | ||
99df3f68 AM |
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 | |
8fffc476 AM |
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)) | |
b2707e99 AM |
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 | ||
8fffc476 AM |
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 | " } | |
99df3f68 AM |
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 | |
8fffc476 AM |
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 | |
99df3f68 | 881 | if previous_line =~# defaultORcase.'\|{'.endline |
8fffc476 AM |
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 | |
99df3f68 | 907 | let b:PHP_CurrentIndentLevel = b:PHP_default_indenting " line added in version 1.02 to prevent optimized mode |
8fffc476 | 908 | " from acting in some special cases |
99df3f68 AM |
909 | |
910 | " case and default are indented 1 level below | |
911 | if cline =~# defaultORcase | |
912 | let ind = ind - &sw | |
913 | endif | |
8fffc476 AM |
914 | return ind |
915 | endif | |
916 | endif | |
917 | ||
8fffc476 AM |
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 | |
1d607244 | 922 | " the first part of the regex removes // from the end of line when they are |
8fffc476 AM |
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 | ||
99df3f68 AM |
928 | |
929 | if ind == b:PHP_default_indenting | |
930 | if last_line =~ terminated | |
931 | let LastLineClosed = 1 | |
932 | endif | |
933 | endif | |
934 | ||
8fffc476 AM |
935 | " Indent blocks enclosed by {} or () (default indenting) |
936 | if !LastLineClosed " the last line isn't a .*; or a }$ line | |
99df3f68 AM |
937 | " Indent correctly multilevel and multiline '(.*)' things |
938 | ||
8fffc476 AM |
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 | |
99df3f68 AM |
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 | |
8fffc476 AM |
949 | let b:PHP_CurrentIndentLevel = ind |
950 | return ind | |
951 | endif | |
99df3f68 AM |
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 | |
99df3f68 AM |
974 | |
975 | elseif last_line =~# defaultORcase | |
976 | let ind = ind + &sw | |
8fffc476 AM |
977 | endif |
978 | ||
99df3f68 | 979 | " If the current line closes a multiline function call or array def XXX |
8fffc476 AM |
980 | if cline =~ '^\s*);\=' |
981 | let ind = ind - &sw | |
99df3f68 AM |
982 | " CASE and DEFAULT are indented at the same level than the SWITCH |
983 | elseif cline =~# defaultORcase | |
8fffc476 | 984 | let ind = ind - &sw |
99df3f68 | 985 | |
8fffc476 AM |
986 | endif |
987 | ||
988 | let b:PHP_CurrentIndentLevel = ind | |
989 | return ind | |
990 | endfunction | |
991 | ||
992 | " vim: set ts=4 sw=4: | |
a4c85cb5 | 993 | " vim: set ff=unix: |