From 6f1a9d00af6502383236018bbb0ab26270474067 Mon Sep 17 00:00:00 2001 From: Israel Chauca Fuentes Date: Sat, 4 Feb 2017 20:12:21 -0500 Subject: [PATCH] Implement autoclosingpairs. --- autoload/delimitMate.vim | 798 +++++++--------------------------- plugin/delimitMate.vim | 398 +---------------- test/_setup.vim | 27 +- test/autoclose_matchpairs.vim | 95 ++-- 4 files changed, 242 insertions(+), 1076 deletions(-) diff --git a/autoload/delimitMate.vim b/autoload/delimitMate.vim index 3d9a720..35db4e4 100644 --- a/autoload/delimitMate.vim +++ b/autoload/delimitMate.vim @@ -1,667 +1,199 @@ -" File: autoload/delimitMate.vim -" Version: 2.7 -" Modified: 2013-07-15 -" Description: This plugin provides auto-completion for quotes, parens, etc. -" Maintainer: Israel Chauca F. -" Manual: Read ":help delimitMate". -" ============================================================================ - "let delimitMate_loaded = 1 -if !exists('s:options') - let s:options = {} -endif +let s:defaults = {} +let s:defaults.delimitMate_pairs = ['()', '[]', '{}'] +let s:defaults.delimitMate_quotes = ['"', "'", '`'] +let s:defaults.delimitMate_enabled = 1 +let s:defaults.delimitMate_autoclose = 1 +let s:defaults.delimitMate_expand_space = 0 -function! s:set(name, value, ...) "{{{ - let scope = a:0 ? a:1 : 's' - let bufnr = bufnr('%') - if !exists('s:options[bufnr]') - let s:options[bufnr] = {} - endif - if scope == 's' - let name = 's:options.' . bufnr . '.' . a:name +function! s:defaults.consolidate() + let g = filter(copy(g:), 'v:key =~# "^delimitMate_"') + let b = filter(copy(b:), 'v:key =~# "^delimitMate_"') + call extend(g, b, 'force') + call extend(g, self, 'keep') + let short_options = {} + call map(g, 'extend(short_options, {substitute(v:key, "^delimitMate_", "", ""): v:val}, "force")') + return short_options +endfunction + +let s:info = {} +let s:info.char = '' +let s:info.template = {} + +function! s:info.template.is_escaped(...) + let str = a:0 ? a1 : self.behind + return len(matchstr(str, '\\*$')) % 2 +endfunction + +function! s:option(name, ...) + if a:0 + let opt = get(a:1, 'delimitMate_' . a:name, '') else - let name = scope . ':delimitMate_' . a:name - if exists('name') - exec 'unlet! ' . name - endif + let opt = get(b:, 'delimitMate_' . a:name, + \ get(g:, 'delimitMate_' . a:name, + \ get(s:defaults, 'delimitMate_' . a:name, ''))) endif - exec 'let ' . name . ' = a:value' -endfunction "}}} - -function! s:get(name, ...) "{{{ - if a:0 == 2 - return deepcopy(get(a:2, 'delimitMate_' . a:name, a:1)) - elseif a:0 == 1 - let bufoptions = get(s:options, bufnr('%'), {}) - return deepcopy(get(bufoptions, a:name, a:1)) - else - return deepcopy(eval('s:options.' . bufnr('%') . '.' . a:name)) + if type(opt) == v:t_list + return copy(opt) endif -endfunction "}}} + return opt +endfunction -function! s:exists(name, ...) "{{{ - let scope = a:0 ? a:1 : 's' - if scope == 's' - let bufnr = bufnr('%') - let name = 'options.' . bufnr . '.' . a:name - else - let name = 'delimitMate_' . a:name +function! delimitMate#ex_cmd(global, action) + let scope = a:global ? g: : b: endif - return exists(scope . ':' . name) -endfunction "}}} - -function! s:is_jump(...) "{{{ - " Returns 1 if the next character is a closing delimiter. - let char = s:get_char(0) - let list = s:get('right_delims') + s:get('quotes_list') - - " Closing delimiter on the right. - if (!a:0 && index(list, char) > -1) - \ || (a:0 && char == a:1) - return 1 + if a:action ==# 'enable' + let scope.delimitMate_enabled = 1 + elseif a:action ==# 'disable' + let scope.delimitMate_enabled = 0 + elseif a:action ==# 'switch' + let scope.delimitMate_enabled = !s:option('enabled') endif +endfunction - " Closing delimiter with space expansion. - let nchar = s:get_char(1) - if !a:0 && s:get('expand_space') && char == " " - if index(list, nchar) > -1 - return 2 - endif - elseif a:0 && s:get('expand_space') && nchar == a:1 && char == ' ' - return 3 - endif - - if !s:get('jump_expansion') +function! delimitMate#InsertCharPre(str) + if s:option('disabled') + echom 11 return 0 endif + return map(split(a:str, '\zs'), 's:handle_vchar(v:val)') +endfunction - " Closing delimiter with CR expansion. - let uchar = matchstr(getline(line('.') + 1), '^\s*\zs\S') - if !a:0 && s:get('expand_cr') && char == "" - if index(list, uchar) > -1 - return 4 - endif - elseif a:0 && s:get('expand_cr') && uchar == a:1 - return 5 - endif - return 0 -endfunction "}}} - -function! s:rquote(char) "{{{ - let pos = matchstr(getline('.')[col('.') : ], escape(a:char, '[]*.^$\'), 1) - let i = 0 - while s:get_char(i) ==# a:char - let i += 1 - endwhile - return i -endfunction "}}} - -function! s:lquote(char) "{{{ - let i = 0 - while s:get_char(i - 1) ==# a:char - let i -= 1 - endwhile - return i * -1 -endfunction "}}} - -function! s:get_char(...) "{{{ - let idx = col('.') - 1 - if !a:0 || (a:0 && a:1 >= 0) - " Get char from cursor. - let line = getline('.')[idx :] - let pos = a:0 ? a:1 : 0 - return matchstr(line, '^'.repeat('.', pos).'\zs.') - endif - " Get char behind cursor. - let line = getline('.')[: idx - 1] - let pos = 0 - (1 + a:1) - return matchstr(line, '.\ze'.repeat('.', pos).'$') -endfunction "s:get_char }}} - -function! s:is_cr_expansion(...) " {{{ - let nchar = getline(line('.')-1)[-1:] - let schar = matchstr(getline(line('.')+1), '^\s*\zs\S') - let isEmpty = a:0 ? getline('.') =~ '^\s*$' : empty(getline('.')) - if index(s:get('left_delims'), nchar) > -1 - \ && index(s:get('left_delims'), nchar) - \ == index(s:get('right_delims'), schar) - \ && isEmpty - return 1 - elseif index(s:get('quotes_list'), nchar) > -1 - \ && index(s:get('quotes_list'), nchar) - \ == index(s:get('quotes_list'), schar) - \ && isEmpty - return 1 +function! s:handle_vchar(str) + echom 'ICP (' . a:str . '): ' . get(s:info, 'cur', {'text': ''}).text + let s:info.char = a:str + let opts = s:defaults.consolidate() + if s:info.cur.is_escaped() + echom 12 + return + elseif a:str == ' ' + echom 13 + let [keys, append2vchar] = s:keys4space(s:info, opts) + let v:char .= append2vchar + elseif a:str == "\" + echom 14 + return 0 + elseif !empty(filter(copy(opts.quotes), 'v:val ==# a:str')) + echom 15 + return 0 + elseif !empty(filter(copy(opts.pairs), 'strcharpart(v:val, 0, 1) ==# a:str')) + echom 16 + let pair = get(filter(copy(opts.pairs), 'strcharpart(v:val, 0, 1) ==# a:str'), 0, '') + let [keys, append2vchar] = s:keys4left(a:str, pair, s:info, opts) + let v:char .= append2vchar + "echom strtrans(keys) + "echom string(pair) + elseif !empty(filter(copy(opts.pairs), 'strcharpart(v:val, 1, 1) ==# a:str')) + let pair = get(filter(copy(opts.pairs), 'strcharpart(v:val, 1, 1) ==# a:str'), 0, '') + let keys = s:keys4right(a:str, pair, s:info, opts) + echom 17 + echom keys else + echom 18 return 0 endif -endfunction " }}} s:is_cr_expansion() + return feedkeys(keys, 'mt') +endfunction -function! s:is_space_expansion() " {{{ - if col('.') > 2 - let pchar = s:get_char(-2) - let nchar = s:get_char(1) - let isSpaces = - \ (s:get_char(-1) - \ == s:get_char(0) - \ && s:get_char(-1) == " ") +function! s:keys4space(info, opts) + if !a:opts.expand_space && !empty(filter(copy(a:opts.pairs), 'v:val ==# a:info.cur.around')) + return ['', ''] + endif + if a:opts.expand_space + return ["\U\", ' '] +endfunction - if index(s:get('left_delims'), pchar) > -1 && - \ index(s:get('left_delims'), pchar) - \ == index(s:get('right_delims'), nchar) && - \ isSpaces - return 1 - elseif index(s:get('quotes_list'), pchar) > -1 && - \ index(s:get('quotes_list'), pchar) - \ == index(s:get('quotes_list'), nchar) && - \ isSpaces - return 1 +function! s:keys4left(char, pair, info, opts) + if a:opts.autoclose + return [strcharpart(a:pair, 1, 1) . "\U\", ''] + endif + return ['', ''] +endfunction + +function! s:keys4right(char, pair, info, opts) + if !a:opts.autoclose + if s:info.cur.around == a:pair + return "\" + elseif s:info.cur.p_char == strcharpart(a:pair, 0, 1) + return "\U\" endif + return "" endif - return 0 -endfunction " }}} IsSpaceExpansion() - -function! s:is_empty_matchpair() "{{{ - " get char before the cursor. - let open = s:get_char(-1) - let idx = index(s:get('left_delims'), open) - if idx == -1 - return 0 + if strcharpart(a:info.cur.text[a:info.cur.col - 1 :], 0, 1) ==# a:char + echom 41 + return "\" endif - let close = get(s:get('right_delims'), idx, '') - return close ==# s:get_char(0) -endfunction "}}} + return '' +endfunction -function! s:is_empty_quotes() "{{{ - " get char before the cursor. - let quote = s:get_char(-1) - let idx = index(s:get('quotes_list'), quote) - if idx == -1 - return 0 - endif - return quote ==# s:get_char(0) -endfunction "}}} - -function! s:cursor_idx() "{{{ - let idx = len(split(getline('.')[: col('.') - 1], '\zs')) - 1 - return idx -endfunction "delimitMate#CursorCol }}} - -function! s:get_syn_name() "{{{ - let col = col('.') - if col == col('$') - let col = col - 1 - endif - return synIDattr(synIDtrans(synID(line('.'), col, 1)), 'name') -endfunction " }}} - -function! s:is_excluded_ft(ft) "{{{ - if !exists("g:delimitMate_excluded_ft") - return 0 - endif - return index(split(g:delimitMate_excluded_ft, ','), a:ft, 0, 1) >= 0 -endfunction "}}} - -function! s:is_forbidden(char) "{{{ - if s:is_excluded_ft(&filetype) - return 1 - endif - if !s:get('excluded_regions_enabled') - return 0 - endif - let region = s:get_syn_name() - return index(s:get('excluded_regions_list'), region) >= 0 -endfunction "}}} - -function! s:balance_matchpairs(char) "{{{ - " Returns: - " = 0 => Parens balanced. - " > 0 => More opening parens. - " < 0 => More closing parens. - - let line = getline('.') - let col = s:cursor_idx() - 1 - let col = col >= 0 ? col : 0 - let list = split(line, '\zs') - let left = s:get('left_delims')[index(s:get('right_delims'), a:char)] - let right = a:char - let opening = 0 - let closing = 0 - - " If the cursor is not at the beginning, count what's behind it. - if col > 0 - " Find the first opening paren: - let start = index(list, left) - " Must be before cursor: - let start = start < col ? start : col - 1 - " Now count from the first opening until the cursor, this will prevent - " extra closing parens from being counted. - let opening = count(list[start : col - 1], left) - let closing = count(list[start : col - 1], right) - " I don't care if there are more closing parens than opening parens. - let closing = closing > opening ? opening : closing - endif - - " Evaluate parens from the cursor to the end: - let opening += count(list[col :], left) - let closing += count(list[col :], right) - - " Return the found balance: - return opening - closing -endfunction "}}} - -function! s:is_smart_quote(char) "{{{ - " TODO: Allow using a:char in the pattern. - let tmp = s:get('smart_quotes') - if empty(tmp) - return 0 - endif - let regex = matchstr(tmp, '^!\?\zs.*') - " Flip matched value if regex starts with ! - let mod = tmp =~ '^!' ? [1, 0] : [0, 1] - let matched = search(regex, 'ncb', line('.')) > 0 - let noescaped = substitute(getline('.'), '\\.', '', 'g') - let odd = (count(split(noescaped, '\zs'), a:char) % 2) - let result = mod[matched] || odd - return result -endfunction "delimitMate#SmartQuote }}} - -function! delimitMate#Set(...) "{{{ - return call('s:set', a:000) -endfunction "}}} - -function! delimitMate#Get(...) "{{{ - return call('s:get', a:000) -endfunction "}}} - -function! delimitMate#ShouldJump(...) "{{{ - return call('s:is_jump', a:000) -endfunction "}}} - -function! delimitMate#IsEmptyPair(str) "{{{ - if strlen(substitute(a:str, ".", "x", "g")) != 2 - return 0 - endif - let idx = index(s:get('left_delims'), matchstr(a:str, '^.')) - if idx > -1 && - \ s:get('right_delims')[idx] == matchstr(a:str, '.$') - return 1 - endif - let idx = index(s:get('quotes_list'), matchstr(a:str, '^.')) - if idx > -1 && - \ s:get('quotes_list')[idx] == matchstr(a:str, '.$') - return 1 - endif - return 0 -endfunction "}}} - -function! delimitMate#WithinEmptyPair() "{{{ - " if cursor is at column 1 return 0 - if col('.') == 1 - return 0 - endif - " get char before the cursor. - let char1 = s:get_char(-1) - " get char under the cursor. - let char2 = s:get_char(0) - return delimitMate#IsEmptyPair( char1.char2 ) -endfunction "}}} - -function! delimitMate#SkipDelim(char) "{{{ - if s:is_forbidden(a:char) - return a:char - endif - let col = col('.') - 1 - let line = getline('.') - if col > 0 - let cur = s:get_char(0) - let pre = s:get_char(-1) +function! s:get_info(...) + if a:0 + let d = a:1 else - let cur = s:get_char(0) - let pre = "" + let d = {} + let d.text = getline('.') + let d.col = col('.') + let d.line = line('.') endif - if pre == "\\" - " Escaped character - return a:char - elseif cur == a:char - " Exit pair - return a:char . "\" - elseif delimitMate#IsEmptyPair( pre . a:char ) - " Add closing delimiter and jump back to the middle. - return a:char . s:joinUndo() . "\" - else - " Nothing special here, return the same character. - return a:char - endif -endfunction "}}} + let d.ahead = len(d.text) >= d.col ? d.text[d.col - 1 : ] : '' + let d.behind = d.col >= 2 ? d.text[: d.col - 2] : '' + let d.p_char = strcharpart(d.behind, strchars(d.behind) - 1, 1) + let d.n_char = strcharpart(d.ahead, 0, 1) + let d.around = d.p_char . d.n_char + call extend(d, s:info.template, 'keep') + echom string(d) + return d +endfunction -function! delimitMate#ParenDelim(right) " {{{ - let left = s:get('left_delims')[index(s:get('right_delims'),a:right)] - if s:is_forbidden(a:right) - return left - endif - " Try to balance matchpairs - if s:get('balance_matchpairs') && - \ s:balance_matchpairs(a:right) < 0 - return left - endif - let line = getline('.') - let col = col('.')-2 - if s:get('smart_matchpairs') != '' - let smart_matchpairs = substitute(s:get('smart_matchpairs'), '\\!', left, 'g') - let smart_matchpairs = substitute(smart_matchpairs, '\\#', a:right, 'g') - if line[col+1:] =~ smart_matchpairs - return left - endif - endif - if len(line) == (col + 1) && s:get('insert_eol_marker') == 1 - let tail = s:get('eol_marker') - else - let tail = '' - endif - return left . a:right . tail . repeat(s:joinUndo() . "\", len(split(tail, '\zs')) + 1) -endfunction " }}} +function! delimitMate#CursorMovedI(...) + let s:info.prev = s:info.cur + let s:info.cur = call('s:get_info', a:000) + echom 'INFO: ' . string(s:info) + echom 'CMI: ' . s:info.prev.text +endfunction -function! delimitMate#QuoteDelim(char) "{{{ - if s:is_forbidden(a:char) - return a:char - endif - let char_at = s:get_char(0) - let char_before = s:get_char(-1) - let nesting_on = index(s:get('nesting_quotes'), a:char) > -1 - let left_q = nesting_on ? s:lquote(a:char) : 0 - if nesting_on && left_q > 1 - " Nesting quotes. - let right_q = s:rquote(a:char) - let quotes = right_q > left_q + 1 ? 0 : left_q - right_q + 2 - let lefts = quotes - 1 - return repeat(a:char, quotes) . repeat(s:joinUndo() . "\", lefts) - elseif char_at == a:char - " Inside an empty pair, jump out - return a:char . "\" - elseif a:char == '"' && index(split(&ft, '\.'), "vim") != -1 && getline('.') =~ '^\s*$' - " If we are in a vim file and it looks like we're starting a comment, do - " not add a closing char. - return a:char - elseif s:is_smart_quote(a:char) - " Seems like a smart quote, insert a single char. - return a:char - elseif (char_before == a:char && char_at != a:char) - \ && !empty(s:get('smart_quotes')) - " Seems like we have an unbalanced quote, insert one quotation - " mark and jump to the middle. - return a:char . s:joinUndo() . "\" - else - " Insert a pair and jump to the middle. - let sufix = '' - if !empty(s:get('eol_marker')) && col('.') - 1 == len(getline('.')) - let idx = len(s:get('eol_marker')) * -1 - let marker = getline('.')[idx : ] - let has_marker = marker == s:get('eol_marker') - let sufix = !has_marker ? s:get('eol_marker') : '' - endif - return a:char . a:char . s:joinUndo() . "\" - endif -endfunction "}}} +function! delimitMate#InsertEnter(...) + let s:info.cur = call('s:get_info', a:000) + let s:info.prev = {} + echom 'IE: ' . s:info.cur.text +endfunction -function! delimitMate#JumpOut(char) "{{{ - if s:is_forbidden(a:char) - return a:char +function! delimitMate#TextChangedI(...) + echom 'TCI: ' . s:info.cur.text + call s:is_bs() +endfunction + +function! s:is_bs() + if !s:option('enabled') + echom 21 + return endif - let jump = s:is_jump(a:char) - if jump == 1 - " HACK: Instead of , we remove the char to be jumped over and - " insert it again. This will trigger re-indenting via 'indentkeys'. - " Ref: https://github.com/Raimondi/delimitMate/issues/168 - return "\".a:char - elseif jump == 3 - return s:joinUndo() . "\" . s:joinUndo() . "\" - elseif jump == 5 - return "\\I" . s:joinUndo() . "\" - else - return a:char + if s:info.cur.line != s:info.prev.line + echom 22 + return endif -endfunction " }}} - -function! delimitMate#JumpAny(...) " {{{ - if s:is_forbidden('') - return '' + if s:info.prev.col - s:info.cur.col != len(s:info.prev.p_char) + echom 23 + return endif - if !s:is_jump() - return '' + if len(s:info.prev.text) == len(s:info.cur.text) + echom 24 + return endif - " Let's get the character on the right. - let char = s:get_char(0) - if char == " " - " Space expansion. - return s:joinUndo() . "\" . s:joinUndo() . "\" - elseif char == "" - " CR expansion. - return "\" . getline(line('.') + 1)[0] . "\\" - else - return s:joinUndo() . "\" + echom s:info.prev.around + let pair = filter(s:option('pairs'), 'v:val ==# (s:info.cur.p_char . matchstr(s:info.cur.ahead, "^\\s\\zs\\S"))') + if s:option('expand_space') && !empty(pair) + echom 25 + return feedkeys("\", 'tn') endif -endfunction " delimitMate#JumpAny() }}} - -function! delimitMate#JumpMany() " {{{ - let line = split(getline('.')[col('.') - 1 : ], '\zs') - let rights = "" - let found = 0 - for char in line - if index(s:get('quotes_list'), char) >= 0 || - \ index(s:get('right_delims'), char) >= 0 - let rights .= s:joinUndo() . "\" - let found = 1 - elseif found == 0 - let rights .= s:joinUndo() . "\" - else - break - endif - endfor - if found == 1 - return rights - else - return '' + let pair = filter(s:option('pairs'), 'v:val ==# s:info.prev.around') + if empty(pair) + echom 26 + return endif -endfunction " delimitMate#JumpMany() }}} + let keys = "\" + call feedkeys(keys, 'tn') +endfunction -function! delimitMate#ExpandReturn() "{{{ - if s:is_forbidden("") - return "\" - endif - let escaped = s:cursor_idx() >= 2 - \ && s:get_char(-2) == '\' - let expand_right_matchpair = s:get('expand_cr') == 2 - \ && index(s:get('right_delims'), s:get_char(0)) > -1 - let expand_inside_quotes = s:get('expand_inside_quotes') - \ && s:is_empty_quotes() - \ && !escaped - let is_empty_matchpair = s:is_empty_matchpair() - if !pumvisible( ) - \ && ( is_empty_matchpair - \ || expand_right_matchpair - \ || expand_inside_quotes) - let val = "\a" - if is_empty_matchpair && s:get('insert_eol_marker') == 2 - \ && !search(escape(s:get('eol_marker'), '[]\.*^$').'$', 'cnW', '.') - let tail = getline('.')[col('.') - 1 : ] - let times = len(split(tail, '\zs')) - let val .= repeat(s:joinUndo() . "\", times) . s:get('eol_marker') . repeat(s:joinUndo() . "\", times + 1) - endif - let val .= "\" - if &smartindent && !&cindent && !&indentexpr - \ && s:get_char(0) == '}' - " indentation is controlled by 'smartindent', and the first character on - " the new line is '}'. If this were typed manually it would reindent to - " match the current line. Let's reproduce that behavior. - let shifts = indent('.') / &sw - let spaces = indent('.') - (shifts * &sw) - let val .= "^\".repeat("\", shifts).repeat(' ', spaces) - endif - " Expand: - " XXX zv prevents breaking expansion with syntax folding enabled by - " InsertLeave. - let val .= "\zvO" - return val - else - return "\" - endif -endfunction "}}} - -function! delimitMate#ExpandSpace() "{{{ - if s:is_forbidden("\") - return "\" - endif - let escaped = s:cursor_idx() >= 2 - \ && s:get_char(-2) == '\' - let expand_inside_quotes = s:get('expand_inside_quotes') - \ && s:is_empty_quotes() - \ && !escaped - if s:is_empty_matchpair() || expand_inside_quotes - " Expand: - return "\\" . s:joinUndo() . "\" - else - return "\" - endif -endfunction "}}} - -function! delimitMate#BS() " {{{ - if s:is_forbidden("") - let extra = '' - elseif &bs !~ 'start\|2' - let extra = '' - elseif delimitMate#WithinEmptyPair() - let extra = "\" - elseif s:is_space_expansion() - let extra = "\" - elseif s:is_cr_expansion() - let extra = repeat("\", - \ len(matchstr(getline(line('.') + 1), '^\s*\S'))) - else - let extra = '' - endif - return "\" . extra -endfunction " }}} delimitMate#BS() - -function! delimitMate#Test() "{{{ - %d _ - " Check for script options: - let result = [ - \ 'delimitMate Report', - \ '==================', - \ '', - \ '* Options: ( ) default, (g) global, (b) buffer', - \ ''] - for option in sort(keys(s:options[bufnr('%')])) - if s:exists(option, 'b') - let scope = '(b)' - elseif s:exists(option, 'g') - let scope = '(g)' - else - let scope = '( )' - endif - call add(result, - \ scope . ' delimitMate_' . option - \ . ' = ' - \ . string(s:get(option))) - endfor - call add(result, '') - - let option = 'delimitMate_excluded_ft' - call add(result, - \(exists('g:'.option) ? '(g) ' : '( ) g:') . option . ' = ' - \. string(get(g:, option, ''))) - - call add(result, '--------------------') - call add(result, '') - - " Check if mappings were set. - let left_delims = s:get('autoclose') ? s:get('left_delims') : [] - let special_keys = ['', '', '', 'g'] - if s:get('expand_cr') - call add(special_keys, '') - endif - if s:get('expand_space') - call add(special_keys, '') - endif - let maps = - \ s:get('right_delims') - \ + left_delims - \ + s:get('quotes_list') - \ + s:get('apostrophes_list') - \ + special_keys - - call add(result, '* Mappings:') - call add(result, '') - for map in maps - let output = '' - if map == '|' - let map = '' - endif - redir => output | execute "verbose imap ".map | redir END - call extend(result, split(output, '\n')) - endfor - - call add(result, '--------------------') - call add(result, '') - call add(result, '* Showcase:') - call add(result, '') - call setline(1, result) - call s:test_mappings(s:get('left_delims'), 1) - call s:test_mappings(s:get('quotes_list'), 0) - - let result = [] - redir => setoptions - echo " * Vim configuration:\" - filetype - echo "" - set - version - redir END - call extend(result, split(setoptions,"\n")) - call add(result, '--------------------') - setlocal nowrap - call append('$', result) - call feedkeys("\\", 'n') -endfunction "}}} - -function! s:test_mappings(list, is_matchpair) "{{{ - let prefix = "normal Go0\" - let last = "|" - let open = s:get('autoclose') ? 'Open: ' : 'Open & close: ' - for s in a:list - if a:is_matchpair - let pair = s:get('right_delims')[index(s:get('left_delims'), s)] - else - let pair = s - endif - if !s:get('autoclose') - let s .= pair - endif - exec prefix . open . s . last - exec prefix . "Delete: " . s . "\" . last - exec prefix . "Exit: " . s . pair . last - if s:get('expand_space') - \ && (a:is_matchpair || s:get('expand_inside_quotes')) - exec prefix . "Space: " . s . " " . last - exec prefix . "Delete space: " . s . " \" . last - endif - if s:get('expand_cr') - \ && (a:is_matchpair || s:get('expand_inside_quotes')) - exec prefix . "Car return: " . s . "\" . last - exec prefix . "Delete car return: " . s . "\0\\" . last - endif - call append('$', '') - endfor -endfunction "}}} - -function! s:joinUndo() "{{{ - if v:version < 704 - \ || ( v:version == 704 && !has('patch849') ) - return '' - endif - return "\U" -endfunction "}}} - -" vim:foldmethod=marker:foldcolumn=4:ts=2:sw=2 +" vim: sw=2 et diff --git a/plugin/delimitMate.vim b/plugin/delimitMate.vim index 106351a..56acc30 100644 --- a/plugin/delimitMate.vim +++ b/plugin/delimitMate.vim @@ -1,403 +1,29 @@ -" File: plugin/delimitMate.vim -" Version: 2.7 -" Modified: 2013-07-15 -" Description: This plugin provides auto-completion for quotes, parens, etc. -" Maintainer: Israel Chauca F. -" Manual: Read ":help delimitMate". -" ============================================================================ - -" Initialization: {{{ - if exists("g:loaded_delimitMate") || &cp - " User doesn't want this plugin or compatible is set, let's get out! finish endif let g:loaded_delimitMate = 1 let save_cpo = &cpo set cpo&vim -if v:version < 700 - echoerr "delimitMate: this plugin requires vim >= 7!" +if v:version < 800 + echohl ErrorMsg + echom "delimitMate: this plugin requires vim 8.0 or later!" + echohl None finish endif -let s:loaded_delimitMate = 1 -let delimitMate_version = "2.8" - -"}}} - -" Functions: {{{ - -function! s:option_init(name, default) "{{{ - let b = exists("b:delimitMate_" . a:name) - let g = exists("g:delimitMate_" . a:name) - " Find value to use. - if !b && !g - let value = a:default - elseif b - exec "let value = b:delimitMate_" . a:name - else - exec "let value = g:delimitMate_" . a:name - endif - call s:set(a:name, value) -endfunction "}}} - -function! s:init() "{{{ -" Initialize variables: - " autoclose - call s:option_init("autoclose", 1) - " matchpairs - call s:option_init("matchpairs", string(&matchpairs)[1:-2]) - call s:option_init("matchpairs_list", map(split(s:get('matchpairs'), '.:.\zs,\ze.:.'), 'split(v:val, ''^.\zs:\ze.$'')')) - let pairs = s:get('matchpairs_list') - if len(filter(pairs, 'v:val[0] ==# v:val[1]')) - echohl ErrorMsg - echom 'delimitMate: each member of a pair in delimitMate_matchpairs must be different from each other.' - echom 'delimitMate: invalid pairs: ' . join(map(pairs, 'join(v:val, ":")'), ', ') - echohl Normal - return 0 - endif - call s:option_init("left_delims", map(copy(s:get('matchpairs_list')), 'v:val[0]')) - call s:option_init("right_delims", map(copy(s:get('matchpairs_list')), 'v:val[1]')) - " quotes - call s:option_init("quotes", "\" ' `") - call s:option_init("quotes_list",split(s:get('quotes'), '\s\+')) - " nesting_quotes - call s:option_init("nesting_quotes", []) - " excluded_regions - call s:option_init("excluded_regions", "Comment") - call s:option_init("excluded_regions_list", split(s:get('excluded_regions'), ',\s*')) - let enabled = len(s:get('excluded_regions_list')) > 0 - call s:option_init("excluded_regions_enabled", enabled) - " expand_space - if exists("b:delimitMate_expand_space") && type(b:delimitMate_expand_space) == type("") - echom "b:delimitMate_expand_space is '".b:delimitMate_expand_space."' but it must be either 1 or 0!" - echom "Read :help 'delimitMate_expand_space' for more details." - unlet b:delimitMate_expand_space - let b:delimitMate_expand_space = 1 - endif - if exists("g:delimitMate_expand_space") && type(g:delimitMate_expand_space) == type("") - echom "delimitMate_expand_space is '".g:delimitMate_expand_space."' but it must be either 1 or 0!" - echom "Read :help 'delimitMate_expand_space' for more details." - unlet g:delimitMate_expand_space - let g:delimitMate_expand_space = 1 - endif - call s:option_init("expand_space", 0) - " expand_cr - if exists("b:delimitMate_expand_cr") && type(b:delimitMate_expand_cr) == type("") - echom "b:delimitMate_expand_cr is '".b:delimitMate_expand_cr."' but it must be either 1 or 0!" - echom "Read :help 'delimitMate_expand_cr' for more details." - unlet b:delimitMate_expand_cr - let b:delimitMate_expand_cr = 1 - endif - if exists("g:delimitMate_expand_cr") && type(g:delimitMate_expand_cr) == type("") - echom "delimitMate_expand_cr is '".g:delimitMate_expand_cr."' but it must be either 1 or 0!" - echom "Read :help 'delimitMate_expand_cr' for more details." - unlet g:delimitMate_expand_cr - let g:delimitMate_expand_cr = 1 - endif - if ((&backspace !~ 'eol' || &backspace !~ 'start') && &backspace != 2) && - \ ((exists('b:delimitMate_expand_cr') && b:delimitMate_expand_cr == 1) || - \ (exists('g:delimitMate_expand_cr') && g:delimitMate_expand_cr == 1)) - echom "delimitMate: There seems to be some incompatibility with your settings that may interfer with the expansion of . See :help 'delimitMate_expand_cr' for details." - endif - call s:option_init("expand_cr", 0) - " expand_in_quotes - call s:option_init('expand_inside_quotes', 0) - " jump_expansion - call s:option_init("jump_expansion", 0) - " smart_matchpairs - call s:option_init("smart_matchpairs", '^\%(\w\|\!\|[£$]\|[^[:punct:][:space:]]\)') - " smart_quotes - " XXX: backward compatibility. Ugly, should go the way of the dodo soon. - let quotes = escape(join(s:get('quotes_list'), ''), '\-^[]') - let word_pat = '\w\|[^[:punct:][:space:]' . quotes . ']\|\%(\\\\\)*\\' - let default_smart_quotes = '\%(' . word_pat . '\)\%#\|\%#\%(' . word_pat . '\)' - if exists('g:delimitMate_smart_quotes') && type(g:delimitMate_smart_quotes) == type(0) - if g:delimitMate_smart_quotes - unlet g:delimitMate_smart_quotes - else - unlet g:delimitMate_smart_quotes - let g:delimitMate_smart_quotes = '' - endif - endif - if exists('b:delimitMate_smart_quotes') && type(b:delimitMate_smart_quotes) == type(0) - if b:delimitMate_smart_quotes - unlet b:delimitMate_smart_quotes - if exists('g:delimitMate_smart_quotes') && type(g:delimitMate_smart_quotes) && g:delimitMate_smart_quotes - let b:delimitMate_smart_quotes = default_smart_quotes - endif - else - unlet b:delimitMate_smart_quotes - let b:delimitMate_smart_quotes = '' - endif - endif - call s:option_init("smart_quotes", default_smart_quotes) - " apostrophes - call s:option_init("apostrophes", "") - call s:option_init("apostrophes_list", split(s:get('apostrophes'), ":\s*")) - " tab2exit - call s:option_init("tab2exit", 1) - " balance_matchpairs - call s:option_init("balance_matchpairs", 0) - " eol marker - call s:option_init("insert_eol_marker", 1) - call s:option_init("eol_marker", "") - " Everything is fine. - return 1 -endfunction "}}} Init() - -function! s:get(...) " {{{ - return call('delimitMate#Get', a:000) -endfunction " }}} - -function! s:set(...) " {{{ - return call('delimitMate#Set', a:000) -endfunction " }}} - -function! s:Map() "{{{ - " Set mappings: - try - let save_keymap = &keymap - let save_iminsert = &iminsert - let save_imsearch = &imsearch - let save_cpo = &cpo - set keymap= - set cpo&vim - silent! doautocmd User delimitMate_map - if s:get('autoclose') - call s:AutoClose() - else - call s:NoAutoClose() - endif - call s:ExtraMappings() - finally - let &cpo = save_cpo - let &keymap = save_keymap - let &iminsert = save_iminsert - let &imsearch = save_imsearch - endtry - - let b:delimitMate_enabled = 1 -endfunction "}}} Map() - -function! s:Unmap() " {{{ - let imaps = - \ s:get('right_delims', []) + - \ s:get('left_delims', []) + - \ s:get('quotes_list', []) + - \ s:get('apostrophes_list', []) + - \ ['', '', '', '', '', '', '', ''] + - \ ['', '', '', '', '', ''] + - \ ['', ''] + - \ ['', '', '', '', '', '', 'g'] - - for map in imaps - if maparg(map, "i") =~# '^delimitMate' - if map == '|' - let map = '' - endif - exec 'silent! iunmap ' . map - endif - endfor - silent! doautocmd User delimitMate_unmap - let b:delimitMate_enabled = 0 -endfunction " }}} s:Unmap() - -function! s:test() "{{{ - if &modified - let confirm = input("Modified buffer, type \"yes\" to write and proceed " - \ . "with test: ") ==? 'yes' - if !confirm - return - endif - endif - call delimitMate#Test() - g/\%^$/d - 0 -endfunction "}}} - -function! s:setup(...) "{{{ - let swap = a:0 && a:1 == 2 - let enable = a:0 && a:1 - let disable = a:0 && !a:1 - " First, remove all magic, if needed: - if get(b:, 'delimitMate_enabled', 0) - call s:Unmap() - " Switch - if swap - echo "delimitMate is disabled." - return - endif - endif - if disable - " Just disable the mappings. - return - endif - if !a:0 - " Check if this file type is excluded: - if exists("g:delimitMate_excluded_ft") && - \ index(split(g:delimitMate_excluded_ft, ','), &filetype, 0, 1) >= 0 - " Finish here: - return 1 - endif - " Check if user tried to disable using b:loaded_delimitMate - if exists("b:loaded_delimitMate") - return 1 - endif - endif - " Initialize settings: - if ! s:init() - " Something went wrong. - return - endif - if enable || swap || !get(g:, 'delimitMate_offByDefault', 0) - " Now, add magic: - call s:Map() - if a:0 - echo "delimitMate is enabled." - endif - endif -endfunction "}}} - -function! s:TriggerAbb() "{{{ - if v:version < 703 - \ || ( v:version == 703 && !has('patch489') ) - \ || pumvisible() - return '' - endif - return "\" -endfunction "}}} - -function! s:NoAutoClose() "{{{ - " inoremap ) =delimitMate#SkipDelim('\)') - for delim in s:get('right_delims') + s:get('quotes_list') - if delim == '|' - let delim = '' - endif - exec 'inoremap delimitMate' . delim . ' =TriggerAbb().delimitMate#SkipDelim("' . escape(delim,'"') . '")' - exec 'silent! imap '.delim.' delimitMate'.delim - endfor -endfunction "}}} - -function! s:AutoClose() "{{{ - " Add matching pair and jump to the midle: - " inoremap ( () - let i = 0 - while i < len(s:get('matchpairs_list')) - let ld = s:get('left_delims')[i] == '|' ? '' : s:get('left_delims')[i] - let rd = s:get('right_delims')[i] == '|' ? '' : s:get('right_delims')[i] - exec 'inoremap delimitMate' . ld - \. ' TriggerAbb().delimitMate#ParenDelim("' . escape(rd, '|') . '")' - exec 'silent! imap '.ld - \.' delimitMate'.ld - let i += 1 - endwhile - - " Exit from inside the matching pair: - for delim in s:get('right_delims') - let delim = delim == '|' ? '' : delim - exec 'inoremap delimitMate' . delim - \. ' TriggerAbb().delimitMate#JumpOut("\' . delim . '")' - exec 'silent! imap ' . delim - \. ' delimitMate'. delim - endfor - - " Add matching quote and jump to the midle, or exit if inside a pair of matching quotes: - " inoremap " =delimitMate#QuoteDelim("\"") - for delim in s:get('quotes_list') - if delim == '|' - let delim = '' - endif - exec 'inoremap delimitMate' . delim - \. ' TriggerAbb()."=delimitMate#QuoteDelim(\"\\\' . delim . '\")"' - exec 'silent! imap ' . delim - \. ' delimitMate' . delim - endfor - - " Try to fix the use of apostrophes (kept for backward compatibility): - " inoremap n't n't - for map in s:get('apostrophes_list') - exec "inoremap " . map . " " . map - exec 'silent! imap ' . map . ' delimitMate' . map - endfor -endfunction "}}} - -function! s:ExtraMappings() "{{{ - " If pair is empty, delete both delimiters: - inoremap delimitMateBS =delimitMate#BS() - if !hasmapto('delimitMateBS','i') - if empty(maparg('', 'i')) - silent! imap delimitMateBS - endif - if empty(maparg('', 'i')) - silent! imap delimitMateBS - endif - endif - " If pair is empty, delete closing delimiter: - inoremap delimitMateS-BS delimitMate#WithinEmptyPair() ? "\" : "\" - if !hasmapto('delimitMateS-BS','i') && maparg('', 'i') == '' - silent! imap delimitMateS-BS - endif - " Expand return if inside an empty pair: - inoremap delimitMateCR TriggerAbb()."\=delimitMate#ExpandReturn()\" - if s:get('expand_cr') && !hasmapto('delimitMateCR', 'i') && maparg('', 'i') == '' - silent! imap delimitMateCR - endif - " Expand space if inside an empty pair: - inoremap delimitMateSpace TriggerAbb()."\=delimitMate#ExpandSpace()\" - if s:get('expand_space') && !hasmapto('delimitMateSpace', 'i') && maparg('', 'i') == '' - silent! imap delimitMateSpace - endif - " Jump over any delimiter: - inoremap delimitMateS-Tab TriggerAbb()."\=delimitMate#JumpAny()\" - if s:get('tab2exit') && !hasmapto('delimitMateS-Tab', 'i') && maparg('', 'i') == '' - silent! imap delimitMateS-Tab - endif - " Jump over next delimiters - inoremap delimitMateJumpMany TriggerAbb()."\=delimitMate#JumpMany()\" - if !hasmapto('delimitMateJumpMany', 'i') && maparg("g", 'i') == '' - imap g delimitMateJumpMany - endif -endfunction "}}} - -"}}} - -" Commands: {{{ - -" Let me refresh without re-loading the buffer: -command! -bar DelimitMateReload call s:setup(1) -" Quick test: -command! -bar DelimitMateTest call s:test() -" Switch On/Off: -command! -bar DelimitMateSwitch call s:setup(2) -" Enable mappings: -command! -bar DelimitMateOn call s:setup(1) -" Disable mappings: -command! -bar DelimitMateOff call s:setup(0) - -"}}} - -" Autocommands: {{{ +command! -bar -bang DelimitMateSwitch call delimitMate#ex_cmd(0,'switch') +command! -bar -bang DelimitMateOn call delimitMate#ex_cmd(0,'enable') +command! -bar -bang DelimitMateOff call delimitMate#ex_cmd(0,'disable') augroup delimitMate au! - " Run on file type change. - au FileType * call setup() - - " Run on new buffers. - au BufNewFile,BufRead,BufEnter * - \ if !exists('b:delimitMate_was_here') | - \ call setup() | - \ let b:delimitMate_was_here = 1 | - \ endif + au InsertCharPre * call delimitMate#InsertCharPre(v:char) + au TextChangedI * call delimitMate#TextChangedI() + au InsertEnter * call delimitMate#InsertEnter() + au CursorMovedI * call delimitMate#CursorMovedI() augroup END -"}}} - -" This is for the default buffer when it does not have a filetype. -call s:setup() - let &cpo = save_cpo " GetLatestVimScripts: 2754 1 :AutoInstall: delimitMate.vim -" vim:foldmethod=marker:foldcolumn=4:ts=2:sw=2 +" vim: sw=2 et diff --git a/test/_setup.vim b/test/_setup.vim index 8ebfc4b..6dc05c4 100644 --- a/test/_setup.vim +++ b/test/_setup.vim @@ -13,8 +13,8 @@ let &rtp = expand(':p:h:h') . ',' . &rtp . ',' . expand(':p:h:h') . '/after' set bs=2 set hidden -let g:delimitMate_matchpairs = '(:),{:},[:],<:>,¿:?,¡:!,,::' -let g:delimitMate_quotes = '" '' ` « |' +let g:delimitMate_pairs = ['()','{}','[]','<>','¿?','¡!',',:'] +let g:delimitMate_quotes = ['"', "'", '`', '«', '|'] ru plugin/delimitMate.vim let runVimTests = expand(':p:h').'/build/runVimTests' if isdirectory(runVimTests) @@ -30,7 +30,7 @@ function! s:setup_buffer(buf_content) silent %d_ if !empty(a:buf_content) call setline(1, a:buf_content) - call feedkeys("\gg0", 'ntx') + call feedkeys("gg0", 'ntx') endif endfunction @@ -55,10 +55,15 @@ function! DMTest_single(setup, typed, expected, ...) call vimtap#Todo(1) endif call s:setup_buffer(setup) - call feedkeys('i', 'nt') - call feedkeys(a:typed, 'mt') - call feedkeys('', 'ntx') - call vimtap#Is(getline(1,'$'), expected, strtrans(a:typed)) + for cmd in a:typed + echom strtrans(cmd) + call feedkeys(cmd, 'mt') + call feedkeys('', 'x') + doau delimitMate CursorMovedI + doau delimitMate TextChangedI + call feedkeys('', 'x') + endfor + call vimtap#Is(getline(1,'$'), expected, string(map(copy(a:typed), 'strtrans(v:val)'))) endfunction function! s:do_set(pat, sub, set, setup, typed, expected, ...) @@ -76,15 +81,17 @@ function! s:do_set(pat, sub, set, setup, typed, expected, ...) else let expected = [a:expected] endif - if len(split(elem, '\zs')) > 1 - let [left, right] = map(split(elem, '\zs'), 'escape(v:val, escaped)') + if strchars(elem) > 1 + "let [left, right] = map(split(elem, '\zs'), 'escape(v:val, escaped)') + let left = escape(strcharpart(elem, 0, 1), escaped) + let right = escape(strcharpart(elem, 1, 1), escaped) let sub = a:sub else let quote = escape(elem, escaped) let sub = eval(a:sub) endif call map(setup, "substitute(v:val, a:pat, sub, 'g')") - let typed = substitute(a:typed, a:pat, sub, 'g') + let typed = map(copy(a:typed), "substitute(v:val, a:pat, sub, 'g')") call map(expected, "substitute(v:val, a:pat, sub, 'g')") call DMTest_single(setup, typed, expected, skip_expr, todo_expr) endfor diff --git a/test/autoclose_matchpairs.vim b/test/autoclose_matchpairs.vim index e94ce02..3f43278 100644 --- a/test/autoclose_matchpairs.vim +++ b/test/autoclose_matchpairs.vim @@ -11,68 +11,69 @@ " - Add 5 to vimtap#Plan(). call vimtest#StartTap() -call vimtap#Plan(217) +call vimtap#Plan(224) let g:delimitMate_matchpairs = '(:),{:},[:],<:>,¿:?,¡:!,,::' let g:delimitMate_autoclose = 1 -DelimitMateReload -call DMTest_pairs('', "(x", "(x)") -call DMTest_pairs('', "(\x", "x") -call DMTest_pairs('', "()x", "()x") -call DMTest_pairs('', "((\gx", "(())x") -call DMTest_pairs('', "(x\u", "") -call DMTest_pairs('', "@(x", "@(x)") -call DMTest_pairs('', "@#\(x", "@(x)#") -call DMTest_pairs('', "(\x", "()x") +call DMTest_pairs('', ["i("], "()") +call DMTest_pairs('()', ["a\"], "") +call DMTest_pairs('()', ["a)", 'ax'], "()x") +"call DMTest_pairs('', "((\gx", "(())x") +call DMTest_pairs('', ["i(x\u"], "") +call DMTest_pairs('', ["i@(","ax"], "@(x)") +call DMTest_pairs('@#', ["a(","ax"], "@(x)#") +call DMTest_pairs('\', ["a(","ax"], '\(x') +call DMTest_pairs('', ["a(",'a\', 'a)', "ax"], '(\)x)') +"call DMTest_pairs('', "(\x", "()x") let g:delimitMate_autoclose = 0 -DelimitMateReload -call DMTest_pairs('', "(x", "(x") -call DMTest_pairs('', "()x", "(x)") -call DMTest_pairs('', "())x", "()x") -call DMTest_pairs('', "()\x", "x") -call DMTest_pairs('', "@()x", "@(x)") -call DMTest_pairs('', "@#\()x", "@(x)#") +call DMTest_pairs('', ["i(", "ax"], "(x") +call DMTest_pairs('', ["i(", "a)", "ax"], "(x)") +call DMTest_pairs('', ["i(", "a)", "a)", "ax"], "()x") +call DMTest_pairs('', ["i(", "a)", "a\", "ax"], "x") +call DMTest_pairs('', ["i@(", "a)", "ax"], "@(x)") +call DMTest_pairs('@#', ["a(", "a)", "ax"], "@(x)#") let g:delimitMate_expand_space = 1 let g:delimitMate_autoclose = 1 -DelimitMateReload -call DMTest_pairs('', "(\x", "( x )") -call DMTest_pairs('', "(\\x", "(x)") +call DMTest_pairs('', ['i(', "a\", 'ax'], "( x )") +" needs to be after so the cursor stays in the expected place for when +" the doau commands fire. +call DMTest_pairs('( )', ["2|a\\"], 'ix'], "(x)") let g:delimitMate_autoclose = 0 -DelimitMateReload -call DMTest_pairs('', "()\\x", "(x)") +call DMTest_pairs('', ["i(", "a)", "a\", "a\\", "ix"], "(x)") let g:delimitMate_autoclose = 1 -DelimitMateReload " Handle backspace gracefully. set backspace= -call DMTest_pairs('', "(\a\x", "(x)") -set bs=2 +call DMTest_pairs('', ["i(", "a\\", "ix"], "(x)") +set backspace=2 " closing parens removes characters. #133 -call DMTest_pairs('', "(a\i)", "()a)") +call DMTest_pairs('', ["i(", "aa", "i)"], "()a)") " Add semicolon next to the closing paren. Issue #77. -new -let b:delimitMate_eol_marker = ';' -DelimitMateReload -call DMTest_pairs('', "abc(x", "abc(x);") -" BS should behave accordingly. -call DMTest_pairs('', "abc(\", "abc;") +"new +"let b:delimitMate_eol_marker = ';' +"call DMTest_pairs('', "abc(x", "abc(x);") +"" BS should behave accordingly. +"call DMTest_pairs('', "abc(\", "abc;") +"unlet b:delimitMate_eol_marker " Expand iabbreviations -unlet b:delimitMate_eol_marker -DelimitMateReload iabb def ghi -call DMTest_pairs('', "def(", "ghi()") +call DMTest_pairs('', ["idef("], "ghi()") iunabb def - -call DMTest_pairs('', "abc а\(", "abc (а") -call DMTest_pairs('', "abc ñ\(", "abc (ñ") -call DMTest_pairs('', "abc $\(", "abc ($") -call DMTest_pairs('', "abc £\(", "abc (£") -call DMTest_pairs('', "abc d\(", "abc (d") -call DMTest_pairs('', "abc \(\(", "abc ((") -call DMTest_pairs('', "abc .\(", "abc ().") -call DMTest_pairs('', "abc \(", "abc () ") - -" Play nice with undo. -call DMTest_pairs('', "a\u(c)b\u", "a") +" +"call DMTest_pairs('', "abc а\(", "abc (а") +"call DMTest_pairs('', "abc ñ\(", "abc (ñ") +"call DMTest_pairs('', "abc $\(", "abc ($") +"call DMTest_pairs('', "abc £\(", "abc (£") +"call DMTest_pairs('', "abc d\(", "abc (d") +"call DMTest_pairs('', "abc \(\(", "abc ((") +"call DMTest_pairs('', "abc .\(", "abc ().") +"call DMTest_pairs('', "abc \(", "abc () ") +" +"" Play nice with undo. +"call DMTest_pairs('', "a\u(c)b\u", "a") +" +"let g:delimitMate_autoclose = 1 +"let g:delimitMate_balance_matchpairs = 1 +"call DMTest_pairs('', ")\(x", '(x)') call vimtest#Quit()