auto-pairs/plugin/auto-pairs.vim

581 lines
14 KiB
VimL
Raw Normal View History

2011-05-24 07:31:55 -04:00
" Insert or delete brackets, parens, quotes in pairs.
2011-05-22 13:11:23 -04:00
" Maintainer: JiangMiao <jiangfriend@gmail.com>
2011-12-29 08:42:43 -05:00
" Contributor: camthompson
2019-01-14 15:18:06 -05:00
" Last Change: 2019-01-15
2019-01-15 04:17:06 -05:00
" Version: 2.0.0
" Homepage: http://www.vim.org/scripts/script.php?script_id=3599
2011-05-22 13:11:23 -04:00
" Repository: https://github.com/jiangmiao/auto-pairs
2013-03-17 07:32:25 -04:00
" License: MIT
2011-05-22 13:11:23 -04:00
if exists('g:AutoPairsLoaded') || &cp
finish
end
let g:AutoPairsLoaded = 1
if !exists('g:AutoPairs')
2012-01-17 00:14:56 -05:00
let g:AutoPairs = {'(':')', '[':']', '{':'}',"'":"'",'"':'"', '`':'`'}
2011-05-22 13:11:23 -04:00
end
if !exists('g:AutoPairsMapBS')
let g:AutoPairsMapBS = 1
end
" Map <C-h> as the same BS
if !exists('g:AutoPairsMapCh')
let g:AutoPairsMapCh = 1
end
if !exists('g:AutoPairsMapCR')
let g:AutoPairsMapCR = 1
end
2019-01-15 04:03:32 -05:00
if !exists('g:AutoPairsWildClosedPair')
let g:AutoPairsWildClosedPair = ']'
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 = '<M-p>'
end
2011-06-09 14:36:51 -04:00
if !exists('g:AutoPairsShortcutFastWrap')
let g:AutoPairsShortcutFastWrap = '<M-e>'
end
if !exists('g:AutoPairsMoveCharacter')
let g:AutoPairsMoveCharacter = "()[]{}\"'"
end
if !exists('g:AutoPairsShortcutJump')
let g:AutoPairsShortcutJump = '<M-n>'
endif
2012-05-13 21:54:25 -04:00
" 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')
2012-05-16 12:16:58 -04:00
let g:AutoPairsFlyMode = 0
2012-05-13 21:54:25 -04:00
endif
2011-05-24 07:07:26 -04:00
" When skipping the closed pair, look at the current and
" next line as well.
if !exists('g:AutoPairsMultilineClose')
let g:AutoPairsMultilineClose = 1
endif
2012-05-13 21:54:25 -04:00
" Work with Fly Mode, insert pair where jumped
if !exists('g:AutoPairsShortcutBackInsert')
let g:AutoPairsShortcutBackInsert = '<M-b>'
endif
if !exists('g:AutoPairsSmartQuotes')
let g:AutoPairsSmartQuotes = 1
endif
2016-03-27 15:06:51 -04:00
" 7.4.849 support <C-G>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
2016-11-14 05:52:47 -05:00
if v:version > 704 || v:version == 704 && has("patch849")
2016-03-27 15:06:51 -04:00
let s:Go = "\<C-G>U"
else
let s:Go = ""
endif
let s:Left = s:Go."\<LEFT>"
let s:Right = s:Go."\<RIGHT>"
2012-05-13 21:54:25 -04:00
2011-05-24 07:07:26 -04:00
2011-05-22 13:11:23 -04:00
2019-01-14 15:21:48 -05:00
" 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
2019-01-14 15:21:48 -05:00
func! s:delete(s)
return repeat("\<DEL>", s:ulen(a:s))
endf
func! s:backspace(s)
return repeat("\<BS>", s:ulen(a:s))
endf
func! s:getline(...)
2011-05-22 13:11:23 -04:00
let line = getline('.')
let pos = col('.') - 1
let before = strpart(line, 0, pos)
let after = strpart(line, pos)
if a:0 == 0 && g:AutoPairsMultilineClose
2019-01-14 15:21:48 -05:00
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 i = i+1
endwhile
end
2019-01-14 15:21:48 -05:00
return [before, after]
endf
2019-01-15 04:03:32 -05:00
" 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
2019-01-14 15:21:48 -05:00
" 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
2011-05-22 13:11:23 -04:00
end
2019-01-14 15:21:48 -05:00
return r
endf
2011-05-22 13:11:23 -04:00
2019-01-14 15:21:48 -05:00
func! AutoPairsInsert(key)
if !b:autopairs_enabled
2011-05-22 13:11:23 -04:00
return a:key
end
2019-01-14 15:21:48 -05:00
let b:autopairs_saved_pair = [a:key, getpos('.')]
2011-05-22 13:11:23 -04:00
2019-01-14 15:21:48 -05:00
let [before, after] = s:getline()
2019-01-14 15:21:48 -05:00
" Ignore auto close if prev character is \
if before[-1:-1] == '\'
2011-12-30 03:05:37 -05:00
return a:key
end
2019-01-14 15:21:48 -05:00
" check close pairs
for [open, close] in b:AutoPairsList
2019-01-15 04:17:06 -05:00
if a:key == g:AutoPairsWildClosedPair || close[0] == a:key
if open == close
let [before, after] = s:getline(0)
end
2019-01-15 04:17:06 -05:00
let m = s:matchbegin(after, '\v\s*\zs\V'.close)
2019-01-14 15:21:48 -05:00
if len(m) > 0
" skip close pair
2019-01-15 05:45:46 -05:00
let c = matchstr(after, '^\V'.close)
if c != ""
return s:right(c)
else
call search(m[1], 'We')
return "\<Right>"
end
2019-01-14 15:21:48 -05:00
end
2012-01-17 00:17:21 -05:00
end
2019-01-14 15:21:48 -05:00
endfor
2012-01-17 00:17:21 -05:00
2019-01-14 15:21:48 -05:00
" check open pairs
let text=before.a:key
for [open, close] in b:AutoPairsList
2019-01-15 04:03:32 -05:00
let m = s:matchend(text, open)
if len(m) > 0
2019-01-14 15:21:48 -05:00
" process the open pair
" remove inserted pair
2019-01-14 23:43:46 -05:00
" eg: if the pairs include < > and <!-- -->
2019-01-14 15:21:48 -05:00
" when <!-- is detected the inserted pair < > should be clean up
2019-01-15 04:03:32 -05:00
" <?php ?> should backspace 4 times php and <?
let target = m[1]
let openPair = m[2]
let text = before
let i = 0
while len(text) >= len(target) && target != text
let found = 0
" delete pair
for [o, c] in b:AutoPairsList
let m = s:matchend(text, o)
if len(m) > 0
let found = 1
let text = m[1]
let i = i + 1
break
end
endfor
if !found
" delete charactor
let m = s:matchend(text, '\v.')
if len(m) == 0
break
end
let i = i + 1
let text = m[1]
end
endwhile
let bs = repeat("\<BS>", i)
2019-01-14 15:21:48 -05:00
if bs != ''
call feedkeys(bs)
end
2019-01-15 04:03:32 -05:00
call feedkeys(openPair.close.s:left(close), "n")
2019-01-14 15:21:48 -05:00
return ""
" return m.close.s:left(close)
end
2019-01-14 15:21:48 -05:00
endfor
2019-01-14 15:21:48 -05:00
" Fly Mode, and the key is closed-pairs, search closed-pair and jump
if g:AutoPairsFlyMode && a:key =~ '\v[\}\]\)]'
if search(a:key, 'We')
return "\<Right>"
endif
endif
2019-01-14 15:21:48 -05:00
return a:key
endf
2011-05-22 13:11:23 -04:00
2019-01-14 15:21:48 -05:00
func! AutoPairsDelete()
if !b:autopairs_enabled
return "\<BS>"
end
2019-01-14 15:21:48 -05:00
let [before, after] = s:getline()
for [open, close] in b:AutoPairsList
2019-01-15 04:03:32 -05:00
let b = matchstr(before, '\V'.open.'\v\s?$')
let a = matchstr(after, '^\v\s*\V'.close)
2019-01-14 15:21:48 -05:00
if b != '' && a != ''
2019-01-15 04:03:32 -05:00
if b[-1:-1] == ' '
if a[0] == ' '
return "\<BS>\<DELETE>"
else
return "\<BS>"
end
end
2019-01-14 15:21:48 -05:00
return repeat("\<BS>", s:ulen(b)).repeat("\<DELETE>", s:ulen(a))
end
2019-01-14 15:21:48 -05:00
endfor
return "\<BS>"
endf
2019-01-14 15:21:48 -05:00
" Fast wrap the word in brackets
func! AutoPairsFastWrap()
2019-01-14 23:43:46 -05:00
let c = @"
2019-01-14 15:21:48 -05:00
normal! x
let [before, after] = s:getline()
2019-01-15 04:03:32 -05:00
if after[0] =~ '\v[\{\[\(\<]'
2019-01-14 15:21:48 -05:00
normal! %
normal! p
else
2019-01-15 04:03:32 -05:00
for [open, close] in b:AutoPairsList
if after =~ '^\s*\V'.open
call search(close, 'We')
normal! p
let @" = c
return ""
end
endfor
2019-01-14 15:21:48 -05:00
normal! e
normal! p
2011-05-22 13:11:23 -04:00
end
2019-01-14 23:43:46 -05:00
let @" = c
2019-01-14 15:21:48 -05:00
return ""
endf
2011-05-22 13:11:23 -04:00
2019-01-14 15:21:48 -05:00
func! AutoPairsJump()
call search('["\]'')}]','W')
endf
2019-01-14 15:21:48 -05:00
func! AutoPairsMoveCharacter(key)
let c = getline(".")[col(".")-1]
let escaped_key = substitute(a:key, "'", "''", 'g')
return "\<DEL>\<ESC>:call search("."'".escaped_key."'".")\<CR>a".c."\<LEFT>"
endf
2019-01-14 15:21:48 -05:00
func! 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 ''
endf
2011-05-22 13:11:23 -04:00
2019-01-14 15:21:48 -05:00
func! AutoPairsReturn()
if b:autopairs_enabled == 0
return ''
2011-05-24 07:07:26 -04:00
end
2019-01-14 15:21:48 -05:00
let before = getline(line('.')-1)
let after = getline('.')
let cmd = ''
for [open, close] in b:AutoPairsList
if before =~ '\V'.open.'\v\s*$' && after =~ '^\s*\V'.close
if g:AutoPairsCenterLine && winline() * 3 >= winheight(0) * 2
" Recenter before adding new line to avoid replacing line content
let cmd = "zz"
end
2011-05-24 07:07:26 -04:00
2019-01-14 15:21:48 -05:00
" If equalprg has been set, then avoid call =
" https://github.com/jiangmiao/auto-pairs/issues/24
if &equalprg != ''
return "\<ESC>".cmd."O"
endif
" conflict with javascript and coffee
" javascript need indent new line
" coffeescript forbid indent new line
if &filetype == 'coffeescript' || &filetype == 'coffee'
return "\<ESC>".cmd."k==o"
else
return "\<ESC>".cmd."=ko"
endif
end
2019-01-14 15:21:48 -05:00
endfor
return ''
endf
func! AutoPairsSpace()
if !b:autopairs_enabled
return "\<SPACE>"
2011-06-09 14:23:47 -04:00
end
2011-05-24 07:07:26 -04:00
2019-01-14 15:21:48 -05:00
let [before, after] = s:getline()
for [open, close] in b:AutoPairsList
if before =~ '\V'.open.'\v$' && after =~ '^\V'.close
return "\<SPACE>\<SPACE>".s:Left
end
endfor
return "\<SPACE>"
endf
func! AutoPairsMap(key)
2013-02-17 22:23:05 -05:00
" | is special key which separate map command from text
let key = a:key
if key == '|'
let key = '<BAR>'
end
let escaped_key = substitute(key, "'", "''", 'g')
2012-05-14 07:01:22 -04:00
" use expr will cause search() doesn't work
2013-02-17 22:23:05 -05:00
execute 'inoremap <buffer> <silent> '.key." <C-R>=AutoPairsInsert('".escaped_key."')<CR>"
2019-01-14 15:21:48 -05:00
endf
2011-05-22 13:11:23 -04:00
2019-01-14 15:21:48 -05:00
func! AutoPairsToggle()
if b:autopairs_enabled
let b:autopairs_enabled = 0
echo 'AutoPairs Disabled.'
else
let b:autopairs_enabled = 1
echo 'AutoPairs Enabled.'
end
return ''
2019-01-14 15:21:48 -05:00
endf
2019-01-14 15:21:48 -05:00
func! s:sortByLength(i1, i2)
return len(a:i2[0])-len(a:i1[0])
endf
2012-05-13 21:54:25 -04:00
2019-01-14 15:21:48 -05:00
func! AutoPairsInit()
let b:autopairs_loaded = 1
if !exists('b:autopairs_enabled')
let b:autopairs_enabled = 1
end
if !exists('b:AutoPairs')
let b:AutoPairs = g:AutoPairs
end
2019-01-14 15:21:48 -05:00
if !exists('b:AutoPairsMoveCharacter')
let b:AutoPairsMoveCharacter = g:AutoPairsMoveCharacter
end
2019-01-15 04:03:32 -05:00
let b:AutoPairsList = []
" buffer level map pairs keys
for [open, close] in items(b:AutoPairs)
2019-01-15 04:03:32 -05:00
let o = open[len(open)-1]
let m = matchlist(close, '\v(.*)//(.*)$')
let mapclose = 1
if len(m) > 0
if m[1] =~ 'n'
let mapclose = 0
end
let close = m[1]
end
let c = close[0]
call AutoPairsMap(o)
2019-01-15 05:45:46 -05:00
if o != c && c != '' && mapclose
2019-01-15 04:03:32 -05:00
call AutoPairsMap(c)
2011-05-22 13:11:23 -04:00
end
2019-01-15 04:03:32 -05:00
let b:AutoPairsList += [[open, close]]
2011-05-22 13:11:23 -04:00
endfor
2019-01-15 04:03:32 -05:00
" sort pairs by length, longer pair should have higher priority
let b:AutoPairsList = sort(b:AutoPairsList, "s:sortByLength")
for key in split(b:AutoPairsMoveCharacter, '\s*')
let escaped_key = substitute(key, "'", "''", 'g')
execute 'inoremap <silent> <buffer> <M-'.key."> <C-R>=AutoPairsMoveCharacter('".escaped_key."')<CR>"
endfor
" Still use <buffer> level mapping for <BS> <SPACE>
if g:AutoPairsMapBS
2012-05-14 23:26:53 -04:00
" Use <C-R> instead of <expr> for issue #14 sometimes press BS output strange words
execute 'inoremap <buffer> <silent> <BS> <C-R>=AutoPairsDelete()<CR>'
end
if g:AutoPairsMapCh
execute 'inoremap <buffer> <silent> <C-h> <C-R>=AutoPairsDelete()<CR>'
endif
if g:AutoPairsMapSpace
" Try to respect abbreviations on a <SPACE>
let do_abbrev = ""
if v:version == 703 && has("patch489") || v:version > 703
let do_abbrev = "<C-]>"
endif
execute 'inoremap <buffer> <silent> <SPACE> '.do_abbrev.'<C-R>=AutoPairsSpace()<CR>'
end
if g:AutoPairsShortcutFastWrap != ''
execute 'inoremap <buffer> <silent> '.g:AutoPairsShortcutFastWrap.' <C-R>=AutoPairsFastWrap()<CR>'
end
2012-05-13 21:54:25 -04:00
if g:AutoPairsShortcutBackInsert != ''
execute 'inoremap <buffer> <silent> '.g:AutoPairsShortcutBackInsert.' <C-R>=AutoPairsBackInsert()<CR>'
end
if g:AutoPairsShortcutToggle != ''
" use <expr> to ensure showing the status when toggle
execute 'inoremap <buffer> <silent> <expr> '.g:AutoPairsShortcutToggle.' AutoPairsToggle()'
execute 'noremap <buffer> <silent> '.g:AutoPairsShortcutToggle.' :call AutoPairsToggle()<CR>'
2011-05-22 13:11:23 -04:00
end
if g:AutoPairsShortcutJump != ''
execute 'inoremap <buffer> <silent> ' . g:AutoPairsShortcutJump. ' <ESC>:call AutoPairsJump()<CR>a'
execute 'noremap <buffer> <silent> ' . g:AutoPairsShortcutJump. ' :call AutoPairsJump()<CR>'
end
2019-01-12 09:58:06 -05:00
if &keymap != ''
let l:imsearch = &imsearch
let l:iminsert = &iminsert
let l:imdisable = &imdisable
execute 'setlocal keymap=' . &keymap
execute 'setlocal imsearch=' . l:imsearch
execute 'setlocal iminsert=' . l:iminsert
if l:imdisable
execute 'setlocal imdisable'
else
execute 'setlocal noimdisable'
end
end
2019-01-14 15:21:48 -05:00
endf
2011-05-22 13:11:23 -04:00
2019-01-14 15:21:48 -05:00
func! s:ExpandMap(map)
2012-05-14 04:06:43 -04:00
let map = a:map
2013-03-23 04:56:21 -04:00
let map = substitute(map, '\(<Plug>\w\+\)', '\=maparg(submatch(1), "i")', 'g')
2012-05-14 04:06:43 -04:00
return map
2019-01-14 15:21:48 -05:00
endf
2012-05-14 04:06:43 -04:00
2019-01-14 15:21:48 -05:00
func! AutoPairsTryInit()
if exists('b:autopairs_loaded')
return
2012-05-14 03:22:43 -04:00
end
2012-05-14 03:22:43 -04:00
" for auto-pairs starts with 'a', so the priority is higher than supertab and vim-endwise
"
2012-05-14 03:26:40 -04:00
" vim-endwise doesn't support <Plug>AutoPairsReturn
" when use <Plug>AutoPairsReturn will cause <Plug> isn't expanded
2012-05-14 03:22:43 -04:00
"
2012-05-14 03:26:40 -04:00
" supertab doesn't support <SID>AutoPairsReturn
2012-05-14 03:22:43 -04:00
" when use <SID>AutoPairsReturn will cause Duplicated <CR>
"
2016-11-21 01:29:39 -05:00
" and when load after vim-endwise will cause unexpected endwise inserted.
2012-05-14 04:06:43 -04:00
" so always load AutoPairs at last
2016-11-21 01:29:39 -05:00
2012-05-14 03:22:43 -04:00
" Buffer level keys mapping
" comptible with other plugin
if g:AutoPairsMapCR
if v:version == 703 && has('patch32') || v:version > 703
" VIM 7.3 supports advancer maparg which could get <expr> info
" then auto-pairs could remap <CR> in any case.
let info = maparg('<CR>', 'i', 0, 1)
if empty(info)
let old_cr = '<CR>'
let is_expr = 0
else
let old_cr = info['rhs']
let old_cr = s:ExpandMap(old_cr)
let old_cr = substitute(old_cr, '<SID>', '<SNR>' . info['sid'] . '_', 'g')
2013-03-27 05:24:17 -04:00
let is_expr = info['expr']
let wrapper_name = '<SID>AutoPairsOldCRWrapper73'
endif
2012-05-14 04:06:43 -04:00
else
" VIM version less than 7.3
" the mapping's <expr> info is lost, so guess it is expr or not, it's
" not accurate.
let old_cr = maparg('<CR>', 'i')
if old_cr == ''
let old_cr = '<CR>'
let is_expr = 0
else
let old_cr = s:ExpandMap(old_cr)
2013-03-27 05:24:17 -04:00
" old_cr contain (, I guess the old cr is in expr mode
2013-09-03 04:39:44 -04:00
let is_expr = old_cr =~ '\V(' && toupper(old_cr) !~ '\V<C-R>'
" The old_cr start with " it must be in expr mode
2013-09-03 04:39:44 -04:00
let is_expr = is_expr || old_cr =~ '\v^"'
let wrapper_name = '<SID>AutoPairsOldCRWrapper'
end
2013-03-23 04:56:21 -04:00
end
2012-05-14 03:22:43 -04:00
if old_cr !~ 'AutoPairsReturn'
if is_expr
" remap <expr> to `name` to avoid mix expr and non-expr mode
execute 'inoremap <buffer> <expr> <script> '. wrapper_name . ' ' . old_cr
let old_cr = wrapper_name
end
2013-07-13 00:00:27 -04:00
" Always silent mapping
execute 'inoremap <script> <buffer> <silent> <CR> '.old_cr.'<SID>AutoPairsReturn'
2012-05-14 03:22:43 -04:00
end
endif
2012-05-14 03:22:43 -04:00
call AutoPairsInit()
2019-01-14 15:21:48 -05:00
endf
2012-03-06 10:12:55 -05:00
" Always silent the command
inoremap <silent> <SID>AutoPairsReturn <C-R>=AutoPairsReturn()<CR>
imap <script> <Plug>AutoPairsReturn <SID>AutoPairsReturn
au BufEnter * :call AutoPairsTryInit()