diff --git a/README.md b/README.md index 7cdcbf9..862c9b9 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,13 @@ Insert or delete brackets, parens, quotes in pair. Installation ------------ -copy plugin/auto-pairs.vim to ~/.vim/plugin -or if you are using `pathogen`: - -```git clone git://github.com/jiangmiao/auto-pairs.git ~/.vim/bundle/auto-pairs``` +* Manual + * Copy `plugin/auto-pairs.vim` to `~/.vim/plugin` +* [Pathogen](https://github.com/tpope/vim-pathogen) + * `git clone git://github.com/jiangmiao/auto-pairs.git ~/.vim/bundle/auto-pairs` +* [Vundle](https://github.com/VundleVim/Vundle.vim) + * `Plugin 'jiangmiao/auto-pairs'` Features -------- @@ -57,13 +59,6 @@ Features * Fast Wrap - input: |'hello' (press ( at |) - output: ('hello') - - wrap string, only support c style string - input: |'h\\el\'lo' (press ( at |) - output ('h\\ello\'') - input: |[foo, bar()] (press ( at |) output: ([foo, bar()]) @@ -89,25 +84,6 @@ Features }| -* Support ``` ''' and """ - - input: - ''' - - output: - '''|''' - -* Delete Repeated Pairs in one time - - input: """|""" (press at |) - output: | - - input: {{|}} (press at |) - output: | - - input: [[[[[[|]]]]]] (press at |) - output: | - * Fly Mode input: if(a[3) @@ -137,6 +113,11 @@ Features See Fly Mode section for details +* Multibyte Pairs + + Support any multibyte pairs such as , <% %>, """ """ + See multibyte pairs section for details + Fly Mode -------- Fly Mode will always force closed-pair jumping instead of inserting. only for ")", "}", "]" @@ -264,6 +245,14 @@ Options Map to move character under the cursor to the pair. +* g:AutoPairsWildClosedPair + + Default: ']' + + Jump over following closed pair + for pair {'begin': 'end//n]'}, e is not mapped, use wild closed pair ] to jump over 'end' + use to back insert ] after jumping + Buffer Level Pairs Setting -------------------------- @@ -273,6 +262,96 @@ eg: " When the filetype is FILETYPE then make AutoPairs only match for parenthesis au Filetype FILETYPE let b:AutoPairs = {"(": ")"} + au FileType php let b:AutoPairs = AutoPairsDefine({'', ''}) + +Multibyte Pairs +--------------- + + The default pairs is {'(':')', '[':']', '{':'}',"'":"'",'"':'"', '`':'`'} + You could also define multibyte pairs such as , <% %> and so on + +* Function AutoPairsDefine(addPairs:dict[, removeOpenPairList:list]) + + add or delete pairs base on g:AutoPairs + + eg: + au FileType html let b:AutoPairs = AutoPairsDefine({''}, ['{']) + add pair and remove '{' for html file + + the pair implict start with \V, so if want to match start of line ^ should be write in \^ vim comment {'\^"': ''} + +* General usage + + au FileType php let b:AutoPairs = AutoPairsDefine({'', ''}) + + the first key of closed pair ? will be mapped + + pairs: '', '' + input: + + input: + + input: he (press at|) + output: he| + + input: (press ? at|) + output: | + + pair: '[[':']]' + input: [[|]] (press ) + output: | ([[ and ]] will be deleted the [['s priority is higher than [ for it's longer) + +* Modifier + + The text after // in close pair is modifiers + + n - do not map the first charactor of closed pair + + for 'begin' 'end' pair, e is a charactor, if map e to jump will be annoy, so use modifier 'n' to skip key map + + au FileType ruby let b:AutoPairs = AutoPairsDefine({'begin': 'end//n]'}) + + + input: begin + output: begin|end + + input: begin|end (press on |) + output: | + + input: begin|end (press e on |) + output: begineend (will not jump for e is not mapped) + +* Advanced usage + + au FileType rust let b:AutoPairs = AutoPairsDefine({'\w\zs<': '>'}) + + if press < after a word will generate the pair + + when use regexp MUST use \zs to prevent catching + if use '\w<' without \zs, for text hello<|> press on | will output 'hell', the 'o' has been deleted + + pair: '\w\zs<': '>' + input: h < + output: h < + + input: h< + output: h<|> + + input: h<|> press + output: h| + + pair: '\w<': '>' (WRONG pair which missed \zs) + input: h<|> press + output: | (charactor 'h' is deleted) + + + the 'begin' 'end' pair write in + + au FileType ruby let b:AutoPairs = AutoPairsDefine({'\v(^|[^\w])\zsbegin': 'end//n'}) + + will be better, only auto pair when at start of line or follow non-word text TroubleShooting --------------- diff --git a/doc/AutoPairs.txt b/doc/AutoPairs.txt index 49ff549..afe589e 100644 --- a/doc/AutoPairs.txt +++ b/doc/AutoPairs.txt @@ -178,7 +178,7 @@ System Shortcuts: : BackInsert (|g:AutoPairsShortcutBackInsert|) - To rebind keys , or or in case of conflicts conflicts with + To rebind keys , or or in case of conflicts with another keys: let g:AutoPairsShortcutToggle = '' diff --git a/plugin/auto-pairs.vim b/plugin/auto-pairs.vim index a1693bb..d329bb5 100644 --- a/plugin/auto-pairs.vim +++ b/plugin/auto-pairs.vim @@ -1,8 +1,8 @@ " Insert or delete brackets, parens, quotes in pairs. " Maintainer: JiangMiao " Contributor: camthompson -" Last Change: 2017-06-17 -" Version: 1.3.3 +" Last Change: 2019-01-15 +" Version: 2.0.0 " Homepage: http://www.vim.org/scripts/script.php?script_id=3599 " Repository: https://github.com/jiangmiao/auto-pairs " License: MIT @@ -16,10 +16,6 @@ if !exists('g:AutoPairs') let g:AutoPairs = {'(':')', '[':']', '{':'}',"'":"'",'"':'"', '`':'`'} end -if !exists('g:AutoPairsParens') - let g:AutoPairsParens = {'(':')', '[':']', '{':'}'} -end - if !exists('g:AutoPairsMapBS') let g:AutoPairsMapBS = 1 end @@ -33,6 +29,10 @@ if !exists('g:AutoPairsMapCR') let g:AutoPairsMapCR = 1 end +if !exists('g:AutoPairsWildClosedPair') + let g:AutoPairsWildClosedPair = ']' +end + if !exists('g:AutoPairsMapSpace') let g:AutoPairsMapSpace = 1 end @@ -91,278 +91,302 @@ let s:Left = s:Go."\" let s:Right = s:Go."\" -" Will auto generated {']' => '[', ..., '}' => '{'}in initialize. -let g:AutoPairsClosedPairs = {} -function! AutoPairsInsert(key) - if !b:autopairs_enabled - return a:key - end +" unicode len +func! s:ulen(s) + return len(split(a:s, '\zs')) +endf +func! s:left(s) + return repeat(s:Left, s:ulen(a:s)) +endf + +func! s:right(s) + return repeat(s:Right, s:ulen(a:s)) +endf + +func! s:delete(s) + return repeat("\", s:ulen(a:s)) +endf + +func! s:backspace(s) + return repeat("\", s:ulen(a:s)) +endf + +func! s:getline() let line = getline('.') let pos = col('.') - 1 let before = strpart(line, 0, pos) let after = strpart(line, pos) - let next_chars = split(after, '\zs') - let current_char = get(next_chars, 0, '') - let next_char = get(next_chars, 1, '') - let prev_chars = split(before, '\zs') - let prev_char = get(prev_chars, -1, '') - - let eol = 0 - if col('$') - col('.') <= 1 - let eol = 1 - end - - " Ignore auto close if prev character is \ - if prev_char == '\' - return a:key - end - - " The key is difference open-pair, then it means only for ) ] } by default - if !has_key(b:AutoPairs, a:key) - let b:autopairs_saved_pair = [a:key, getpos('.')] - - " Skip the character if current character is the same as input - if current_char == a:key - return s:Right - end - - " Skip the character if closed pair is next character - if current_char == ' ' && next_char == a:key - " Remove the space we added if the pair is empty - if has_key(b:AutoPairsClosedPairs, a:key) - let end_of_prevchar_index = matchend(before, '\S\ze\s*$') - if end_of_prevchar_index > -1 - let end_of_prevchar = get(prev_chars, end_of_prevchar_index-1, '') - if end_of_prevchar == b:AutoPairsClosedPairs[a:key] - return "\".s:Right - endif - endif - endif - - return s:Right.s:Right - endif - - if !g:AutoPairsFlyMode - " Skip the character if closed pair is next character - if current_char == '' - if g:AutoPairsMultilineClose - let next_lineno = line('.')+1 - let next_line = getline(nextnonblank(next_lineno)) - let next_char = matchstr(next_line, '\s*\zs.') - else - let next_char = matchstr(line, '\s*\zs.') - end - if next_char == a:key - return "\e^a" - endif - endif - endif - - " Fly Mode, and the key is closed-pairs, search closed-pair and jump - if g:AutoPairsFlyMode && has_key(b:AutoPairsClosedPairs, a:key) - let n = stridx(after, a:key) - if n != -1 - return repeat(s:Right, n+1) - end - if search(a:key, 'W') - " force break the '.' when jump to different line - return "\" - endif - endif - - " Insert directly if the key is not an open key - return a:key - end - - let open = a:key - let close = b:AutoPairs[open] - - if current_char == close && open == close - return s:Right - end - - " Ignore auto close ' if follows a word - " MUST after closed check. 'hello|' - if a:key == "'" && prev_char =~ '\v\w' - return a:key - end - - " support for ''' ``` and """ - if open == close - " The key must be ' " ` - let pprev_char = line[col('.')-3] - if pprev_char == open && prev_char == open - " Double pair found - return repeat(a:key, 4) . repeat(s:Left, 3) - end - end - - let quotes_num = 0 - " Ignore comment line for vim file - if &filetype == 'vim' && a:key == '"' - if before =~ '^\s*$' - return a:key - end - if before =~ '^\s*"' - let quotes_num = -1 - end - end - - " Keep quote number is odd. - " Because quotes should be matched in the same line in most of situation - if g:AutoPairsSmartQuotes && open == close - " Remove \\ \" \' - let cleaned_line = substitute(line, '\v(\\.)', '', 'g') - let n = quotes_num - let pos = 0 - while 1 - let pos = stridx(cleaned_line, open, pos) - if pos == -1 + let afterline = after + if a:0 == 0 && g:AutoPairsMultilineClose + let n = line('$') + let i = line('.')+1 + while i <= n + let line = getline(i) + let after = after.' '.line + if !(line =~ '\v^\s*$') break end - let n = n + 1 - let pos = pos + 1 + let i = i+1 endwhile - if n % 2 == 1 - return a:key + end + return [before, after, afterline] +endf + +" split text to two part +func! s:matchend(text, open) + let m = matchstr(a:text, '\V'.a:open.'\v$') + if m == "" + return [] + end + return [a:text, strpart(a:text, 0, len(a:text)-len(m)), m] +endf +func! s:matchbegin(text, close) + let m = matchstr(a:text, '^\V'.a:close) + if m == "" + return [] + end + return [a:text, m, strpart(a:text, len(m), len(a:text)-len(m))] +endf + +" add or delete pairs base on g:AutoPairs +" AutoPairsDefine(addPairs:dict[, removeOpenPairList:list]) +" +" eg: +" au FileType html let b:AutoPairs = AutoPairsDefine({''}, ['{']) +" add pair and remove '{' for html file +func! AutoPairsDefine(pairs, ...) + let r = copy(g:AutoPairs) + for [open, close] in items(a:pairs) + let r[open] = close + endfor + if a:0 > 1 + for open in a:1 + unlet r[open] + endfor + end + return r +endf + +func! AutoPairsInsert(key) + if !b:autopairs_enabled + return a:key + end + + let b:autopairs_saved_pair = [a:key, getpos('.')] + + let [before, after, afterline] = s:getline() + + " Ignore auto close if prev character is \ + if before[-1:-1] == '\' + return a:key + end + + " check close pairs + for [open, close] in b:AutoPairsList + if a:key == g:AutoPairsWildClosedPair || close[0] == a:key + " the close pair is in the same line + let m = matchstr(afterline, '^\v\s*\V'.close) + if m != '' + if before =~ '\V'.open.'\v\s*$' && m[0] =~ '\v\s' + " remove the space we inserted if the text in pairs is blank + return "\".s:right(m[1:]) + else + return s:right(m) + end + end + if open != close + let m = s:matchend(after, '^\v\s*\zs\V'.close) + if len(m) > 0 + " skip close pair greedy + call search(m[1], 'We') + return "\" + end + end + end + endfor + + " check open pairs + let text=before.a:key + for [open, close] in b:AutoPairsList + let m = s:matchend(text, open) + if len(m) > 0 + " process the open pair + + " remove inserted pair + " eg: if the pairs include < > and + " when