]> git.r.bdr.sh - rbdr/dotfiles/blame - vim/autoload/delimitMate.vim
Add DS_Store to gitignore
[rbdr/dotfiles] / vim / autoload / delimitMate.vim
CommitLineData
0d23b6e5
BB
1" File: autoload/delimitMate.vim
2" Version: 2.6
3" Modified: 2011-01-14
4" Description: This plugin provides auto-completion for quotes, parens, etc.
5" Maintainer: Israel Chauca F. <israelchauca@gmail.com>
6" Manual: Read ":help delimitMate".
7" ============================================================================
8
9" Utilities {{{
10
11let delimitMate_loaded = 1
12
13function! delimitMate#ShouldJump() "{{{
14 " Returns 1 if the next character is a closing delimiter.
15 let col = col('.')
16 let lcol = col('$')
17 let char = getline('.')[col - 1]
18
19 " Closing delimiter on the right.
20 for cdel in b:_l_delimitMate_right_delims + b:_l_delimitMate_quotes_list
21 if char == cdel
22 return 1
23 endif
24 endfor
25
26 " Closing delimiter with space expansion.
27 let nchar = getline('.')[col]
28 if b:_l_delimitMate_expand_space && char == " "
29 for cdel in b:_l_delimitMate_right_delims + b:_l_delimitMate_quotes_list
30 if nchar == cdel
31 return 1
32 endif
33 endfor
34 endif
35
36 " Closing delimiter with CR expansion.
37 let uchar = getline(line('.') + 1)[0]
38 if b:_l_delimitMate_expand_cr && char == ""
39 for cdel in b:_l_delimitMate_right_delims + b:_l_delimitMate_quotes_list
40 if uchar == cdel
41 return 1
42 endif
43 endfor
44 endif
45
46 return 0
47endfunction "}}}
48
49function! delimitMate#IsEmptyPair(str) "{{{
50 for pair in b:_l_delimitMate_matchpairs_list
51 if a:str == join( split( pair, ':' ),'' )
52 return 1
53 endif
54 endfor
55 for quote in b:_l_delimitMate_quotes_list
56 if a:str == quote . quote
57 return 1
58 endif
59 endfor
60 return 0
61endfunction "}}}
62
63function! delimitMate#IsCRExpansion() " {{{
64 let nchar = getline(line('.')-1)[-1:]
65 let schar = getline(line('.')+1)[:0]
66 let isEmpty = getline('.') == ""
67 if index(b:_l_delimitMate_left_delims, nchar) > -1 &&
68 \ index(b:_l_delimitMate_left_delims, nchar) == index(b:_l_delimitMate_right_delims, schar) &&
69 \ isEmpty
70 return 1
71 elseif index(b:_l_delimitMate_quotes_list, nchar) > -1 &&
72 \ index(b:_l_delimitMate_quotes_list, nchar) == index(b:_l_delimitMate_quotes_list, schar) &&
73 \ isEmpty
74 return 1
75 else
76 return 0
77 endif
78endfunction " }}} delimitMate#IsCRExpansion()
79
80function! delimitMate#IsSpaceExpansion() " {{{
81 let line = getline('.')
82 let col = col('.')-2
83 if col > 0
84 let pchar = line[col - 1]
85 let nchar = line[col + 2]
86 let isSpaces = (line[col] == line[col+1] && line[col] == " ")
87
88 if index(b:_l_delimitMate_left_delims, pchar) > -1 &&
89 \ index(b:_l_delimitMate_left_delims, pchar) == index(b:_l_delimitMate_right_delims, nchar) &&
90 \ isSpaces
91 return 1
92 elseif index(b:_l_delimitMate_quotes_list, pchar) > -1 &&
93 \ index(b:_l_delimitMate_quotes_list, pchar) == index(b:_l_delimitMate_quotes_list, nchar) &&
94 \ isSpaces
95 return 1
96 endif
97 endif
98 return 0
99endfunction " }}} IsSpaceExpansion()
100
101function! delimitMate#WithinEmptyPair() "{{{
102 let cur = strpart( getline('.'), col('.')-2, 2 )
103 return delimitMate#IsEmptyPair( cur )
104endfunction "}}}
105
106function! delimitMate#WriteBefore(str) "{{{
107 let len = len(a:str)
108 let line = getline('.')
109 let col = col('.')-2
110 if col < 0
111 call setline('.',line[(col+len+1):])
112 else
113 call setline('.',line[:(col)].line[(col+len+1):])
114 endif
115 return a:str
116endfunction " }}}
117
118function! delimitMate#WriteAfter(str) "{{{
119 let len = len(a:str)
120 let line = getline('.')
121 let col = col('.')-2
122 if (col) < 0
123 call setline('.',a:str.line)
124 else
125 call setline('.',line[:(col)].a:str.line[(col+len):])
126 endif
127 return ''
128endfunction " }}}
129
130function! delimitMate#GetSyntaxRegion(line, col) "{{{
131 return synIDattr(synIDtrans(synID(a:line, a:col, 1)), 'name')
132endfunction " }}}
133
134function! delimitMate#GetCurrentSyntaxRegion() "{{{
135 let col = col('.')
136 if col == col('$')
137 let col = col - 1
138 endif
139 return delimitMate#GetSyntaxRegion(line('.'), col)
140endfunction " }}}
141
142function! delimitMate#GetCurrentSyntaxRegionIf(char) "{{{
143 let col = col('.')
144 let origin_line = getline('.')
145 let changed_line = strpart(origin_line, 0, col - 1) . a:char . strpart(origin_line, col - 1)
146 call setline('.', changed_line)
147 let region = delimitMate#GetSyntaxRegion(line('.'), col)
148 call setline('.', origin_line)
149 return region
150endfunction "}}}
151
152function! delimitMate#IsForbidden(char) "{{{
153 if b:_l_delimitMate_excluded_regions_enabled == 0
154 return 0
155 endif
156 "let result = index(b:_l_delimitMate_excluded_regions_list, delimitMate#GetCurrentSyntaxRegion()) >= 0
157 if index(b:_l_delimitMate_excluded_regions_list, delimitMate#GetCurrentSyntaxRegion()) >= 0
158 "echom "Forbidden 1!"
159 return 1
160 endif
161 let region = delimitMate#GetCurrentSyntaxRegionIf(a:char)
162 "let result = index(b:_l_delimitMate_excluded_regions_list, region) >= 0
163 "return result || region == 'Comment'
164 "echom "Forbidden 2!"
165 return index(b:_l_delimitMate_excluded_regions_list, region) >= 0
166endfunction "}}}
167
168function! delimitMate#FlushBuffer() " {{{
169 let b:_l_delimitMate_buffer = []
170 return ''
171endfunction " }}}
172
173function! delimitMate#BalancedParens(char) "{{{
174 " Returns:
175 " = 0 => Parens balanced.
176 " > 0 => More opening parens.
177 " < 0 => More closing parens.
178
179 let line = getline('.')
180 let col = col('.') - 2
181 let col = col >= 0 ? col : 0
182 let list = split(line, '\zs')
183 let left = b:_l_delimitMate_left_delims[index(b:_l_delimitMate_right_delims, a:char)]
184 let right = a:char
185 let opening = 0
186 let closing = 0
187
188 " If the cursor is not at the beginning, count what's behind it.
189 if col > 0
190 " Find the first opening paren:
191 let start = index(list, left)
192 " Must be before cursor:
193 let start = start < col ? start : col - 1
194 " Now count from the first opening until the cursor, this will prevent
195 " extra closing parens from being counted.
196 let opening = count(list[start : col - 1], left)
197 let closing = count(list[start : col - 1], right)
198 " I don't care if there are more closing parens than opening parens.
199 let closing = closing > opening ? opening : closing
200 endif
201
202 " Evaluate parens from the cursor to the end:
203 let opening += count(list[col :], left)
204 let closing += count(list[col :], right)
205
206 "echom "–––––––––"
207 "echom line
208 "echom col
209 ""echom left.":".a:char
210 "echom string(list)
211 "echom string(list[start : col - 1]) . " : " . string(list[col :])
212 "echom opening . " - " . closing . " = " . (opening - closing)
213
214 " Return the found balance:
215 return opening - closing
216endfunction "}}}
217
218function! delimitMate#RmBuffer(num) " {{{
219 if len(b:_l_delimitMate_buffer) > 0
220 call remove(b:_l_delimitMate_buffer, 0, (a:num-1))
221 endif
222 return ""
223endfunction " }}}
224
225" }}}
226
227" Doers {{{
228function! delimitMate#SkipDelim(char) "{{{
229 if delimitMate#IsForbidden(a:char)
230 return a:char
231 endif
232 let col = col('.') - 1
233 let line = getline('.')
234 if col > 0
235 let cur = line[col]
236 let pre = line[col-1]
237 else
238 let cur = line[col]
239 let pre = ""
240 endif
241 if pre == "\\"
242 " Escaped character
243 return a:char
244 elseif cur == a:char
245 " Exit pair
246 "return delimitMate#WriteBefore(a:char)
247 return a:char . delimitMate#Del()
248 elseif delimitMate#IsEmptyPair( pre . a:char )
249 " Add closing delimiter and jump back to the middle.
250 call insert(b:_l_delimitMate_buffer, a:char)
251 return delimitMate#WriteAfter(a:char)
252 else
253 " Nothing special here, return the same character.
254 return a:char
255 endif
256endfunction "}}}
257
258function! delimitMate#ParenDelim(char) " {{{
259 if delimitMate#IsForbidden(a:char)
260 return ''
261 endif
262 " Try to balance matchpairs
263 if b:_l_delimitMate_balance_matchpairs &&
264 \ delimitMate#BalancedParens(a:char) <= 0
265 return ''
266 endif
267 let line = getline('.')
268 let col = col('.')-2
269 let left = b:_l_delimitMate_left_delims[index(b:_l_delimitMate_right_delims,a:char)]
270 let smart_matchpairs = substitute(b:_l_delimitMate_smart_matchpairs, '\\!', left, 'g')
271 let smart_matchpairs = substitute(smart_matchpairs, '\\#', a:char, 'g')
272 "echom left.':'.smart_matchpairs . ':' . matchstr(line[col+1], smart_matchpairs)
273 if b:_l_delimitMate_smart_matchpairs != '' &&
274 \ line[col+1:] =~ smart_matchpairs
275 return ''
276 elseif (col) < 0
277 call setline('.',a:char.line)
278 call insert(b:_l_delimitMate_buffer, a:char)
279 else
280 "echom string(col).':'.line[:(col)].'|'.line[(col+1):]
281 call setline('.',line[:(col)].a:char.line[(col+1):])
282 call insert(b:_l_delimitMate_buffer, a:char)
283 endif
284 return ''
285endfunction " }}}
286
287function! delimitMate#QuoteDelim(char) "{{{
288 if delimitMate#IsForbidden(a:char)
289 return a:char
290 endif
291 let line = getline('.')
292 let col = col('.') - 2
293 if line[col] == "\\"
294 " Seems like a escaped character, insert one quotation mark.
295 return a:char
296 elseif line[col + 1] == a:char &&
297 \ index(b:_l_delimitMate_nesting_quotes, a:char) < 0
298 " Get out of the string.
299 return a:char . delimitMate#Del()
300 elseif (line[col] =~ '\w' && a:char == "'") ||
301 \ (b:_l_delimitMate_smart_quotes &&
302 \ (line[col] =~ '\w' ||
303 \ line[col + 1] =~ '\w'))
304 " Seems like an apostrophe or a smart quote case, insert a single quote.
305 return a:char
306 elseif (line[col] == a:char && line[col + 1 ] != a:char) && b:_l_delimitMate_smart_quotes
307 " Seems like we have an unbalanced quote, insert one quotation mark and jump to the middle.
308 call insert(b:_l_delimitMate_buffer, a:char)
309 return delimitMate#WriteAfter(a:char)
310 else
311 " Insert a pair and jump to the middle.
312 call insert(b:_l_delimitMate_buffer, a:char)
313 call delimitMate#WriteAfter(a:char)
314 return a:char
315 endif
316endfunction "}}}
317
318function! delimitMate#JumpOut(char) "{{{
319 if delimitMate#IsForbidden(a:char)
320 return a:char
321 endif
322 let line = getline('.')
323 let col = col('.')-2
324 if line[col+1] == a:char
325 return a:char . delimitMate#Del()
326 else
327 return a:char
328 endif
329endfunction " }}}
330
331function! delimitMate#JumpAny(key) " {{{
332 if delimitMate#IsForbidden('')
333 return a:key
334 endif
335 if !delimitMate#ShouldJump()
336 return a:key
337 endif
338 " Let's get the character on the right.
339 let char = getline('.')[col('.')-1]
340 if char == " "
341 " Space expansion.
342 "let char = char . getline('.')[col('.')] . delimitMate#Del()
343 return char . getline('.')[col('.')] . delimitMate#Del() . delimitMate#Del()
344 "call delimitMate#RmBuffer(1)
345 elseif char == ""
346 " CR expansion.
347 "let char = "\<CR>" . getline(line('.') + 1)[0] . "\<Del>"
348 let b:_l_delimitMate_buffer = []
349 return "\<CR>" . getline(line('.') + 1)[0] . "\<Del>"
350 else
351 "call delimitMate#RmBuffer(1)
352 return char . delimitMate#Del()
353 endif
354endfunction " delimitMate#JumpAny() }}}
355
356function! delimitMate#JumpMany() " {{{
357 let line = getline('.')[col('.') - 1 : ]
358 let len = len(line)
359 let rights = ""
360 let found = 0
361 let i = 0
362 while i < len
363 let char = line[i]
364 if index(b:_l_delimitMate_quotes_list, char) >= 0 ||
365 \ index(b:_l_delimitMate_right_delims, char) >= 0
366 let rights .= "\<Right>"
367 let found = 1
368 elseif found == 0
369 let rights .= "\<Right>"
370 else
371 break
372 endif
373 let i += 1
374 endwhile
375 if found == 1
376 return rights
377 else
378 return ''
379 endif
380endfunction " delimitMate#JumpMany() }}}
381
382function! delimitMate#ExpandReturn() "{{{
383 if delimitMate#IsForbidden("")
384 return "\<CR>"
385 endif
386 if delimitMate#WithinEmptyPair()
387 " Expand:
388 call delimitMate#FlushBuffer()
389 "return "\<Esc>a\<CR>x\<CR>\<Esc>k$\"_xa"
390 return "\<CR>\<UP>\<Esc>o"
391 else
392 return "\<CR>"
393 endif
394endfunction "}}}
395
396function! delimitMate#ExpandSpace() "{{{
397 if delimitMate#IsForbidden("\<Space>")
398 return "\<Space>"
399 endif
400 if delimitMate#WithinEmptyPair()
401 " Expand:
402 call insert(b:_l_delimitMate_buffer, 's')
403 return delimitMate#WriteAfter(' ') . "\<Space>"
404 else
405 return "\<Space>"
406 endif
407endfunction "}}}
408
409function! delimitMate#BS() " {{{
410 if delimitMate#IsForbidden("")
411 return "\<BS>"
412 endif
413 if delimitMate#WithinEmptyPair()
414 "call delimitMate#RmBuffer(1)
415 return "\<BS>" . delimitMate#Del()
416" return "\<Right>\<BS>\<BS>"
417 elseif delimitMate#IsSpaceExpansion()
418 "call delimitMate#RmBuffer(1)
419 return "\<BS>" . delimitMate#Del()
420 elseif delimitMate#IsCRExpansion()
421 return "\<BS>\<Del>"
422 else
423 return "\<BS>"
424 endif
425endfunction " }}} delimitMate#BS()
426
427function! delimitMate#Del() " {{{
428 if len(b:_l_delimitMate_buffer) > 0
429 let line = getline('.')
430 let col = col('.') - 2
431 call delimitMate#RmBuffer(1)
432 call setline('.', line[:col] . line[col+2:])
433 return ''
434 else
435 return "\<Del>"
436 endif
437endfunction " }}}
438
439function! delimitMate#Finish(move_back) " {{{
440 let len = len(b:_l_delimitMate_buffer)
441 if len > 0
442 let buffer = join(b:_l_delimitMate_buffer, '')
443 let len2 = len(buffer)
444 " Reset buffer:
445 let b:_l_delimitMate_buffer = []
446 let line = getline('.')
447 let col = col('.') -2
448 "echom 'col: ' . col . '-' . line[:col] . "|" . line[col+len+1:] . '%' . buffer
449 if col < 0
450 call setline('.', line[col+len2+1:])
451 else
452 call setline('.', line[:col] . line[col+len2+1:])
453 endif
454 let i = 1
455 let lefts = ""
456 while i <= len && a:move_back
457 let lefts = lefts . "\<Left>"
458 let i += 1
459 endwhile
460 return substitute(buffer, "s", "\<Space>", 'g') . lefts
461 endif
462 return ''
463endfunction " }}}
464
465" }}}
466
467" Tools: {{{
468function! delimitMate#TestMappings() "{{{
469 let options = sort(keys(delimitMate#OptionsList()))
470 let optoutput = ['delimitMate Report', '==================', '', '* Options: ( ) default, (g) global, (b) buffer','']
471 for option in options
472 exec 'call add(optoutput, ''('.(exists('b:delimitMate_'.option) ? 'b' : exists('g:delimitMate_'.option) ? 'g' : ' ').') delimitMate_''.option.'' = ''.string(b:_l_delimitMate_'.option.'))'
473 endfor
474 call append(line('$'), optoutput + ['--------------------',''])
475
476 " Check if mappings were set. {{{
477 let imaps = b:_l_delimitMate_right_delims
478 let imaps = imaps + ( b:_l_delimitMate_autoclose ? b:_l_delimitMate_left_delims : [] )
479 let imaps = imaps +
480 \ b:_l_delimitMate_quotes_list +
481 \ b:_l_delimitMate_apostrophes_list +
482 \ ['<BS>', '<S-BS>', '<Del>', '<S-Tab>', '<Esc>'] +
483 \ ['<Up>', '<Down>', '<Left>', '<Right>', '<LeftMouse>', '<RightMouse>'] +
484 \ ['<Home>', '<End>', '<PageUp>', '<PageDown>', '<S-Down>', '<S-Up>', '<C-G>g']
485 let imaps = imaps + ( b:_l_delimitMate_expand_cr ? ['<CR>'] : [] )
486 let imaps = imaps + ( b:_l_delimitMate_expand_space ? ['<Space>'] : [] )
487
488 let vmaps =
489 \ b:_l_delimitMate_right_delims +
490 \ b:_l_delimitMate_left_delims +
491 \ b:_l_delimitMate_quotes_list
492
493 let ibroken = []
494 for map in imaps
495 if maparg(map, "i") !~? 'delimitMate'
496 let output = ''
497 if map == '|'
498 let map = '<Bar>'
499 endif
500 redir => output | execute "verbose imap ".map | redir END
501 let ibroken = ibroken + [map.": is not set:"] + split(output, '\n')
502 endif
503 endfor
504
505 unlet! output
506 if ibroken == []
507 let output = ['* Mappings:', '', 'All mappings were set-up.', '--------------------', '', '']
508 else
509 let output = ['* Mappings:', ''] + ibroken + ['--------------------', '']
510 endif
511 call append('$', output+['* Showcase:', ''])
512 " }}}
513 if b:_l_delimitMate_autoclose
514 " {{{
515 for i in range(len(b:_l_delimitMate_left_delims))
516 exec "normal Go0\<C-D>Open: " . b:_l_delimitMate_left_delims[i]. "|"
517 exec "normal o0\<C-D>Delete: " . b:_l_delimitMate_left_delims[i] . "\<BS>|"
518 exec "normal o0\<C-D>Exit: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "|"
519 if b:_l_delimitMate_expand_space == 1
520 exec "normal o0\<C-D>Space: " . b:_l_delimitMate_left_delims[i] . " |"
521 exec "normal o0\<C-D>Delete space: " . b:_l_delimitMate_left_delims[i] . " \<BS>|"
522 endif
523 if b:_l_delimitMate_expand_cr == 1
524 exec "normal o0\<C-D>Car return: " . b:_l_delimitMate_left_delims[i] . "\<CR>|"
525 exec "normal Go0\<C-D>Delete car return: " . b:_l_delimitMate_left_delims[i] . "\<CR>0\<C-D>\<BS>|"
526 endif
527 call append(line('$'), '')
528 endfor
529 for i in range(len(b:_l_delimitMate_quotes_list))
530 exec "normal Go0\<C-D>Open: " . b:_l_delimitMate_quotes_list[i] . "|"
531 exec "normal o0\<C-D>Delete: " . b:_l_delimitMate_quotes_list[i] . "\<BS>|"
532 exec "normal o0\<C-D>Exit: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "|"
533 if b:_l_delimitMate_expand_space == 1
534 exec "normal o0\<C-D>Space: " . b:_l_delimitMate_quotes_list[i] . " |"
535 exec "normal o0\<C-D>Delete space: " . b:_l_delimitMate_quotes_list[i] . " \<BS>|"
536 endif
537 if b:_l_delimitMate_expand_cr == 1
538 exec "normal o0\<C-D>Car return: " . b:_l_delimitMate_quotes_list[i] . "\<CR>|"
539 exec "normal Go0\<C-D>Delete car return: " . b:_l_delimitMate_quotes_list[i] . "\<CR>\<BS>|"
540 endif
541 call append(line('$'), '')
542 endfor
543 "}}}
544 else
545 "{{{
546 for i in range(len(b:_l_delimitMate_left_delims))
547 exec "normal GoOpen & close: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "|"
548 exec "normal oDelete: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "\<BS>|"
549 exec "normal oExit: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . b:_l_delimitMate_right_delims[i] . "|"
550 if b:_l_delimitMate_expand_space == 1
551 exec "normal oSpace: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . " |"
552 exec "normal oDelete space: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . " \<BS>|"
553 endif
554 if b:_l_delimitMate_expand_cr == 1
555 exec "normal oCar return: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "\<CR>|"
556 exec "normal GoDelete car return: " . b:_l_delimitMate_left_delims[i] . b:_l_delimitMate_right_delims[i] . "\<CR>\<BS>|"
557 endif
558 call append(line('$'), '')
559 endfor
560 for i in range(len(b:_l_delimitMate_quotes_list))
561 exec "normal GoOpen & close: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "|"
562 exec "normal oDelete: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "\<BS>|"
563 exec "normal oExit: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "|"
564 if b:_l_delimitMate_expand_space == 1
565 exec "normal oSpace: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . " |"
566 exec "normal oDelete space: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . " \<BS>|"
567 endif
568 if b:_l_delimitMate_expand_cr == 1
569 exec "normal oCar return: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "\<CR>|"
570 exec "normal GoDelete car return: " . b:_l_delimitMate_quotes_list[i] . b:_l_delimitMate_quotes_list[i] . "\<CR>\<BS>|"
571 endif
572 call append(line('$'), '')
573 endfor
574 endif "}}}
575 redir => setoptions | set | filetype | redir END
576 call append(line('$'), split(setoptions,"\n")
577 \ + ['--------------------'])
578 setlocal nowrap
579endfunction "}}}
580
581function! delimitMate#OptionsList() "{{{
582 return {'autoclose' : 1,'matchpairs': &matchpairs, 'quotes' : '" '' `', 'nesting_quotes' : [], 'expand_cr' : 0, 'expand_space' : 0, 'smart_quotes' : 1, 'smart_matchpairs' : '\w', 'balance_matchpairs' : 0, 'excluded_regions' : 'Comment', 'excluded_ft' : '', 'apostrophes' : ''}
583endfunction " delimitMate#OptionsList }}}
584"}}}
585
586" vim:foldmethod=marker:foldcolumn=4