diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..926ccaa --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +doc/tags diff --git a/README.md b/README.md index a797540..6882515 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 -------- @@ -27,7 +29,10 @@ Features input: {|} (press at |) output: { | - } + } (press } to close the pair) + output: { + }| (the inserted blank line will be deleted) + * Insert spaces before closing characters, only for [], (), {} @@ -57,16 +62,17 @@ 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()]) +* Quick move char to closed pair + + input: (|){["foo"]} (press at |) + output: ({["foo"]}|) + + input: |[foo, bar()] (press ( at |) + output: ([foo, bar()]|) + * Quick jump to closed pair. input: @@ -81,25 +87,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) @@ -129,6 +116,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 ")", "}", "]" @@ -167,7 +159,7 @@ Options ------- * g:AutoPairs - Default: {'(':')', '[':']', '{':'}',"'":"'",'"':'"', '`':'`'} + Default: {'(':')', '[':']', '{':'}',"'":"'",'"':'"', "`":"`", '```':'```', '"""':'"""', "'''":"'''"} * b:AutoPairs @@ -256,6 +248,13 @@ Options Sets the characters to stop auto closing. For example, when set to ['\', '#'], the plugin will not automatically close `#'` +* g:AutoPairsMoveCharacter + + Default: "()[]{}\"'" + + Map to + move character under the cursor to the pair. + Buffer Level Pairs Setting -------------------------- @@ -265,6 +264,108 @@ 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 to close key + m - close key jumps through multi line + s - close key jumps only in the same line + k[KEY] - map the close key to [KEY] + + by default if open key equals close key the multi line is turn off + + "" ? jumps only in the same line + "//m" force ? jumping through multi line + "" ? will jump through multi line + "//s" force ? only jumping in the same line + "//n" do not jump totally + "//k]" use key ] to jump through ?> + + 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 --------------- @@ -291,7 +392,7 @@ TroubleShooting To fix the issue, you need remap or disable the related shortcut. Known Issues ------------------------ +------------ Breaks '.' - [issue #3](https://github.com/jiangmiao/auto-pairs/issues/3) Description: After entering insert mode and inputing `[hello` then leave insert diff --git a/doc/AutoPairs.txt b/doc/AutoPairs.txt new file mode 100644 index 0000000..afe589e --- /dev/null +++ b/doc/AutoPairs.txt @@ -0,0 +1,356 @@ +*AutoPairs.txt* Insert or delete brackets, parens, quotes in pair + +Author: jiangmiao +License: MIT +URL: https://github.com/jiangmiao/auto-pairs + +============================================================================== +CONTENTS *autopairs-contents* + + 1. Installation ............................. |autopairs-installation| + 2. Features ..................................... |autopairs-features| + 3. Fly Mode ..................................... |autopairs-fly-mode| + 4. Shortcuts ................................... |autopairs-shortcuts| + 5. Options ....................................... |autopairs-options| + 6. Troubleshooting ...................... |autopairs-troubleshooting| + +============================================================================== +1. Introduction *autopairs-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 + +============================================================================== +2. Features *autopairs-features* + +Insert in pair: > + + input: [ + output: [|] + +Delete in pair: > + + input: foo[] + output: foo + +Insert new indented line after Return: > + + input: {|} (press at |) + output: { + | + } + +Insert spaces before closing characters, only for [], (), {}: > + + input: {|} (press at |) + output: { | } + + input: {|} (press foo} at |) + output: { foo }| + + input: '|' (press at |) + output: ' |' + +Skip ' when inside a word: > + + input: foo| (press ' at |) + output: foo' + +Skip closed bracket: > + + input: [] + output: [] + +Ignore auto pair when previous character is '\': > + + input: "\' + output: "\'" + +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()]) + +Quick jump to closed pair: > + + input: + { + something;| + } + + (press } at |) + + output: + { + + }| + +Support ```, ''' and """: > + + input: + ''' + + output: + '''|''' + +Delete Repeated Pairs in one time: > + + input: """|""" (press at |) + output: | + + input: {{|}} (press at |) + output: | + + input: [[[[[[|]]]]]] (press at |) + output: | + +Fly Mode (|autopairs-flymode|): > + + input: if(a[3) + output: if(a[3])| (In Fly Mode) + output: if(a[3)]) (Without Fly Mode) + + input: + { + hello();| + world(); + } + + (press } at |) + + output: + { + hello(); + world(); + }| + + (then press at | to do backinsert) + output: + { + hello();}| + world(); + } + + See |Fly Mode| section for details + +============================================================================== +3. Fly Mode *autopairs-flymode* + +Fly Mode will always force closed-pair jumping instead of inserting. Only for +")", "}", "]". If jumps in mistake, you can use |g:AutoPairsBackInsert| (default +Key: ) to jump back and insert closed pair. + +The most situation maybe you want to insert single closed pair in the string, +eg: > + + ")" + +Fly Mode is DISABLED by default. To enable Fly Mode add following to your +'.vimrc': > + + let g:AutoPairsFlyMode = 1 + +Default Options: > + + let g:AutoPairsFlyMode = 0 + let g:AutoPairsShortcutBackInsert = '' + +============================================================================== +4. Shortcuts *autopairs-shortcuts* + +System Shortcuts: + : Insert new indented line after return if cursor in blank brackets + or quotes. + : Delete brackets in pair + : Toggle Autopairs (|g:AutoPairsShortcutToggle|) + : Fast Wrap (|g:AutoPairsShortcutFastWrap|) + : Jump to next closed pair (|g:AutoPairsShortcutJump|) + : BackInsert (|g:AutoPairsShortcutBackInsert|) + + + To rebind keys , or or in case of conflicts with + another keys: + + let g:AutoPairsShortcutToggle = '' + + If the key is empty string '', then the shortcut will be disabled. + +============================================================================== +5. Options *autopairs-options* + + *g:AutoPairs* +|g:AutoPairs| dict + +Default: > + {'(':')', '[':']', '{':'}',"'":"'",'"':'"', '`':'`'} + +Specifies which symbols should be automatically paired. + +To append new pairs without overwriting defaults, add values in your `.vimrc`.: + + let g:AutoPairs['<']='>' + +This example will enable matching of `<` with `>`. + + + *b:AutoPairs* +|b:AutoPairs| dict + +Default: |g:AutoPairs| + +Buffer level pairs set. + +You can set |b:AutoPairs| before |BufEnter|: > + + au Filetype FILETYPE let b:AutoPairs = {"(": ")"} + +This sets |AutoPairs| to only match for parenthesis for 'FILETYPE'. + + + + *g:AutoPairsShortcutToggle* +|g:AutoPairsShortcutToggle| string + +Default: + +The shortcut to toggle autopairs. + + + + *g:AutoPairsShortcutFastWrap* +|g:AutoPairsShortcutFastWrap| string + +Default: + +Fast wrap the word. All pairs will be considered as a block (including <>). + + (|)'hello' after fast wrap at |, the word will be ('hello') + (|) after fast wrap at |, the word will be () + + + + *g:AutoPairsShortcutJump* +|g:AutoPairsShortcutJump| string + +Default: + +Jump to the next closed pair. + + + *g:AutoPairsShortcutBackInsert* +|g:AutoPairsShortcutBackInsert| string + +Default: + +Work with |autopairs-flymode|, insert the key at the Fly Mode jumped position. + + + + *g:AutoPairsMapBS* +|g:AutoPairsMapBS| int + +Default: 1 + +Map to delete brackets and quotes in pair, executes: + + inoremap =AutoPairsDelete() + + + *g:AutoPairsMapCh* +|g:AutoPairsMapCh| int + +Default: 1 + +Map to delete brackets and quotes in pair. + + + *g:AutoPairsMapCR* +|g:AutoPairsMapCR| int + +Default: 1 + +Map to insert a new indented line if cursor in (|), {|} [|], '|', "|". +Executes: + + inoremap =AutoPairsReturn() + + + *g:AutoPairsCenterLine* +|g:AutoPairsCenterLine| int + +Default: 1 + +When |g:AutoPairsMapCR| is on, center current line after return if the line +is at the bottom 1/3 of the window. + + + *g:AutoPairsMapSpace* +|g:AutoPairsMapSpace| int + +Default: 1 + +Map to insert a space after the opening character and before the +closing one. + +Executes: + + inoremap =AutoPairsSpace() + + + *g:AutoPairsFlyMode* +|g:AutoPairsFlyMode| int + +Default: 0 + +Set it to 1 to enable |autopairs-flymode|. + + + *g:AutoPairsMultilineClose* +|g:AutoPairsMultilineClose| int + +Default: 1 + +When you press the key for the closing pair (e.g. `)`) it jumps past it. +If set to 1, then it'll jump to the next line, if there is only 'whitespace'. +If set to 0, then it'll only jump to a closing pair on the same line. + +============================================================================== +6. Troubleshooting *autopairs-troubleshooting* + +This plugin remaps keys `([{'"}]) ` + +If auto pairs cannot work, use |:imap| to check if the map is corrected. + +The correct map should be: > + + =AutoPairsInsert("\(") + +Or the plugin conflicts with some other plugins. Use command: > + + :call AutoPairsInit() to remap the keys. + +--- How to insert parens purely? --- + +There are 3 ways: + + 1. Use Ctrl-V ) to insert paren without trigger the plugin. + + 2. Use Alt-P to turn off the plugin. + + 3. Use DEL or x to delete the character insert by plugin. + +--- Swedish Character Conflict --- + +Because AutoPairs uses Meta(Alt) key as a shortcut, it conflicts with some +Swedish character such as å. To fix the issue, you need remap or disable the +related shortcut. diff --git a/plugin/auto-pairs.vim b/plugin/auto-pairs.vim index afe55c3..28c2f25 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: 2013-07-13 -" Version: 1.3.2 +" Last Change: 2019-02-02 +" Version: 2.0.0 " Homepage: http://www.vim.org/scripts/script.php?script_id=3599 " Repository: https://github.com/jiangmiao/auto-pairs " License: MIT @@ -13,12 +13,30 @@ end let g:AutoPairsLoaded = 1 if !exists('g:AutoPairs') - let g:AutoPairs = {'(':')', '[':']', '{':'}',"'":"'",'"':'"', '`':'`'} + let g:AutoPairs = {'(':')', '[':']', '{':'}',"'":"'",'"':'"', '```':'```', '"""':'"""', "'''":"'''", "`":"`"} end -if !exists('g:AutoPairsParens') - let g:AutoPairsParens = {'(':')', '[':']', '{':'}'} -end +" default pairs base on filetype +func! AutoPairsDefaultPairs() + if exists('b:autopairs_defaultpairs') + return b:autopairs_defaultpairs + end + let r = copy(g:AutoPairs) + let allPairs = { + \ 'vim': {'\v^\s*\zs"': ''}, + \ 'rust': {'\w\zs<': '>', '&\zs''': ''}, + \ 'php': {'//k]', '//k]'} + \ } + for [filetype, pairs] in items(allPairs) + if &filetype == filetype + for [open, close] in items(pairs) + let r[open] = close + endfor + end + endfor + let b:autopairs_defaultpairs = r + return r +endf if !exists('g:AutoPairsMapBS') let g:AutoPairsMapBS = 1 @@ -33,6 +51,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 @@ -49,6 +71,10 @@ if !exists('g:AutoPairsShortcutFastWrap') let g:AutoPairsShortcutFastWrap = '' end +if !exists('g:AutoPairsMoveCharacter') + let g:AutoPairsMoveCharacter = "()[]{}\"'" +end + if !exists('g:AutoPairsShortcutJump') let g:AutoPairsShortcutJump = '' endif @@ -81,7 +107,7 @@ endif " 7.4.849 support U to avoid breaking '.' " Issue talk: https://github.com/jiangmiao/auto-pairs/issues/3 " Vim note: https://github.com/vim/vim/releases/tag/v7.4.849 -if v:version >= 704 && has("patch849") +if v:version > 704 || v:version == 704 && has("patch849") let s:Go = "\U" else let s:Go = "" @@ -91,267 +117,335 @@ 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 in g:AutoPairsIgnoreCharacters - if index(g:AutoPairsIgnoreCharacters, prev_char) != -1 - 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 - - if !g:AutoPairsFlyMode - " Skip the character if next character is space - if current_char == ' ' && next_char == a:key - return s:Right.s:Right - end - - " 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 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 +" returns [orig, text_before_open, open] +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 + +" returns [orig, close, text_after_close] +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 = AutoPairsDefaultPairs() + if a:0 > 0 + for open in a:1 + unlet r[open] + endfor + end + for [open, close] in items(a:pairs) + let r[open] = close + endfor + 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 open pairs + for [open, close, opt] in b:AutoPairsList + let ms = s:matchend(before.a:key, open) + let m = matchstr(afterline, '^\v\s*\zs\V'.close) + if len(ms) > 0 + " process the open pair + + " remove inserted pair + " eg: if the pairs include < > and + " when