PageRenderTime 66ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/plugin/paredit.vim

https://bitbucket.org/kovisoft/slimv/
Vim Script | 1848 lines | 1448 code | 130 blank | 270 comment | 493 complexity | 52a1e293e42c2d35c6acf5fe5a61bfb3 MD5 | raw file
Possible License(s): EPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. " paredit.vim:
  2. " Paredit mode for Slimv
  3. " Version: 0.9.13
  4. " Last Change: 05 Sep 2015
  5. " Maintainer: Tamas Kovacs <kovisoft at gmail dot com>
  6. " License: This file is placed in the public domain.
  7. " No warranty, express or implied.
  8. " *** *** Use At-Your-Own-Risk! *** ***
  9. "
  10. " =====================================================================
  11. "
  12. " Load Once:
  13. if &cp || exists( 'g:paredit_loaded' )
  14. finish
  15. endif
  16. let g:paredit_loaded = 1
  17. " Needed to load filetype and indent plugins
  18. filetype plugin on
  19. filetype indent on
  20. " =====================================================================
  21. " Global variable definitions
  22. " =====================================================================
  23. " Paredit mode selector
  24. if !exists( 'g:paredit_mode' )
  25. let g:paredit_mode = 1
  26. endif
  27. " Match delimiter this number of lines before and after cursor position
  28. if !exists( 'g:paredit_matchlines' )
  29. let g:paredit_matchlines = 100
  30. endif
  31. " Use short keymaps, i.e. J instead of <Leader>J
  32. if !exists( 'g:paredit_shortmaps' )
  33. let g:paredit_shortmaps = 0
  34. endif
  35. " Use smart jumping to the nearest paren, curly brace, or square bracket in
  36. " clojure
  37. if !exists( 'g:paredit_smartjump' )
  38. let g:paredit_smartjump = 0
  39. endif
  40. " Custom <Leader> for the Paredit plugin
  41. if !exists( 'g:paredit_leader' )
  42. if exists( 'mapleader' )
  43. let g:paredit_leader = '<leader>'
  44. else
  45. let g:paredit_leader = ','
  46. endif
  47. endif
  48. " Use 'Electric Return', i.e. add double newlines if enter pressed before a closing paren
  49. if !exists( 'g:paredit_electric_return' )
  50. let g:paredit_electric_return = 1
  51. endif
  52. " =====================================================================
  53. " Other variable definitions
  54. " =====================================================================
  55. " Skip matches inside string or comment or after '\'
  56. let s:skip_sc = '(synIDattr(synID(line("."), col("."), 0), "name") =~ "[Ss]tring\\|[Cc]omment\\|[Ss]pecial\\|clojureRegexp\\|clojurePattern" || getline(line("."))[col(".")-2] == "\\")'
  57. " Valid macro prefix characters
  58. let s:any_macro_prefix = "'" . '\|`\|#\|@\|\~\|,\|\^'
  59. " Repeat count for some remapped edit functions (like 'd')
  60. let s:repeat = 0
  61. let s:yank_pos = []
  62. " Filetypes with [] and {} pairs balanced as well
  63. let s:fts_balancing_all_brackets = '.*\(clojure\|hy\|scheme\|racket\).*'
  64. " =====================================================================
  65. " General utility functions
  66. " =====================================================================
  67. " Buffer specific initialization
  68. function! PareditInitBuffer()
  69. let b:paredit_init = 1
  70. " in case they are accidentally removed
  71. " Also define regular expressions to identify special characters used by paredit
  72. if &ft =~ s:fts_balancing_all_brackets
  73. let b:any_matched_char = '(\|)\|\[\|\]\|{\|}\|\"'
  74. let b:any_matched_pair = '()\|\[\]\|{}\|\"\"'
  75. let b:any_opening_char = '(\|\[\|{'
  76. let b:any_closing_char = ')\|\]\|}'
  77. let b:any_openclose_char = '(\|)\|\[\|\]\|{\|}'
  78. let b:any_wsopen_char = '\s\|(\|\[\|{'
  79. let b:any_wsclose_char = '\s\|)\|\]\|}'
  80. else
  81. let b:any_matched_char = '(\|)\|\"'
  82. let b:any_matched_pair = '()\|\"\"'
  83. let b:any_opening_char = '('
  84. let b:any_closing_char = ')'
  85. let b:any_openclose_char = '(\|)'
  86. let b:any_wsopen_char = '\s\|('
  87. let b:any_wsclose_char = '\s\|)'
  88. endif
  89. if g:paredit_mode
  90. " Paredit mode is on: add buffer specific keybindings
  91. inoremap <buffer> <expr> ( PareditInsertOpening('(',')')
  92. inoremap <buffer> <silent> ) <C-R>=PareditInsertClosing('(',')')<CR>
  93. inoremap <buffer> <expr> " PareditInsertQuotes()
  94. inoremap <buffer> <expr> <BS> PareditBackspace(0)
  95. inoremap <buffer> <expr> <C-h> PareditBackspace(0)
  96. inoremap <buffer> <expr> <Del> PareditDel()
  97. if &ft =~ s:fts_balancing_all_brackets && g:paredit_smartjump
  98. noremap <buffer> <silent> ( :<C-U>call PareditSmartJumpOpening(0)<CR>
  99. noremap <buffer> <silent> ) :<C-U>call PareditSmartJumpClosing(0)<CR>
  100. vnoremap <buffer> <silent> ( <Esc>:<C-U>call PareditSmartJumpOpening(1)<CR>
  101. vnoremap <buffer> <silent> ) <Esc>:<C-U>call PareditSmartJumpClosing(1)<CR>
  102. else
  103. noremap <buffer> <silent> ( :<C-U>call PareditFindOpening('(',')',0)<CR>
  104. noremap <buffer> <silent> ) :<C-U>call PareditFindClosing('(',')',0)<CR>
  105. vnoremap <buffer> <silent> ( <Esc>:<C-U>call PareditFindOpening('(',')',1)<CR>
  106. vnoremap <buffer> <silent> ) <Esc>:<C-U>call PareditFindClosing('(',')',1)<CR>
  107. endif
  108. noremap <buffer> <silent> [[ :<C-U>call PareditFindDefunBck()<CR>
  109. noremap <buffer> <silent> ]] :<C-U>call PareditFindDefunFwd()<CR>
  110. call RepeatableNNoRemap('x', ':<C-U>call PareditEraseFwd()')
  111. nnoremap <buffer> <silent> <Del> :<C-U>call PareditEraseFwd()<CR>
  112. call RepeatableNNoRemap('X', ':<C-U>call PareditEraseBck()')
  113. nnoremap <buffer> <silent> s :<C-U>call PareditEraseFwd()<CR>i
  114. call RepeatableNNoRemap('D', 'v$:<C-U>call PareditDelete(visualmode(),1)')
  115. nnoremap <buffer> <silent> C v$:<C-U>call PareditChange(visualmode(),1)<CR>
  116. nnoremap <buffer> <silent> d :<C-U>call PareditSetDelete(v:count)<CR>g@
  117. vnoremap <buffer> <silent> d :<C-U>call PareditDelete(visualmode(),1)<CR>
  118. vnoremap <buffer> <silent> x :<C-U>call PareditDelete(visualmode(),1)<CR>
  119. vnoremap <buffer> <silent> <Del> :<C-U>call PareditDelete(visualmode(),1)<CR>
  120. nnoremap <buffer> <silent> c :set opfunc=PareditChange<CR>g@
  121. vnoremap <buffer> <silent> c :<C-U>call PareditChange(visualmode(),1)<CR>
  122. call RepeatableNNoRemap('dd', ':<C-U>call PareditDeleteLines()')
  123. nnoremap <buffer> <silent> cc :<C-U>call PareditChangeLines()<CR>
  124. nnoremap <buffer> <silent> cw :<C-U>call PareditChangeSpec('cw',1)<CR>
  125. nnoremap <buffer> <silent> cW :set opfunc=PareditChange<CR>g@E
  126. nnoremap <buffer> <silent> cb :<C-U>call PareditChangeSpec('cb',0)<CR>
  127. nnoremap <buffer> <silent> ciw :<C-U>call PareditChangeSpec('ciw',1)<CR>
  128. nnoremap <buffer> <silent> caw :<C-U>call PareditChangeSpec('caw',1)<CR>
  129. call RepeatableNNoRemap('p', ':<C-U>call PareditPut("p")')
  130. call RepeatableNNoRemap('P', ':<C-U>call PareditPut("P")')
  131. call RepeatableNNoRemap(g:paredit_leader . 'w(', ':<C-U>call PareditWrap("(",")")')
  132. execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'w( :<C-U>call PareditWrapSelection("(",")")<CR>'
  133. call RepeatableNNoRemap(g:paredit_leader . 'w"', ':<C-U>call PareditWrap('."'".'"'."','".'"'."')")
  134. execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'w" :<C-U>call PareditWrapSelection('."'".'"'."','".'"'."')<CR>"
  135. " Spliec s-expression killing backward/forward
  136. execute 'nmap <buffer> <silent> ' . g:paredit_leader.'<Up> d[(:<C-U>call PareditSplice()<CR>'
  137. execute 'nmap <buffer> <silent> ' . g:paredit_leader.'<Down> d])%:<C-U>call PareditSplice()<CR>'
  138. call RepeatableNNoRemap(g:paredit_leader . 'I', ':<C-U>call PareditRaise()')
  139. if &ft =~ s:fts_balancing_all_brackets
  140. inoremap <buffer> <expr> [ PareditInsertOpening('[',']')
  141. inoremap <buffer> <silent> ] <C-R>=PareditInsertClosing('[',']')<CR>
  142. inoremap <buffer> <expr> { PareditInsertOpening('{','}')
  143. inoremap <buffer> <silent> } <C-R>=PareditInsertClosing('{','}')<CR>
  144. call RepeatableNNoRemap(g:paredit_leader . 'w[', ':<C-U>call PareditWrap("[","]")')
  145. execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'w[ :<C-U>call PareditWrapSelection("[","]")<CR>'
  146. call RepeatableNNoRemap(g:paredit_leader . 'w{', ':<C-U>call PareditWrap("{","}")')
  147. execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'w{ :<C-U>call PareditWrapSelection("{","}")<CR>'
  148. endif
  149. if g:paredit_shortmaps
  150. " Shorter keymaps: old functionality of KEY is remapped to <Leader>KEY
  151. call RepeatableNNoRemap('<', ':<C-U>call PareditMoveLeft()')
  152. call RepeatableNNoRemap('>', ':<C-U>call PareditMoveRight()')
  153. call RepeatableNNoRemap('O', ':<C-U>call PareditSplit()')
  154. call RepeatableNNoRemap('J', ':<C-U>call PareditJoin()')
  155. call RepeatableNNoRemap('W', ':<C-U>call PareditWrap("(",")")')
  156. vnoremap <buffer> <silent> W :<C-U>call PareditWrapSelection('(',')')<CR>
  157. call RepeatableNNoRemap('S', ':<C-U>call PareditSplice()')
  158. execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'< :<C-U>normal! <<CR>'
  159. execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'> :<C-U>normal! ><CR>'
  160. execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'O :<C-U>normal! O<CR>'
  161. execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'J :<C-U>normal! J<CR>'
  162. execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'W :<C-U>normal! W<CR>'
  163. execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'W :<C-U>normal! W<CR>'
  164. execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'S :<C-U>normal! S<CR>'
  165. else
  166. " Longer keymaps with <Leader> prefix
  167. nnoremap <buffer> <silent> S V:<C-U>call PareditChange(visualmode(),1)<CR>
  168. call RepeatableNNoRemap(g:paredit_leader . '<', ':<C-U>call PareditMoveLeft()')
  169. call RepeatableNNoRemap(g:paredit_leader . '>', ':<C-U>call PareditMoveRight()')
  170. call RepeatableNNoRemap(g:paredit_leader . 'O', ':<C-U>call PareditSplit()')
  171. call RepeatableNNoRemap(g:paredit_leader . 'J', ':<C-U>call PareditJoin()')
  172. call RepeatableNNoRemap(g:paredit_leader . 'W', ':<C-U>call PareditWrap("(",")")')
  173. execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'W :<C-U>call PareditWrapSelection("(",")")<CR>'
  174. call RepeatableNNoRemap(g:paredit_leader . 'S', ':<C-U>call PareditSplice()')
  175. endif
  176. if g:paredit_electric_return && mapcheck( "<CR>", "i" ) == ""
  177. " Do not override any possible mapping for <Enter>
  178. inoremap <buffer> <expr> <CR> PareditEnter()
  179. endif
  180. else
  181. " Paredit mode is off: remove keybindings
  182. silent! iunmap <buffer> (
  183. silent! iunmap <buffer> )
  184. silent! iunmap <buffer> "
  185. silent! iunmap <buffer> <BS>
  186. silent! iunmap <buffer> <C-h>
  187. silent! iunmap <buffer> <Del>
  188. silent! unmap <buffer> (
  189. silent! unmap <buffer> )
  190. silent! unmap <buffer> [[
  191. silent! unmap <buffer> ]]
  192. silent! unmap <buffer> x
  193. silent! unmap <buffer> <Del>
  194. silent! unmap <buffer> X
  195. silent! unmap <buffer> s
  196. silent! unmap <buffer> D
  197. silent! unmap <buffer> C
  198. silent! unmap <buffer> d
  199. silent! unmap <buffer> c
  200. silent! unmap <buffer> dd
  201. silent! unmap <buffer> cc
  202. silent! unmap <buffer> cw
  203. silent! unmap <buffer> cW
  204. silent! unmap <buffer> cb
  205. silent! unmap <buffer> ciw
  206. silent! unmap <buffer> caw
  207. if &ft =~ s:fts_balancing_all_brackets
  208. silent! iunmap <buffer> [
  209. silent! iunmap <buffer> ]
  210. silent! iunmap <buffer> {
  211. silent! iunmap <buffer> }
  212. endif
  213. if mapcheck( "<CR>", "i" ) == "PareditEnter()"
  214. " Remove only if we have added this mapping
  215. silent! iunmap <buffer> <CR>
  216. endif
  217. endif
  218. endfunction
  219. " Run the command normally but append a call to repeat#set afterwards
  220. function! RepeatableMap(map_type, keys, command)
  221. let escaped_keys = substitute(a:keys, '["<]', '\\\0', "g")
  222. execute a:map_type . ' <silent> <buffer> ' .
  223. \ a:keys . ' ' . a:command .
  224. \ '\|silent! call repeat#set("' . escaped_keys . '")<CR>'
  225. endfunction
  226. function! RepeatableNMap(keys, command)
  227. call RepeatableMap('nmap', a:keys, a:command)
  228. endfunction
  229. function! RepeatableNNoRemap(keys, command)
  230. call RepeatableMap('nnoremap', a:keys, a:command)
  231. endfunction
  232. " Include all prefix and special characters in 'iskeyword'
  233. function! s:SetKeyword()
  234. let old_value = &iskeyword
  235. if &ft =~ s:fts_balancing_all_brackets
  236. setlocal iskeyword+=+,-,*,/,%,<,=,>,:,$,?,!,@-@,94,~,#,\|,&
  237. else
  238. setlocal iskeyword+=+,-,*,/,%,<,=,>,:,$,?,!,@-@,94,~,#,\|,&,.,{,},[,]
  239. endif
  240. return old_value
  241. endfunction
  242. " General Paredit operator function
  243. function! PareditOpfunc( func, type, visualmode )
  244. let sel_save = &selection
  245. let ve_save = &virtualedit
  246. set virtualedit=all
  247. let regname = v:register
  248. let save_0 = getreg( '0' )
  249. if a:visualmode " Invoked from Visual mode, use '< and '> marks.
  250. silent exe "normal! `<" . a:type . "`>"
  251. elseif a:type == 'line'
  252. let &selection = "inclusive"
  253. silent exe "normal! '[V']"
  254. elseif a:type == 'block'
  255. let &selection = "inclusive"
  256. silent exe "normal! `[\<C-V>`]"
  257. else
  258. let &selection = "inclusive"
  259. silent exe "normal! `[v`]"
  260. endif
  261. if !g:paredit_mode || (a:visualmode && (a:type == 'block' || a:type == "\<C-V>"))
  262. " Block mode is too difficult to handle at the moment
  263. silent exe "normal! d"
  264. let putreg = getreg( '"' )
  265. else
  266. silent exe "normal! y"
  267. let putreg = getreg( '"' )
  268. if a:func == 'd'
  269. " Register "0 is corrupted by the above 'y' command
  270. call setreg( '0', save_0 )
  271. elseif a:visualmode && &selection == "inclusive" && len(getline("'>")) < col("'>") && len(putreg) > 0
  272. " Remove extra space added at the end of line when selection=inclusive, all, or onemore
  273. let putreg = putreg[:-2]
  274. endif
  275. " Find and keep unbalanced matched characters in the region
  276. let instring = s:InsideString( line("'<"), col("'<") )
  277. if col("'>") > 1 && !s:InsideString( line("'<"), col("'<") - 1 )
  278. " We are at the beginning of the string
  279. let instring = 0
  280. endif
  281. let matched = s:GetMatchedChars( putreg, instring, s:InsideComment( line("'<"), col("'<") ) )
  282. let matched = s:Unbalanced( matched )
  283. let matched = substitute( matched, '\s', '', 'g' )
  284. if matched == ''
  285. if a:func == 'c' && (a:type == 'v' || a:type == 'V' || a:type == 'char')
  286. silent exe "normal! gvc"
  287. else
  288. silent exe "normal! gvd"
  289. endif
  290. else
  291. silent exe "normal! gvc" . matched
  292. silent exe "normal! l"
  293. let offs = len(matched)
  294. if matched[0] =~ b:any_closing_char
  295. let offs = offs + 1
  296. endif
  297. if a:func == 'd'
  298. let offs = offs - 1
  299. elseif instring && matched == '"'
  300. " Keep cursor inside the double quotes
  301. let offs = offs + 1
  302. endif
  303. if offs > 0
  304. silent exe "normal! " . string(offs) . "h"
  305. endif
  306. endif
  307. endif
  308. let &selection = sel_save
  309. let &virtualedit = ve_save
  310. if a:func == 'd' && regname == '"'
  311. " Do not currupt the '"' register and hence the "0 register
  312. call setreg( '1', putreg )
  313. else
  314. call setreg( regname, putreg )
  315. endif
  316. endfunction
  317. " Set delete mode also saving repeat count
  318. function! PareditSetDelete( count )
  319. let s:repeat = a:count
  320. set opfunc=PareditDelete
  321. endfunction
  322. " General delete operator handling
  323. function! PareditDelete( type, ... )
  324. call PareditOpfunc( 'd', a:type, a:0 )
  325. if s:repeat > 1
  326. call feedkeys( (s:repeat-1) . "." )
  327. endif
  328. let s:repeat = 0
  329. endfunction
  330. " General change operator handling
  331. function! PareditChange( type, ... )
  332. let ve_save = &virtualedit
  333. set virtualedit=all
  334. call PareditOpfunc( 'c', a:type, a:0 )
  335. if len(getline('.')) == 0
  336. let v:lnum = line('.')
  337. let expr = &indentexpr
  338. if expr == ''
  339. " No special 'indentexpr', call default lisp indent
  340. let expr = 'lispindent(v:lnum)'
  341. endif
  342. execute "call setline( v:lnum, repeat( ' ', " . expr . " ) )"
  343. call cursor(v:lnum, len(getline(v:lnum))+1)
  344. else
  345. normal! l
  346. endif
  347. startinsert
  348. let &virtualedit = ve_save
  349. endfunction
  350. " Delete v:count number of lines
  351. function! PareditDeleteLines()
  352. if v:count > 1
  353. silent exe "normal! V" . (v:count-1) . "j\<Esc>"
  354. else
  355. silent exe "normal! V\<Esc>"
  356. endif
  357. call PareditDelete(visualmode(),1)
  358. endfunction
  359. " Change v:count number of lines
  360. function! PareditChangeLines()
  361. if v:count > 1
  362. silent exe "normal! V" . (v:count-1) . "j\<Esc>"
  363. else
  364. silent exe "normal! V\<Esc>"
  365. endif
  366. call PareditChange(visualmode(),1)
  367. endfunction
  368. " Handle special change command, e.g. cw
  369. " Check if we may revert to its original Vim function
  370. " This way '.' can be used to repeat the command
  371. function! PareditChangeSpec( cmd, dir )
  372. let line = getline( '.' )
  373. if a:dir == 0
  374. " Changing backwards
  375. let c = col( '.' ) - 2
  376. while c >= 0 && line[c] =~ b:any_matched_char
  377. " Shouldn't delete a matched character, just move left
  378. call feedkeys( 'h', 'n')
  379. let c = c - 1
  380. endwhile
  381. if c < 0 && line[0] =~ b:any_matched_char
  382. " Can't help, still on matched character, insert instead
  383. call feedkeys( 'i', 'n')
  384. return
  385. endif
  386. else
  387. " Changing forward
  388. let c = col( '.' ) - 1
  389. while c < len(line) && line[c] =~ b:any_matched_char
  390. " Shouldn't delete a matched character, just move right
  391. call feedkeys( 'l', 'n')
  392. let c = c + 1
  393. endwhile
  394. if c == len(line)
  395. " Can't help, still on matched character, append instead
  396. call feedkeys( 'a', 'n')
  397. return
  398. endif
  399. endif
  400. " Safe to use Vim's built-in change function
  401. call feedkeys( a:cmd, 'n')
  402. endfunction
  403. " Paste text from put register in a balanced way
  404. function! PareditPut( cmd )
  405. let regname = v:register
  406. let reg_save = getreg( regname )
  407. let putreg = reg_save
  408. " Find unpaired matched characters by eliminating paired ones
  409. let matched = s:GetMatchedChars( putreg, s:InsideString(), s:InsideComment() )
  410. let matched = s:Unbalanced( matched )
  411. if matched !~ '\S\+'
  412. " Register contents is balanced, perform default put function
  413. silent exe "normal! " . (v:count>1 ? v:count : '') . (regname=='"' ? '' : '"'.regname) . a:cmd
  414. return
  415. endif
  416. " Replace all unpaired matched characters with a space in order to keep balance
  417. let i = 0
  418. while i < len( putreg )
  419. if matched[i] !~ '\s'
  420. let putreg = strpart( putreg, 0, i ) . ' ' . strpart( putreg, i+1 )
  421. endif
  422. let i = i + 1
  423. endwhile
  424. " Store balanced text in put register and call the appropriate put command
  425. call setreg( regname, putreg )
  426. silent exe "normal! " . (v:count>1 ? v:count : '') . (regname=='"' ? '' : '"'.regname) . a:cmd
  427. call setreg( regname, reg_save )
  428. endfunction
  429. " Toggle paredit mode
  430. function! PareditToggle()
  431. " Don't disable paredit if it was not initialized yet for the current buffer
  432. if exists( 'b:paredit_init') || g:paredit_mode == 0
  433. let g:paredit_mode = 1 - g:paredit_mode
  434. endif
  435. echo g:paredit_mode ? 'Paredit mode on' : 'Paredit mode off'
  436. call PareditInitBuffer()
  437. endfunction
  438. " Does the current syntax item match the given regular expression?
  439. function! s:SynIDMatch( regexp, line, col, match_eol )
  440. let col = a:col
  441. if a:match_eol && col > len( getline( a:line ) )
  442. let col = col - 1
  443. endif
  444. return synIDattr( synID( a:line, col, 0), 'name' ) =~ a:regexp
  445. endfunction
  446. " Is the current cursor position inside a comment?
  447. function! s:InsideComment( ... )
  448. let l = a:0 ? a:1 : line('.')
  449. let c = a:0 ? a:2 : col('.')
  450. if &syntax == ''
  451. " No help from syntax engine,
  452. " remove strings and search for ';' up to the cursor position
  453. let line = strpart( getline(l), 0, c - 1 )
  454. let line = substitute( line, '\\"', '', 'g' )
  455. let line = substitute( line, '"[^"]*"', '', 'g' )
  456. return match( line, ';' ) >= 0
  457. endif
  458. if s:SynIDMatch( 'clojureComment', l, c, 1 )
  459. if strpart( getline(l), c-1, 2 ) == '#_' || strpart( getline(l), c-2, 2 ) == '#_'
  460. " This is a commented out clojure form of type #_(...), treat it as regular form
  461. return 0
  462. endif
  463. endif
  464. return s:SynIDMatch( '[Cc]omment', l, c, 1 )
  465. endfunction
  466. " Is the current cursor position inside a string?
  467. function! s:InsideString( ... )
  468. let l = a:0 ? a:1 : line('.')
  469. let c = a:0 ? a:2 : col('.')
  470. if &syntax == ''
  471. " No help from syntax engine,
  472. " count quote characters up to the cursor position
  473. let line = strpart( getline(l), 0, c - 1 )
  474. let line = substitute( line, '\\"', '', 'g' )
  475. let quotes = substitute( line, '[^"]', '', 'g' )
  476. return len(quotes) % 2
  477. endif
  478. " VimClojure and vim-clojure-static define special syntax for regexps
  479. return s:SynIDMatch( '[Ss]tring\|clojureRegexp\|clojurePattern', l, c, 0 )
  480. endfunction
  481. " Is this a Slimv or VimClojure REPL buffer?
  482. function! s:IsReplBuffer()
  483. if exists( 'b:slimv_repl_buffer' ) || exists( 'b:vimclojure_repl' )
  484. return 1
  485. else
  486. return 0
  487. endif
  488. endfunction
  489. " Get Slimv or VimClojure REPL buffer last command prompt position
  490. " Return [0, 0] if this is not the REPL buffer
  491. function! s:GetReplPromptPos()
  492. if !s:IsReplBuffer()
  493. return [0, 0]
  494. endif
  495. if exists( 'b:vimclojure_repl')
  496. let cur_pos = getpos( '.' )
  497. call cursor( line( '$' ), 1)
  498. call cursor( line( '.' ), col( '$') )
  499. call search( b:vimclojure_namespace . '=>', 'bcW' )
  500. let target_pos = getpos( '.' )[1:2]
  501. call setpos( '.', cur_pos )
  502. return target_pos
  503. else
  504. return [ b:repl_prompt_line, b:repl_prompt_col ]
  505. endif
  506. endfunction
  507. " Is the current top level form balanced, i.e all opening delimiters
  508. " have a matching closing delimiter
  509. function! s:IsBalanced()
  510. let l = line( '.' )
  511. let c = col( '.' )
  512. let line = getline( '.' )
  513. if c > len(line)
  514. let c = len(line)
  515. endif
  516. let matchb = max( [l-g:paredit_matchlines, 1] )
  517. let matchf = min( [l+g:paredit_matchlines, line('$')] )
  518. let [prompt, cp] = s:GetReplPromptPos()
  519. if s:IsReplBuffer() && l >= prompt && matchb < prompt
  520. " Do not go before the last command prompt in the REPL buffer
  521. let matchb = prompt
  522. endif
  523. if line[c-1] == '('
  524. let p1 = searchpair( '(', '', ')', 'brnmWc', s:skip_sc, matchb )
  525. let p2 = searchpair( '(', '', ')', 'rnmW' , s:skip_sc, matchf )
  526. elseif line[c-1] == ')'
  527. let p1 = searchpair( '(', '', ')', 'brnmW' , s:skip_sc, matchb )
  528. let p2 = searchpair( '(', '', ')', 'rnmWc', s:skip_sc, matchf )
  529. else
  530. let p1 = searchpair( '(', '', ')', 'brnmW' , s:skip_sc, matchb )
  531. let p2 = searchpair( '(', '', ')', 'rnmW' , s:skip_sc, matchf )
  532. endif
  533. if p1 != p2
  534. " Number of opening and closing parens differ
  535. return 0
  536. endif
  537. if &ft =~ s:fts_balancing_all_brackets
  538. if line[c-1] == '['
  539. let b1 = searchpair( '\[', '', '\]', 'brnmWc', s:skip_sc, matchb )
  540. let b2 = searchpair( '\[', '', '\]', 'rnmW' , s:skip_sc, matchf )
  541. elseif line[c-1] == ']'
  542. let b1 = searchpair( '\[', '', '\]', 'brnmW' , s:skip_sc, matchb )
  543. let b2 = searchpair( '\[', '', '\]', 'rnmWc', s:skip_sc, matchf )
  544. else
  545. let b1 = searchpair( '\[', '', '\]', 'brnmW' , s:skip_sc, matchb )
  546. let b2 = searchpair( '\[', '', '\]', 'rnmW' , s:skip_sc, matchf )
  547. endif
  548. if b1 != b2
  549. " Number of opening and closing brackets differ
  550. return 0
  551. endif
  552. if line[c-1] == '{'
  553. let b1 = searchpair( '{', '', '}', 'brnmWc', s:skip_sc, matchb )
  554. let b2 = searchpair( '{', '', '}', 'rnmW' , s:skip_sc, matchf )
  555. elseif line[c-1] == '}'
  556. let b1 = searchpair( '{', '', '}', 'brnmW' , s:skip_sc, matchb )
  557. let b2 = searchpair( '{', '', '}', 'rnmWc', s:skip_sc, matchf )
  558. else
  559. let b1 = searchpair( '{', '', '}', 'brnmW' , s:skip_sc, matchb )
  560. let b2 = searchpair( '{', '', '}', 'rnmW' , s:skip_sc, matchf )
  561. endif
  562. if b1 != b2
  563. " Number of opening and closing curly braces differ
  564. return 0
  565. endif
  566. endif
  567. return 1
  568. endfunction
  569. " Filter out all non-matched characters from the region
  570. function! s:GetMatchedChars( lines, start_in_string, start_in_comment )
  571. let inside_string = a:start_in_string
  572. let inside_comment = a:start_in_comment
  573. let matched = repeat( ' ', len( a:lines ) )
  574. let i = 0
  575. while i < len( a:lines )
  576. if inside_string
  577. " We are inside a string, skip parens, wait for closing '"'
  578. " but skip escaped \" characters
  579. if a:lines[i] == '"' && a:lines[i-1] != '\'
  580. let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 )
  581. let inside_string = 0
  582. endif
  583. elseif inside_comment
  584. " We are inside a comment, skip parens, wait for end of line
  585. if a:lines[i] == "\n"
  586. let inside_comment = 0
  587. endif
  588. elseif i > 0 && a:lines[i-1] == '\' && (i < 2 || a:lines[i-2] != '\')
  589. " This is an escaped character, ignore it
  590. else
  591. " We are outside of strings and comments, now we shall count parens
  592. if a:lines[i] == '"'
  593. let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 )
  594. let inside_string = 1
  595. endif
  596. if a:lines[i] == ';'
  597. let inside_comment = 1
  598. endif
  599. if a:lines[i] =~ b:any_openclose_char
  600. let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 )
  601. endif
  602. endif
  603. let i = i + 1
  604. endwhile
  605. return matched
  606. endfunction
  607. " Find unpaired matched characters by eliminating paired ones
  608. function! s:Unbalanced( matched )
  609. let matched = a:matched
  610. let tmp = matched
  611. while 1
  612. let matched = tmp
  613. let tmp = substitute( tmp, '(\(\s*\))', ' \1 ', 'g')
  614. if &ft =~ s:fts_balancing_all_brackets
  615. let tmp = substitute( tmp, '\[\(\s*\)\]', ' \1 ', 'g')
  616. let tmp = substitute( tmp, '{\(\s*\)}', ' \1 ', 'g')
  617. endif
  618. let tmp = substitute( tmp, '"\(\s*\)"', ' \1 ', 'g')
  619. if tmp == matched
  620. " All paired chars eliminated
  621. let tmp = substitute( tmp, ')\(\s*\)(', ' \1 ', 'g')
  622. if &ft =~ s:fts_balancing_all_brackets
  623. let tmp = substitute( tmp, '\]\(\s*\)\[', ' \1 ', 'g')
  624. let tmp = substitute( tmp, '}\(\s*\){', ' \1 ', 'g')
  625. endif
  626. if tmp == matched
  627. " Also no more inverse pairs can be eliminated
  628. break
  629. endif
  630. endif
  631. endwhile
  632. return matched
  633. endfunction
  634. " Find opening matched character
  635. function! PareditFindOpening( open, close, select )
  636. let open = escape( a:open , '[]' )
  637. let close = escape( a:close, '[]' )
  638. call searchpair( open, '', close, 'bW', s:skip_sc )
  639. if a:select
  640. call searchpair( open, '', close, 'W', s:skip_sc )
  641. let save_ve = &ve
  642. set ve=all
  643. normal! lvh
  644. let &ve = save_ve
  645. call searchpair( open, '', close, 'bW', s:skip_sc )
  646. if &selection == 'inclusive'
  647. " Trim last character from the selection, it will be included anyway
  648. normal! oho
  649. endif
  650. endif
  651. endfunction
  652. " Find closing matched character
  653. function! PareditFindClosing( open, close, select )
  654. let open = escape( a:open , '[]' )
  655. let close = escape( a:close, '[]' )
  656. if a:select
  657. let line = getline( '.' )
  658. if line[col('.')-1] != a:open
  659. normal! h
  660. endif
  661. call searchpair( open, '', close, 'W', s:skip_sc )
  662. call searchpair( open, '', close, 'bW', s:skip_sc )
  663. normal! v
  664. call searchpair( open, '', close, 'W', s:skip_sc )
  665. if &selection != 'inclusive'
  666. normal! l
  667. endif
  668. else
  669. call searchpair( open, '', close, 'W', s:skip_sc )
  670. endif
  671. endfunction
  672. " Returns the nearest opening character to the cursor
  673. " Used for smart jumping in Clojure
  674. function! PareditSmartJumpOpening( select )
  675. let [paren_line, paren_col] = searchpairpos('(', '', ')', 'bWn', s:skip_sc)
  676. let [bracket_line, bracket_col] = searchpairpos('\[', '', '\]', 'bWn', s:skip_sc)
  677. let [brace_line, brace_col] = searchpairpos('{', '', '}', 'bWn', s:skip_sc)
  678. let paren_score = paren_line * 10000 + paren_col
  679. let bracket_score = bracket_line * 10000 + bracket_col
  680. let brace_score = brace_line * 10000 + brace_col
  681. if (brace_score > paren_score || paren_score == 0) && (brace_score > bracket_score || bracket_score == 0) && brace_score != 0
  682. call PareditFindOpening('{','}', a:select)
  683. elseif (bracket_score > paren_score || paren_score == 0) && bracket_score != 0
  684. call PareditFindOpening('[',']', a:select)
  685. else
  686. call PareditFindOpening('(',')', a:select)
  687. endif
  688. endfunction
  689. " Returns the nearest opening character to the cursor
  690. " Used for smart jumping in Clojure
  691. function! PareditSmartJumpClosing( select )
  692. let [paren_line, paren_col] = searchpairpos('(', '', ')', 'Wn', s:skip_sc)
  693. let [bracket_line, bracket_col] = searchpairpos('\[', '', '\]', 'Wn', s:skip_sc)
  694. let [brace_line, brace_col] = searchpairpos('{', '', '}', 'Wn', s:skip_sc)
  695. let paren_score = paren_line * 10000 + paren_col
  696. let bracket_score = bracket_line * 10000 + bracket_col
  697. let brace_score = brace_line * 10000 + brace_col
  698. if (brace_score < paren_score || paren_score == 0) && (brace_score < bracket_score || bracket_score == 0) && brace_score != 0
  699. call PareditFindClosing('{','}', a:select)
  700. elseif (bracket_score < paren_score || paren_score == 0) && bracket_score != 0
  701. call PareditFindClosing('[',']', a:select)
  702. else
  703. call PareditFindClosing('(',')', a:select)
  704. endif
  705. endfunction
  706. " Find defun start backwards
  707. function! PareditFindDefunBck()
  708. let l = line( '.' )
  709. let matchb = max( [l-g:paredit_matchlines, 1] )
  710. let oldpos = getpos( '.' )
  711. let newpos = searchpairpos( '(', '', ')', 'brW', s:skip_sc, matchb )
  712. if newpos[0] == 0
  713. " Already standing on a defun, find the end of the previous one
  714. let newpos = searchpos( ')', 'bW' )
  715. while newpos[0] != 0 && (s:InsideComment() || s:InsideString())
  716. let newpos = searchpos( ')', 'W' )
  717. endwhile
  718. if newpos[0] == 0
  719. " No ')' found, don't move cursor
  720. call setpos( '.', oldpos )
  721. else
  722. " Find opening paren
  723. let pairpos = searchpairpos( '(', '', ')', 'brW', s:skip_sc, matchb )
  724. if pairpos[0] == 0
  725. " ')' has no matching pair
  726. call setpos( '.', oldpos )
  727. endif
  728. endif
  729. endif
  730. endfunction
  731. " Find defun start forward
  732. function! PareditFindDefunFwd()
  733. let l = line( '.' )
  734. let matchf = min( [l+g:paredit_matchlines, line('$')] )
  735. let oldpos = getpos( '.' )
  736. call searchpair( '(', '', ')', 'brW', s:skip_sc, matchf )
  737. normal! %
  738. let newpos = searchpos( '(', 'W' )
  739. while newpos[0] != 0 && (s:InsideComment() || s:InsideString())
  740. let newpos = searchpos( '(', 'W' )
  741. endwhile
  742. if newpos[0] == 0
  743. " No '(' found, don't move cursor
  744. call setpos( '.', oldpos )
  745. endif
  746. endfunction
  747. " Insert opening type of a paired character, like ( or [.
  748. function! PareditInsertOpening( open, close )
  749. if !g:paredit_mode || s:InsideComment() || s:InsideString() || !s:IsBalanced()
  750. return a:open
  751. endif
  752. let line = getline( '.' )
  753. let pos = col( '.' ) - 1
  754. if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\')
  755. " About to enter a \( or \[
  756. return a:open
  757. elseif line[pos] !~ b:any_wsclose_char && pos < len( line )
  758. " Add a space after if needed
  759. let retval = a:open . a:close . " \<Left>\<Left>"
  760. else
  761. let retval = a:open . a:close . "\<Left>"
  762. endif
  763. if pos > 0 && line[pos-1] !~ b:any_wsopen_char && line[pos-1] !~ s:any_macro_prefix
  764. " Add a space before if needed
  765. let retval = " " . retval
  766. endif
  767. return retval
  768. endfunction
  769. " Re-gather electric returns up
  770. function! s:ReGatherUp()
  771. if g:paredit_electric_return && getline('.') =~ '^\s*)'
  772. " Re-gather electric returns in the current line for ')'
  773. normal! k
  774. while getline( line('.') ) =~ '^\s*$'
  775. " Delete all empty lines
  776. normal! ddk
  777. endwhile
  778. normal! Jl
  779. elseif g:paredit_electric_return && getline('.') =~ '^\s*\(\]\|}\)' && &ft =~ s:fts_balancing_all_brackets
  780. " Re-gather electric returns in the current line for ']' and '}'
  781. normal! k
  782. while getline( line('.') ) =~ '^\s*$'
  783. " Delete all empty lines
  784. normal! ddk
  785. endwhile
  786. call setline( line('.'), substitute( line, '\s*$', '', 'g' ) )
  787. normal! Jxl
  788. endif
  789. " Already have the desired character, move right
  790. normal! l
  791. endfunction
  792. " Insert closing type of a paired character, like ) or ].
  793. function! PareditInsertClosing( open, close )
  794. let retval = ""
  795. if pumvisible()
  796. let retval = "\<C-Y>"
  797. endif
  798. let save_ve = &ve
  799. set ve=all
  800. let line = getline( '.' )
  801. let pos = col( '.' ) - 1
  802. if !g:paredit_mode || s:InsideComment() || s:InsideString() || !s:IsBalanced()
  803. call setline( line('.'), line[0 : pos-1] . a:close . line[pos : -1] )
  804. normal! l
  805. let &ve = save_ve
  806. return retval
  807. endif
  808. if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\')
  809. " About to enter a \) or \]
  810. call setline( line('.'), line[0 : pos-1] . a:close . line[pos : -1] )
  811. normal! l
  812. let &ve = save_ve
  813. return retval
  814. elseif line[pos] == a:close
  815. call s:ReGatherUp()
  816. let &ve = save_ve
  817. return retval
  818. endif
  819. let open = escape( a:open , '[]' )
  820. let close = escape( a:close, '[]' )
  821. let newpos = searchpairpos( open, '', close, 'nW', s:skip_sc )
  822. if g:paredit_electric_return && newpos[0] > line('.')
  823. " Closing paren is in a line below, check if there are electric returns to re-gather
  824. while getline('.') =~ '^\s*$'
  825. " Delete all empty lines above the cursor
  826. normal! ddk
  827. endwhile
  828. let oldpos = getpos( '.' )
  829. normal! j
  830. while getline('.') =~ '^\s*$'
  831. " Delete all empty lines below the cursor
  832. normal! dd
  833. endwhile
  834. let nextline = substitute( getline('.'), '\s', '', 'g' )
  835. call setpos( '.', oldpos )
  836. if len(nextline) > 0 && nextline[0] == ')'
  837. " Re-gather electric returns in the line of the closing ')'
  838. call setline( line('.'), substitute( getline('.'), '\s*$', '', 'g' ) )
  839. normal! Jl
  840. let &ve = save_ve
  841. return retval
  842. endif
  843. if len(nextline) > 0 && nextline[0] =~ '\]\|}' && &ft =~ s:fts_balancing_all_brackets
  844. " Re-gather electric returns in the line of the closing ']' or '}'
  845. call setline( line('.'), substitute( line, '\s*$', '', 'g' ) )
  846. normal! Jxl
  847. let &ve = save_ve
  848. return retval
  849. endif
  850. elseif g:paredit_electric_return && line =~ '^\s*)'
  851. " Re-gather electric returns in the current line
  852. call s:ReGatherUp()
  853. let &ve = save_ve
  854. return retval
  855. endif
  856. if searchpair( open, '', close, 'W', s:skip_sc ) > 0
  857. normal! l
  858. endif
  859. "TODO: indent after going to closing character
  860. let &ve = save_ve
  861. return retval
  862. endfunction
  863. " Insert an (opening or closing) double quote
  864. function! PareditInsertQuotes()
  865. if !g:paredit_mode || s:InsideComment()
  866. return '"'
  867. endif
  868. let line = getline( '.' )
  869. let pos = col( '.' ) - 1
  870. if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\')
  871. " About to enter a \"
  872. return '"'
  873. elseif s:InsideString()
  874. "TODO: skip comments in search(...)
  875. if line[pos] == '"'
  876. " Standing on a ", just move to the right
  877. return "\<Right>"
  878. elseif search('[^\\]"\|^"', 'nW') == 0
  879. " We don't have any closing ", insert one
  880. return '"'
  881. else
  882. " Move to the closing "
  883. return "\<C-O>:call search('" . '[^\\]"\|^"' . "','eW')\<CR>\<Right>"
  884. endif
  885. else
  886. " Outside of string: insert a pair of ""
  887. return '""' . "\<Left>"
  888. endif
  889. endfunction
  890. " Handle <Enter> keypress, insert electric return if applicable
  891. function! PareditEnter()
  892. if pumvisible()
  893. " Pressing <CR> in a pop up selects entry.
  894. return "\<C-Y>"
  895. else
  896. let line = getline( '.' )
  897. let pos = col( '.' ) - 1
  898. if g:paredit_electric_return && pos > 0 && line[pos] =~ b:any_closing_char && !s:InsideString() && s:IsBalanced()
  899. " Electric Return
  900. return "\<CR>\<CR>\<Up>"
  901. else
  902. " Regular Return
  903. return "\<CR>"
  904. endif
  905. endif
  906. endfunction
  907. " Handle <BS> keypress
  908. function! PareditBackspace( repl_mode )
  909. let [lp, cp] = s:GetReplPromptPos()
  910. if a:repl_mode && line( "." ) == lp && col( "." ) <= cp
  911. " No BS allowed before the previous EOF mark in the REPL
  912. " i.e. don't delete Lisp prompt
  913. return ""
  914. endif
  915. if !g:paredit_mode || s:InsideComment()
  916. return "\<BS>"
  917. endif
  918. let line = getline( '.' )
  919. let pos = col( '.' ) - 1
  920. if pos == 0
  921. " We are at the beginning of the line
  922. return "\<BS>"
  923. elseif s:InsideString() && line[pos-1] =~ b:any_openclose_char
  924. " Deleting a paren inside a string
  925. return "\<BS>"
  926. elseif pos > 1 && line[pos-1] =~ b:any_matched_char && line[pos-2] == '\' && (pos < 3 || line[pos-3] != '\')
  927. " Deleting an escaped matched character
  928. return "\<BS>\<BS>"
  929. elseif line[pos-1] !~ b:any_matched_char
  930. " Deleting a non-special character
  931. return "\<BS>"
  932. elseif line[pos-1] != '"' && !s:IsBalanced()
  933. " Current top-form is unbalanced, can't retain paredit mode
  934. return "\<BS>"
  935. endif
  936. if line[pos-1:pos] =~ b:any_matched_pair
  937. " Deleting an empty character-pair
  938. return "\<Right>\<BS>\<BS>"
  939. else
  940. " Character-pair is not empty, don't delete just move inside
  941. return "\<Left>"
  942. endif
  943. endfunction
  944. " Handle <Del> keypress
  945. function! PareditDel()
  946. if !g:paredit_mode || s:InsideComment()
  947. return "\<Del>"
  948. endif
  949. let line = getline( '.' )
  950. let pos = col( '.' ) - 1
  951. if pos == len(line)
  952. " We are at the end of the line
  953. return "\<Del>"
  954. elseif line[pos] == '\' && line[pos+1] =~ b:any_matched_char && (pos < 1 || line[pos-1] != '\')
  955. " Deleting an escaped matched character
  956. return "\<Del>\<Del>"
  957. elseif line[pos] !~ b:any_matched_char
  958. " Erasing a non-special character
  959. return "\<Del>"
  960. elseif line[pos] != '"' && !s:IsBalanced()
  961. " Current top-form is unbalanced, can't retain paredit mode
  962. return "\<Del>"
  963. elseif pos == 0
  964. return "\<Right>"
  965. endif
  966. if line[pos-1:pos] =~ b:any_matched_pair
  967. " Erasing an empty character-pair
  968. return "\<Left>\<Del>\<Del>"
  969. else
  970. " Character-pair is not empty, don't erase just move inside
  971. return "\<Right>"
  972. endif
  973. endfunction
  974. " Initialize yank position list
  975. function! s:InitYankPos()
  976. call setreg( &clipboard == 'unnamed' ? '*' : '"', '' )
  977. let s:yank_pos = []
  978. endfunction
  979. " Add position to the yank list
  980. function! s:AddYankPos( pos )
  981. let s:yank_pos = [a:pos] + s:yank_pos
  982. endfunction
  983. " Remove the head of yank position list and return it
  984. function! s:RemoveYankPos()
  985. if len(s:yank_pos) > 0
  986. let pos = s:yank_pos[0]
  987. let s:yank_pos = s:yank_pos[1:]
  988. return pos
  989. else
  990. return 0
  991. endif
  992. endfunction
  993. " Forward erasing a character in normal mode, do not check if current form balanced
  994. function! s:EraseFwd( count, startcol )
  995. let line = getline( '.' )
  996. let pos = col( '.' ) - 1
  997. let reg = ''
  998. let ve_save = &virtualedit
  999. set virtualedit=all
  1000. let c = a:count
  1001. while c > 0
  1002. if line[pos] == '\' && line[pos+1] =~ b:any_matched_char && (pos < 1 || line[pos-1] != '\')
  1003. " Erasing an escaped matched character
  1004. let reg = reg . line[pos : pos+1]
  1005. let line = strpart( line, 0, pos ) . strpart( line, pos+2 )
  1006. elseif s:InsideComment() && line[pos] == ';' && a:startcol >= 0
  1007. " Erasing the whole comment, only when erasing a block of characters
  1008. let reg = reg . strpart( line, pos )
  1009. let line = strpart( line, 0, pos )
  1010. elseif s:InsideComment() || ( s:InsideString() && line[pos] != '"' )
  1011. " Erasing any character inside string or comment
  1012. let reg = reg . line[pos]
  1013. let line = strpart( line, 0, pos ) . strpart( line, pos+1 )
  1014. elseif pos > 0 && line[pos-1:pos] =~ b:any_matched_pair
  1015. if pos > a:startcol
  1016. " Erasing an empty character-pair
  1017. let p2 = s:RemoveYankPos()
  1018. let reg = strpart( reg, 0, p2 ) . line[pos-1] . strpart( reg, p2 )
  1019. let reg = reg . line[pos]
  1020. let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 )
  1021. let pos = pos - 1
  1022. normal! h
  1023. else
  1024. " Can't erase character-pair: it would move the cursor before startcol
  1025. let pos = pos + 1
  1026. normal! l
  1027. endif
  1028. elseif line[pos] =~ b:any_matched_char
  1029. " Character-pair is not empty, don't erase just move inside
  1030. call s:AddYankPos( len(reg) )
  1031. let pos = pos + 1
  1032. normal! l
  1033. elseif pos < len(line) && pos >= a:startcol
  1034. " Erasing a non-special character
  1035. let chars = split(strpart(line, pos), '\zs')
  1036. if len(chars) > 0
  1037. " Identify the character to be erased and it's length
  1038. " The length may be >1 if this is a multi-byte character
  1039. let ch = chars[0]
  1040. let reg = reg . ch
  1041. let line = strpart( line, 0, pos ) . strpart( line, pos+len(ch) )
  1042. endif
  1043. endif
  1044. let c = c - 1
  1045. endwhile
  1046. let &virtualedit = ve_save
  1047. call setline( '.', line )
  1048. call setreg( &clipboard == 'unnamed' ? '*' : '"', reg )
  1049. endfunction
  1050. " Backward erasing a character in normal mode, do not check if current form balanced
  1051. function! s:EraseBck( count )
  1052. let line = getline( '.' )
  1053. let pos = col( '.' ) - 1
  1054. let reg = ''
  1055. let c = a:count
  1056. while c > 0 && pos > 0
  1057. if pos > 1 && line[pos-2] == '\' && line[pos-1] =~ b:any_matched_char && (pos < 3 || line[pos-3] != '\')
  1058. " Erasing an escaped matched character
  1059. let reg = reg . line[pos-2 : pos-1]
  1060. let line = strpart( line, 0, pos-2 ) . strpart( line, pos )
  1061. normal! h
  1062. let pos = pos - 1
  1063. elseif s:InsideComment() || ( s:InsideString() && line[pos-1] != '"' )
  1064. let reg = reg . line[pos-1]
  1065. let line = strpart( line, 0, pos-1 ) . strpart( line, pos )
  1066. elseif line[pos-1:pos] =~ b:any_matched_pair
  1067. " Erasing an empty character-pair
  1068. let p2 = s:RemoveYankPos()
  1069. let reg = strpart( reg, 0, p2 ) . line[pos-1] . strpart( reg, p2 )
  1070. let reg = reg . line[pos]
  1071. let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 )
  1072. elseif line[pos-1] =~ b:any_matched_char
  1073. " Character-pair is not empty, don't erase
  1074. call s:AddYankPos( len(reg) )
  1075. else
  1076. " Erasing a non-special character
  1077. let chars = split(strpart(line, 0, pos), '\zs')
  1078. if len(chars) > 0
  1079. " Identify the character to be erased and it's length
  1080. " The length may be >1 if this is a multi-byte character
  1081. let ch = chars[-1]
  1082. let reg = reg . ch
  1083. let line = strpart( line, 0, pos-len(ch) ) . strpart( line, pos )
  1084. let pos = pos - len(ch) + 1
  1085. endif
  1086. endif
  1087. normal! h
  1088. let pos = pos - 1
  1089. let c = c - 1
  1090. endwhile
  1091. call setline( '.', line )
  1092. call setreg( &clipboard == 'unnamed' ? '*' : '"', reg )
  1093. endfunction
  1094. " Forward erasing a character in normal mode
  1095. function! PareditEraseFwd()
  1096. if !g:paredit_mode || !s:IsBalanced()
  1097. if v:count > 0
  1098. silent execute 'normal! ' . v:count . 'x'
  1099. else
  1100. normal! x
  1101. endif
  1102. return
  1103. endif
  1104. call s:InitYankPos()
  1105. call s:EraseFwd( v:count1, -1 )
  1106. endfunction
  1107. " Backward erasing a character in normal mode
  1108. function! PareditEraseBck()
  1109. if !g:paredit_mode || !s:IsBalanced()
  1110. if v:count > 0
  1111. silent execute 'normal! ' . v:count . 'X'
  1112. else
  1113. normal! X
  1114. endif
  1115. return
  1116. endif
  1117. call s:InitYankPos()
  1118. call s:EraseBck( v:count1 )
  1119. endfunction
  1120. " Find beginning of previous element (atom or sub-expression) in a form
  1121. " skip_whitespc: skip whitespaces before the previous element
  1122. function! s:PrevElement( skip_whitespc )
  1123. let [l0, c0] = [line( '.' ), col( '.' )]
  1124. let symbol_pos = [0, 0]
  1125. let symbol_end = [0, 0]
  1126. " Move to the beginning of the prefix if any
  1127. let line = getline( '.' )
  1128. let c = col('.') - 1
  1129. if c > 0 && line[c-1] =~ s:any_macro_prefix
  1130. normal! h
  1131. endif
  1132. let moved = 0
  1133. while 1
  1134. " Go to previous character
  1135. if !moved
  1136. let [l1, c1] = [line( '.' ), col( '.' )]
  1137. let save_ww = &whichwrap
  1138. set whichwrap=
  1139. normal! h
  1140. let &whichwrap = save_ww
  1141. endif
  1142. let moved = 0
  1143. let [l, c] = [line( '.' ), col( '.' )]
  1144. if [l, c] == [l1, c1]
  1145. " Beginning of line reached
  1146. if symbol_pos != [0, 0]
  1147. let symbol_end = [l, c]
  1148. if !a:skip_whitespc && !s:InsideString()
  1149. " Newline before previous symbol
  1150. call setpos( '.', [0, l0, c0, 0] )
  1151. return [l, c]
  1152. endif
  1153. endif
  1154. normal! k$
  1155. let [l, c] = [line( '.' ), col( '.' )]
  1156. if [l, c] == [l1, c1]
  1157. " Beginning of file reached: stop
  1158. call setpos( '.', [0, l0, c0, 0] )
  1159. return [0, 0]
  1160. endif
  1161. let moved = 1
  1162. elseif s:InsideComment()
  1163. " Skip comments
  1164. else
  1165. let line = getline( '.' )
  1166. if s:InsideString() && !(a:skip_whitespc && line[c] =~ '\s' && symbol_end != [0, 0])
  1167. let symbol_pos = [l, c]
  1168. elseif symbol_pos == [0, 0]
  1169. if line[c-1] =~ b:any_closing_char
  1170. " Skip to the beginning of this sub-expression
  1171. let symbol_pos = [l, c]
  1172. normal! %
  1173. let line2 = getline( '.' )
  1174. let c2 = col('.') - 1
  1175. if c2 > 0 && line2[c2-1] =~ s:any_macro_prefix
  1176. normal! h
  1177. endif
  1178. elseif line[c-1] =~ b:any_opening_char
  1179. " Opening delimiter found: stop
  1180. call setpos( '.', [0, l0, c0, 0] )
  1181. return [0, 0]
  1182. elseif line[c-1] =~ '\S'
  1183. " Previous symbol starting
  1184. let symbol_pos = [l, c]
  1185. endif
  1186. else
  1187. if line[c-1] =~ b:any_opening_char || (a:skip_whitespc && line[c-1] =~ '\S' && symbol_end != [0, 0])
  1188. " Previous symbol beginning reached, opening delimiter or second previous symbol starting
  1189. call setpos( '.', [0, l0, c0, 0] )
  1190. return [l, c+1]
  1191. elseif line[c-1] =~ '\s' || symbol_pos[0] != l
  1192. " Whitespace before previous symbol
  1193. let symbol_end = [l, c]
  1194. if !a:skip_whitespc
  1195. call setpos( '.', [0, l0, c0, 0] )
  1196. return [l, c+1]
  1197. endif
  1198. endif
  1199. endif
  1200. endif
  1201. endwhile
  1202. endfunction
  1203. " Find end of next element (atom or sub-expression) in a form
  1204. " skip_whitespc: skip whitespaces after the next element
  1205. function! s:NextElement( skip_whitespc )
  1206. let [l0, c0] = [line( '.' ), col( '.'

Large files files are truncated, but you can click here to view the full file