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