" Insert or delete brackets, parens, quotes in pairs. " Maintainer: JiangMiao " Contributor: camthompson " Last Change: 2012-07-06 " Version: 1.2.3 " Homepage: http://www.vim.org/scripts/script.php?script_id=3599 " Repository: https://github.com/jiangmiao/auto-pairs if exists('g:AutoPairsLoaded') || &cp finish end let g:AutoPairsLoaded = 1 if !exists('g:AutoPairs') let g:AutoPairs = {'(':')', '[':']', '{':'}',"'":"'",'"':'"', '`':'`'} end if !exists('g:AutoPairsParens') let g:AutoPairsParens = {'(':')', '[':']', '{':'}'} end let g:AutoExtraPairs = copy(g:AutoPairs) let g:AutoExtraPairs['<'] = '>' if !exists('g:AutoPairsMapBS') let g:AutoPairsMapBS = 1 end if !exists('g:AutoPairsMapCR') let g:AutoPairsMapCR = 1 end if !exists('g:AutoPairsMapSpace') let g:AutoPairsMapSpace = 1 end if !exists('g:AutoPairsCenterLine') let g:AutoPairsCenterLine = 1 end if !exists('g:AutoPairsShortcutToggle') let g:AutoPairsShortcutToggle = '' end if !exists('g:AutoPairsShortcutFastWrap') let g:AutoPairsShortcutFastWrap = '' end if !exists('g:AutoPairsShortcutJump') let g:AutoPairsShortcutJump = '' endif " Fly mode will for closed pair to jump to closed pair instead of insert. " also support AutoPairsBackInsert to insert pairs where jumped. if !exists('g:AutoPairsFlyMode') let g:AutoPairsFlyMode = 0 endif " Work with Fly Mode, insert pair where jumped if !exists('g:AutoPairsShortcutBackInsert') let g:AutoPairsShortcutBackInsert = '' endif " Will auto generated {']' => '[', ..., '}' => '{'}in initialize. let g:AutoPairsClosedPairs = {} function! AutoPairsInsert(key) if !b:autopairs_enabled return a:key end let line = getline('.') let prev_char = line[col('.')-2] let current_char = line[col('.')-1] let next_char = line[col('.')] 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(g: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 "\" end if !g:AutoPairsFlyMode " Skip the character if next character is space if current_char == ' ' && next_char == a:key return "\\" end " Skip the character if closed pair is next character if current_char == '' let next_lineno = line('.')+1 let next_line = getline(nextnonblank(next_lineno)) let next_char = matchstr(next_line, '\s*\zs.') 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(g:AutoPairsClosedPairs, a:key) if search(a:key, 'W') return "\" endif endif " Input directly if the key is not an open key return a:key end let open = a:key let close = g:AutoPairs[open] if current_char == close && open == close return "\" 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 a:key end end return open.close."\" endfunction function! AutoPairsDelete() let line = getline('.') let current_char = line[col('.')-1] let prev_char = line[col('.')-2] let pprev_char = line[col('.')-3] if pprev_char == '\' return "\" end " Delete last two spaces in parens, work with MapSpace if has_key(g:AutoPairs, pprev_char) && prev_char == ' ' && current_char == ' ' return "\\" endif if has_key(g:AutoPairs, prev_char) let close = g:AutoPairs[prev_char] if match(line,'^\s*'.close, col('.')-1) != -1 let space = matchstr(line, '^\s*', col('.')-1) return "\". repeat("\", len(space)+1) else let nline = getline(line('.')+1) if nline =~ '^\s*'.close let space = matchstr(nline, '^\s*') return "\\". repeat("\", len(space)+1) end end end return "\" endfunction function! AutoPairsJump() call search('["\]'')}]','W') endfunction " string_chunk cannot use standalone let s:string_chunk = '\v%(\\\_.|[^\1]|[\r\n]){-}' let s:ss_pattern = '\v''' . s:string_chunk . '''' let s:ds_pattern = '\v"' . s:string_chunk . '"' func! s:RegexpQuote(str) return substitute(a:str, '\v[\[\{\(\<\>\)\}\]]', '\\&', 'g') endf func! s:RegexpQuoteInSquare(str) return substitute(a:str, '\v[\[\]]', '\\&', 'g') endf " Search next open or close pair func! s:FormatChunk(open, close) let open = s:RegexpQuote(a:open) let close = s:RegexpQuote(a:close) let open2 = s:RegexpQuoteInSquare(a:open) let close2 = s:RegexpQuoteInSquare(a:close) if open == close return '\v'.open.s:string_chunk.close else return '\v%(' . s:ss_pattern . '|' . s:ds_pattern . '|' . '[^'.open2.close2.']|[\r\n]' . '){-}(['.open2.close2.'])' end endf " Fast wrap the word in brackets function! AutoPairsFastWrap() let line = getline('.') let current_char = line[col('.')-1] let next_char = line[col('.')] let open_pair_pattern = '\v[({\[''"]' let at_end = col('.') >= col('$') - 1 normal x " Skip blank if next_char =~ '\v\s' || at_end call search('\v\S', 'W') let line = getline('.') let next_char = line[col('.')-1] end if has_key(g:AutoPairs, next_char) let followed_open_pair = next_char let inputed_close_pair = current_char let followed_close_pair = g:AutoPairs[next_char] if followed_close_pair != followed_open_pair " TODO replace system searchpair to skip string and nested pair. " eg: (|){"hello}world"} will transform to ({"hello})world"} call searchpair('\V'.followed_open_pair, '', '\V'.followed_close_pair, 'W') else call search(s:FormatChunk(followed_open_pair, followed_close_pair), 'We') end return "\".inputed_close_pair."\" else normal e return "\".current_char."\" end endfunction function! AutoPairsMap(key) let escaped_key = substitute(a:key, "'", "''", 'g') " use expr will cause search() doesn't work execute 'inoremap '.a:key." =AutoPairsInsert('".escaped_key."')" endfunction function! AutoPairsToggle() if b:autopairs_enabled let b:autopairs_enabled = 0 echo 'AutoPairs Disabled.' else let b:autopairs_enabled = 1 echo 'AutoPairs Enabled.' end return '' endfunction function! AutoPairsReturn() let line = getline('.') let pline = getline(line('.')-1) let prev_char = pline[strlen(pline)-1] let cmd = '' let cur_char = line[col('.')-1] if has_key(g:AutoPairs, prev_char) && g:AutoPairs[prev_char] == cur_char if g:AutoPairsCenterLine && winline() * 1.5 >= winheight(0) let cmd = " \zz\cl" end " conflict with javascript and coffee " javascript need indent new line " coffeescript forbid indent new line if &filetype == 'coffeescript' || &filetype == 'coffee' return "\k==o".cmd else return "\=ko".cmd endif end return '' endfunction function! AutoPairsSpace() let line = getline('.') let prev_char = line[col('.')-2] let cmd = '' let cur_char =line[col('.')-1] if has_key(g:AutoPairsParens, prev_char) && g:AutoPairsParens[prev_char] == cur_char let cmd = "\\" endif return "\".cmd endfunction function! AutoPairsBackInsert() if exists('b:autopairs_saved_pair') let pair = b:autopairs_saved_pair[0] let pos = b:autopairs_saved_pair[1] call setpos('.', pos) return pair endif return '' endfunction function! AutoPairsInit() let b:autopairs_loaded = 1 let b:autopairs_enabled = 1 " buffer level map pairs keys for [open, close] in items(g:AutoPairs) call AutoPairsMap(open) if open != close call AutoPairsMap(close) end let g:AutoPairsClosedPairs[close] = open endfor " Still use level mapping for if g:AutoPairsMapBS " Use instead of for issue #14 sometimes press BS output strange words execute 'inoremap =AutoPairsDelete()' end if g:AutoPairsMapSpace execute 'inoremap =AutoPairsSpace()' end if g:AutoPairsShortcutFastWrap != '' execute 'inoremap '.g:AutoPairsShortcutFastWrap.' =AutoPairsFastWrap()' end if g:AutoPairsShortcutBackInsert != '' execute 'inoremap '.g:AutoPairsShortcutBackInsert.' =AutoPairsBackInsert()' end if g:AutoPairsShortcutToggle != '' " use to ensure showing the status when toggle execute 'inoremap '.g:AutoPairsShortcutToggle.' AutoPairsToggle()' execute 'noremap '.g:AutoPairsShortcutToggle.' :call AutoPairsToggle()' end if g:AutoPairsShortcutJump != '' execute 'inoremap ' . g:AutoPairsShortcutJump. ' :call AutoPairsJump()a' execute 'noremap ' . g:AutoPairsShortcutJump. ' :call AutoPairsJump()' end endfunction function! s:ExpandMap(map) let map = a:map if map =~ '' let map = substitute(map, '\(\w\+\)', '\=maparg(submatch(1), "i")', 'g') endif return map endfunction function! AutoPairsForceInit() if exists('b:autopairs_loaded') return end " for auto-pairs starts with 'a', so the priority is higher than supertab and vim-endwise " " vim-endwise doesn't support AutoPairsReturn " when use AutoPairsReturn will cause isn't expanded " " supertab doesn't support AutoPairsReturn " when use AutoPairsReturn will cause Duplicated " " and when load after vim-endwise will cause unexpected endwise inserted. " so always load AutoPairs at last " Buffer level keys mapping " comptible with other plugin if g:AutoPairsMapCR let old_cr = maparg('', 'i') if old_cr == '' let old_cr = '' else let old_cr = s:ExpandMap(old_cr) endif " compatible with clang_complete " https://github.com/jiangmiao/auto-pairs/issues/18 let pattern = '\d\+_HandlePossibleSelectionEnter()' if old_cr =~ pattern execute 'imap