diff options
| author | Nick Shipp <nick@shipp.ninja> | 2017-05-07 09:04:01 -0400 | 
|---|---|---|
| committer | Nick Shipp <nick@shipp.ninja> | 2017-05-07 09:04:01 -0400 | 
| commit | c012f55efda29f09179e921cf148d79deb57616e (patch) | |
| tree | ff0ad37f22622d51194cab192a2aa4b0106d7ad0 /vim/bundle/slimv/plugin | |
| parent | 4ca8f6608883d230131f8a9e8b6d6c091c516049 (diff) | |
Much maturering of vim configs
Diffstat (limited to 'vim/bundle/slimv/plugin')
| -rw-r--r-- | vim/bundle/slimv/plugin/paredit.vim | 1863 | 
1 files changed, 1863 insertions, 0 deletions
| diff --git a/vim/bundle/slimv/plugin/paredit.vim b/vim/bundle/slimv/plugin/paredit.vim new file mode 100644 index 0000000..befc118 --- /dev/null +++ b/vim/bundle/slimv/plugin/paredit.vim @@ -0,0 +1,1863 @@ +" paredit.vim: +"               Paredit mode for Slimv +" Version:      0.9.13 +" Last Change:  15 Jan 2017 +" Maintainer:   Tamas Kovacs <kovisoft at gmail dot com> +" License:      This file is placed in the public domain. +"               No warranty, express or implied. +"               *** ***   Use At-Your-Own-Risk!   *** *** +" +" ===================================================================== +" +"  Load Once: +if &cp || exists( 'g:paredit_loaded' ) +    finish +endif + +let g:paredit_loaded = 1 + +" Needed to load filetype and indent plugins +filetype plugin on +filetype indent on + +" ===================================================================== +"  Global variable definitions +" ===================================================================== + +" Paredit mode selector +if !exists( 'g:paredit_mode' ) +    let g:paredit_mode = 1 +endif + +" Match delimiter this number of lines before and after cursor position +if !exists( 'g:paredit_matchlines' ) +    let g:paredit_matchlines = 100 +endif + +" Use short keymaps, i.e. J instead of <Leader>J +if !exists( 'g:paredit_shortmaps' ) +    let g:paredit_shortmaps = 0 +endif + +" Use smart jumping to the nearest paren, curly brace, or square bracket in +" clojure +if !exists( 'g:paredit_smartjump' ) +    let g:paredit_smartjump = 0 +endif + +" Custom <Leader> for the Paredit plugin +if !exists( 'g:paredit_leader' ) +    if exists( 'mapleader' ) +        let g:paredit_leader = '<leader>' +    else +        let g:paredit_leader = ',' +    endif +endif + +" Use 'Electric Return', i.e. add double newlines if enter pressed before a closing paren +if !exists( 'g:paredit_electric_return' ) +    let g:paredit_electric_return = 1 +endif + +" ===================================================================== +"  Other variable definitions +" ===================================================================== + +" Valid macro prefix characters +let s:any_macro_prefix   = "'" . '\|`\|#\|@\|\~\|,\|\^' + +" Repeat count for some remapped edit functions (like 'd') +let s:repeat             = 0 + +let s:yank_pos           = [] + +" Filetypes with [] and {} pairs balanced as well +let s:fts_balancing_all_brackets = '.*\(clojure\|hy\|scheme\|racket\|shen\).*' + +" ===================================================================== +"  General utility functions +" ===================================================================== +" Buffer specific initialization +function! PareditInitBuffer() +    let b:paredit_init = 1 +    " in case they are accidentally removed +    " Also define regular expressions to identify special characters used by paredit +    if &ft =~ s:fts_balancing_all_brackets +        let b:any_matched_char   = '(\|)\|\[\|\]\|{\|}\|\"' +        let b:any_matched_pair   = '()\|\[\]\|{}\|\"\"' +        let b:any_opening_char   = '(\|\[\|{' +        let b:any_closing_char   = ')\|\]\|}' +        let b:any_openclose_char = '(\|)\|\[\|\]\|{\|}' +        let b:any_wsopen_char    = '\s\|(\|\[\|{' +        let b:any_wsclose_char   = '\s\|)\|\]\|}' +    else +        let b:any_matched_char   = '(\|)\|\"' +        let b:any_matched_pair   = '()\|\"\"' +        let b:any_opening_char   = '(' +        let b:any_closing_char   = ')' +        let b:any_openclose_char = '(\|)' +        let b:any_wsopen_char    = '\s\|(' +        let b:any_wsclose_char   = '\s\|)' +    endif + +    if g:paredit_mode +        " Paredit mode is on: add buffer specific keybindings +        inoremap <buffer> <expr>   (            PareditInsertOpening('(',')') +        inoremap <buffer> <silent> )            <C-R>=PareditInsertClosing('(',')')<CR> +        inoremap <buffer> <expr>   "            PareditInsertQuotes() +        inoremap <buffer> <expr>   <BS>         PareditBackspace(0) +        inoremap <buffer> <expr>   <C-h>        PareditBackspace(0) +        inoremap <buffer> <expr>   <Del>        PareditDel() +        if &ft =~ s:fts_balancing_all_brackets && g:paredit_smartjump +            noremap  <buffer> <silent> (            :<C-U>call PareditSmartJumpOpening(0)<CR> +            noremap  <buffer> <silent> )            :<C-U>call PareditSmartJumpClosing(0)<CR> +            vnoremap <buffer> <silent> (            <Esc>:<C-U>call PareditSmartJumpOpening(1)<CR> +            vnoremap <buffer> <silent> )            <Esc>:<C-U>call PareditSmartJumpClosing(1)<CR> +        else +            noremap  <buffer> <silent> (            :<C-U>call PareditFindOpening('(',')',0)<CR> +            noremap  <buffer> <silent> )            :<C-U>call PareditFindClosing('(',')',0)<CR> +            vnoremap <buffer> <silent> (            <Esc>:<C-U>call PareditFindOpening('(',')',1)<CR> +            vnoremap <buffer> <silent> )            <Esc>:<C-U>call PareditFindClosing('(',')',1)<CR> +        endif +        noremap  <buffer> <silent> [[           :<C-U>call PareditFindDefunBck()<CR> +        noremap  <buffer> <silent> ]]           :<C-U>call PareditFindDefunFwd()<CR> + +        call RepeatableNNoRemap('x', ':<C-U>call PareditEraseFwd()') +        nnoremap <buffer> <silent> <Del>        :<C-U>call PareditEraseFwd()<CR> +        call RepeatableNNoRemap('X', ':<C-U>call PareditEraseBck()') +        nnoremap <buffer> <silent> s            :<C-U>call PareditEraseFwd()<CR>i +        call RepeatableNNoRemap('D', 'v$:<C-U>call PareditDelete(visualmode(),1)') +        nnoremap <buffer> <silent> C            v$:<C-U>call PareditChange(visualmode(),1)<CR> +        nnoremap <buffer> <silent> d            :<C-U>call PareditSetDelete(v:count)<CR>g@ +        vnoremap <buffer> <silent> d            :<C-U>call PareditDelete(visualmode(),1)<CR> +        vnoremap <buffer> <silent> x            :<C-U>call PareditDelete(visualmode(),1)<CR> +        vnoremap <buffer> <silent> <Del>        :<C-U>call PareditDelete(visualmode(),1)<CR> +        nnoremap <buffer> <silent> c            :set opfunc=PareditChange<CR>g@ +        vnoremap <buffer> <silent> c            :<C-U>call PareditChange(visualmode(),1)<CR> +        call RepeatableNNoRemap('dd', ':<C-U>call PareditDeleteLines()') +        nnoremap <buffer> <silent> cc           :<C-U>call PareditChangeLines()<CR> +        nnoremap <buffer> <silent> cw           :<C-U>call PareditChangeSpec('cw',1)<CR> +        nnoremap <buffer> <silent> cW           :set opfunc=PareditChange<CR>g@E +        nnoremap <buffer> <silent> cb           :<C-U>call PareditChangeSpec('cb',0)<CR> +        nnoremap <buffer> <silent> ciw          :<C-U>call PareditChangeSpec('ciw',1)<CR> +        nnoremap <buffer> <silent> caw          :<C-U>call PareditChangeSpec('caw',1)<CR> +        call RepeatableNNoRemap('p', ':<C-U>call PareditPut("p")') +        call RepeatableNNoRemap('P', ':<C-U>call PareditPut("P")') +        call RepeatableNNoRemap(g:paredit_leader . 'w(', ':<C-U>call PareditWrap("(",")")') +        execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'w(  :<C-U>call PareditWrapSelection("(",")")<CR>' +        call RepeatableNNoRemap(g:paredit_leader . 'w"', ':<C-U>call PareditWrap('."'".'"'."','".'"'."')") +        execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'w"  :<C-U>call PareditWrapSelection('."'".'"'."','".'"'."')<CR>" +        " Spliec s-expression killing backward/forward +        execute 'nmap     <buffer> <silent> ' . g:paredit_leader.'<Up>    d[(:<C-U>call PareditSplice()<CR>' +        execute 'nmap     <buffer> <silent> ' . g:paredit_leader.'<Down>  d])%:<C-U>call PareditSplice()<CR>' +        call RepeatableNNoRemap(g:paredit_leader . 'I', ':<C-U>call PareditRaise()') +        if &ft =~ s:fts_balancing_all_brackets +            inoremap <buffer> <expr>   [            PareditInsertOpening('[',']') +            inoremap <buffer> <silent> ]            <C-R>=PareditInsertClosing('[',']')<CR> +            inoremap <buffer> <expr>   {            PareditInsertOpening('{','}') +            inoremap <buffer> <silent> }            <C-R>=PareditInsertClosing('{','}')<CR> +            call RepeatableNNoRemap(g:paredit_leader . 'w[', ':<C-U>call PareditWrap("[","]")') +            execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'w[  :<C-U>call PareditWrapSelection("[","]")<CR>' +            call RepeatableNNoRemap(g:paredit_leader . 'w{', ':<C-U>call PareditWrap("{","}")') +            execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'w{  :<C-U>call PareditWrapSelection("{","}")<CR>' +        endif + +        if g:paredit_shortmaps +            " Shorter keymaps: old functionality of KEY is remapped to <Leader>KEY +            call RepeatableNNoRemap('<', ':<C-U>call PareditMoveLeft()')  +            call RepeatableNNoRemap('>', ':<C-U>call PareditMoveRight()')  +            call RepeatableNNoRemap('O', ':<C-U>call PareditSplit()')  +            call RepeatableNNoRemap('J', ':<C-U>call PareditJoin()')  +            call RepeatableNNoRemap('W', ':<C-U>call PareditWrap("(",")")')  +            vnoremap <buffer> <silent> W            :<C-U>call PareditWrapSelection('(',')')<CR> +            call RepeatableNNoRemap('S', ':<C-U>call PareditSplice()')  +            execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'<  :<C-U>normal! <<CR>' +            execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'>  :<C-U>normal! ><CR>' +            execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'O  :<C-U>normal! O<CR>' +            execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'J  :<C-U>normal! J<CR>' +            execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'W  :<C-U>normal! W<CR>' +            execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'W  :<C-U>normal! W<CR>' +            execute 'nnoremap <buffer> <silent> ' . g:paredit_leader.'S  :<C-U>normal! S<CR>' +        else +            " Longer keymaps with <Leader> prefix +            nnoremap <buffer> <silent> S            V:<C-U>call PareditChange(visualmode(),1)<CR> +            call RepeatableNNoRemap(g:paredit_leader . '<', ':<C-U>call PareditMoveLeft()')  +            call RepeatableNNoRemap(g:paredit_leader . '>', ':<C-U>call PareditMoveRight()')  +            call RepeatableNNoRemap(g:paredit_leader . 'O', ':<C-U>call PareditSplit()')  +            call RepeatableNNoRemap(g:paredit_leader . 'J', ':<C-U>call PareditJoin()')  +            call RepeatableNNoRemap(g:paredit_leader . 'W', ':<C-U>call PareditWrap("(",")")')  +            execute 'vnoremap <buffer> <silent> ' . g:paredit_leader.'W  :<C-U>call PareditWrapSelection("(",")")<CR>' +            call RepeatableNNoRemap(g:paredit_leader . 'S', ':<C-U>call PareditSplice()')  +        endif + +        if g:paredit_electric_return && mapcheck( "<CR>", "i" ) == "" +            " Do not override any possible mapping for <Enter> +            inoremap <buffer> <expr>   <CR>         PareditEnter() +        endif +    else +        " Paredit mode is off: remove keybindings +        silent! iunmap <buffer> ( +        silent! iunmap <buffer> ) +        silent! iunmap <buffer> " +        silent! iunmap <buffer> <BS> +        silent! iunmap <buffer> <C-h> +        silent! iunmap <buffer> <Del> +        silent! unmap  <buffer> ( +        silent! unmap  <buffer> ) +        silent! unmap  <buffer> [[ +        silent! unmap  <buffer> ]] +        silent! unmap  <buffer> x +        silent! unmap  <buffer> <Del> +        silent! unmap  <buffer> X +        silent! unmap  <buffer> s +        silent! unmap  <buffer> D +        silent! unmap  <buffer> C +        silent! unmap  <buffer> d +        silent! unmap  <buffer> c +        silent! unmap  <buffer> dd +        silent! unmap  <buffer> cc +        silent! unmap  <buffer> cw +        silent! unmap  <buffer> cW +        silent! unmap  <buffer> cb +        silent! unmap  <buffer> ciw +        silent! unmap  <buffer> caw +        if &ft =~ s:fts_balancing_all_brackets +            silent! iunmap <buffer> [ +            silent! iunmap <buffer> ] +            silent! iunmap <buffer> { +            silent! iunmap <buffer> } +        endif +        if mapcheck( "<CR>", "i" ) == "PareditEnter()" +            " Remove only if we have added this mapping +            silent! iunmap <buffer> <CR> +        endif +    endif +endfunction + +" Run the command normally but append a call to repeat#set afterwards +function! RepeatableMap(map_type, keys, command) +  let escaped_keys = substitute(a:keys, '["<]', '\\\0', "g") +  execute a:map_type . ' <silent> <buffer> ' . +        \ a:keys . ' ' . a:command . +        \ '\|silent! call repeat#set("' . escaped_keys . '")<CR>' +endfunction + +function! RepeatableNMap(keys, command) +  call RepeatableMap('nmap', a:keys, a:command) +endfunction + +function! RepeatableNNoRemap(keys, command) +  call RepeatableMap('nnoremap', a:keys, a:command) +endfunction + +" Include all prefix and special characters in 'iskeyword' +function! s:SetKeyword() +    let old_value = &iskeyword +    if &ft =~ s:fts_balancing_all_brackets +        setlocal iskeyword+=+,-,*,/,%,<,=,>,:,$,?,!,@-@,94,~,#,\|,& +    else +        setlocal iskeyword+=+,-,*,/,%,<,=,>,:,$,?,!,@-@,94,~,#,\|,&,.,{,},[,] +    endif +    return old_value +endfunction + +" General Paredit operator function +function! PareditOpfunc( func, type, visualmode ) +    let sel_save = &selection +    let ve_save = &virtualedit +    set virtualedit=all +    let regname = v:register +    let save_0 = getreg( '0' ) + +    if a:visualmode  " Invoked from Visual mode, use '< and '> marks. +        silent exe "normal! `<" . a:type . "`>" +    elseif a:type == 'line' +        let &selection = "inclusive" +        silent exe "normal! '[V']" +    elseif a:type == 'block' +        let &selection = "inclusive" +        silent exe "normal! `[\<C-V>`]" +    else +        let &selection = "inclusive" +        silent exe "normal! `[v`]" +    endif + +    if !g:paredit_mode || (a:visualmode && (a:type == 'block' || a:type == "\<C-V>")) +        " Block mode is too difficult to handle at the moment +        silent exe "normal! d" +        let putreg = getreg( '"' ) +    else +        silent exe "normal! y" +        let putreg = getreg( '"' ) +        if a:func == 'd' +            " Register "0 is corrupted by the above 'y' command +            call setreg( '0', save_0 )  +        elseif a:visualmode && &selection == "inclusive" && len(getline("'>")) < col("'>") && len(putreg) > 0 +            " Remove extra space added at the end of line when selection=inclusive, all, or onemore +            let putreg = putreg[:-2] +        endif + +        " Find and keep unbalanced matched characters in the region +        let instring = s:InsideString( line("'<"), col("'<") ) +        if col("'>") > 1 && !s:InsideString( line("'<"), col("'<") - 1 ) +            " We are at the beginning of the string +            let instring = 0 +        endif +        let matched = s:GetMatchedChars( putreg, instring, s:InsideComment( line("'<"), col("'<") ) ) +        let matched = s:Unbalanced( matched ) +        let matched = substitute( matched, '\s', '', 'g' ) + +        if matched == '' +            if a:func == 'c' && (a:type == 'v' || a:type == 'V' || a:type == 'char') +                silent exe "normal! gvc" +            else +                silent exe "normal! gvd" +            endif +        else +            silent exe "normal! gvc" . matched +            silent exe "normal! l" +            let offs = len(matched) +            if matched[0] =~ b:any_closing_char +                let offs = offs + 1 +            endif +            if a:func == 'd' +                let offs = offs - 1 +            elseif instring && matched == '"' +                " Keep cursor inside the double quotes +                let offs = offs + 1 +            endif +            if offs > 0 +                silent exe "normal! " . string(offs) . "h" +            endif +        endif +    endif + +    let &selection = sel_save +    let &virtualedit = ve_save +    if a:func == 'd' && regname == '"' +        " Do not currupt the '"' register and hence the "0 register +        call setreg( '1', putreg )  +    else +        call setreg( regname, putreg )  +    endif +endfunction + +" Set delete mode also saving repeat count +function! PareditSetDelete( count ) +    let s:repeat = a:count +    set opfunc=PareditDelete +endfunction + +" General delete operator handling +function! PareditDelete( type, ... ) +    call PareditOpfunc( 'd', a:type, a:0 ) +    if s:repeat > 1 +        call feedkeys( (s:repeat-1) . "." ) +    endif +    let s:repeat = 0 +endfunction + +" General change operator handling +function! PareditChange( type, ... ) +    let ve_save = &virtualedit +    set virtualedit=all +    call PareditOpfunc( 'c', a:type, a:0 ) +    if len(getline('.')) == 0 +        let v:lnum = line('.') +        let expr = &indentexpr +        if expr == '' +            " No special 'indentexpr', call default lisp indent +            let expr = 'lispindent(v:lnum)' +        endif +        execute "call setline( v:lnum, repeat( ' ', " . expr . " ) )" +        call cursor(v:lnum, len(getline(v:lnum))+1) +    else +        normal! l +    endif +    startinsert +    let &virtualedit = ve_save +endfunction + +" Delete v:count number of lines +function! PareditDeleteLines() +    if v:count > 1 +        silent exe "normal! V" . (v:count-1) . "j\<Esc>" +    else +        silent exe "normal! V\<Esc>" +    endif +    call PareditDelete(visualmode(),1) +endfunction + +" Change v:count number of lines +function! PareditChangeLines() +    if v:count > 1 +        silent exe "normal! V" . (v:count-1) . "j\<Esc>" +    else +        silent exe "normal! V\<Esc>" +    endif +    call PareditChange(visualmode(),1) +endfunction + +" Handle special change command, e.g. cw +" Check if we may revert to its original Vim function +" This way '.' can be used to repeat the command +function! PareditChangeSpec( cmd, dir ) +    let line = getline( '.' ) +    if a:dir == 0 +        " Changing backwards +        let c =  col( '.' ) - 2 +        while c >= 0 && line[c] =~ b:any_matched_char +            " Shouldn't delete a matched character, just move left +            call feedkeys( 'h', 'n') +            let c = c - 1 +        endwhile +        if c < 0 && line[0] =~ b:any_matched_char +            " Can't help, still on matched character, insert instead +            call feedkeys( 'i', 'n') +            return +        endif +    else +        " Changing forward +        let c =  col( '.' ) - 1 +        while c < len(line) && line[c] =~ b:any_matched_char +            " Shouldn't delete a matched character, just move right +            call feedkeys( 'l', 'n') +            let c = c + 1 +        endwhile +        if c == len(line) +            " Can't help, still on matched character, append instead +            call feedkeys( 'a', 'n') +            return +        endif +    endif +     +    " Safe to use Vim's built-in change function +    call feedkeys( a:cmd, 'n') +endfunction + +" Paste text from put register in a balanced way +function! PareditPut( cmd ) +    let regname = v:register +    let reg_save = getreg( regname ) +    let putreg = reg_save + +    " Find unpaired matched characters by eliminating paired ones +    let matched = s:GetMatchedChars( putreg, s:InsideString(), s:InsideComment() ) +    let matched = s:Unbalanced( matched ) + +    if matched !~ '\S\+' +        " Register contents is balanced, perform default put function +        silent exe "normal! " . (v:count>1 ? v:count : '') . (regname=='"' ? '' : '"'.regname) . a:cmd +        return +    endif + +    " Replace all unpaired matched characters with a space in order to keep balance +    let i = 0 +    while i < len( putreg ) +        if matched[i] !~ '\s' +            let putreg = strpart( putreg, 0, i ) . ' ' . strpart( putreg, i+1 ) +        endif +        let i = i + 1 +    endwhile + +    " Store balanced text in put register and call the appropriate put command +    call setreg( regname, putreg )  +    silent exe "normal! " . (v:count>1 ? v:count : '') . (regname=='"' ? '' : '"'.regname) . a:cmd +    call setreg( regname, reg_save )  +endfunction + +" Toggle paredit mode +function! PareditToggle() +    " Don't disable paredit if it was not initialized yet for the current buffer +    if exists( 'b:paredit_init') || g:paredit_mode == 0 +        let g:paredit_mode = 1 - g:paredit_mode +    endif +    echo g:paredit_mode ? 'Paredit mode on' : 'Paredit mode off' +    call PareditInitBuffer() +endfunction + +" Does the current syntax item match the given regular expression? +function! s:SynIDMatch( regexp, line, col, match_eol ) +    let col  = a:col +    if a:match_eol && col > len( getline( a:line ) ) +        let col = col - 1 +    endif +    return synIDattr( synID( a:line, col, 0), 'name' ) =~ a:regexp +endfunction + +" Expression used to check whether we should skip a match with searchpair() +function! s:SkipExpr() +    let l = line('.') +    let c = col('.') +    if synIDattr(synID(l, c, 0), "name") =~ "[Ss]tring\\|[Cc]omment\\|[Ss]pecial\\|clojureRegexp\\|clojurePattern" +        " Skip parens inside strings, comments, special elements +        return 1 +    endif +    if getline(l)[c-2] == "\\" && getline(l)[c-3] != "\\" +        " Skip parens escaped by '\' +        return 1 +    endif +    return 0 +endfunction + +" Is the current cursor position inside a comment? +function! s:InsideComment( ... ) +    let l = a:0 ? a:1 : line('.') +    let c = a:0 ? a:2 : col('.') +    if &syntax == '' +        " No help from syntax engine, +        " remove strings and search for ';' up to the cursor position +        let line = strpart( getline(l), 0, c - 1 ) +        let line = substitute( line, '\\"', '', 'g' ) +        let line = substitute( line, '"[^"]*"', '', 'g' ) +        return match( line, ';' ) >= 0 +    endif +    if s:SynIDMatch( 'clojureComment', l, c, 1 ) +        if strpart( getline(l), c-1, 2 ) == '#_' || strpart( getline(l), c-2, 2 ) == '#_' +            " This is a commented out clojure form of type #_(...), treat it as regular form +            return 0 +        endif +    endif +    return s:SynIDMatch( '[Cc]omment', l, c, 1 ) +endfunction + +" Is the current cursor position inside a string? +function! s:InsideString( ... ) +    let l = a:0 ? a:1 : line('.') +    let c = a:0 ? a:2 : col('.') +    if &syntax == '' +        " No help from syntax engine, +        " count quote characters up to the cursor position +        let line = strpart( getline(l), 0, c - 1 ) +        let line = substitute( line, '\\"', '', 'g' ) +        let quotes = substitute( line, '[^"]', '', 'g' ) +        return len(quotes) % 2 +    endif +    " VimClojure and vim-clojure-static define special syntax for regexps +    return s:SynIDMatch( '[Ss]tring\|clojureRegexp\|clojurePattern', l, c, 0 ) +endfunction + +" Is this a Slimv or VimClojure REPL buffer? +function! s:IsReplBuffer() +    if exists( 'b:slimv_repl_buffer' ) || exists( 'b:vimclojure_repl' ) +        return 1 +    else +        return 0 +    endif +endfunction + +" Get Slimv or VimClojure REPL buffer last command prompt position +" Return [0, 0] if this is not the REPL buffer +function! s:GetReplPromptPos() +    if !s:IsReplBuffer() +        return [0, 0] +    endif +    if exists( 'b:vimclojure_repl') +        let cur_pos = getpos( '.' ) +        call cursor( line( '$' ), 1) +        call cursor( line( '.' ), col( '$') ) +        call search( b:vimclojure_namespace . '=>', 'bcW' ) +        let target_pos = getpos( '.' )[1:2] +        call setpos( '.', cur_pos ) +        return target_pos +    else +        return [ b:repl_prompt_line, b:repl_prompt_col ] +    endif +endfunction + +" Is the current top level form balanced, i.e all opening delimiters +" have a matching closing delimiter +function! s:IsBalanced() +    let l = line( '.' ) +    let c =  col( '.' ) +    let line = getline( '.' ) +    if c > len(line) +        let c = len(line) +    endif +    let matchb = max( [l-g:paredit_matchlines, 1] ) +    let matchf = min( [l+g:paredit_matchlines, line('$')] ) +    let [prompt, cp] = s:GetReplPromptPos() +    if s:IsReplBuffer() && l >= prompt && matchb < prompt +        " Do not go before the last command prompt in the REPL buffer +        let matchb = prompt +    endif +    if line[c-1] == '(' +        let p1 = searchpair( '(', '', ')', 'brnmWc', 's:SkipExpr()', matchb ) +        let p2 = searchpair( '(', '', ')',  'rnmW' , 's:SkipExpr()', matchf ) +    elseif line[c-1] == ')' +        let p1 = searchpair( '(', '', ')', 'brnmW' , 's:SkipExpr()', matchb ) +        let p2 = searchpair( '(', '', ')',  'rnmWc', 's:SkipExpr()', matchf ) +    else +        let p1 = searchpair( '(', '', ')', 'brnmW' , 's:SkipExpr()', matchb ) +        let p2 = searchpair( '(', '', ')',  'rnmW' , 's:SkipExpr()', matchf ) +    endif +    if p1 != p2 +        " Number of opening and closing parens differ +        return 0 +    endif + +    if &ft =~ s:fts_balancing_all_brackets +        if line[c-1] == '[' +            let b1 = searchpair( '\[', '', '\]', 'brnmWc', 's:SkipExpr()', matchb ) +            let b2 = searchpair( '\[', '', '\]',  'rnmW' , 's:SkipExpr()', matchf ) +        elseif line[c-1] == ']' +            let b1 = searchpair( '\[', '', '\]', 'brnmW' , 's:SkipExpr()', matchb ) +            let b2 = searchpair( '\[', '', '\]',  'rnmWc', 's:SkipExpr()', matchf ) +        else +            let b1 = searchpair( '\[', '', '\]', 'brnmW' , 's:SkipExpr()', matchb ) +            let b2 = searchpair( '\[', '', '\]',  'rnmW' , 's:SkipExpr()', matchf ) +        endif +        if b1 != b2 +            " Number of opening and closing brackets differ +            return 0 +        endif +        if line[c-1] == '{' +            let b1 = searchpair( '{', '', '}', 'brnmWc', 's:SkipExpr()', matchb ) +            let b2 = searchpair( '{', '', '}',  'rnmW' , 's:SkipExpr()', matchf ) +        elseif line[c-1] == '}' +            let b1 = searchpair( '{', '', '}', 'brnmW' , 's:SkipExpr()', matchb ) +            let b2 = searchpair( '{', '', '}',  'rnmWc', 's:SkipExpr()', matchf ) +        else +            let b1 = searchpair( '{', '', '}', 'brnmW' , 's:SkipExpr()', matchb ) +            let b2 = searchpair( '{', '', '}',  'rnmW' , 's:SkipExpr()', matchf ) +        endif +        if b1 != b2 +            " Number of opening and closing curly braces differ +            return 0 +        endif +    endif +    return 1 +endfunction + +" Filter out all non-matched characters from the region +function! s:GetMatchedChars( lines, start_in_string, start_in_comment ) +    let inside_string  = a:start_in_string +    let inside_comment = a:start_in_comment +    let matched = repeat( ' ', len( a:lines ) ) +    let i = 0 +    while i < len( a:lines ) +        if inside_string +            " We are inside a string, skip parens, wait for closing '"' +            " but skip escaped \" characters +            if a:lines[i] == '"' && a:lines[i-1] != '\' +                let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 ) +                let inside_string = 0 +            endif +        elseif inside_comment +            " We are inside a comment, skip parens, wait for end of line +            if a:lines[i] == "\n" +                let inside_comment = 0 +            endif +        elseif i > 0 && a:lines[i-1] == '\' && (i < 2 || a:lines[i-2] != '\') +            " This is an escaped character, ignore it +        else +            " We are outside of strings and comments, now we shall count parens +            if a:lines[i] == '"' +                let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 ) +                let inside_string = 1 +            endif +            if a:lines[i] == ';' +                let inside_comment = 1 +            endif +            if a:lines[i] =~ b:any_openclose_char +                let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 ) +            endif +        endif +        let i = i + 1 +    endwhile +    return matched +endfunction + +" Find unpaired matched characters by eliminating paired ones +function! s:Unbalanced( matched ) +    let matched = a:matched +    let tmp = matched +    while 1 +        let matched = tmp +        let tmp = substitute( tmp, '(\(\s*\))',   ' \1 ', 'g') +        if &ft =~ s:fts_balancing_all_brackets +            let tmp = substitute( tmp, '\[\(\s*\)\]', ' \1 ', 'g') +            let tmp = substitute( tmp, '{\(\s*\)}',   ' \1 ', 'g') +        endif +        let tmp = substitute( tmp, '"\(\s*\)"',   ' \1 ', 'g') +        if tmp == matched +            " All paired chars eliminated +            let tmp = substitute( tmp, ')\(\s*\)(',   ' \1 ', 'g') +            if &ft =~ s:fts_balancing_all_brackets +                let tmp = substitute( tmp, '\]\(\s*\)\[', ' \1 ', 'g') +                let tmp = substitute( tmp, '}\(\s*\){',   ' \1 ', 'g') +            endif +            if tmp == matched +                " Also no more inverse pairs can be eliminated +                break +            endif +        endif +    endwhile +    return matched +endfunction + +" Find opening matched character +function! PareditFindOpening( open, close, select ) +    let open  = escape( a:open , '[]' ) +    let close = escape( a:close, '[]' ) +    call searchpair( open, '', close, 'bW', 's:SkipExpr()' ) +    if a:select +        call searchpair( open, '', close, 'W', 's:SkipExpr()' ) +        let save_ve = &ve +        set ve=all  +        normal! lvh +        let &ve = save_ve +        call searchpair( open, '', close, 'bW', 's:SkipExpr()' ) +        if &selection == 'inclusive' +            " Trim last character from the selection, it will be included anyway +            normal! oho +        endif +    endif +endfunction + +" Find closing matched character +function! PareditFindClosing( open, close, select ) +    let open  = escape( a:open , '[]' ) +    let close = escape( a:close, '[]' ) +    if a:select +        let line = getline( '.' ) +        if line[col('.')-1] != a:open +            normal! h +        endif +        call searchpair( open, '', close, 'W', 's:SkipExpr()' ) +        call searchpair( open, '', close, 'bW', 's:SkipExpr()' ) +        normal! v +        call searchpair( open, '', close, 'W', 's:SkipExpr()' ) +        if &selection != 'inclusive' +            normal! l +        endif +    else +        call searchpair( open, '', close, 'W', 's:SkipExpr()' ) +    endif +endfunction + +" Returns the nearest opening character to the cursor +" Used for smart jumping in Clojure +function! PareditSmartJumpOpening( select ) +    let [paren_line, paren_col] = searchpairpos('(', '', ')', 'bWn', 's:SkipExpr()') +    let [bracket_line, bracket_col] = searchpairpos('\[', '', '\]', 'bWn', 's:SkipExpr()') +    let [brace_line, brace_col] = searchpairpos('{', '', '}', 'bWn', 's:SkipExpr()') +    let paren_score = paren_line * 10000 + paren_col +    let bracket_score = bracket_line * 10000 + bracket_col +    let brace_score = brace_line * 10000 + brace_col +    if (brace_score > paren_score || paren_score == 0) && (brace_score > bracket_score || bracket_score == 0) && brace_score != 0 +	call PareditFindOpening('{','}', a:select) +    elseif (bracket_score > paren_score || paren_score == 0) && bracket_score != 0 +	call PareditFindOpening('[',']', a:select) +    else +	call PareditFindOpening('(',')', a:select) +    endif +endfunction + +" Returns the nearest opening character to the cursor +" Used for smart jumping in Clojure +function! PareditSmartJumpClosing( select ) +    let [paren_line, paren_col] = searchpairpos('(', '', ')', 'Wn', 's:SkipExpr()') +    let [bracket_line, bracket_col] = searchpairpos('\[', '', '\]', 'Wn', 's:SkipExpr()') +    let [brace_line, brace_col] = searchpairpos('{', '', '}', 'Wn', 's:SkipExpr()') +    let paren_score = paren_line * 10000 + paren_col +    let bracket_score = bracket_line * 10000 + bracket_col +    let brace_score = brace_line * 10000 + brace_col +    if (brace_score < paren_score || paren_score == 0) && (brace_score < bracket_score || bracket_score == 0) && brace_score != 0 +	call PareditFindClosing('{','}', a:select) +    elseif (bracket_score < paren_score || paren_score == 0) && bracket_score != 0 +	call PareditFindClosing('[',']', a:select) +    else +	call PareditFindClosing('(',')', a:select) +    endif +endfunction + +" Find defun start backwards +function! PareditFindDefunBck() +    let l = line( '.' ) +    let matchb = max( [l-g:paredit_matchlines, 1] ) +    let oldpos = getpos( '.' )  +    let newpos = searchpairpos( '(', '', ')', 'brW', 's:SkipExpr()', matchb ) +    if newpos[0] == 0 +        " Already standing on a defun, find the end of the previous one +        let newpos = searchpos( ')', 'bW' ) +        while newpos[0] != 0 && (s:InsideComment() || s:InsideString()) +            let newpos = searchpos( ')', 'W' ) +        endwhile +        if newpos[0] == 0 +            " No ')' found, don't move cursor +            call setpos( '.', oldpos ) +        else +            " Find opening paren +            let pairpos = searchpairpos( '(', '', ')', 'brW', 's:SkipExpr()', matchb ) +            if pairpos[0] == 0 +                " ')' has no matching pair +                call setpos( '.', oldpos ) +            endif +        endif +    endif +endfunction + +" Find defun start forward +function! PareditFindDefunFwd() +    let l = line( '.' ) +    let matchf = min( [l+g:paredit_matchlines, line('$')] ) +    let oldpos = getpos( '.' )  +    call searchpair( '(', '', ')', 'brW', 's:SkipExpr()', matchf ) +    normal! % +    let newpos = searchpos( '(', 'W' ) +    while newpos[0] != 0 && (s:InsideComment() || s:InsideString()) +        let newpos = searchpos( '(', 'W' ) +    endwhile +    if newpos[0] == 0 +        " No '(' found, don't move cursor +        call setpos( '.', oldpos ) +    endif +endfunction + +" Insert opening type of a paired character, like ( or [. +function! PareditInsertOpening( open, close ) +    if !g:paredit_mode || s:InsideComment() || s:InsideString() || !s:IsBalanced() +        return a:open +    endif +    let line = getline( '.' ) +    let pos = col( '.' ) - 1 +    if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\') +        " About to enter a \( or \[ +        return a:open +    elseif line[pos] !~ b:any_wsclose_char && pos < len( line ) +        " Add a space after if needed +        let retval = a:open . a:close . " \<Left>\<Left>" +    else +        let retval = a:open . a:close . "\<Left>" +    endif +    if pos > 0 && line[pos-1] !~ b:any_wsopen_char && line[pos-1] !~ s:any_macro_prefix +        " Add a space before if needed +        let retval = " " . retval +    endif +    return retval +endfunction + +" Re-gather electric returns up +function! s:ReGatherUp() +    if g:paredit_electric_return && getline('.') =~ '^\s*)' +        " Re-gather electric returns in the current line for ')' +        normal! k +        while getline( line('.') ) =~ '^\s*$' +            " Delete all empty lines +            normal! ddk +        endwhile +        normal! Jl +    elseif g:paredit_electric_return && getline('.') =~ '^\s*\(\]\|}\)' && &ft =~ s:fts_balancing_all_brackets +        " Re-gather electric returns in the current line for ']' and '}' +        normal! k +        while getline( line('.') ) =~ '^\s*$' +            " Delete all empty lines +            normal! ddk +        endwhile +        call setline( line('.'), substitute( line, '\s*$', '', 'g' ) ) +        normal! Jxl +    endif +    " Already have the desired character, move right +    normal! l +endfunction + +" Insert closing type of a paired character, like ) or ]. +function! PareditInsertClosing( open, close ) +    let retval = "" +    if pumvisible() +        let retval = "\<C-Y>" +    endif +    let save_ve = &ve +    set ve=all  +    let line = getline( '.' ) +    let pos = col( '.' ) - 1 +    if !g:paredit_mode || s:InsideComment() || s:InsideString() || !s:IsBalanced() +        call setline( line('.'), line[0 : pos-1] . a:close . line[pos : -1] ) +        normal! l +        let &ve = save_ve +        return retval +    endif +    if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\') +        " About to enter a \) or \] +        call setline( line('.'), line[0 : pos-1] . a:close . line[pos : -1] ) +        normal! l +        let &ve = save_ve +        return retval +    elseif line[pos] == a:close +        call s:ReGatherUp() +        let &ve = save_ve +        return retval +    endif +    let open  = escape( a:open , '[]' ) +    let close = escape( a:close, '[]' ) +    let newpos = searchpairpos( open, '', close, 'nW', 's:SkipExpr()' ) +    if g:paredit_electric_return && newpos[0] > line('.') +        " Closing paren is in a line below, check if there are electric returns to re-gather +        while getline('.') =~ '^\s*$' +            " Delete all empty lines above the cursor +            normal! ddk +        endwhile +        let oldpos = getpos( '.' )  +        normal! j +        while getline('.') =~ '^\s*$' +            " Delete all empty lines below the cursor +            normal! dd +        endwhile +        let nextline = substitute( getline('.'), '\s', '', 'g' ) +        call setpos( '.', oldpos ) +        if len(nextline) > 0 && nextline[0] == ')' +            " Re-gather electric returns in the line of the closing ')' +            call setline( line('.'), substitute( getline('.'), '\s*$', '', 'g' ) ) +            normal! Jl +            let &ve = save_ve +            return retval +        endif +        if len(nextline) > 0 && nextline[0] =~ '\]\|}' && &ft =~ s:fts_balancing_all_brackets +            " Re-gather electric returns in the line of the closing ']' or '}' +            call setline( line('.'), substitute( line, '\s*$', '', 'g' ) ) +            normal! Jxl +            let &ve = save_ve +            return retval +        endif +    elseif g:paredit_electric_return && line =~ '^\s*)' +        " Re-gather electric returns in the current line +        call s:ReGatherUp() +        let &ve = save_ve +        return retval +    endif +    if searchpair( open, '', close, 'W', 's:SkipExpr()' ) > 0 +        normal! l +    endif +    "TODO: indent after going to closing character +    let &ve = save_ve +    return retval +endfunction + +" Insert an (opening or closing) double quote +function! PareditInsertQuotes() +    if !g:paredit_mode || s:InsideComment() +        return '"' +    endif +    let line = getline( '.' ) +    let pos = col( '.' ) - 1 +    if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\') +        " About to enter a \" +        return '"' +    elseif s:InsideString() +        "TODO: skip comments in search(...) +        if line[pos] == '"' +            " Standing on a ", just move to the right +            return "\<Right>" +        elseif search('[^\\]"\|^"', 'nW') == 0 +            " We don't have any closing ", insert one +            return '"' +        else +            " Move to the closing " +            return "\<C-O>:call search('" . '[^\\]"\|^"' . "','eW')\<CR>\<Right>" +        endif +    else +        " Outside of string: insert a pair of "" +        return '""' . "\<Left>" +    endif +endfunction + +" Handle <Enter> keypress, insert electric return if applicable +function! PareditEnter() +    if pumvisible() +        " Pressing <CR> in a pop up selects entry. +        return "\<C-Y>" +    else +        let line = getline( '.' ) +        let pos = col( '.' ) - 1 +        if g:paredit_electric_return && pos > 0 && line[pos] =~ b:any_closing_char && !s:InsideString() && s:IsBalanced() +            " Electric Return +            return "\<CR>\<CR>\<Up>" +        else +            " Regular Return +            return "\<CR>" +        endif +    endif +endfunction + +" Handle <BS> keypress +function! PareditBackspace( repl_mode ) +    let [lp, cp] = s:GetReplPromptPos() +    if a:repl_mode && line( "." ) == lp && col( "." ) <= cp +        " No BS allowed before the previous EOF mark in the REPL +        " i.e. don't delete Lisp prompt +        return "" +    endif + +    if !g:paredit_mode || s:InsideComment() +        return "\<BS>" +    endif + +    let line = getline( '.' ) +    let pos = col( '.' ) - 1 + +    if pos == 0 +        " We are at the beginning of the line +        return "\<BS>" +    elseif s:InsideString() && line[pos-1] =~ b:any_openclose_char +        " Deleting a paren inside a string +        return "\<BS>" +    elseif pos > 1 && line[pos-1] =~ b:any_matched_char && line[pos-2] == '\' && (pos < 3 || line[pos-3] != '\') +        " Deleting an escaped matched character +        return "\<BS>\<BS>" +    elseif line[pos-1] !~ b:any_matched_char +        " Deleting a non-special character +        return "\<BS>" +    elseif line[pos-1] != '"' && !s:IsBalanced() +        " Current top-form is unbalanced, can't retain paredit mode +        return "\<BS>" +    endif + +    if line[pos-1:pos] =~ b:any_matched_pair +        " Deleting an empty character-pair +        return "\<Right>\<BS>\<BS>" +    else +        " Character-pair is not empty, don't delete just move inside +        return "\<Left>" +    endif +endfunction + +" Handle <Del> keypress +function! PareditDel() +    if !g:paredit_mode || s:InsideComment() +        return "\<Del>" +    endif + +    let line = getline( '.' ) +    let pos = col( '.' ) - 1 + +    if pos == len(line) +        " We are at the end of the line +        return "\<Del>" +    elseif line[pos] == '\' && line[pos+1] =~ b:any_matched_char && (pos < 1 || line[pos-1] != '\') +        " Deleting an escaped matched character +        return "\<Del>\<Del>" +    elseif line[pos] !~ b:any_matched_char +        " Erasing a non-special character +        return "\<Del>" +    elseif line[pos] != '"' && !s:IsBalanced() +        " Current top-form is unbalanced, can't retain paredit mode +        return "\<Del>" +    elseif pos == 0 +        return "\<Right>" +    endif + +    if line[pos-1:pos] =~ b:any_matched_pair +        " Erasing an empty character-pair +        return "\<Left>\<Del>\<Del>" +    else +        " Character-pair is not empty, don't erase just move inside +        return "\<Right>" +    endif +endfunction + +" Initialize yank position list +function! s:InitYankPos() +    call setreg( &clipboard == 'unnamed' ? '*' : '"', '' )  +    let s:yank_pos = [] +endfunction + +" Add position to the yank list +function! s:AddYankPos( pos ) +    let s:yank_pos = [a:pos] + s:yank_pos +endfunction + +" Remove the head of yank position list and return it +function! s:RemoveYankPos() +    if len(s:yank_pos) > 0 +        let pos = s:yank_pos[0] +        let s:yank_pos = s:yank_pos[1:] +        return pos +    else +        return 0 +    endif +endfunction + +" Forward erasing a character in normal mode, do not check if current form balanced +function! s:EraseFwd( count, startcol ) +    let line = getline( '.' ) +    let pos = col( '.' ) - 1 +    let reg = '' +    let ve_save = &virtualedit +    set virtualedit=all +    let c = a:count +    while c > 0 +        if line[pos] == '\' && line[pos+1] =~ b:any_matched_char && (pos < 1 || line[pos-1] != '\') +            " Erasing an escaped matched character +            let reg = reg . line[pos : pos+1] +            let line = strpart( line, 0, pos ) . strpart( line, pos+2 ) +        elseif s:InsideComment() && line[pos] == ';' && a:startcol >= 0 +            " Erasing the whole comment, only when erasing a block of characters +            let reg = reg . strpart( line, pos ) +            let line = strpart( line, 0, pos ) +        elseif s:InsideComment() || ( s:InsideString() && line[pos] != '"' ) +            " Erasing any character inside string or comment +            let reg = reg . line[pos] +            let line = strpart( line, 0, pos ) . strpart( line, pos+1 ) +        elseif pos > 0 && line[pos-1:pos] =~ b:any_matched_pair +            if pos > a:startcol +                " Erasing an empty character-pair +                let p2 = s:RemoveYankPos() +                let reg = strpart( reg, 0, p2 ) . line[pos-1] . strpart( reg, p2 ) +                let reg = reg . line[pos] +                let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 ) +                let pos = pos - 1 +                normal! h +            else +                " Can't erase character-pair: it would move the cursor before startcol +                let pos = pos + 1 +                normal! l +            endif +        elseif line[pos] =~ b:any_matched_char +            " Character-pair is not empty, don't erase just move inside +            call s:AddYankPos( len(reg) ) +            let pos = pos + 1 +            normal! l +        elseif pos < len(line) && pos >= a:startcol +            " Erasing a non-special character +            let chars = split(strpart(line, pos), '\zs') +            if len(chars) > 0 +                " Identify the character to be erased and it's length +                " The length may be >1 if this is a multi-byte character +                let ch = chars[0] +                let reg = reg . ch +                let line = strpart( line, 0, pos ) . strpart( line, pos+len(ch) ) +            endif +        endif +        let c = c - 1 +    endwhile +    let &virtualedit = ve_save +    call setline( '.', line ) +    call setreg( &clipboard == 'unnamed' ? '*' : '"', reg )  +endfunction + +" Backward erasing a character in normal mode, do not check if current form balanced +function! s:EraseBck( count ) +    let line = getline( '.' ) +    let pos = col( '.' ) - 1 +    let reg = '' +    let c = a:count +    while c > 0 && pos > 0 +        if pos > 1 && line[pos-2] == '\' && line[pos-1] =~ b:any_matched_char && (pos < 3 || line[pos-3] != '\') +            " Erasing an escaped matched character +            let reg = reg . line[pos-2 : pos-1] +            let line = strpart( line, 0, pos-2 ) . strpart( line, pos ) +            normal! h +            let pos = pos - 1 +        elseif s:InsideComment() || ( s:InsideString() && line[pos-1] != '"' ) +            let reg = reg . line[pos-1] +            let line = strpart( line, 0, pos-1 ) . strpart( line, pos ) +        elseif line[pos-1:pos] =~ b:any_matched_pair +            " Erasing an empty character-pair +            let p2 = s:RemoveYankPos() +            let reg = strpart( reg, 0, p2 ) . line[pos-1] . strpart( reg, p2 ) +            let reg = reg . line[pos] +            let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 ) +        elseif line[pos-1] =~ b:any_matched_char +            " Character-pair is not empty, don't erase +            call s:AddYankPos( len(reg) ) +        else +            " Erasing a non-special character +            let chars = split(strpart(line, 0, pos), '\zs') +            if len(chars) > 0 +                " Identify the character to be erased and it's length +                " The length may be >1 if this is a multi-byte character +                let ch = chars[-1] +                let reg = reg . ch +                let line = strpart( line, 0, pos-len(ch) ) . strpart( line, pos ) +                let pos = pos - len(ch) + 1 +            endif +        endif +        normal! h +        let pos = pos - 1 +        let c = c - 1 +    endwhile +    call setline( '.', line ) +    call setreg( &clipboard == 'unnamed' ? '*' : '"', reg )  +endfunction + +" Forward erasing a character in normal mode +function! PareditEraseFwd() +    if !g:paredit_mode || !s:IsBalanced() +        if v:count > 0 +            silent execute 'normal! ' . v:count . 'x' +        else +            normal! x +        endif +        return +    endif + +    call s:InitYankPos() +    call s:EraseFwd( v:count1, -1 ) +endfunction + +" Backward erasing a character in normal mode +function! PareditEraseBck() +    if !g:paredit_mode || !s:IsBalanced() +        if v:count > 0 +            silent execute 'normal! ' . v:count . 'X' +        else +            normal! X +        endif +        return +    endif + +    call s:InitYankPos() +    call s:EraseBck( v:count1 ) +endfunction + +" Find beginning of previous element (atom or sub-expression) in a form +" skip_whitespc: skip whitespaces before the previous element +function! s:PrevElement( skip_whitespc ) +    let [l0, c0] = [line( '.' ), col( '.' )] +    let symbol_pos = [0, 0] +    let symbol_end = [0, 0] + +    " Move to the beginning of the prefix if any +    let line = getline( '.' ) +    let c = col('.') - 1 +    if c > 0 && line[c-1] =~ s:any_macro_prefix +        normal! h +    endif + +    let moved = 0 +    while 1 +        " Go to previous character +        if !moved +            let [l1, c1] = [line( '.' ), col( '.' )] +            let save_ww = &whichwrap +            set whichwrap= +            normal! h +            let &whichwrap = save_ww +        endif +        let moved = 0 +        let [l, c] = [line( '.' ), col( '.' )] + +        if [l, c] == [l1, c1] +            " Beginning of line reached +            if symbol_pos != [0, 0] +                let symbol_end = [l, c] +                if !a:skip_whitespc && !s:InsideString() +                    " Newline before previous symbol +                    call setpos( '.', [0, l0, c0, 0] ) +                    return [l, c] +                endif +            endif +            normal! k$ +            let [l, c] = [line( '.' ), col( '.' )] +            if [l, c] == [l1, c1] +                " Beginning of file reached: stop +                call setpos( '.', [0, l0, c0, 0] ) +                return [0, 0] +            endif +            let moved = 1 +        elseif s:InsideComment() +            " Skip comments +        else +            let line = getline( '.' ) +            if s:InsideString() && !(a:skip_whitespc && line[c] =~ '\s' && symbol_end != [0, 0]) +                let symbol_pos = [l, c] +            elseif symbol_pos == [0, 0] +                if line[c-1] =~ b:any_closing_char +                    " Skip to the beginning of this sub-expression +                    let symbol_pos = [l, c] +                    normal! % +                    let line2 = getline( '.' ) +                    let c2 = col('.') - 1 +                    if c2 > 0 && line2[c2-1] =~ s:any_macro_prefix +                        normal! h +                    endif +                elseif line[c-1] =~ b:any_opening_char +                    " Opening delimiter found: stop +                    call setpos( '.', [0, l0, c0, 0] ) +                    return [0, 0] +                elseif line[c-1] =~ '\S' +                    " Previous symbol starting +                    let symbol_pos = [l, c] +                endif +            else +                if line[c-1] =~ b:any_opening_char || (a:skip_whitespc && line[c-1] =~ '\S' && symbol_end != [0, 0]) +                    " Previous symbol beginning reached, opening delimiter or second previous symbol starting +                    call setpos( '.', [0, l0, c0, 0] ) +                    return [l, c+1] +                elseif line[c-1] =~ '\s' || symbol_pos[0] != l +                    " Whitespace before previous symbol +                    let symbol_end = [l, c] +                    if !a:skip_whitespc +                        call setpos( '.', [0, l0, c0, 0] ) +                        return [l, c+1] +                    endif +                endif +            endif +        endif +    endwhile +endfunction + +" Find end of next element (atom or sub-expression) in a form +" skip_whitespc: skip whitespaces after the next element +function! s:NextElement( skip_whitespc ) +    let [l0, c0] = [line( '.' ), col( '.' )] +    let symbol_pos = [0, 0] +    let symbol_end = [0, 0] + +    while 1 +        " Go to next character +        let [l1, c1] = [line( '.' ), col( '.' )] +        let save_ww = &whichwrap +        set whichwrap= +        normal! l +        let &whichwrap = save_ww +        let [l, c] = [line( '.' ), col( '.' )] + +        " Skip comments +        while [l, c] == [l1, c1] || s:InsideComment() +            if symbol_pos != [0, 0] +                let symbol_end = [l, c] +                if !a:skip_whitespc && !s:InsideString() +                    " Next symbol ended with comment +                    call setpos( '.', [0, l0, c0, 0] ) +                    return [l, c + ([l, c] == [l1, c1])] +                endif +            endif +            normal! 0j0 +            let [l, c] = [line( '.' ), col( '.' )] +            if [l, c] == [l1, c1] +                " End of file reached: stop +                call setpos( '.', [0, l0, c0, 0] ) +                return [0, 0] +            endif +        endwhile + +        let line = getline( '.' ) +        if s:InsideString() && !(a:skip_whitespc && line[c-2] =~ '\s' && symbol_end != [0, 0]) +            let symbol_pos = [l, c] +        elseif symbol_pos == [0, 0] +            if line[c-1] =~ s:any_macro_prefix && line[c] =~ b:any_opening_char +                " Skip to the end of this prefixed sub-expression +                let symbol_pos = [l, c] +                normal! l% +            elseif line[c-1] =~ b:any_opening_char +                " Skip to the end of this sub-expression +                let symbol_pos = [l, c] +                normal! % +            elseif line[c-1] =~ b:any_closing_char +                " Closing delimiter found: stop +                call setpos( '.', [0, l0, c0, 0] ) +                return [0, 0] +            elseif line[c-1] =~ '\S' +                " Next symbol starting +                let symbol_pos = [l, c] +            endif +        else +            if line[c-1] =~ b:any_closing_char || (a:skip_whitespc && line[c-1] =~ '\S' && symbol_end != [0, 0]) +                " Next symbol ended, closing delimiter or second next symbol starting +                call setpos( '.', [0, l0, c0, 0] ) +                return [l, c] +            elseif line[c-1] =~ '\s' || symbol_pos[0] != l +                " Next symbol ending with whitespace +                let symbol_end = [l, c] +                if !a:skip_whitespc +                    call setpos( '.', [0, l0, c0, 0] ) +                    return [l, c] +                endif +            endif +        endif +    endwhile +endfunction + +" Move character from [l0, c0] to [l1, c1] +" Set position to [l1, c1] +function! s:MoveChar( l0, c0, l1, c1 ) +    let line = getline( a:l0 ) +    let c = line[a:c0-1] +    if a:l1 == a:l0 +        " Move character inside line +        if a:c1 > a:c0 +            let line = strpart( line, 0, a:c0-1 ) . strpart( line, a:c0, a:c1-a:c0-1 ) . c . strpart( line, a:c1-1 ) +            call setline( a:l0, line ) +            call setpos( '.', [0, a:l1, a:c1-1, 0] )  +        else +            let line = strpart( line, 0, a:c1-1 ) . c . strpart( line, a:c1-1, a:c0-a:c1 ) . strpart( line, a:c0 ) +            call setline( a:l0, line ) +            call setpos( '.', [0, a:l1, a:c1, 0] )  +        endif +    else +        " Move character to another line +        let line = strpart( line, 0, a:c0-1 ) . strpart( line, a:c0 ) +        call setline( a:l0, line ) +        let line1 = getline( a:l1 ) +        if a:c1 > 1 +            let line1 = strpart( line1, 0, a:c1-1 ) . c . strpart( line1, a:c1-1 ) +            call setline( a:l1, line1 ) +            call setpos( '.', [0, a:l1, a:c1, 0] ) +        else +            let line1 = c . line1 +            call setline( a:l1, line1 ) +            call setpos( '.', [0, a:l1, 1, 0] )  +        endif +    endif +endfunction + +" Find a paren nearby to move +function! s:FindParenNearby() +    let line = getline( '.' ) +    let c0 =  col( '.' ) +    if line[c0-1] !~ b:any_openclose_char +        " OK, we are not standing on a paren to move, but check if there is one nearby +        if (c0 < 2 || line[c0-2] !~ b:any_openclose_char) && line[c0] =~ b:any_openclose_char +            normal! l +        elseif c0 > 1 && line[c0-2] =~ b:any_openclose_char && line[c0] !~ b:any_openclose_char +            normal! h +        endif +    endif + +    " Skip macro prefix character     +    let c0 =  col( '.' ) +    if line[c0-1] =~ s:any_macro_prefix && line[c0] =~ b:any_opening_char +        normal! l +    endif + +    " If still not standing on a paren then find the next closing one +    if line[c0-1] !~ b:any_openclose_char +        call search(b:any_closing_char, 'W') +    endif +endfunction + +" Reindent current form +function! PareditReindentForm() +    let l = line('.') +    let c = col('.') +    let old_indent = len(matchstr(getline(l), '^\s*')) +    normal! =ib +    let new_indent = len(matchstr(getline(l), '^\s*')) +    call cursor( l, c + new_indent - old_indent ) +endfunction + +" Move delimiter one atom or s-expression to the left +function! PareditMoveLeft() +    call s:FindParenNearby() + +    let line = getline( '.' ) +    let l0 = line( '.' ) +    let c0 =  col( '.' ) + +    if line[c0-1] =~ b:any_opening_char +        let closing = 0 +    elseif line[c0-1] =~ b:any_closing_char +        let closing = 1 +    else +        " Can move only delimiters +        return +    endif + +    let [lp, cp] = s:GetReplPromptPos() +    let [l1, c1] = s:PrevElement( closing ) +    if [l1, c1] == [0, 0] +        " No previous element found +        return +    elseif [lp, cp] != [0, 0] && l0 >= lp && (l1 < lp || (l1 == lp && c1 < cp)) +        " Do not go before the last command prompt in the REPL buffer +        return +    endif +    if !closing && c0 > 0 && line[c0-2] =~ s:any_macro_prefix +        call s:MoveChar( l0, c0-1, l1, c1 ) +        call s:MoveChar( l0, c0 - (l0 != l1), l1, c1+1 ) +        let len = 2 +    else +        call s:MoveChar( l0, c0, l1, c1 ) +        let len = 1 +    endif +    let line = getline( '.' ) +    let c =  col( '.' ) - 1 +    if closing && c+1 < len(line) && line[c+1] !~ b:any_wsclose_char +        " Insert a space after if needed +        execute "normal! a " +        normal! h +    endif +    let line = getline( '.' ) +    let c =  col( '.' ) - 1 +    if !closing && c > 0 && line[c-len] !~ b:any_wsopen_char +        " Insert a space before if needed +        if len > 1 +            execute "normal! hi " +            normal! ll +        else +            execute "normal! i " +            normal! l +        endif +    endif +    call PareditReindentForm() +endfunction + +" Move delimiter one atom or s-expression to the right +function! PareditMoveRight() +    call s:FindParenNearby() + +    "TODO: move ')' in '() xxx' leaves space +    let line = getline( '.' ) +    let l0 = line( '.' ) +    let c0 =  col( '.' ) + +    if line[c0-1] =~ b:any_opening_char +        let opening = 1 +    elseif line[c0-1] =~ b:any_closing_char +        let opening = 0 +    else +        " Can move only delimiters +        return +    endif + +    let [lp, cp] = s:GetReplPromptPos() +    let [l1, c1] = s:NextElement( opening ) +    if [l1, c1] == [0, 0] +        " No next element found +        return +    elseif [lp, cp] != [0, 0] && l0 < lp && l1 >= lp +        " Do not go after the last command prompt in the REPL buffer +        return +    endif +    if opening && c0 > 1 && line[c0-2] =~ s:any_macro_prefix +        call s:MoveChar( l0, c0-1, l1, c1 ) +        call s:MoveChar( l0, c0-1, l1, c1 + (l0 != l1) ) +        let len = 2 +    else +        call s:MoveChar( l0, c0, l1, c1 ) +        let len = 1 +    endif +    let line = getline( '.' ) +    let c =  col( '.' ) - 1 +    if opening && c > 0 && line[c-len] !~ b:any_wsopen_char +        " Insert a space before if needed +        if len > 1 +            execute "normal! hi " +            normal! ll +        else +            execute "normal! i " +            normal! l +        endif +    endif +    let line = getline( '.' ) +    let c =  col( '.' ) - 1 +    if !opening && c+1 < len(line) && line[c+1] !~ b:any_wsclose_char +        " Insert a space after if needed +        execute "normal! a " +        normal! h +    endif +    call PareditReindentForm() +endfunction + +" Find closing of the innermost structure: (...) or [...] or {...} +" Return a list where first element is the closing character, +" second and third is its position (line, column) +function! s:FindClosing() +    let l = line( '.' ) +    let c = col( '.' ) +    let paren = '' +    let l2 = 0 +    let c2 = 0 + +    call PareditFindClosing( '(', ')', 0 ) +    let lp = line( '.' ) +    let cp = col( '.' ) +    if [lp, cp] != [l, c] +        " Do we have a closing ')'? +        let paren = ')' +        let l2 = lp +        let c2 = cp +    endif +    call setpos( '.', [0, l, c, 0] ) + +    if &ft =~ s:fts_balancing_all_brackets +        call PareditFindClosing( '[', ']', 0 ) +        let lp = line( '.' ) +        let cp = col( '.' ) +        if [lp, cp] != [l, c] && (lp < l2 || (lp == l2 && cp < c2)) +            " Do we have a ']' closer? +            let paren = ']' +            let l2 = lp +            let c2 = cp +        endif +        call setpos( '.', [0, l, c, 0] ) + +        call PareditFindClosing( '{', '}', 0 ) +        let lp = line( '.' ) +        let cp = col( '.' ) +        if [lp, cp] != [l, c] && (lp < l2 || (lp == l2 && cp < c2)) +            " Do we have a '}' even closer? +            let paren = '}' +            let l2 = lp +            let c2 = cp +        endif +        call setpos( '.', [0, l, c, 0] ) +    endif + +    return [paren, l2, c2] +endfunction + +" Split list or string at the cursor position +" Current symbol will be split into the second part +function! PareditSplit() +    if !g:paredit_mode || s:InsideComment() +        return +    endif + +    if s:InsideString() +        normal! i" " +    else +        " Go back to the beginning of the current symbol +        let c = col('.') - 1 +        if getline('.')[c] =~ '\S' +            if c == 0 || (c > 0 && getline('.')[c-1] =~ b:any_wsopen_char) +                " OK, we are standing on the first character of the symbol +            else +                normal! b +            endif +        endif + +        " First find which kind of paren is the innermost +        let [p, l, c] = s:FindClosing() +        if p !~ b:any_closing_char +            " Not found any kind of parens +            return +        endif + +        " Delete all whitespaces around cursor position +        while getline('.')[col('.')-1] =~ '\s' +            normal! x +        endwhile +        while col('.') > 1 && getline('.')[col('.')-2] =~ '\s' +            normal! X +        endwhile + +        if p == ')' +            normal! i) ( +        elseif p == '}' +            normal! i} { +        else +            normal! i] [ +        endif +    endif +endfunction + +" Join two neighboring lists or strings +function! PareditJoin() +    if !g:paredit_mode || s:InsideComment() || s:InsideString() +        return +    endif + +    "TODO: skip parens in comments +    let [l0, c0] = searchpos(b:any_matched_char, 'nbW') +    let [l1, c1] = searchpos(b:any_matched_char, 'ncW') +    if [l0, c0] == [0, 0] || [l1, c1] == [0, 0] +        return +    endif +    let line0 = getline( l0 ) +    let line1 = getline( l1 ) +    let p0 = line0[c0-1] +    let p1 = line1[c1-1] +    if (p0 == ')' && p1 == '(') || (p0 == ']' && p1 == '[') || (p0 == '}' && p1 == '{') || (p0 == '"' && p1 == '"') +        if l0 == l1 +            " First list ends on the same line where the second list begins +            let line0 = strpart( line0, 0, c0-1 ) . ' ' . strpart( line0, c1 ) +            call setline( l0, line0 ) +        else +            " First list ends on a line different from where the second list begins +            let line0 = strpart( line0, 0, c0-1 ) +            let line1 = strpart( line1, 0, c1-1 ) . strpart( line1, c1 ) +            call setline( l0, line0 ) +            call setline( l1, line1 ) +        endif +    endif +endfunction + +" Wrap current visual block in parens of the given kind +function! s:WrapSelection( open, close ) +    let l0 = line( "'<" ) +    let l1 = line( "'>" ) +    let c0 = col( "'<" ) +    let c1 = col( "'>" ) +    if &selection == 'inclusive' +        let c1 = c1 + strlen(matchstr(getline(l1)[c1-1 :], '.')) +    endif +    if [l0, c0] == [0, 0] || [l1, c1] == [0, 0] +        " No selection +        return +    endif +    if l0 > l1 || (l0 == l1 && c0 > c1) +        " Swap both ends of selection to make [l0, c0] < [l1, c1] +        let [ltmp, ctmp] = [l0, c0] +        let [l0, c0] = [l1, c1] +        let [l1, c1] = [ltmp, ctmp] +    endif +    let save_ve = &ve +    set ve=all  +    call setpos( '.', [0, l0, c0, 0] ) +    execute "normal! i" . a:open +    call setpos( '.', [0, l1, c1 + (l0 == l1), 0] ) +    execute "normal! i" . a:close +    let &ve = save_ve +endfunction + +" Wrap current visual block in parens of the given kind +" Keep visual mode +function! PareditWrapSelection( open, close ) +    call s:WrapSelection( a:open, a:close ) +    " Always leave the cursor to the opening char's pos after +    " wrapping selection. +    if getline('.')[col('.')-1] =~ b:any_closing_char +        normal! % +    endif +endfunction + +" Wrap current symbol in parens of the given kind +" If standing on a paren then wrap the whole s-expression +" Stand on the opening paren (if not wrapping in "") +function! PareditWrap( open, close ) +    let isk_save = s:SetKeyword() +    let sel_save = &selection +    let line = line('.') +    let column = col('.') +    let line_content = getline(line) +    let current_char = line_content[column - 1] + +    if a:open != '"' && current_char =~ b:any_openclose_char +        execute "normal! " . "v%\<Esc>" +    else +        let inside_comment = s:InsideComment(line, column) + +        if current_char == '"' && !inside_comment +            let escaped_quote = line_content[column - 2] == "\\" +            if escaped_quote +                execute "normal! " . "vh\<Esc>" +            else +                let is_starting_quote = 1 +                if column == 1 && line > 1 +                    let endOfPreviousLine = col([line - 1, '$']) +                    if s:InsideString(line - 1, endOfPreviousLine - 1) +                        let previous_line_content = getline(line - 1) +                        if previous_line_content[endOfPreviousLine - 2] != '"' +                            let is_starting_quote = 0 +                        elseif previous_line_content[endOfPreviousLine - 3] == "\\" +                            let is_starting_quote = 0 +                        endif +                    endif +                elseif s:InsideString(line, column - 1) +                    if line_content[column - 2] != '"' +                        let is_starting_quote = 0 +                    elseif line_content[column - 3] == "\\" +                        let is_starting_quote = 0 +                    endif +                endif +                let &selection="inclusive" +                normal! v +                if is_starting_quote +                    call search( '\\\@<!"', 'W', 's:SkipExpr()' ) +                else +                    call search( '\\\@<!"', 'bW', 's:SkipExpr()' ) +                endif +                execute "normal! " . "\<Esc>" +            endif +        else +            execute "normal! " . "viw\<Esc>" +        endif +    endif +    call s:WrapSelection( a:open, a:close ) +    if a:open != '"' +        normal! % +    else +      call cursor(line, column + 1) +    endif +    let &selection = sel_save +    let &iskeyword = isk_save +endfunction + +" Splice current list into the containing list +function! PareditSplice() +    if !g:paredit_mode +        return +    endif + +    " First find which kind of paren is the innermost +    let [p, l, c] = s:FindClosing() +    if p !~ b:any_closing_char +        " Not found any kind of parens +        return +    endif + +    call setpos( '.', [0, l, c, 0] ) +    normal! % +    let l = line( '.' ) +    let c = col( '.' ) +    normal! %x +    call setpos( '.', [0, l, c, 0] ) +    normal! x +    if c > 1 && getline('.')[c-2] =~ s:any_macro_prefix +        normal! X +    endif +endfunction + +" Raise: replace containing form with the current symbol or sub-form +function! PareditRaise() +    let isk_save = s:SetKeyword() +    let ch = getline('.')[col('.')-1] +    if ch =~ b:any_openclose_char +        " Jump to the closing char in order to find the outer +        " closing char. +        if ch =~ b:any_opening_char +            normal! % +        endif + +        let [p, l, c] = s:FindClosing() +        if p =~ b:any_closing_char +            " Raise sub-form and re-indent +            exe "normal! y%d%da" . p +            if getline('.')[col('.')-1] == ' ' +              normal! "0p=% +            else +              normal! "0P=% +            endif +        elseif ch =~ b:any_opening_char +            " Restore position if there is no appropriate +            " closing char. +            normal! % +        endif +    else +        let [p, l, c] = s:FindClosing() +        if p =~ b:any_closing_char +            " Raise symbol +            exe "normal! yiwda" . p +            normal! "0Pb +        endif +    endif +    let &iskeyword = isk_save +endfunction + +" ===================================================================== +"  Autocommands +" ===================================================================== + +if !exists("g:paredit_disable_lisp") +    au FileType lisp      call PareditInitBuffer() +endif + +if !exists("g:paredit_disable_clojure") +    au FileType *clojure* call PareditInitBuffer() +endif + +if !exists("g:paredit_disable_hy") +    au FileType hy        call PareditInitBuffer() +endif + +if !exists("g:paredit_disable_scheme") +    au FileType scheme    call PareditInitBuffer() +    au FileType racket    call PareditInitBuffer() +endif + +if !exists("g:paredit_disable_shen") +    au FileType shen      call PareditInitBuffer() +endif | 
