xref: /vim-8.2.3635/runtime/indent/php.vim (revision 5b8d8fdb)
1" Vim indent file
2" Language:	PHP
3" Author:	John Wellesz <John.wellesz (AT) teaser (DOT) fr>
4" URL:		http://www.2072productions.com/vim/indent/php.vim
5" Last Change: 2005 Aug 15
6" Version: 1.17
7"
8" For a complete change log and lots of comments in the code, download the script on
9" 2072productions.com at the URI provided above.
10"
11"
12"
13"  If you find a bug, please e-mail me at John.wellesz (AT) teaser (DOT) fr
14"  with an example of code that break the algorithm.
15"
16"
17"	Thanks a lot for using this script.
18"
19"
20" NOTE: This script must be used with PHP syntax ON and with the php syntax
21"		script by Lutz Eymers (http://www.isp.de/data/php.vim ) that's the script bundled with Gvim.
22"
23"
24"	In the case you have syntax errors in your script such as end of HereDoc
25"	tags not at col 1 you'll have to indent your file 2 times (This script
26"	will automatically put HereDoc end tags at col 1).
27"
28"
29" NOTE: If you are editing file in Unix file format and that (by accident)
30" there are '\r' before new lines, this script won't be able to proceed
31" correctly and will make many mistakes because it won't be able to match
32" '\s*$' correctly.
33" So you have to remove those useless characters first with a command like:
34"
35" :%s /\r$//g
36"
37" or simply 'let' the option PHP_removeCRwhenUnix to 1 and the script will
38" silently remove them when VIM load this script (at each bufread).
39
40" Options: PHP_default_indenting = # of sw (default is 0), # of sw will be
41"		   added to the indent of each line of PHP code.
42"
43" Options: PHP_removeCRwhenUnix = 1 to make the script automatically remove CR
44"		   at end of lines (by default this option is unset), NOTE that you
45"		   MUST remove CR when the fileformat is UNIX else the indentation
46"		   won't be correct...
47"
48" Options: PHP_BracesAtCodeLevel = 1 to indent the '{' and '}' at the same
49"		   level than the code they contain.
50"		   Exemple:
51"			Instead of:
52"				if ($foo)
53"				{
54"					foo();
55"				}
56"
57"			You will write:
58"				if ($foo)
59"					{
60"					foo();
61"					}
62"
63"			NOTE: The script will be a bit slower if you use this option because
64"			some optimizations won't be available.
65
66
67if exists("b:did_indent")
68	finish
69endif
70let b:did_indent = 1
71
72"	This script set the option php_sync_method of PHP syntax script to 0
73"	(fromstart indenting method) in order to have an accurate syntax.
74"	If you are using very big PHP files (which is a bad idea) you will
75"	experience slowings down while editing, if your code contains only PHP
76"	code you can comment the line below.
77
78let php_sync_method = 0
79
80
81if exists("PHP_default_indenting")
82	let b:PHP_default_indenting = PHP_default_indenting * &sw
83else
84	let b:PHP_default_indenting = 0
85endif
86
87if exists("PHP_BracesAtCodeLevel")
88	let b:PHP_BracesAtCodeLevel = PHP_BracesAtCodeLevel
89else
90	let b:PHP_BracesAtCodeLevel = 0
91endif
92
93
94let b:PHP_lastindented = 0
95let b:PHP_indentbeforelast = 0
96let b:PHP_indentinghuge = 0
97let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
98let b:PHP_LastIndentedWasComment = 0
99let b:PHP_InsideMultilineComment = 0
100let b:InPHPcode = 0
101let b:InPHPcode_checked = 0
102let b:InPHPcode_and_script = 0
103let b:InPHPcode_tofind = ""
104let b:PHP_oldchangetick = b:changedtick
105let b:UserIsTypingComment = 0
106let b:optionsset = 0
107
108setlocal nosmartindent
109setlocal noautoindent
110setlocal nocindent
111setlocal nolisp " autoindent must be on, so this line is also useless...
112
113setlocal indentexpr=GetPhpIndent()
114setlocal indentkeys=0{,0},0),:,!^F,o,O,e,*<Return>,=?>,=<?,=*/
115
116
117if version <= 603 && &encoding == 'utf-8'
118	let s:searchpairflags = 'bW'
119else
120	let s:searchpairflags = 'bWr'
121endif
122
123if &fileformat == "unix" && exists("PHP_removeCRwhenUnix") && PHP_removeCRwhenUnix
124	silent! %s/\r$//g
125endif
126
127if exists("*GetPhpIndent")
128	finish " XXX
129endif
130
131let s:endline= '\s*\%(//.*\|#.*\|/\*.*\*/\s*\)\=$'
132let s:PHP_startindenttag = '<?\%(.*?>\)\@!\|<script[^>]*>\%(.*<\/script>\)\@!'
133"setlocal debug=msg " XXX
134
135
136function! GetLastRealCodeLNum(startline) " {{{
137	"Inspired from the function SkipJavaBlanksAndComments by Toby Allsopp for indent/java.vim
138	let lnum = a:startline
139	let old_lnum = lnum
140
141	while lnum > 1
142		let lnum = prevnonblank(lnum)
143		let lastline = getline(lnum)
144
145		if b:InPHPcode_and_script && lastline =~ '?>\s*$'
146			let lnum = lnum - 1
147		elseif lastline =~ '^\s*?>.*<?\%(php\)\=\s*$'
148			let lnum = lnum - 1
149		elseif lastline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)' " if line is under comment
150			let lnum = lnum - 1
151		elseif lastline =~ '\*/\s*$' " skip multiline comments
152			call cursor(lnum, 1)
153			call search('\*/\zs', 'W') " positition the cursor after the first */
154			let lnum = searchpair('/\*', '', '\*/\zs', s:searchpairflags) " find the most outside /*
155
156			let lastline = getline(lnum)
157			if lastline =~ '^\s*/\*' " if line contains nothing but comment
158				let lnum = lnum - 1 " do the job again on the line before (a comment can hide another...)
159			else
160				break
161			endif
162
163
164		elseif lastline =~? '\%(//\s*\|?>.*\)\@<!<?\%(php\)\=\s*$\|^\s*<script\>' " skip non php code
165
166			while lastline !~ '\(<?.*\)\@<!?>' && lnum > 1
167				let lnum = lnum - 1
168				let lastline = getline(lnum)
169			endwhile
170			if lastline =~ '^\s*?>' " if line contains nothing but end tag
171				let lnum = lnum - 1
172			else
173				break " else there is something important before the ?>
174			endif
175
176
177		elseif lastline =~? '^\a\w*;$' && lastline !~? s:notPhpHereDoc " match the end of a heredoc
178			let tofind=substitute( lastline, '\([^;]\+\);', '<<<\1$', '')
179			while getline(lnum) !~? tofind && lnum > 1
180				let lnum = lnum - 1
181			endwhile
182		else
183			break " if none of these were true then we are done
184		endif
185	endwhile
186
187	if lnum==1 && getline(lnum)!~ '<?'
188		let lnum=0
189	endif
190
191	if b:InPHPcode_and_script && !b:InPHPcode
192		let b:InPHPcode_and_script = 0
193	endif
194	return lnum
195endfunction
196" }}}
197
198function! Skippmatch()  " {{{
199	let synname = synIDattr(synID(line("."), col("."), 0), "name")
200	if synname == "Delimiter" || synname == "phpParent" || synname == "javaScriptBraces" || synname == "phpComment" && b:UserIsTypingComment
201		return 0
202	else
203		return 1
204	endif
205endfun
206" }}}
207
208function! FindOpenBracket(lnum) " {{{
209	call cursor(a:lnum, 1) " set the cursor to the start of the lnum line
210	return searchpair('{', '', '}', 'bW', 'Skippmatch()')
211endfun
212" }}}
213
214function! FindTheIfOfAnElse (lnum, StopAfterFirstPrevElse) " {{{
215" A very clever recoursive function created by me (John Wellesz) that find the "if" corresponding to an
216" "else". This function can easily be adapted for other languages :)
217
218	if getline(a:lnum) =~# '^\s*}\s*else\%(if\)\=\>'
219		let beforeelse = a:lnum " we do this so we can find the opened bracket to speed up the process
220	else
221		let beforeelse = GetLastRealCodeLNum(a:lnum - 1)
222	endif
223
224	if !s:level
225		let s:iftoskip = 0
226	endif
227
228	if getline(beforeelse) =~# '^\s*\%(}\s*\)\=else\%(\s*if\)\@!\>'
229		let s:iftoskip = s:iftoskip + 1
230	endif
231
232	if getline(beforeelse) =~ '^\s*}'
233		let beforeelse = FindOpenBracket(beforeelse)
234
235		if getline(beforeelse) =~ '^\s*{'
236			let beforeelse = GetLastRealCodeLNum(beforeelse - 1)
237		endif
238	endif
239
240
241	if !s:iftoskip && a:StopAfterFirstPrevElse && getline(beforeelse) =~# '^\s*\%([}]\s*\)\=else\%(if\)\=\>'
242		return beforeelse
243	endif
244
245	if getline(beforeelse) !~# '^\s*if\>' && beforeelse>1 || s:iftoskip && beforeelse>1
246
247		if  s:iftoskip && getline(beforeelse) =~# '^\s*if\>'
248			let s:iftoskip = s:iftoskip - 1
249		endif
250
251		let s:level =  s:level + 1
252		let beforeelse = FindTheIfOfAnElse(beforeelse, a:StopAfterFirstPrevElse)
253	endif
254
255	return beforeelse
256
257endfunction
258" }}}
259
260function! IslinePHP (lnum, tofind) " {{{
261	let cline = getline(a:lnum)
262
263	if a:tofind==""
264		let tofind = "^\\s*[\"']*\s*\\zs\\S" " This correct the issue where lines beginning by a
265		" single or double quote were not indented in some cases.
266	else
267		let tofind = a:tofind
268	endif
269
270	let tofind = tofind . '\c' " ignorecase
271
272	let coltotest = match (cline, tofind) + 1 "find the first non blank char in the current line
273
274	let synname = synIDattr(synID(a:lnum, coltotest, 0), "name") " ask to syntax what is its name
275
276	if synname =~ '^php' || synname=="Delimiter" || synname =~? '^javaScript'
277		return synname
278	else
279		return ""
280	endif
281endfunction
282" }}}
283
284let s:notPhpHereDoc = '\%(break\|return\|continue\|exit\);'
285let s:blockstart = '\%(\%(\%(}\s*\)\=else\%(\s\+\)\=\)\=if\>\|while\>\|switch\>\|for\%(each\)\=\>\|declare\>\|[|&]\)'
286
287let s:autorestoptions = 0
288if ! s:autorestoptions
289	au BufWinEnter,Syntax	*.php,*.php3,*.php4,*.php5	call ResetOptions()
290	let s:autorestoptions = 1
291endif
292
293function! ResetOptions()
294	if ! b:optionsset
295		setlocal formatoptions=qroc
296		let b:optionsset = 1
297	endif
298endfunc
299
300function! GetPhpIndent()
301	"##############################################
302	"########### MAIN INDENT FUNCTION #############
303	"##############################################
304
305	let UserIsEditing=0
306	if 	b:PHP_oldchangetick != b:changedtick
307		let b:PHP_oldchangetick = b:changedtick
308		let UserIsEditing=1
309	endif
310
311	if b:PHP_default_indenting
312		let b:PHP_default_indenting = g:PHP_default_indenting * &sw
313	endif
314
315	let cline = getline(v:lnum) " current line
316
317	if !b:PHP_indentinghuge && b:PHP_lastindented > b:PHP_indentbeforelast
318		if b:PHP_indentbeforelast
319			let b:PHP_indentinghuge = 1
320			echom 'Large indenting detected, speed optimizations engaged'
321		endif
322		let b:PHP_indentbeforelast = b:PHP_lastindented
323	endif
324
325	if b:InPHPcode_checked && prevnonblank(v:lnum - 1) != b:PHP_lastindented
326		if b:PHP_indentinghuge
327			echom 'Large indenting deactivated'
328			let b:PHP_indentinghuge = 0
329			let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
330		endif
331		let b:PHP_lastindented = v:lnum
332		let b:PHP_LastIndentedWasComment=0
333		let b:PHP_InsideMultilineComment=0
334		let b:PHP_indentbeforelast = 0
335
336		let b:InPHPcode = 0
337		let b:InPHPcode_checked = 0
338		let b:InPHPcode_and_script = 0
339		let b:InPHPcode_tofind = ""
340
341	elseif v:lnum > b:PHP_lastindented " we are indenting line in > order (we can rely on the line before)
342		let real_PHP_lastindented = b:PHP_lastindented
343		let b:PHP_lastindented = v:lnum
344	endif
345
346
347	if !b:InPHPcode_checked " {{{ One time check
348		let b:InPHPcode_checked = 1
349
350		let synname = IslinePHP (prevnonblank(v:lnum), "") " the line could be blank (if the user presses 'return')
351
352		if synname!=""
353			if synname != "phpHereDoc"
354				let b:InPHPcode = 1
355				let b:InPHPcode_tofind = ""
356
357				if synname == "phpComment"
358					let b:UserIsTypingComment = 1
359				else
360					let b:UserIsTypingComment = 0
361				endif
362
363				if synname =~? '^javaScript'
364					let b:InPHPcode_and_script = 1
365				endif
366
367			else
368				let b:InPHPcode = 0
369				let b:UserIsTypingComment = 0
370
371				let lnum = v:lnum - 1
372				while getline(lnum) !~? '<<<\a\w*$' && lnum > 1
373					let lnum = lnum - 1
374				endwhile
375
376				let b:InPHPcode_tofind = substitute( getline(lnum), '^.*<<<\(\a\w*\)\c', '^\\s*\1;$', '')
377			endif
378		else " IslinePHP returned "" => we are not in PHP or Javascript
379			let b:InPHPcode = 0
380			let b:UserIsTypingComment = 0
381			" Then we have to find a php start tag...
382			let b:InPHPcode_tofind = '<?\%(.*?>\)\@!\|<script.*>'
383		endif
384	endif "!b:InPHPcode_checked }}}
385
386
387	let lnum = prevnonblank(v:lnum - 1)
388	let last_line = getline(lnum)
389
390	if b:InPHPcode_tofind!=""
391		if cline =~? b:InPHPcode_tofind
392			let	b:InPHPcode = 1
393			let b:InPHPcode_tofind = ""
394			let b:UserIsTypingComment = 0
395			if cline =~ '\*/' " End comment tags must be indented like start comment tags
396				call cursor(v:lnum, 1)
397				call search('\*/\zs', 'W')
398				let lnum = searchpair('/\*', '', '\*/\zs', s:searchpairflags) " find the most outside /*
399
400				let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
401				let b:PHP_LastIndentedWasComment = 0 " prevent a problem if multiline /**/ comment are surounded by
402													 " other types of comments
403
404				if cline =~ '^\s*\*/'
405					return indent(lnum) + 1
406				else
407					return indent(lnum)
408				endif
409
410			elseif cline =~? '<script\>' " a more accurate test is useless since there isn't any other possibility
411				let b:InPHPcode_and_script = 1
412			endif
413		endif
414	endif
415
416
417	if b:InPHPcode
418
419		if !b:InPHPcode_and_script && last_line =~ '\%(<?.*\)\@<!?>\%(.*<?\)\@!' && IslinePHP(lnum, '?>')=="Delimiter"
420			if cline !~? s:PHP_startindenttag
421				let b:InPHPcode = 0
422				let b:InPHPcode_tofind = s:PHP_startindenttag
423			elseif cline =~? '<script\>'
424				let b:InPHPcode_and_script = 1
425			endif
426
427		elseif last_line =~? '<<<\a\w*$'
428			let b:InPHPcode = 0
429			let b:InPHPcode_tofind = substitute( last_line, '^.*<<<\(\a\w*\)\c', '^\\s*\1;$', '')
430
431		elseif !UserIsEditing && cline =~ '^\s*/\*\%(.*\*/\)\@!' && getline(v:lnum + 1) !~ '^\s*\*' " XXX indent comments
432			let b:InPHPcode = 0
433			let b:InPHPcode_tofind = '\*/'
434
435		elseif cline =~? '^\s*</script>'
436			let b:InPHPcode = 0
437			let b:InPHPcode_tofind = s:PHP_startindenttag
438		endif
439	endif " }}}
440
441	if !b:InPHPcode && !b:InPHPcode_and_script
442		return -1
443	endif
444
445
446	" Indent successive // or # comment the same way the first is {{{
447	if cline =~ '^\s*\%(//\|#\|/\*.*\*/\s*$\)'
448		if b:PHP_LastIndentedWasComment == 1
449			return indent(real_PHP_lastindented) " line replaced in 1.02
450		endif
451		let b:PHP_LastIndentedWasComment = 1
452	else
453		let b:PHP_LastIndentedWasComment = 0
454	endif
455	" }}}
456
457	" Indent multiline /* comments correctly {{{
458
459
460	if b:PHP_InsideMultilineComment || b:UserIsTypingComment
461		if cline =~ '^\s*\*\%(\/\)\@!'   " if cline == '*'
462			if last_line =~ '^\s*/\*' " if last_line == '/*'
463				return indent(lnum) + 1
464			else
465				return indent(lnum)
466			endif
467		else
468			let b:PHP_InsideMultilineComment = 0
469		endif
470	endif
471
472	if !b:PHP_InsideMultilineComment && cline =~ '^\s*/\*' " if cline == '/*'
473		let b:PHP_InsideMultilineComment = 1
474		return -1
475	endif
476	" }}}
477
478	if cline =~# '^\s*<?' && cline !~ '?>' " Added the ^\s* part in version 1.03
479		return 0
480	endif
481
482	if  cline =~ '^\s*?>' && cline !~# '<?'
483		return 0
484	endif
485
486	if cline =~? '^\s*\a\w*;$' && cline !~? s:notPhpHereDoc
487		return 0
488	endif
489	" }}}
490
491	let s:level = 0
492
493	let lnum = GetLastRealCodeLNum(v:lnum - 1)
494	let last_line = getline(lnum)    " last line
495	let ind = indent(lnum) " by default
496	let endline= s:endline
497
498	if ind==0 && b:PHP_default_indenting
499		let ind = b:PHP_default_indenting
500	endif
501
502	if lnum == 0
503		return b:PHP_default_indenting
504	endif
505
506
507	if cline =~ '^\s*}\%(}}\)\@!'
508		let ind = indent(FindOpenBracket(v:lnum))
509		let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
510		return ind
511	endif
512
513	if cline =~ '^\s*\*/' " End comment tags must be indented like start comment tags
514		call cursor(v:lnum, 1)
515		call search('\*/\zs', 'W')
516		let lnum = searchpair('/\*', '', '\*/\zs', s:searchpairflags) " find the most outside /*
517
518		let b:PHP_CurrentIndentLevel = b:PHP_default_indenting
519
520		if cline =~ '^\s*\*/'
521			return indent(lnum) + 1
522		else
523			return indent(lnum)
524		endif
525	endif
526
527	let defaultORcase = '^\s*\%(default\|case\).*:'
528
529	if last_line =~ '[;}]'.endline && last_line !~# defaultORcase
530		if ind==b:PHP_default_indenting " if no indentation for the previous line
531			return b:PHP_default_indenting
532		elseif b:PHP_indentinghuge && ind==b:PHP_CurrentIndentLevel && cline !~# '^\s*\%(else\|\%(case\|default\).*:\|[})];\=\)' && last_line !~# '^\s*\%(\%(}\s*\)\=else\)' && getline(GetLastRealCodeLNum(lnum - 1))=~';'.endline
533			return b:PHP_CurrentIndentLevel
534		endif
535	endif
536
537	let LastLineClosed = 0 " used to prevent redundant tests in the last part of the script
538
539	let terminated = '\%(;\%(\s*?>\)\=\|<<<\a\w*\|}\)'.endline
540
541	let unstated   = '\%(^\s*'.s:blockstart.'.*)\|\%(//.*\)\@<!\<e'.'lse\>\)'.endline
542
543	if ind != b:PHP_default_indenting && cline =~# '^\s*else\%(if\)\=\>'
544		let b:PHP_CurrentIndentLevel = b:PHP_default_indenting " prevent optimized to work at next call
545		return indent(FindTheIfOfAnElse(v:lnum, 1))
546	elseif last_line =~# unstated && cline !~ '^\s*{\|^\s*);\='.endline
547		let ind = ind + &sw
548		return ind
549
550
551	elseif ind != b:PHP_default_indenting && last_line =~ terminated
552		let previous_line = last_line
553		let last_line_num = lnum
554		let LastLineClosed = 1
555
556
557		while 1
558			if previous_line =~ '^\s*}'
559				let last_line_num = FindOpenBracket(last_line_num)
560
561				if getline(last_line_num) =~ '^\s*{'
562					let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
563				endif
564
565				let previous_line = getline(last_line_num)
566
567				continue
568			else
569				if getline(last_line_num) =~# '^\s*else\%(if\)\=\>'
570					let last_line_num = FindTheIfOfAnElse(last_line_num, 0)
571					continue " re-run the loop (we could find a '}' again)
572				endif
573
574
575				let last_match = last_line_num " remember the 'topest' line we found so far
576
577				let one_ahead_indent = indent(last_line_num)
578				let last_line_num = GetLastRealCodeLNum(last_line_num - 1)
579				let two_ahead_indent = indent(last_line_num)
580				let after_previous_line = previous_line
581				let previous_line = getline(last_line_num)
582
583
584				if previous_line =~# defaultORcase.'\|{'.endline
585					break
586				endif
587
588				if after_previous_line=~# '^\s*'.s:blockstart.'.*)'.endline && previous_line =~# '[;}]'.endline
589					break
590				endif
591
592				if one_ahead_indent == two_ahead_indent || last_line_num < 1
593					if previous_line =~# '[;}]'.endline || last_line_num < 1
594						break
595					endif
596				endif
597			endif
598		endwhile
599
600		if indent(last_match) != ind " if nothing was done lets the old script continue
601			let ind = indent(last_match) " let's use the indent of the last line matched by the alhorithm above
602			let b:PHP_CurrentIndentLevel = b:PHP_default_indenting " line added in version 1.02 to prevent optimized mode
603			" from acting in some special cases
604
605			if cline =~# defaultORcase
606				let ind = ind - &sw
607			endif
608			return ind
609		endif
610	endif
611
612	let plinnum = GetLastRealCodeLNum(lnum - 1)
613	let pline = getline(plinnum) " previous to last line
614
615	let last_line = substitute(last_line,"\\(//\\|#\\)\\(\\(\\([^\"']*\\([\"']\\)[^\"']*\\5\\)\\+[^\"']*$\\)\\|\\([^\"']*$\\)\\)",'','')
616
617
618	if ind == b:PHP_default_indenting
619		if last_line =~ terminated
620			let LastLineClosed = 1
621		endif
622	endif
623
624	if !LastLineClosed " the last line isn't a .*; or a }$ line
625		if last_line =~# '[{(]'.endline || last_line =~? '\h\w*\s*(.*,$' && pline !~ '[,(]'.endline
626
627			if !b:PHP_BracesAtCodeLevel || last_line !~# '^\s*{' " XXX mod {
628				let ind = ind + &sw
629			endif
630
631			if b:PHP_BracesAtCodeLevel || cline !~# defaultORcase " XXX mod (2) {
632				" case and default are not indented inside blocks
633				let b:PHP_CurrentIndentLevel = ind
634				return ind
635			endif
636
637		elseif last_line =~ '\S\+\s*),'.endline
638			call cursor(lnum, 1)
639			call search('),'.endline, 'W')
640			let openedparent = searchpair('(', '', ')', 'bW', 'Skippmatch()')
641			if openedparent != lnum
642				let ind = indent(openedparent)
643			endif
644
645		elseif cline !~ '^\s*{' && pline =~ '\%(;\%(\s*?>\)\=\|<<<\a\w*\|{\|^\s*'.s:blockstart.'\s*(.*)\)'.endline.'\|^\s*}\|'.defaultORcase
646
647			let ind = ind + &sw
648
649		endif
650		if  b:PHP_BracesAtCodeLevel && cline =~# '^\s*{' " XXX mod {
651			let ind = ind + &sw
652		endif
653
654	elseif last_line =~# defaultORcase
655		let ind = ind + &sw
656	endif
657
658	if cline =~  '^\s*);\='
659		let ind = ind - &sw
660	elseif cline =~# defaultORcase
661		let ind = ind - &sw
662
663	endif
664
665	let b:PHP_CurrentIndentLevel = ind
666	return ind
667endfunction
668
669" vim: set ts=4 sw=4:
670" vim: set ff=unix:
671