]>
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 | |
5 | " Last Change: 2004 December 27th | |
6 | " Version: 1.07 | |
7 | " | |
8 | " Changes: 1.07 - Added support for "Here document" tags: | |
9 | " - HereDoc end tags are indented properly. | |
10 | " - HereDoc content remains unchanged. | |
11 | " - All the code that is outside PHP delimiters remains | |
12 | " unchanged. | |
13 | " - New feature: The content of <script.*> html tags is considered as PHP | |
14 | " and indented according to the surrounding PHP code. | |
15 | " - "else if" are detected as "elseif". | |
16 | " - Multiline /**/ are indented when the user types it but | |
17 | " remain unchanged when indenting from their beginning. | |
18 | " - Fixed indenting of // and # comments. | |
19 | " - php_sync_method option is set to 0 (fromstart). | |
20 | " This is required for complex PHP scripts else the indent | |
21 | " may fail. | |
22 | " - Files with non PHP code at the beginning could alter the indent | |
23 | " of the following PHP code. | |
24 | " - Other minor improvements and corrections. | |
25 | " | |
26 | " | |
27 | " Changes: 1.06: - Switch block were no longer indented correctly... | |
28 | " - Added an option to use a default indenting instead of 0. | |
29 | " (whereas I still can't find any good reason to use it!) | |
30 | " - A problem with ^\s*);\= lines where ending a non '{}' | |
31 | " structure. | |
32 | " - Changed script local variable to be buffer local | |
33 | " variable instead. | |
34 | " | |
35 | " Changes: 1.05: - Lines containing "<?php ?>" and "?> <?php" | |
36 | " (start and end tag on the same line) are no | |
37 | " longer indented at col 1 but as normal code. | |
38 | " | |
39 | " | |
40 | " Changes: 1.04: - Strings containing "//" could break the indenting | |
41 | " algorithm. | |
42 | " - When a '{}' block was at col 1, the second line of the | |
43 | " block was not indented at all (because of a stupid | |
44 | " optimization coupled with a bug). | |
45 | " | |
46 | " Changes: 1.03: - Some indenting problems corrected: end of non '{}' | |
47 | " structures was not detected in some cases. The part of | |
48 | " code concerned have been re-written | |
49 | " - PHP start tags were not indented at col 1 | |
50 | " - Wrong comment in the code have been corrected | |
51 | " | |
52 | " Changes: 1.02: - The bug I was talking about in version 1.01 (right below) has | |
53 | " been corrected :) | |
54 | " - Also corrected another bug that could occur in | |
55 | " some special cases. | |
56 | " - I removed the debug mode left in 1.01 that could | |
57 | " cause some Vim messages at loading if other script were | |
58 | " bugged. | |
59 | " | |
60 | " Changes: 1.01: - Some little bug corrections reguarding automatic optimized | |
61 | " mode that missed some tests and could break the indenting. | |
62 | " | |
63 | " - There is also a problem with complex non bracked structures, when several | |
64 | " else are following each other, the algorithm do not indent the way it | |
65 | " should. | |
66 | " That will be corrected in the next version. | |
67 | " | |
68 | " Changes: improvements with regard to the original version (0.5) by Miles Lott (whose this script was inspired): | |
69 | " - Commented part of code or non PHP code no longer break the | |
70 | " indent algorithm, the content of those parts are indented | |
71 | " separatly | |
72 | " - corrected a strange thing (a part of code was duplicated) -> | |
73 | " the last version couldn't work. | |
74 | " - Folds can be used without problem | |
75 | " - multilevel non bracked structures are indented (like | |
76 | " in C) | |
77 | " Exemple: | |
78 | " if (!isset($History_lst_sel)) | |
79 | " if (!isset($blabla)) | |
80 | " if (!isset($bloum)) { | |
81 | " $History_lst_sel=0; | |
82 | " } else | |
83 | " $foo="truc"; | |
84 | " else $bla= "foo"; | |
85 | " $command_hist = TRUE; | |
86 | " | |
87 | " - "array( 'blabla'," lines no longer break the indent of the | |
88 | " following lines | |
89 | " - the options php_noindent_switch and php_indent_shortopentags have been removed | |
90 | " (couldn't find any reason why one would want to use them) | |
91 | " - PHP open and close tags are always set to col 1 as for the | |
92 | " immediate following php code | |
93 | " | |
94 | " If you find a bug, please e-mail me at John.wellesz (AT) teaser (DOT) fr | |
95 | " with an example of code that break the algorithm. | |
96 | " | |
97 | " | |
98 | " Thanks a lot for using this script. | |
99 | " | |
100 | ||
101 | " NOTE: This script must be used with PHP syntax ON and with the php syntax | |
102 | " script by Lutz Eymers (http://www.isp.de/data/php.vim ) . Else it won't be able to | |
103 | " work correctly. | |
104 | " | |
105 | " This script set the option php_sync_method of PHP syntax script to 0 | |
106 | " (fromstart indenting method) in order to have an accurate syntax. | |
107 | " | |
108 | " In the case you have syntax errors in your script such as end of HereDoc | |
109 | " tags not at col 1 you'll have to indent your file 2 times (This script | |
110 | " will automatically put HereDoc end tags at col 1). | |
111 | " | |
112 | ||
113 | " Options: PHP_default_indenting = # of sw (default is 0). | |
114 | ||
115 | " This script uses the syntax to test matching {}, comments, <??> validity | |
116 | " and to know if the line it is ask to indent is php code or not... | |
117 | " Unfortunately for complex files the search syntax method isn't accurate so | |
118 | " we must use the fromstart sync method. | |
119 | let php_sync_method = 0 | |
120 | ||
121 | if exists("PHP_default_indenting") | |
122 | let b:PHP_default_indenting = PHP_default_indenting * &sw | |
123 | else | |
124 | let b:PHP_default_indenting = 0 | |
125 | endif | |
126 | " Only load this indent file when no other was loaded. But reset those state | |
127 | " variables if needed | |
128 | ||
129 | let b:PHP_lastindented = 0 | |
130 | let b:PHP_indentbeforelast = 0 | |
131 | let b:PHP_indentinghuge = 0 | |
132 | let b:PHP_CurrentIndentLevel = b:PHP_default_indenting | |
133 | let b:PHP_LastIndentedWasComment = 0 | |
134 | " PHP code detect variables | |
135 | let b:InPHPcode = 0 | |
136 | let b:InPHPcode_checked = 0 | |
137 | let b:InPHPcode_and_script = 0 | |
138 | let b:InPHPcode_tofind = "" | |
139 | let b:PHP_oldchangetick = b:changedtick | |
140 | ||
141 | if exists("b:did_indent") | |
142 | finish | |
143 | endif | |
144 | ||
145 | let b:did_indent = 1 | |
146 | setlocal nosmartindent | |
147 | ||
148 | setlocal nolisp | |
149 | setlocal indentexpr=GetPhpIndent() | |
150 | "setlocal indentkeys+=0=,0),=EO | |
151 | setlocal indentkeys=0{,0},0),:,!^F,o,O,e,*<Return>,=?>,=<? | |
152 | ||
153 | " Only define the function once. | |
154 | if exists("*GetPhpIndent") | |
155 | finish | |
156 | endif | |
157 | ||
158 | let s:endline= '\s*\%(//.*\|#.*\|/\*.*\*/\s*\)\=$' | |
159 | let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!' | |
160 | " setlocal debug=msg | |
161 | ||
162 | ||
163 | function! GetLastRealCodeLNum(startline) " {{{ | |
164 | "Inspired from the function SkipJavaBlanksAndComments by Toby Allsopp for indent/java.vim | |
165 | let lnum = a:startline | |
166 | let old_lnum = lnum | |
167 | ||
168 | while lnum > 1 | |
169 | let lnum = prevnonblank(lnum) | |
170 | let lastline = getline(lnum) | |
171 | ||
172 | " if we are inside an html <script> we must skip ?> tags to indent | |
173 | " everything as php | |
174 | if b:InPHPcode_and_script && lastline =~ '?>\s*$' | |
175 | let lnum = lnum - 1 | |
176 | ||
177 | ||
178 | elseif lastline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)' " if line is under comment | |
179 | let lnum = lnum - 1 | |
180 | elseif lastline =~ '\*/\s*$' " skip multiline comments | |
181 | call cursor(lnum, 1) | |
182 | call search('\*/\zs', 'W') | |
183 | let lnum = searchpair('/\*\zs', '', '\*/\zs', 'bWr', '') " find the most outside /* | |
184 | ||
185 | let lastline = getline(lnum) | |
186 | if lastline =~ '^\s*/\*' " if line contains nothing but comment | |
187 | let lnum = lnum - 1 " do the job again on the line before (a comment can hide another...) | |
188 | else | |
189 | break | |
190 | endif | |
191 | ||
192 | ||
193 | elseif lastline =~? '\%(//\s*\|?>.*\)\@<!<?\%(php\)\=\s*$\|^\s*<script\>' " skip non php code | |
194 | " call cursor(lnum, 1) | |
195 | " call search('<?\zs', 'W') | |
196 | " let lnum = searchpair('?>\zs', '', '<?\zs', 'bW', 'getline(".") =~ "<?.*?>"') " find the most outside /* | |
197 | ||
198 | " let lastline = getline(lnum) | |
199 | while lastline !~ '\(<?.*\)\@<!?>' && lnum > 1 | |
200 | let lnum = lnum - 1 | |
201 | let lastline = getline(lnum) | |
202 | endwhile | |
203 | if lastline =~ '^\s*?>' " if line contains nothing but end tag | |
204 | let lnum = lnum - 1 | |
205 | else | |
206 | break " else there is something important before the ?> | |
207 | endif | |
208 | ||
209 | ||
210 | " Manage "here document" tags | |
211 | elseif lastline =~? '^\a\w*;$' && lastline !~? s:notPhpHereDoc " match the end of a heredoc | |
212 | let tofind=substitute( lastline, '\([^;]\+\);', '<<<\1$', '') | |
213 | while getline(lnum) !~? tofind && lnum > 1 | |
214 | let lnum = lnum - 1 | |
215 | endwhile | |
216 | else | |
217 | break " if none of these were true then we are done | |
218 | endif | |
219 | endwhile | |
220 | ||
221 | if lnum==1 && getline(lnum)!~ '<?' | |
222 | let lnum=0 | |
223 | endif | |
224 | return lnum | |
225 | endfunction | |
226 | " }}} | |
227 | ||
228 | function! Skippmatch() " {{{ | |
229 | " the slowest instruction of this script, remove it and the script is 3 | |
230 | " times faster but you may have troubles with '{' inside comments or strings | |
231 | " that will break the indent algorithm... | |
232 | let synname = synIDattr(synID(line("."), col("."), 0), "name") | |
233 | " echo synname . line(".") | |
234 | if synname == "phpParent" || synname == "javaScriptBraces" | |
235 | "if synname =~? "string" || synname =~? "phpComment" || synname =~? "phpHereDoc" | |
236 | return 0 | |
237 | "return 1 | |
238 | else | |
239 | return 1 | |
240 | "return 0 | |
241 | endif | |
242 | endfun | |
243 | " }}} | |
244 | ||
245 | function! FindOpenBracket(lnum) " {{{ | |
246 | call cursor(a:lnum, 1) " set the cursor to the start of the lnum line | |
247 | return searchpair('{', '', '}', 'bW', 'Skippmatch()') | |
248 | endfun | |
249 | " }}} | |
250 | ||
251 | function! FindTheIfOfAnElse (lnum, StopAfterFirstPrevElse) " {{{ | |
252 | " A very clever recoursive function created by me (John Wellesz) that find the "if" corresponding to an | |
253 | " "else". This function can easily be adapted for other languages :) | |
254 | ||
255 | if getline(a:lnum) =~# '^\s*}\s*else\%(if\)\=\>' | |
256 | let beforeelse = a:lnum " we do this so we can find the opened bracket to speed up the process | |
257 | else | |
258 | let beforeelse = GetLastRealCodeLNum(a:lnum - 1) | |
259 | endif | |
260 | ||
261 | if !s:level | |
262 | let s:iftoskip = 0 | |
263 | endif | |
264 | ||
265 | " If we found another "else" then it means we need to skip the next "if" | |
266 | " we'll found. (since version 1.02) | |
267 | if getline(beforeelse) =~# '^\s*\%(}\s*\)\=else\%(\s*if\)\@!\>' | |
268 | let s:iftoskip = s:iftoskip + 1 | |
269 | endif | |
270 | ||
271 | " A closing bracket? let skip the whole block to save some recursive calls | |
272 | if getline(beforeelse) =~ '^\s*}' " .s:endline | |
273 | let beforeelse = FindOpenBracket(beforeelse) | |
274 | ||
275 | " Put us on the block starter | |
276 | if getline(beforeelse) =~ '^\s*{' | |
277 | let beforeelse = GetLastRealCodeLNum(beforeelse - 1) | |
278 | endif | |
279 | endif | |
280 | ||
281 | ||
282 | if !s:iftoskip && a:StopAfterFirstPrevElse && getline(beforeelse) =~# '^\s*\%([}]\s*\)\=else\%(if\)\=\>' | |
283 | return beforeelse | |
284 | endif | |
285 | " if there was an else, then there is a if... | |
286 | if getline(beforeelse) !~# '^\s*if\>' && beforeelse>1 || s:iftoskip && beforeelse>1 | |
287 | ||
288 | if s:iftoskip && getline(beforeelse) =~# '^\s*if\>' | |
289 | let s:iftoskip = s:iftoskip - 1 | |
290 | endif | |
291 | ||
292 | let s:level = s:level + 1 | |
293 | let beforeelse = FindTheIfOfAnElse(beforeelse, a:StopAfterFirstPrevElse) | |
294 | endif | |
295 | ||
296 | return beforeelse | |
297 | ||
298 | endfunction | |
299 | " }}} | |
300 | ||
301 | function! IslinePHP (lnum, tofind) " {{{ | |
302 | " This function asks to the syntax if the pattern 'tofind' on the line | |
303 | " number 'lnum' is PHP code (very slow...). | |
304 | let cline = getline(a:lnum) | |
305 | ||
306 | if a:tofind=="" | |
307 | let tofind = '\%(^\s*\)\@<=\S' | |
308 | else | |
309 | let tofind = a:tofind | |
310 | endif | |
311 | ||
312 | let tofind = tofind . '\c' " ignorecase | |
313 | ||
314 | let coltotest = match (cline, tofind) + 1 "find the first non blank char in the current line | |
315 | ||
316 | let synname = synIDattr(synID(a:lnum, coltotest, 0), "name") " ask to syntax what is its name | |
317 | ||
318 | if matchstr(synname, '^...') == "php" || synname=="Delimiter" | |
319 | return synname | |
320 | else | |
321 | return "" | |
322 | endif | |
323 | endfunction | |
324 | " }}} | |
325 | ||
326 | let s:notPhpHereDoc = '\%(break\|return\|continue\|exit\);' | |
327 | let s:blockstart = '\%(\%(\%(}\s*\)\=else\%(\s\+\)\=\)\=if\>\|while\>\|for\%(each\)\=\>\|declare\|||\|&&\>\)' | |
328 | ||
329 | function! GetPhpIndent() | |
330 | "############################################## | |
331 | "########### MAIN INDENT FUNCTION ############# | |
332 | "############################################## | |
333 | ||
334 | " This detect if the user is currently typing text between each call | |
335 | let UserIsEditing=0 | |
336 | if b:PHP_oldchangetick != b:changedtick | |
337 | let b:PHP_oldchangetick = b:changedtick | |
338 | let UserIsEditing=1 | |
339 | endif | |
340 | ||
341 | ||
342 | if b:PHP_default_indenting | |
343 | let b:PHP_default_indenting = g:PHP_default_indenting * &sw | |
344 | endif | |
345 | ||
346 | let cline = getline(v:lnum) " current line | |
347 | ||
348 | " Let's detect if we are indenting just one line or more than 3 lines | |
349 | " in the last case we can slightly optimize our algorithm | |
350 | if !b:PHP_indentinghuge && b:PHP_lastindented > b:PHP_indentbeforelast | |
351 | if b:PHP_indentbeforelast | |
352 | let b:PHP_indentinghuge = 1 | |
353 | echom 'Large indenting detected, speed optimizations engaged' | |
354 | endif | |
355 | let b:PHP_indentbeforelast = b:PHP_lastindented | |
356 | endif | |
357 | ||
358 | " If the line we are indenting isn't directly under the previous non-blank | |
359 | " line of the file then deactivate the optimization procedures and reset | |
360 | " status variable (we restart for scratch) | |
361 | if b:InPHPcode_checked && prevnonblank(v:lnum - 1) != b:PHP_lastindented | |
362 | if b:PHP_indentinghuge | |
363 | echom 'Large indenting deactivated' | |
364 | let b:PHP_indentinghuge = 0 | |
365 | let b:PHP_CurrentIndentLevel = b:PHP_default_indenting | |
366 | endif | |
367 | let b:PHP_lastindented = v:lnum | |
368 | let b:PHP_LastIndentedWasComment=0 | |
369 | let b:PHP_indentbeforelast = 0 | |
370 | ||
371 | let UserIsEditing=0 " user did changed | |
372 | ||
373 | let b:InPHPcode = 0 | |
374 | let b:InPHPcode_checked = 0 | |
375 | let b:InPHPcode_and_script = 0 | |
376 | let b:InPHPcode_tofind = "" | |
377 | ||
378 | elseif v:lnum > b:PHP_lastindented " we are indenting line in > order (we can rely on the line before) | |
379 | let real_PHP_lastindented = b:PHP_lastindented | |
380 | let b:PHP_lastindented = v:lnum | |
381 | endif | |
382 | ||
383 | " We must detect if we are in PHPCODE or not, but one time only, then | |
384 | " we will detect php end and start tags, comments /**/ and HereDoc | |
385 | " tags | |
386 | ||
387 | if !b:InPHPcode_checked " {{{ One time check | |
388 | let b:InPHPcode_checked = 1 | |
389 | ||
390 | let synname = IslinePHP (prevnonblank(v:lnum), "") " the line could be blank (if the user presses 'return') | |
391 | ||
392 | if synname!="" | |
393 | if synname != "phpHereDoc" | |
394 | let b:InPHPcode = 1 | |
395 | let b:InPHPcode_tofind = "" | |
396 | else | |
397 | let b:InPHPcode = 0 | |
398 | ||
399 | let lnum = v:lnum - 1 | |
400 | while getline(lnum) !~? '<<<\a\w*$' && lnum >1 | |
401 | let lnum = lnum - 1 | |
402 | endwhile | |
403 | ||
404 | let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<\(\a\w*\)\c', '^\\s*\1;$', '') | |
405 | endif | |
406 | else | |
407 | let b:InPHPcode = 0 | |
408 | " Then we have to find a php start tag... | |
409 | let b:InPHPcode_tofind = '<?\%(.*?>\)\@!\|<script.*>' | |
410 | endif | |
411 | endif "!b:InPHPcode_checked }}} | |
412 | ||
413 | " Now we know where we are so we can verify the line right above the | |
414 | " current one to see if we have to stop or restart php indenting | |
415 | ||
416 | " Test if we are indenting PHP code {{{ | |
417 | " Find an executable php code line above the current line. | |
418 | let lnum = prevnonblank(v:lnum - 1) | |
419 | let last_line = getline(lnum) | |
420 | ||
421 | " If we aren't in php code, then there is something we have to find | |
422 | if b:InPHPcode_tofind!="" | |
423 | if cline =~? b:InPHPcode_tofind | |
424 | let b:InPHPcode = 1 | |
425 | let b:InPHPcode_tofind = "" | |
426 | if cline =~ '\*/' | |
427 | return -1 " we don't want to indent closing '*/' | |
428 | elseif cline =~? '<script\>' " a more accurate test is useless since there isn't any other possibility | |
429 | let b:InPHPcode_and_script = 1 | |
430 | endif | |
431 | endif | |
432 | endif | |
433 | ||
434 | " ### If we are in PHP code, we test the line before to see if we have to stop indenting | |
435 | ||
436 | if b:InPHPcode | |
437 | ||
438 | " Was last line containing a PHP end tag ? | |
439 | if !b:InPHPcode_and_script && last_line =~ '\%(<?.*\)\@<!?>\%(.*<?\)\@!' && IslinePHP(lnum, '?>')=="Delimiter" | |
440 | if cline !~? s:PHP_startindenttag | |
441 | let b:InPHPcode = 0 | |
442 | let b:InPHPcode_tofind = s:PHP_startindenttag | |
443 | elseif cline =~? '<script\>' | |
444 | let b:InPHPcode_and_script = 1 | |
445 | endif | |
446 | ||
447 | " Was last line the start of a HereDoc ? | |
448 | elseif last_line =~? '<<<\a\w*$' | |
449 | " \&& IslinePHP(lnum, '\a\w*$')=="Delimiter" | |
450 | let b:InPHPcode = 0 | |
451 | let b:InPHPcode_tofind = substitute( last_line, '^.*<<<\(\a\w*\)\c', '^\\s*\1;$', '') | |
452 | ||
453 | " Skip /* \n+ */ comments execept when the user is currently | |
454 | " writting them | |
455 | elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' | |
456 | " \ && IslinePHP(v:lnum, '/\*')=="phpComment" | |
457 | let b:InPHPcode = 0 | |
458 | let b:InPHPcode_tofind = '\*/' | |
459 | ||
460 | " is current line the end of a HTML script ? (we indent script the | |
461 | " same as php code) | |
462 | elseif cline =~? '^\s*</script>' | |
463 | let b:InPHPcode = 0 | |
464 | " let b:InPHPcode_and_script = 0 | |
465 | let b:InPHPcode_tofind = s:PHP_startindenttag | |
466 | endif | |
467 | endif " }}} | |
468 | ||
469 | " Non PHP code is let as it is | |
470 | if !b:InPHPcode && !b:InPHPcode_and_script | |
471 | return -1 | |
472 | elseif !b:InPHPcode | |
473 | let b:InPHPcode_and_script = 0 | |
474 | endif | |
475 | ||
476 | " Align correctly multi // or # lines | |
477 | ||
478 | " Indent successive // or # comment the same way the first is {{{ | |
479 | if cline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)' | |
480 | if b:PHP_LastIndentedWasComment == 1 | |
481 | return indent(real_PHP_lastindented) " line replaced in 1.02 | |
482 | endif | |
483 | let b:PHP_LastIndentedWasComment = 1 | |
484 | else | |
485 | let b:PHP_LastIndentedWasComment = 0 | |
486 | endif | |
487 | " }}} | |
488 | ||
489 | " Some tags are always indented to col 1 | |
490 | ||
491 | " Things always indented at col 1 (PHP delimiter: <?, ?>, Heredoc end) {{{ | |
492 | " PHP start tags are always at col 1, useless to indent unless the end tag | |
493 | " is on the same line | |
494 | if cline =~# '^\s*<?' && cline !~ '?>' " Added the ^\s* part in version 1.03 | |
495 | return 0 | |
496 | endif | |
497 | ||
498 | " PHP end tags are always at col 1, useless to indent unless if it's | |
499 | " followed by a start tag on the same line | |
500 | if cline =~ '^\s*?>' && cline !~# '<?' | |
501 | return 0 | |
502 | endif | |
503 | ||
504 | " put HereDoc end tags at start of lines | |
505 | if cline =~? '^\s*\a\w*;$' && cline !~? s:notPhpHereDoc | |
506 | return 0 | |
507 | endif | |
508 | " }}} | |
509 | ||
510 | let s:level = 0 | |
511 | ||
512 | " Find an executable php code line above the current line. | |
513 | let lnum = GetLastRealCodeLNum(v:lnum - 1) | |
514 | let last_line = getline(lnum) " last line | |
515 | let ind = indent(lnum) " by default | |
516 | let endline= s:endline | |
517 | ||
518 | if ind==0 && b:PHP_default_indenting | |
519 | let ind = b:PHP_default_indenting | |
520 | endif | |
521 | ||
522 | " Hit the start of the file, use default indent. | |
523 | if lnum == 0 | |
524 | return b:PHP_default_indenting | |
525 | endif | |
526 | ||
527 | " if the last line is a stated line and it's not indented then why should | |
528 | " we indent this one?? | |
529 | " if optimized mode is active and nor current or previous line are an 'else' | |
530 | " or the end of a possible bracketless thing then indent the same as the previous | |
531 | " line | |
532 | if last_line =~ '[;}]'.endline | |
533 | if ind==b:PHP_default_indenting | |
534 | return b:PHP_default_indenting | |
535 | elseif b:PHP_indentinghuge && ind==b:PHP_CurrentIndentLevel && cline !~# '^\s*\%(else\|\%(case\|default\).*:\|[})];\=\)' && last_line !~# '^\s*\%(\%(}\s*\)\=else\)' && getline(GetLastRealCodeLNum(lnum - 1))=~';'.endline | |
536 | return b:PHP_CurrentIndentLevel | |
537 | endif | |
538 | endif | |
539 | ||
540 | " Search the matching open bracket (with searchpair()) and set the indent of cline | |
541 | " to the indent of the matching line. | |
542 | if cline =~ '^\s*}\%(}}\)\@!' | |
543 | let ind = indent(FindOpenBracket(v:lnum)) | |
544 | let b:PHP_CurrentIndentLevel = b:PHP_default_indenting | |
545 | return ind | |
546 | endif | |
547 | ||
548 | let LastLineClosed = 0 " used to prevent redundant tests in the last part of the script | |
549 | ||
550 | let unstated='\%(^\s*'.s:blockstart.'.*)\|\%(//.*\)\@<!\<e'.'lse\>\)'.endline | |
551 | " What is an unstated line? | |
552 | " - an "else" at the end of line | |
553 | " - a s:blockstart (if while etc...) followed by anything and a ")" at | |
554 | " the end of line | |
555 | ||
556 | " if the current line is an 'else' starting line | |
557 | " (to match an 'else' preceded by a '}' is irrelevant and futile - see | |
558 | " code above) | |
559 | if ind != b:PHP_default_indenting && cline =~# '^\s*else\%(if\)\=\>' | |
560 | let b:PHP_CurrentIndentLevel = b:PHP_default_indenting " prevent optimized to work at next call | |
561 | return indent(FindTheIfOfAnElse(v:lnum, 1)) | |
562 | elseif last_line =~# unstated && cline !~ '^\s*{\|^\s*);\='.endline | |
563 | let ind = ind + &sw | |
564 | return ind | |
565 | ||
566 | " If the last line is terminated by ';' or if it's a closing '}' | |
567 | " We need to check if this isn't the end of a multilevel non '{}' | |
568 | " structure such as: | |
569 | " Exemple: | |
570 | " if ($truc) | |
571 | " echo 'truc'; | |
572 | " | |
573 | " OR | |
574 | " | |
575 | " if ($truc) | |
576 | " while ($truc) { | |
577 | " lkhlkh(); | |
578 | " echo 'infinite loop\n'; | |
579 | " } | |
580 | elseif ind != b:PHP_default_indenting && last_line =~ ';'.endline.'\|^\s*}\%(.*{'. endline.'\)\@!' | |
581 | " If we are here it means that the previous line is: | |
582 | " - a *;$ line | |
583 | " - a [beginning-blanck] } followed by anything but a { $ | |
584 | let previous_line = last_line | |
585 | let last_line_num = lnum | |
586 | let LastLineClosed = 1 | |
587 | " The idea here is to check if the current line is after a non '{}' | |
588 | " structure so we can indent it like the top of that structure. | |
589 | " The top of that structure is caracterized by a if (ff)$ style line | |
590 | " preceded by a stated line. If there is no such structure then we | |
591 | " just have to find two 'normal' lines following each other with the | |
592 | " same indentation and with the first of these two lines terminated by | |
593 | " a ; or by a }... | |
594 | ||
595 | while 1 | |
596 | " let's skip '{}' blocks | |
597 | if previous_line =~ '^\s*}' | |
598 | " find the openning '{' | |
599 | let last_line_num = FindOpenBracket(last_line_num) | |
600 | ||
601 | " if the '{' is alone on the line get the line before | |
602 | if getline(last_line_num) =~ '^\s*{' | |
603 | let last_line_num = GetLastRealCodeLNum(last_line_num - 1) | |
604 | endif | |
605 | ||
606 | let previous_line = getline(last_line_num) | |
607 | ||
608 | continue | |
609 | else | |
610 | " At this point we know that the previous_line isn't a closing | |
611 | " '}' so we can check if we really are in such a structure. | |
612 | ||
613 | " it's not a '}' but it could be an else alone... | |
614 | if getline(last_line_num) =~# '^\s*else\%(if\)\=\>' | |
615 | let last_line_num = FindTheIfOfAnElse(last_line_num, 0) | |
616 | continue " re-run the loop (we could find a '}' again) | |
617 | endif | |
618 | ||
619 | " So now it's ok we can check :-) | |
620 | " A good quality is to have confidence in oneself so to know | |
621 | " if yes or no we are in that struct lets test the indent of | |
622 | " last_line_num and of last_line_num - 1! | |
623 | " If those are == then we are almost done. | |
624 | " | |
625 | " That isn't sufficient, we need to test how the first of the | |
626 | " 2 lines is ended... | |
627 | ||
628 | " Note the indenting of the line we are checking | |
629 | ||
630 | let last_match = last_line_num " remember the 'topest' line we found so far | |
631 | ||
632 | let one_ahead_indent = indent(last_line_num) | |
633 | let last_line_num = GetLastRealCodeLNum(last_line_num - 1) | |
634 | let two_ahead_indent = indent(last_line_num) | |
635 | let after_previous_line = previous_line | |
636 | let previous_line = getline(last_line_num) | |
637 | ||
638 | ||
639 | " If we find a '{' or a case/default then we are inside that block so lets | |
640 | " indent properly... Like the line following that block starter | |
641 | if previous_line =~# '^\s*\%(case\|default\).*:\|{'.endline | |
642 | break | |
643 | endif | |
644 | ||
645 | " The 3 lines below are not necessary for the script to work | |
646 | " but it makes it work a little more faster in some (rare) cases. | |
647 | " We verify if we are at the top of a non '{}' struct. | |
648 | if after_previous_line=~# '^\s*'.s:blockstart.'.*)'.endline && previous_line =~# '[;}]'.endline | |
649 | break | |
650 | endif | |
651 | ||
652 | if one_ahead_indent == two_ahead_indent || last_line_num < 1 | |
653 | " So the previous line and the line before are at the same | |
654 | " col. Now we just have to check if the line before is a ;$ or [}]$ ended line | |
655 | " we always check the most ahead line of the 2 lines so | |
656 | " it's useless to match ')$' since the lines couldn't have | |
657 | " the same indent... | |
658 | if previous_line =~# '[;}]'.endline || last_line_num < 1 | |
659 | break | |
660 | endif | |
661 | endif | |
662 | endif | |
663 | endwhile | |
664 | ||
665 | if indent(last_match) != ind " if nothing was done lets the old script continue | |
666 | let ind = indent(last_match) " let's use the indent of the last line matched by the alhorithm above | |
667 | let b:PHP_CurrentIndentLevel = b:PHP_default_indenting "line added in version 1.02 to prevent optimized mode | |
668 | " from acting in some special cases | |
669 | return ind | |
670 | endif | |
671 | endif | |
672 | ||
673 | ||
674 | let plinnum = GetLastRealCodeLNum(lnum - 1) | |
675 | let pline = getline(plinnum) " previous to last line | |
676 | ||
677 | " REMOVE comments at end of line before treatment | |
678 | " the first par of the regex removes // from the end of line when they are | |
679 | " followed by a number of '"' which is a multiple of 2. The second part | |
680 | " removes // that are not followed by any '"' | |
681 | " Sorry for this unreadable thing... | |
682 | let last_line = substitute(last_line,"\\(//\\|#\\)\\(\\(\\([^\"']*\\([\"']\\)[^\"']*\\5\\)\\+[^\"']*$\\)\\|\\([^\"']*$\\)\\)",'','') | |
683 | ||
684 | " Indent blocks enclosed by {} or () (default indenting) | |
685 | if !LastLineClosed " the last line isn't a .*; or a }$ line | |
686 | " if the last line is a [{(]$ or a multiline function call (or array | |
687 | " declaration) with already one parameter on the opening ( line | |
688 | if last_line =~# '[{(]'.endline || last_line =~? '\h\w*\s*(.*,$' && pline !~ '[,(]'.endline | |
689 | let ind = ind + &sw | |
690 | if cline !~# '^\s*\%(default\|case\).*:' " case and default are not indented inside blocks | |
691 | let b:PHP_CurrentIndentLevel = ind | |
692 | return ind | |
693 | endif | |
694 | endif | |
695 | endif | |
696 | ||
697 | " If the current line closes a multiline function call or array def | |
698 | if cline =~ '^\s*);\=' | |
699 | let ind = ind - &sw | |
700 | elseif cline =~# '^\s*\%(default\|case\).*:' | |
701 | let ind = ind - &sw | |
702 | endif | |
703 | ||
704 | if last_line =~# '^\s*\%(default\|case\).*:' | |
705 | let ind = ind + &sw | |
706 | endif | |
707 | ||
708 | let b:PHP_CurrentIndentLevel = ind | |
709 | return ind | |
710 | endfunction | |
711 | ||
712 | " vim: set ts=4 sw=4: |