delimitMate.vim 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. " File: autoload/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. "let delimitMate_loaded = 1
  9. if !exists('s:options')
  10. let s:options = {}
  11. endif
  12. function! s:set(name, value) "{{{
  13. let bufnr = bufnr('%')
  14. if !has_key(s:options, bufnr)
  15. let s:options[bufnr] = {}
  16. endif
  17. let s:options[bufnr][a:name] = a:value
  18. endfunction "}}}
  19. function! s:get(...) "{{{
  20. let options = deepcopy(eval('s:options.' . bufnr('%')))
  21. if a:0
  22. return options[a:1]
  23. endif
  24. return options
  25. endfunction "}}}
  26. function! s:exists(name, ...) "{{{
  27. let scope = a:0 ? a:1 : 's'
  28. if scope == 's'
  29. let bufnr = bufnr('%')
  30. let name = 'options.' . bufnr . '.' . a:name
  31. else
  32. let name = 'delimitMate_' . a:name
  33. endif
  34. return exists(scope . ':' . name)
  35. endfunction "}}}
  36. function! s:is_jump(...) "{{{
  37. " Returns 1 if the next character is a closing delimiter.
  38. let char = s:get_char(0)
  39. let list = s:get('right_delims') + s:get('quotes_list')
  40. " Closing delimiter on the right.
  41. if (!a:0 && index(list, char) > -1)
  42. \ || (a:0 && char == a:1)
  43. return 1
  44. endif
  45. " Closing delimiter with space expansion.
  46. let nchar = s:get_char(1)
  47. if !a:0 && s:get('expand_space') && char == " "
  48. if index(list, nchar) > -1
  49. return 2
  50. endif
  51. elseif a:0 && s:get('expand_space') && nchar == a:1 && char == ' '
  52. return 3
  53. endif
  54. if !s:get('jump_expansion')
  55. return 0
  56. endif
  57. " Closing delimiter with CR expansion.
  58. let uchar = matchstr(getline(line('.') + 1), '^\s*\zs\S')
  59. if !a:0 && s:get('expand_cr') && char == ""
  60. if index(list, uchar) > -1
  61. return 4
  62. endif
  63. elseif a:0 && s:get('expand_cr') && uchar == a:1
  64. return 5
  65. endif
  66. return 0
  67. endfunction "}}}
  68. function! s:rquote(char) "{{{
  69. let pos = matchstr(getline('.')[col('.') : ], escape(a:char, '[]*.^$\'), 1)
  70. let i = 0
  71. while s:get_char(i) ==# a:char
  72. let i += 1
  73. endwhile
  74. return i
  75. endfunction "}}}
  76. function! s:lquote(char) "{{{
  77. let i = 0
  78. while s:get_char(i - 1) ==# a:char
  79. let i -= 1
  80. endwhile
  81. return i * -1
  82. endfunction "}}}
  83. function! s:get_char(...) "{{{
  84. let idx = col('.') - 1
  85. if !a:0 || (a:0 && a:1 >= 0)
  86. " Get char from cursor.
  87. let line = getline('.')[idx :]
  88. let pos = a:0 ? a:1 : 0
  89. return matchstr(line, '^'.repeat('.', pos).'\zs.')
  90. endif
  91. " Get char behind cursor.
  92. let line = getline('.')[: idx - 1]
  93. let pos = 0 - (1 + a:1)
  94. return matchstr(line, '.\ze'.repeat('.', pos).'$')
  95. endfunction "s:get_char }}}
  96. function! s:is_cr_expansion(...) " {{{
  97. let nchar = getline(line('.')-1)[-1:]
  98. let schar = matchstr(getline(line('.')+1), '^\s*\zs\S')
  99. let isEmpty = a:0 ? getline('.') =~ '^\s*$' : empty(getline('.'))
  100. if index(s:get('left_delims'), nchar) > -1
  101. \ && index(s:get('left_delims'), nchar)
  102. \ == index(s:get('right_delims'), schar)
  103. \ && isEmpty
  104. return 1
  105. elseif index(s:get('quotes_list'), nchar) > -1
  106. \ && index(s:get('quotes_list'), nchar)
  107. \ == index(s:get('quotes_list'), schar)
  108. \ && isEmpty
  109. return 1
  110. else
  111. return 0
  112. endif
  113. endfunction " }}} s:is_cr_expansion()
  114. function! s:is_space_expansion() " {{{
  115. if col('.') > 2
  116. let pchar = s:get_char(-2)
  117. let nchar = s:get_char(1)
  118. let isSpaces =
  119. \ (s:get_char(-1)
  120. \ == s:get_char(0)
  121. \ && s:get_char(-1) == " ")
  122. if index(s:get('left_delims'), pchar) > -1 &&
  123. \ index(s:get('left_delims'), pchar)
  124. \ == index(s:get('right_delims'), nchar) &&
  125. \ isSpaces
  126. return 1
  127. elseif index(s:get('quotes_list'), pchar) > -1 &&
  128. \ index(s:get('quotes_list'), pchar)
  129. \ == index(s:get('quotes_list'), nchar) &&
  130. \ isSpaces
  131. return 1
  132. endif
  133. endif
  134. return 0
  135. endfunction " }}} IsSpaceExpansion()
  136. function! s:is_empty_matchpair() "{{{
  137. " get char before the cursor.
  138. let open = s:get_char(-1)
  139. let idx = index(s:get('left_delims'), open)
  140. if idx == -1
  141. return 0
  142. endif
  143. let close = get(s:get('right_delims'), idx, '')
  144. return close ==# s:get_char(0)
  145. endfunction "}}}
  146. function! s:is_empty_quotes() "{{{
  147. " get char before the cursor.
  148. let quote = s:get_char(-1)
  149. let idx = index(s:get('quotes_list'), quote)
  150. if idx == -1
  151. return 0
  152. endif
  153. return quote ==# s:get_char(0)
  154. endfunction "}}}
  155. function! s:cursor_idx() "{{{
  156. let idx = len(split(getline('.')[: col('.') - 1], '\zs')) - 1
  157. return idx
  158. endfunction "delimitMate#CursorCol }}}
  159. function! s:get_syn_name() "{{{
  160. let col = col('.')
  161. if col == col('$')
  162. let col = col - 1
  163. endif
  164. return synIDattr(synIDtrans(synID(line('.'), col, 1)), 'name')
  165. endfunction " }}}
  166. function! s:is_excluded_ft(ft) "{{{
  167. if !exists("g:delimitMate_excluded_ft")
  168. return 0
  169. endif
  170. return index(split(g:delimitMate_excluded_ft, ','), a:ft, 0, 1) >= 0
  171. endfunction "}}}
  172. function! s:is_forbidden(char) "{{{
  173. if s:is_excluded_ft(&filetype)
  174. return 1
  175. endif
  176. if !s:get('excluded_regions_enabled')
  177. return 0
  178. endif
  179. let region = s:get_syn_name()
  180. return index(s:get('excluded_regions_list'), region) >= 0
  181. endfunction "}}}
  182. function! s:balance_matchpairs(char) "{{{
  183. " Returns:
  184. " = 0 => Parens balanced.
  185. " > 0 => More opening parens.
  186. " < 0 => More closing parens.
  187. let line = getline('.')
  188. let col = s:cursor_idx() - 1
  189. let col = col >= 0 ? col : 0
  190. let list = split(line, '\zs')
  191. let left = s:get('left_delims')[index(s:get('right_delims'), a:char)]
  192. let right = a:char
  193. let opening = 0
  194. let closing = 0
  195. " If the cursor is not at the beginning, count what's behind it.
  196. if col > 0
  197. " Find the first opening paren:
  198. let start = index(list, left)
  199. " Must be before cursor:
  200. let start = start < col ? start : col - 1
  201. " Now count from the first opening until the cursor, this will prevent
  202. " extra closing parens from being counted.
  203. let opening = count(list[start : col - 1], left)
  204. let closing = count(list[start : col - 1], right)
  205. " I don't care if there are more closing parens than opening parens.
  206. let closing = closing > opening ? opening : closing
  207. endif
  208. " Evaluate parens from the cursor to the end:
  209. let opening += count(list[col :], left)
  210. let closing += count(list[col :], right)
  211. " Return the found balance:
  212. return opening - closing
  213. endfunction "}}}
  214. function! s:is_smart_quote(char) "{{{
  215. " TODO: Allow using a:char in the pattern.
  216. let tmp = s:get('smart_quotes')
  217. if empty(tmp)
  218. return 0
  219. endif
  220. let regex = matchstr(tmp, '^!\?\zs.*')
  221. " Flip matched value if regex starts with !
  222. let mod = tmp =~ '^!' ? [1, 0] : [0, 1]
  223. let matched = search(regex, 'ncb', line('.')) > 0
  224. let noescaped = substitute(getline('.'), '\\.', '', 'g')
  225. let odd = (count(split(noescaped, '\zs'), a:char) % 2)
  226. let result = mod[matched] || odd
  227. return result
  228. endfunction "delimitMate#SmartQuote }}}
  229. function! delimitMate#Set(...) "{{{
  230. return call('s:set', a:000)
  231. endfunction "}}}
  232. function! delimitMate#Get(...) "{{{
  233. return call('s:get', a:000)
  234. endfunction "}}}
  235. function! delimitMate#ShouldJump(...) "{{{
  236. return call('s:is_jump', a:000)
  237. endfunction "}}}
  238. function! delimitMate#IsEmptyPair(str) "{{{
  239. if strlen(substitute(a:str, ".", "x", "g")) != 2
  240. return 0
  241. endif
  242. let idx = index(s:get('left_delims'), matchstr(a:str, '^.'))
  243. if idx > -1 &&
  244. \ s:get('right_delims')[idx] == matchstr(a:str, '.$')
  245. return 1
  246. endif
  247. let idx = index(s:get('quotes_list'), matchstr(a:str, '^.'))
  248. if idx > -1 &&
  249. \ s:get('quotes_list')[idx] == matchstr(a:str, '.$')
  250. return 1
  251. endif
  252. return 0
  253. endfunction "}}}
  254. function! delimitMate#WithinEmptyPair() "{{{
  255. " if cursor is at column 1 return 0
  256. if col('.') == 1
  257. return 0
  258. endif
  259. " get char before the cursor.
  260. let char1 = s:get_char(-1)
  261. " get char under the cursor.
  262. let char2 = s:get_char(0)
  263. return delimitMate#IsEmptyPair( char1.char2 )
  264. endfunction "}}}
  265. function! delimitMate#SkipDelim(char) "{{{
  266. if s:is_forbidden(a:char)
  267. return a:char
  268. endif
  269. let col = col('.') - 1
  270. let line = getline('.')
  271. if col > 0
  272. let cur = s:get_char(0)
  273. let pre = s:get_char(-1)
  274. else
  275. let cur = s:get_char(0)
  276. let pre = ""
  277. endif
  278. if pre == "\\"
  279. " Escaped character
  280. return a:char
  281. elseif cur == a:char
  282. " Exit pair
  283. return a:char . "\<Del>"
  284. elseif delimitMate#IsEmptyPair( pre . a:char )
  285. " Add closing delimiter and jump back to the middle.
  286. return a:char . s:joinUndo() . "\<Left>"
  287. else
  288. " Nothing special here, return the same character.
  289. return a:char
  290. endif
  291. endfunction "}}}
  292. function! delimitMate#ParenDelim(right) " {{{
  293. let left = s:get('left_delims')[index(s:get('right_delims'),a:right)]
  294. if s:is_forbidden(a:right)
  295. return left
  296. endif
  297. " Try to balance matchpairs
  298. if s:get('balance_matchpairs') &&
  299. \ s:balance_matchpairs(a:right) < 0
  300. return left
  301. endif
  302. let line = getline('.')
  303. let col = col('.')-2
  304. if s:get('smart_matchpairs') != ''
  305. let smart_matchpairs = substitute(s:get('smart_matchpairs'), '\\!', left, 'g')
  306. let smart_matchpairs = substitute(smart_matchpairs, '\\#', a:right, 'g')
  307. if line[col+1:] =~ smart_matchpairs
  308. return left
  309. endif
  310. endif
  311. if len(line) == (col + 1) && s:get('insert_eol_marker') == 1
  312. let tail = s:get('eol_marker')
  313. else
  314. let tail = ''
  315. endif
  316. return left . a:right . tail . repeat(s:joinUndo() . "\<Left>", len(split(tail, '\zs')) + 1)
  317. endfunction " }}}
  318. function! delimitMate#QuoteDelim(char) "{{{
  319. if s:is_forbidden(a:char)
  320. return a:char
  321. endif
  322. let char_at = s:get_char(0)
  323. let char_before = s:get_char(-1)
  324. let nesting_on = index(s:get('nesting_quotes'), a:char) > -1
  325. let left_q = nesting_on ? s:lquote(a:char) : 0
  326. if nesting_on && left_q > 1
  327. " Nesting quotes.
  328. let right_q = s:rquote(a:char)
  329. let quotes = right_q > left_q + 1 ? 0 : left_q - right_q + 2
  330. let lefts = quotes - 1
  331. return repeat(a:char, quotes) . repeat(s:joinUndo() . "\<Left>", lefts)
  332. elseif char_at == a:char
  333. " Inside an empty pair, jump out
  334. return a:char . "\<Del>"
  335. elseif a:char == '"' && index(split(&ft, '\.'), "vim") != -1 && getline('.') =~ '^\s*$'
  336. " If we are in a vim file and it looks like we're starting a comment, do
  337. " not add a closing char.
  338. return a:char
  339. elseif s:is_smart_quote(a:char)
  340. " Seems like a smart quote, insert a single char.
  341. return a:char
  342. elseif (char_before == a:char && char_at != a:char)
  343. \ && !empty(s:get('smart_quotes'))
  344. " Seems like we have an unbalanced quote, insert one quotation
  345. " mark and jump to the middle.
  346. return a:char . s:joinUndo() . "\<Left>"
  347. else
  348. " Insert a pair and jump to the middle.
  349. let sufix = ''
  350. if !empty(s:get('eol_marker')) && col('.') - 1 == len(getline('.'))
  351. let idx = len(s:get('eol_marker')) * -1
  352. let marker = getline('.')[idx : ]
  353. let has_marker = marker == s:get('eol_marker')
  354. let sufix = !has_marker ? s:get('eol_marker') : ''
  355. endif
  356. return a:char . a:char . s:joinUndo() . "\<Left>"
  357. endif
  358. endfunction "}}}
  359. function! delimitMate#JumpOut(char) "{{{
  360. if s:is_forbidden(a:char)
  361. return a:char
  362. endif
  363. let jump = s:is_jump(a:char)
  364. if jump == 1
  365. " HACK: Instead of <Right>, we remove the char to be jumped over and
  366. " insert it again. This will trigger re-indenting via 'indentkeys'.
  367. " Ref: https://github.com/Raimondi/delimitMate/issues/168
  368. return "\<Del>".a:char
  369. elseif jump == 3
  370. return s:joinUndo() . "\<Right>" . s:joinUndo() . "\<Right>"
  371. elseif jump == 5
  372. return "\<Down>\<C-O>I" . s:joinUndo() . "\<Right>"
  373. else
  374. return a:char
  375. endif
  376. endfunction " }}}
  377. function! delimitMate#JumpAny(...) " {{{
  378. if s:is_forbidden('')
  379. return ''
  380. endif
  381. if !s:is_jump()
  382. return ''
  383. endif
  384. " Let's get the character on the right.
  385. let char = s:get_char(0)
  386. if char == " "
  387. " Space expansion.
  388. return s:joinUndo() . "\<Right>" . s:joinUndo() . "\<Right>"
  389. elseif char == ""
  390. " CR expansion.
  391. return "\<CR>" . getline(line('.') + 1)[0] . "\<Del>\<Del>"
  392. else
  393. return s:joinUndo() . "\<Right>"
  394. endif
  395. endfunction " delimitMate#JumpAny() }}}
  396. function! delimitMate#JumpMany() " {{{
  397. let line = split(getline('.')[col('.') - 1 : ], '\zs')
  398. let rights = ""
  399. let found = 0
  400. for char in line
  401. if index(s:get('quotes_list'), char) >= 0 ||
  402. \ index(s:get('right_delims'), char) >= 0
  403. let rights .= s:joinUndo() . "\<Right>"
  404. let found = 1
  405. elseif found == 0
  406. let rights .= s:joinUndo() . "\<Right>"
  407. else
  408. break
  409. endif
  410. endfor
  411. if found == 1
  412. return rights
  413. else
  414. return ''
  415. endif
  416. endfunction " delimitMate#JumpMany() }}}
  417. function! delimitMate#ExpandReturn() "{{{
  418. if s:is_forbidden("")
  419. return "\<CR>"
  420. endif
  421. let escaped = s:cursor_idx() >= 2
  422. \ && s:get_char(-2) == '\'
  423. let expand_right_matchpair = s:get('expand_cr') == 2
  424. \ && index(s:get('right_delims'), s:get_char(0)) > -1
  425. let expand_inside_quotes = s:get('expand_inside_quotes')
  426. \ && s:is_empty_quotes()
  427. \ && !escaped
  428. let is_empty_matchpair = s:is_empty_matchpair()
  429. if !pumvisible( )
  430. \ && ( is_empty_matchpair
  431. \ || expand_right_matchpair
  432. \ || expand_inside_quotes)
  433. let val = "\<Esc>a"
  434. if is_empty_matchpair && s:get('insert_eol_marker') == 2
  435. \ && !search(escape(s:get('eol_marker'), '[]\.*^$').'$', 'cnW', '.')
  436. let tail = getline('.')[col('.') - 1 : ]
  437. let times = len(split(tail, '\zs'))
  438. let val .= repeat(s:joinUndo() . "\<Right>", times) . s:get('eol_marker') . repeat(s:joinUndo() . "\<Left>", times + 1)
  439. endif
  440. let val .= "\<CR>"
  441. if &smartindent && !&cindent && !&indentexpr
  442. \ && s:get_char(0) == '}'
  443. " indentation is controlled by 'smartindent', and the first character on
  444. " the new line is '}'. If this were typed manually it would reindent to
  445. " match the current line. Let's reproduce that behavior.
  446. let sw = &sw == 0 ? &ts : &sw
  447. let shifts = indent('.') / sw
  448. let spaces = indent('.') - (shifts * sw)
  449. let val .= "^\<C-D>".repeat("\<C-T>", shifts).repeat(' ', spaces)
  450. endif
  451. " Expand:
  452. " XXX zv prevents breaking expansion with syntax folding enabled by
  453. " InsertLeave.
  454. let val .= "\<Esc>zvO"
  455. return val
  456. else
  457. return "\<CR>"
  458. endif
  459. endfunction "}}}
  460. function! delimitMate#ExpandSpace() "{{{
  461. if s:is_forbidden("\<Space>")
  462. return "\<Space>"
  463. endif
  464. let escaped = s:cursor_idx() >= 2
  465. \ && s:get_char(-2) == '\'
  466. let expand_inside_quotes = s:get('expand_inside_quotes')
  467. \ && s:is_empty_quotes()
  468. \ && !escaped
  469. if s:is_empty_matchpair() || expand_inside_quotes
  470. " Expand:
  471. return "\<Space>\<Space>" . s:joinUndo() . "\<Left>"
  472. else
  473. return "\<Space>"
  474. endif
  475. endfunction "}}}
  476. function! delimitMate#BS() " {{{
  477. if s:is_forbidden("")
  478. let extra = ''
  479. elseif &bs !~ 'start\|2'
  480. let extra = ''
  481. elseif delimitMate#WithinEmptyPair()
  482. let extra = "\<Del>"
  483. elseif s:is_space_expansion()
  484. let extra = "\<Del>"
  485. elseif s:is_cr_expansion()
  486. let extra = repeat("\<Del>",
  487. \ len(matchstr(getline(line('.') + 1), '^\s*\S')))
  488. else
  489. let extra = ''
  490. endif
  491. return "\<BS>" . extra
  492. endfunction " }}} delimitMate#BS()
  493. function! delimitMate#Test() "{{{
  494. %d _
  495. " Check for script options:
  496. let result = [
  497. \ 'delimitMate Report',
  498. \ '==================',
  499. \ '',
  500. \ '* Options: ( ) default, (g) global, (b) buffer',
  501. \ '']
  502. for option in sort(keys(s:options[bufnr('%')]))
  503. if s:exists(option, 'b')
  504. let scope = '(b)'
  505. elseif s:exists(option, 'g')
  506. let scope = '(g)'
  507. else
  508. let scope = '( )'
  509. endif
  510. call add(result,
  511. \ scope . ' delimitMate_' . option
  512. \ . ' = '
  513. \ . string(s:get(option)))
  514. endfor
  515. call add(result, '')
  516. let option = 'delimitMate_excluded_ft'
  517. call add(result,
  518. \(exists('g:'.option) ? '(g) ' : '( ) g:') . option . ' = '
  519. \. string(get(g:, option, '')))
  520. call add(result, '--------------------')
  521. call add(result, '')
  522. " Check if mappings were set.
  523. let left_delims = s:get('autoclose') ? s:get('left_delims') : []
  524. let special_keys = ['<BS>', '<S-BS>', '<S-Tab>', '<C-G>g']
  525. if s:get('expand_cr')
  526. call add(special_keys, '<CR>')
  527. endif
  528. if s:get('expand_space')
  529. call add(special_keys, '<Space>')
  530. endif
  531. let maps =
  532. \ s:get('right_delims')
  533. \ + left_delims
  534. \ + s:get('quotes_list')
  535. \ + s:get('apostrophes_list')
  536. \ + special_keys
  537. call add(result, '* Mappings:')
  538. call add(result, '')
  539. for map in maps
  540. let output = ''
  541. if map == '|'
  542. let map = '<Bar>'
  543. endif
  544. redir => output | execute "verbose imap ".map | redir END
  545. call extend(result, split(output, '\n'))
  546. endfor
  547. call add(result, '--------------------')
  548. call add(result, '')
  549. call add(result, '* Showcase:')
  550. call add(result, '')
  551. call setline(1, result)
  552. call s:test_mappings(s:get('left_delims'), 1)
  553. call s:test_mappings(s:get('quotes_list'), 0)
  554. let result = []
  555. redir => setoptions
  556. echo " * Vim configuration:\<NL>"
  557. filetype
  558. echo ""
  559. set
  560. version
  561. redir END
  562. call extend(result, split(setoptions,"\n"))
  563. call add(result, '--------------------')
  564. setlocal nowrap
  565. call append('$', result)
  566. call feedkeys("\<Esc>\<Esc>", 'n')
  567. endfunction "}}}
  568. function! s:test_mappings(list, is_matchpair) "{{{
  569. let prefix = "normal Go0\<C-D>"
  570. let last = "|"
  571. let open = s:get('autoclose') ? 'Open: ' : 'Open & close: '
  572. for s in a:list
  573. if a:is_matchpair
  574. let pair = s:get('right_delims')[index(s:get('left_delims'), s)]
  575. else
  576. let pair = s
  577. endif
  578. if !s:get('autoclose')
  579. let s .= pair
  580. endif
  581. exec prefix . open . s . last
  582. exec prefix . "Delete: " . s . "\<BS>" . last
  583. exec prefix . "Exit: " . s . pair . last
  584. if s:get('expand_space')
  585. \ && (a:is_matchpair || s:get('expand_inside_quotes'))
  586. exec prefix . "Space: " . s . " " . last
  587. exec prefix . "Delete space: " . s . " \<BS>" . last
  588. endif
  589. if s:get('expand_cr')
  590. \ && (a:is_matchpair || s:get('expand_inside_quotes'))
  591. exec prefix . "Car return: " . s . "\<CR>" . last
  592. exec prefix . "Delete car return: " . s . "\<CR>0\<C-D>\<BS>" . last
  593. endif
  594. call append('$', '')
  595. endfor
  596. endfunction "}}}
  597. function! s:joinUndo() "{{{
  598. if v:version < 704
  599. \ || ( v:version == 704 && !has('patch849') )
  600. return ''
  601. endif
  602. return "\<C-G>U"
  603. endfunction "}}}
  604. " vim:foldmethod=marker:foldcolumn=4:ts=2:sw=2