1 " File: autoload/delimitMate.vim
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 " ============================================================================
11 let delimitMate_loaded = 1
13 function! delimitMate#ShouldJump() "{{{
14 " Returns 1 if the next character is a closing delimiter.
17 let char = getline('.')[col - 1]
19 " Closing delimiter on the right.
20 for cdel in b:_l_delimitMate_right_delims + b:_l_delimitMate_quotes_list
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
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
49 function! delimitMate#IsEmptyPair(str) "{{{
50 for pair in b:_l_delimitMate_matchpairs_list
51 if a:str == join( split( pair, ':' ),'' )
55 for quote in b:_l_delimitMate_quotes_list
56 if a:str == quote . quote
63 function! 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) &&
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) &&
78 endfunction " }}} delimitMate#IsCRExpansion()
80 function! delimitMate#IsSpaceExpansion() " {{{
81 let line = getline('.')
84 let pchar = line[col - 1]
85 let nchar = line[col + 2]
86 let isSpaces = (line[col] == line[col+1] && line[col] == " ")
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) &&
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) &&
99 endfunction " }}} IsSpaceExpansion()
101 function! delimitMate#WithinEmptyPair() "{{{
102 let cur = strpart( getline('.'), col('.')-2, 2 )
103 return delimitMate#IsEmptyPair( cur )
106 function! delimitMate#WriteBefore(str) "{{{
108 let line = getline('.')
111 call setline('.',line[(col+len+1):])
113 call setline('.',line[:(col)].line[(col+len+1):])
118 function! delimitMate#WriteAfter(str) "{{{
120 let line = getline('.')
123 call setline('.',a:str.line)
125 call setline('.',line[:(col)].a:str.line[(col+len):])
130 function! delimitMate#GetSyntaxRegion(line, col) "{{{
131 return synIDattr(synIDtrans(synID(a:line, a:col, 1)), 'name')
134 function! delimitMate#GetCurrentSyntaxRegion() "{{{
139 return delimitMate#GetSyntaxRegion(line('.'), col)
142 function! delimitMate#GetCurrentSyntaxRegionIf(char) "{{{
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)
152 function! delimitMate#IsForbidden(char) "{{{
153 if b:_l_delimitMate_excluded_regions_enabled == 0
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!"
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
168 function! delimitMate#FlushBuffer() " {{{
169 let b:_l_delimitMate_buffer = []
173 function! delimitMate#BalancedParens(char) "{{{
175 " = 0 => Parens balanced.
176 " > 0 => More opening parens.
177 " < 0 => More closing parens.
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)]
188 " If the cursor is not at the beginning, count what's behind it.
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
202 " Evaluate parens from the cursor to the end:
203 let opening += count(list[col :], left)
204 let closing += count(list[col :], right)
209 ""echom left.":".a:char
211 "echom string(list[start : col - 1]) . " : " . string(list[col :])
212 "echom opening . " - " . closing . " = " . (opening - closing)
214 " Return the found balance:
215 return opening - closing
218 function! delimitMate#RmBuffer(num) " {{{
219 if len(b:_l_delimitMate_buffer) > 0
220 call remove(b:_l_delimitMate_buffer, 0, (a:num-1))
228 function! delimitMate#SkipDelim(char) "{{{
229 if delimitMate#IsForbidden(a:char)
232 let col = col('.') - 1
233 let line = getline('.')
236 let pre = line[col-1]
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)
253 " Nothing special here, return the same character.
258 function! delimitMate#ParenDelim(char) " {{{
259 if delimitMate#IsForbidden(a:char)
262 " Try to balance matchpairs
263 if b:_l_delimitMate_balance_matchpairs &&
264 \ delimitMate#BalancedParens(a:char) <= 0
267 let line = getline('.')
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
277 call setline('.',a:char.line)
278 call insert(b:_l_delimitMate_buffer, a:char)
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)
287 function! delimitMate#QuoteDelim(char) "{{{
288 if delimitMate#IsForbidden(a:char)
291 let line = getline('.')
292 let col = col('.') - 2
294 " Seems like a escaped character, insert one quotation mark.
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.
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)
311 " Insert a pair and jump to the middle.
312 call insert(b:_l_delimitMate_buffer, a:char)
313 call delimitMate#WriteAfter(a:char)
318 function! delimitMate#JumpOut(char) "{{{
319 if delimitMate#IsForbidden(a:char)
322 let line = getline('.')
324 if line[col+1] == a:char
325 return a:char . delimitMate#Del()
331 function! delimitMate#JumpAny(key) " {{{
332 if delimitMate#IsForbidden('')
335 if !delimitMate#ShouldJump()
338 " Let's get the character on the right.
339 let char = getline('.')[col('.')-1]
342 "let char = char . getline('.')[col('.')] . delimitMate#Del()
343 return char . getline('.')[col('.')] . delimitMate#Del() . delimitMate#Del()
344 "call delimitMate#RmBuffer(1)
347 "let char = "\<CR>" . getline(line('.') + 1)[0] . "\<Del>"
348 let b:_l_delimitMate_buffer = []
349 return "\<CR>" . getline(line('.') + 1)[0] . "\<Del>"
351 "call delimitMate#RmBuffer(1)
352 return char . delimitMate#Del()
354 endfunction " delimitMate#JumpAny() }}}
356 function! delimitMate#JumpMany() " {{{
357 let line = getline('.')[col('.') - 1 : ]
364 if index(b:_l_delimitMate_quotes_list, char) >= 0 ||
365 \ index(b:_l_delimitMate_right_delims, char) >= 0
366 let rights .= "\<Right>"
369 let rights .= "\<Right>"
380 endfunction " delimitMate#JumpMany() }}}
382 function! delimitMate#ExpandReturn() "{{{
383 if delimitMate#IsForbidden("")
386 if delimitMate#WithinEmptyPair()
388 call delimitMate#FlushBuffer()
389 "return "\<Esc>a\<CR>x\<CR>\<Esc>k$\"_xa"
390 return "\<CR>\<UP>\<Esc>o"
396 function! delimitMate#ExpandSpace() "{{{
397 if delimitMate#IsForbidden("\<Space>")
400 if delimitMate#WithinEmptyPair()
402 call insert(b:_l_delimitMate_buffer, 's')
403 return delimitMate#WriteAfter(' ') . "\<Space>"
409 function! delimitMate#BS() " {{{
410 if delimitMate#IsForbidden("")
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()
425 endfunction " }}} delimitMate#BS()
427 function! 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:])
439 function! delimitMate#Finish(move_back) " {{{
440 let len = len(b:_l_delimitMate_buffer)
442 let buffer = join(b:_l_delimitMate_buffer, '')
443 let len2 = len(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
450 call setline('.', line[col+len2+1:])
452 call setline('.', line[:col] . line[col+len2+1:])
456 while i <= len && a:move_back
457 let lefts = lefts . "\<Left>"
460 return substitute(buffer, "s", "\<Space>", 'g') . lefts
468 function! 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.'))'
474 call append(line('$'), optoutput + ['--------------------',''])
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 : [] )
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>'] : [] )
489 \ b:_l_delimitMate_right_delims +
490 \ b:_l_delimitMate_left_delims +
491 \ b:_l_delimitMate_quotes_list
495 if maparg(map, "i") !~? 'delimitMate'
500 redir => output | execute "verbose imap ".map | redir END
501 let ibroken = ibroken + [map.": is not set:"] + split(output, '\n')
507 let output = ['* Mappings:', '', 'All mappings were set-up.', '--------------------', '', '']
509 let output = ['* Mappings:', ''] + ibroken + ['--------------------', '']
511 call append('$', output+['* Showcase:', ''])
513 if b:_l_delimitMate_autoclose
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>|"
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>|"
527 call append(line('$'), '')
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>|"
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>|"
541 call append(line('$'), '')
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>|"
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>|"
558 call append(line('$'), '')
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>|"
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>|"
572 call append(line('$'), '')
575 redir => setoptions | set | filetype | redir END
576 call append(line('$'), split(setoptions,"\n")
577 \ + ['--------------------'])
581 function! 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' : ''}
583 endfunction " delimitMate#OptionsList }}}
586 " vim:foldmethod=marker:foldcolumn=4