delimitMate.vim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. " File: plugin/delimitMate.vim
  2. " Version: 2.7
  3. " Modified: 2013-07-15
  4. " Description: This plugin provides auto-completion for quotes, parens, etc.
  5. " Maintainer: Israel Chauca F. <israelchauca@gmail.com>
  6. " Manual: Read ":help delimitMate".
  7. " ============================================================================
  8. " Initialization: {{{
  9. if exists("g:loaded_delimitMate") || &cp
  10. " User doesn't want this plugin or compatible is set, let's get out!
  11. finish
  12. endif
  13. let g:loaded_delimitMate = 1
  14. let save_cpo = &cpo
  15. set cpo&vim
  16. if v:version < 700
  17. echoerr "delimitMate: this plugin requires vim >= 7!"
  18. finish
  19. endif
  20. let s:loaded_delimitMate = 1
  21. let delimitMate_version = "2.8"
  22. "}}}
  23. " Functions: {{{
  24. function! s:option_init(name, default) "{{{
  25. let opt_name = "delimitMate_" . a:name
  26. " Find value to use.
  27. if !has_key(b:, opt_name) && !has_key(g:, opt_name)
  28. let value = a:default
  29. elseif has_key(b:, opt_name)
  30. let value = b:[opt_name]
  31. else
  32. let value = g:[opt_name]
  33. endif
  34. call s:set(a:name, value)
  35. endfunction "}}}
  36. function! s:init() "{{{
  37. " Initialize variables:
  38. " autoclose
  39. call s:option_init("autoclose", 1)
  40. " matchpairs
  41. call s:option_init("matchpairs", string(&matchpairs)[1:-2])
  42. call s:option_init("matchpairs_list", map(split(s:get('matchpairs', ''), '.:.\zs,\ze.:.'), 'split(v:val, ''^.\zs:\ze.$'')'))
  43. let pairs = s:get('matchpairs_list', [])
  44. if len(filter(pairs, 'v:val[0] ==# v:val[1]'))
  45. echohl ErrorMsg
  46. echom 'delimitMate: each member of a pair in delimitMate_matchpairs must be different from each other.'
  47. echom 'delimitMate: invalid pairs: ' . join(map(pairs, 'join(v:val, ":")'), ', ')
  48. echohl Normal
  49. return 0
  50. endif
  51. call s:option_init("left_delims", map(copy(s:get('matchpairs_list', [])), 'v:val[0]'))
  52. call s:option_init("right_delims", map(copy(s:get('matchpairs_list', [])), 'v:val[1]'))
  53. " quotes
  54. call s:option_init("quotes", "\" ' `")
  55. call s:option_init("quotes_list",split(s:get('quotes', ''), '\s\+'))
  56. " nesting_quotes
  57. call s:option_init("nesting_quotes", [])
  58. " excluded_regions
  59. call s:option_init("excluded_regions", "Comment")
  60. call s:option_init("excluded_regions_list", split(s:get('excluded_regions', ''), ',\s*'))
  61. let enabled = len(s:get('excluded_regions_list', [])) > 0
  62. call s:option_init("excluded_regions_enabled", enabled)
  63. " expand_space
  64. if exists("b:delimitMate_expand_space") && type(b:delimitMate_expand_space) == type("")
  65. echom "b:delimitMate_expand_space is '".b:delimitMate_expand_space."' but it must be either 1 or 0!"
  66. echom "Read :help 'delimitMate_expand_space' for more details."
  67. unlet b:delimitMate_expand_space
  68. let b:delimitMate_expand_space = 1
  69. endif
  70. if exists("g:delimitMate_expand_space") && type(g:delimitMate_expand_space) == type("")
  71. echom "delimitMate_expand_space is '".g:delimitMate_expand_space."' but it must be either 1 or 0!"
  72. echom "Read :help 'delimitMate_expand_space' for more details."
  73. unlet g:delimitMate_expand_space
  74. let g:delimitMate_expand_space = 1
  75. endif
  76. call s:option_init("expand_space", 0)
  77. " expand_cr
  78. if exists("b:delimitMate_expand_cr") && type(b:delimitMate_expand_cr) == type("")
  79. echom "b:delimitMate_expand_cr is '".b:delimitMate_expand_cr."' but it must be either 1 or 0!"
  80. echom "Read :help 'delimitMate_expand_cr' for more details."
  81. unlet b:delimitMate_expand_cr
  82. let b:delimitMate_expand_cr = 1
  83. endif
  84. if exists("g:delimitMate_expand_cr") && type(g:delimitMate_expand_cr) == type("")
  85. echom "delimitMate_expand_cr is '".g:delimitMate_expand_cr."' but it must be either 1 or 0!"
  86. echom "Read :help 'delimitMate_expand_cr' for more details."
  87. unlet g:delimitMate_expand_cr
  88. let g:delimitMate_expand_cr = 1
  89. endif
  90. if ((&backspace !~ 'eol' || &backspace !~ 'start') && &backspace != 2) &&
  91. \ ((exists('b:delimitMate_expand_cr') && b:delimitMate_expand_cr == 1) ||
  92. \ (exists('g:delimitMate_expand_cr') && g:delimitMate_expand_cr == 1))
  93. echom "delimitMate: There seems to be some incompatibility with your settings that may interfer with the expansion of <CR>. See :help 'delimitMate_expand_cr' for details."
  94. endif
  95. call s:option_init("expand_cr", 0)
  96. " expand_in_quotes
  97. call s:option_init('expand_inside_quotes', 0)
  98. " jump_expansion
  99. call s:option_init("jump_expansion", 0)
  100. " smart_matchpairs
  101. call s:option_init("smart_matchpairs", '^\%(\w\|\!\|[£$]\|[^[:punct:][:space:]]\)')
  102. " smart_quotes
  103. " XXX: backward compatibility. Ugly, should go the way of the dodo soon.
  104. let quotes = escape(join(s:get('quotes_list', []), ''), '\-^[]')
  105. let default_smart_quotes = '\%(\w\|[^[:punct:][:space:]' . quotes . ']\|\%(\\\\\)*\\\)\%#\|\%#\%(\w\|[^[:space:][:punct:]' . quotes . ']\)'
  106. if exists('g:delimitMate_smart_quotes') && type(g:delimitMate_smart_quotes) == type(0)
  107. if g:delimitMate_smart_quotes
  108. unlet g:delimitMate_smart_quotes
  109. else
  110. unlet g:delimitMate_smart_quotes
  111. let g:delimitMate_smart_quotes = ''
  112. endif
  113. endif
  114. if exists('b:delimitMate_smart_quotes') && type(b:delimitMate_smart_quotes) == type(0)
  115. if b:delimitMate_smart_quotes
  116. unlet b:delimitMate_smart_quotes
  117. if exists('g:delimitMate_smart_quotes') && type(g:delimitMate_smart_quotes) && g:delimitMate_smart_quotes
  118. let b:delimitMate_smart_quotes = default_smart_quotes
  119. endif
  120. else
  121. unlet b:delimitMate_smart_quotes
  122. let b:delimitMate_smart_quotes = ''
  123. endif
  124. endif
  125. call s:option_init("smart_quotes", default_smart_quotes)
  126. " apostrophes
  127. call s:option_init("apostrophes", "")
  128. call s:option_init("apostrophes_list", split(s:get('apostrophes', ''), ":\s*"))
  129. " tab2exit
  130. call s:option_init("tab2exit", 1)
  131. " balance_matchpairs
  132. call s:option_init("balance_matchpairs", 0)
  133. " eol marker
  134. call s:option_init("insert_eol_marker", 1)
  135. call s:option_init("eol_marker", "")
  136. " Everything is fine.
  137. return 1
  138. endfunction "}}} Init()
  139. function! s:get(name, default) "{{{
  140. let bufoptions = delimitMate#Get()
  141. return get(bufoptions, a:name, a:default)
  142. endfunction "}}}
  143. function! s:set(...) " {{{
  144. return call('delimitMate#Set', a:000)
  145. endfunction " }}}
  146. function! s:Map() "{{{
  147. " Set mappings:
  148. try
  149. let save_keymap = &keymap
  150. let save_iminsert = &iminsert
  151. let save_imsearch = &imsearch
  152. let save_cpo = &cpo
  153. set keymap=
  154. set cpo&vim
  155. silent! doautocmd <nomodeline> User delimitMate_map
  156. if s:get('autoclose', 1)
  157. call s:AutoClose()
  158. else
  159. call s:NoAutoClose()
  160. endif
  161. call s:ExtraMappings()
  162. finally
  163. let &cpo = save_cpo
  164. let &keymap = save_keymap
  165. let &iminsert = save_iminsert
  166. let &imsearch = save_imsearch
  167. endtry
  168. let b:delimitMate_enabled = 1
  169. endfunction "}}} Map()
  170. function! s:Unmap() " {{{
  171. let imaps =
  172. \ s:get('right_delims', []) +
  173. \ s:get('left_delims', []) +
  174. \ s:get('quotes_list', []) +
  175. \ s:get('apostrophes_list', []) +
  176. \ ['<BS>', '<C-h>', '<S-BS>', '<Del>', '<CR>', '<Space>', '<S-Tab>', '<Esc>'] +
  177. \ ['<Up>', '<Down>', '<Left>', '<Right>', '<LeftMouse>', '<RightMouse>'] +
  178. \ ['<C-Left>', '<C-Right>'] +
  179. \ ['<Home>', '<End>', '<PageUp>', '<PageDown>', '<S-Down>', '<S-Up>', '<C-G>g']
  180. for map in imaps
  181. if maparg(map, "i") =~# '^<Plug>delimitMate'
  182. if map == '|'
  183. let map = '<Bar>'
  184. endif
  185. exec 'silent! iunmap <buffer> ' . map
  186. endif
  187. endfor
  188. silent! doautocmd <nomodeline> User delimitMate_unmap
  189. let b:delimitMate_enabled = 0
  190. endfunction " }}} s:Unmap()
  191. function! s:test() "{{{
  192. if &modified
  193. let confirm = input("Modified buffer, type \"yes\" to write and proceed "
  194. \ . "with test: ") ==? 'yes'
  195. if !confirm
  196. return
  197. endif
  198. endif
  199. call delimitMate#Test()
  200. g/\%^$/d
  201. 0
  202. endfunction "}}}
  203. function! s:setup(...) "{{{
  204. let swap = a:0 && a:1 == 2
  205. let enable = a:0 && a:1
  206. let disable = a:0 && !a:1
  207. " First, remove all magic, if needed:
  208. if get(b:, 'delimitMate_enabled', 0)
  209. call s:Unmap()
  210. " Switch
  211. if swap
  212. echo "delimitMate is disabled."
  213. return
  214. endif
  215. endif
  216. if disable
  217. " Just disable the mappings.
  218. return
  219. endif
  220. if !a:0
  221. " Check if this file type is excluded:
  222. if exists("g:delimitMate_excluded_ft") &&
  223. \ index(split(g:delimitMate_excluded_ft, ','), &filetype, 0, 1) >= 0
  224. " Finish here:
  225. return 1
  226. endif
  227. " Check if user tried to disable using b:loaded_delimitMate
  228. if exists("b:loaded_delimitMate")
  229. return 1
  230. endif
  231. endif
  232. " Initialize settings:
  233. if ! s:init()
  234. " Something went wrong.
  235. return
  236. endif
  237. if enable || swap || !get(g:, 'delimitMate_offByDefault', 0)
  238. " Now, add magic:
  239. call s:Map()
  240. if a:0
  241. echo "delimitMate is enabled."
  242. endif
  243. endif
  244. endfunction "}}}
  245. function! s:TriggerAbb() "{{{
  246. if v:version < 703
  247. \ || ( v:version == 703 && !has('patch489') )
  248. \ || pumvisible()
  249. return ''
  250. endif
  251. return "\<C-]>"
  252. endfunction "}}}
  253. function! s:NoAutoClose() "{{{
  254. " inoremap <buffer> ) <C-R>=delimitMate#SkipDelim('\)')<CR>
  255. for delim in s:get('right_delims', []) + s:get('quotes_list', [])
  256. if delim == '|'
  257. let delim = '<Bar>'
  258. endif
  259. exec 'inoremap <silent> <Plug>delimitMate' . delim . ' <C-R>=<SID>TriggerAbb().delimitMate#SkipDelim("' . escape(delim,'"') . '")<CR>'
  260. exec 'silent! imap <unique> <buffer> '.delim.' <Plug>delimitMate'.delim
  261. endfor
  262. endfunction "}}}
  263. function! s:AutoClose() "{{{
  264. " Add matching pair and jump to the midle:
  265. " inoremap <silent> <buffer> ( ()<Left>
  266. let i = 0
  267. while i < len(s:get('matchpairs_list', []))
  268. let ld = s:get('left_delims', [])[i] == '|' ? '<bar>' : s:get('left_delims', [])[i]
  269. let rd = s:get('right_delims', [])[i] == '|' ? '<bar>' : s:get('right_delims', [])[i]
  270. exec 'inoremap <expr><silent> <Plug>delimitMate' . ld
  271. \. ' <SID>TriggerAbb().delimitMate#ParenDelim("' . escape(rd, '|') . '")'
  272. exec 'silent! imap <unique> <buffer> '.ld
  273. \.' <Plug>delimitMate'.ld
  274. let i += 1
  275. endwhile
  276. " Exit from inside the matching pair:
  277. for delim in s:get('right_delims', [])
  278. let delim = delim == '|' ? '<bar>' : delim
  279. exec 'inoremap <expr><silent> <Plug>delimitMate' . delim
  280. \. ' <SID>TriggerAbb().delimitMate#JumpOut("\' . delim . '")'
  281. exec 'silent! imap <unique> <buffer> ' . delim
  282. \. ' <Plug>delimitMate'. delim
  283. endfor
  284. " Add matching quote and jump to the midle, or exit if inside a pair of matching quotes:
  285. " inoremap <silent> <buffer> " <C-R>=delimitMate#QuoteDelim("\"")<CR>
  286. for delim in s:get('quotes_list', [])
  287. if delim == '|'
  288. let delim = '<Bar>'
  289. endif
  290. exec 'inoremap <expr><silent> <Plug>delimitMate' . delim
  291. \. ' <SID>TriggerAbb()."<C-R>=delimitMate#QuoteDelim(\"\\\' . delim . '\")<CR>"'
  292. exec 'silent! imap <unique> <buffer> ' . delim
  293. \. ' <Plug>delimitMate' . delim
  294. endfor
  295. " Try to fix the use of apostrophes (kept for backward compatibility):
  296. " inoremap <silent> <buffer> n't n't
  297. for map in s:get('apostrophes_list', [])
  298. exec "inoremap <silent> " . map . " " . map
  299. exec 'silent! imap <unique> <buffer> ' . map . ' <Plug>delimitMate' . map
  300. endfor
  301. endfunction "}}}
  302. function! s:ExtraMappings() "{{{
  303. " If pair is empty, delete both delimiters:
  304. inoremap <silent> <Plug>delimitMateBS <C-R>=delimitMate#BS()<CR>
  305. if !hasmapto('<Plug>delimitMateBS','i')
  306. if empty(maparg('<BS>', 'i'))
  307. silent! imap <unique> <buffer> <BS> <Plug>delimitMateBS
  308. endif
  309. if empty(maparg('<C-H>', 'i'))
  310. silent! imap <unique> <buffer> <C-h> <Plug>delimitMateBS
  311. endif
  312. endif
  313. " If pair is empty, delete closing delimiter:
  314. inoremap <silent> <expr> <Plug>delimitMateS-BS delimitMate#WithinEmptyPair() ? "\<Del>" : "\<S-BS>"
  315. if !hasmapto('<Plug>delimitMateS-BS','i') && maparg('<S-BS>', 'i') == ''
  316. silent! imap <unique> <buffer> <S-BS> <Plug>delimitMateS-BS
  317. endif
  318. " Expand return if inside an empty pair:
  319. inoremap <expr><silent> <Plug>delimitMateCR <SID>TriggerAbb()."\<C-R>=delimitMate#ExpandReturn()\<CR>"
  320. if s:get('expand_cr', 0) && !hasmapto('<Plug>delimitMateCR', 'i') && maparg('<CR>', 'i') == ''
  321. silent! imap <unique> <buffer> <CR> <Plug>delimitMateCR
  322. endif
  323. " Expand space if inside an empty pair:
  324. inoremap <expr><silent> <Plug>delimitMateSpace <SID>TriggerAbb()."\<C-R>=delimitMate#ExpandSpace()\<CR>"
  325. if s:get('expand_space', 0) && !hasmapto('<Plug>delimitMateSpace', 'i') && maparg('<Space>', 'i') == ''
  326. silent! imap <unique> <buffer> <Space> <Plug>delimitMateSpace
  327. endif
  328. " Jump over any delimiter:
  329. inoremap <expr><silent> <Plug>delimitMateS-Tab <SID>TriggerAbb()."\<C-R>=delimitMate#JumpAny()\<CR>"
  330. if s:get('tab2exit', 0) && !hasmapto('<Plug>delimitMateS-Tab', 'i') && maparg('<S-Tab>', 'i') == ''
  331. silent! imap <unique> <buffer> <S-Tab> <Plug>delimitMateS-Tab
  332. endif
  333. " Jump over next delimiters
  334. inoremap <expr><buffer> <Plug>delimitMateJumpMany <SID>TriggerAbb()."\<C-R>=delimitMate#JumpMany()\<CR>"
  335. if !hasmapto('<Plug>delimitMateJumpMany', 'i') && maparg("<C-G>g", 'i') == ''
  336. imap <silent> <buffer> <C-G>g <Plug>delimitMateJumpMany
  337. endif
  338. endfunction "}}}
  339. "}}}
  340. " Commands: {{{
  341. " Let me refresh without re-loading the buffer:
  342. command! -bar DelimitMateReload call s:setup(1)
  343. " Quick test:
  344. command! -bar DelimitMateTest call s:test()
  345. " Switch On/Off:
  346. command! -bar DelimitMateSwitch call s:setup(2)
  347. " Enable mappings:
  348. command! -bar DelimitMateOn call s:setup(1)
  349. " Disable mappings:
  350. command! -bar DelimitMateOff call s:setup(0)
  351. "}}}
  352. " Autocommands: {{{
  353. augroup delimitMate
  354. au!
  355. " Run on file type change.
  356. au FileType * call <SID>setup()
  357. au FileType python let b:delimitMate_nesting_quotes = ['"', "'"]
  358. " Run on new buffers.
  359. au BufNewFile,BufRead,BufEnter,CmdwinEnter *
  360. \ if !exists('b:delimitMate_was_here') |
  361. \ call <SID>setup() |
  362. \ let b:delimitMate_was_here = 1 |
  363. \ endif
  364. augroup END
  365. "}}}
  366. " This is for the default buffer when it does not have a filetype.
  367. call s:setup()
  368. let &cpo = save_cpo
  369. " GetLatestVimScripts: 2754 1 :AutoInstall: delimitMate.vim
  370. " vim:foldmethod=marker:foldcolumn=4:ts=2:sw=2