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