309 lines
7.7 KiB
VimL
309 lines
7.7 KiB
VimL
"
|
|
" limp/vim/sexp.vim
|
|
"
|
|
" URL:
|
|
" http://mikael.jansson.be/hacking
|
|
"
|
|
" Description:
|
|
" Things to help you out with s-exps.
|
|
"
|
|
" Version:
|
|
" 0.2
|
|
"
|
|
" Date:
|
|
" 2008-04-20
|
|
"
|
|
" Authors:
|
|
" Mikael Jansson <mail@mikael.jansson.be>
|
|
"
|
|
" Changelog:
|
|
" 2008-04-20
|
|
" * Initial version.
|
|
" * Based on ViLisp.vim by Larry Clapp <vim@theclapp.org>
|
|
|
|
" Mark Top: mark visual block
|
|
nnoremap <buffer> <Plug>MarkTop 99[(V%
|
|
|
|
" Format Current: reindent/format
|
|
" Format Top:
|
|
nnoremap <buffer> <Plug>FormatCurrent [(=%`'
|
|
nnoremap <buffer> <Plug>FormatTop 99[(=%`'
|
|
|
|
" Sexp Wrap: wrap the current form in a list
|
|
" Sexp Peel: peel a list off the current form
|
|
nnoremap <silent> <buffer> <Plug>SexpWrap :call Cursor_push()<CR>[(%a)<ESC>h%i(<ESC>:call Cursor_pop()<CR>
|
|
nnoremap <silent> <buffer> <Plug>SexpPeel :call Cursor_push()<CR>[(:call Cursor_push()<CR>%x:call Cursor_pop()<CR>x:call Cursor_pop()<CR>
|
|
|
|
" Sexp Previous: navigate to previous s-exp
|
|
" Sexp Next: navigate to previous s-exp
|
|
nnoremap <silent> <buffer> <Plug>SexpPrevious :call Sexp_Previous()<CR>
|
|
nnoremap <silent> <buffer> <Plug>SexpNext :call Sexp_Next()<CR>
|
|
|
|
" Sexp Move Back: swap this and previous s-exp
|
|
" Sexp Move Forward: swap this and next s-exp
|
|
nnoremap <silent> <buffer> <Plug>SexpMoveBack :call Sexp_MoveBack()<CR>
|
|
nnoremap <silent> <buffer> <Plug>SexpMoveForward :call Sexp_MoveForward()<CR>
|
|
|
|
" Sexp Comment: comment all the way from the top level
|
|
nnoremap <silent> <buffer> <Plug>SexpComment :call Cursor_push()<CR>99[(%a\|#<ESC>hh%i#\|<ESC>:call Cursor_pop()<CR>
|
|
|
|
" Sexp Comment Current: comment current form
|
|
nnoremap <silent> <buffer> <Plug>SexpCommentCurrent :call Cursor_push()<CR>[(%a\|#<ESC>hh%i#\|<ESC>:call Cursor_pop()<CR>
|
|
|
|
|
|
"-------------------------------------------------------------------
|
|
|
|
fun! Sexp_Next()
|
|
let [l, c] = Sexp_get_Next()
|
|
call cursor(l, c)
|
|
endfun
|
|
|
|
fun! Sexp_Previous()
|
|
let [l, c] = Sexp_get_Previous()
|
|
if l == 0 && c == 0
|
|
return
|
|
endif
|
|
call cursor(l, c)
|
|
return
|
|
endfun
|
|
|
|
" return the position of the next s-exp
|
|
fun! Sexp_get_Next()
|
|
return searchpos('(', 'nW')
|
|
endfun
|
|
|
|
" return the position of the previous s-exp
|
|
fun! Sexp_get_Previous()
|
|
let p = getpos(".")
|
|
|
|
" If outside of *any* s-exps, move to the previous s-exp first.
|
|
let [l, c] = searchpairpos('(', '', ')', 'bnW')
|
|
if l == 0 && c == 0
|
|
call searchpos(')', 'Wb')
|
|
endif
|
|
|
|
" now, move to the start of this s-exp, wherever it may be.
|
|
let [l, c] = searchpos('(', 'Wnb')
|
|
|
|
call setpos(".", p)
|
|
|
|
return [l, c]
|
|
endfun
|
|
|
|
"XXX: MoveBack/MoveForward share much code
|
|
|
|
fun! Sexp_MoveBack()
|
|
" Inside an s-exp?
|
|
let [l, c] = searchpairpos('(', '', ')', 'bcnW')
|
|
if l == 0 || c == 0
|
|
" Nope,
|
|
return
|
|
endif
|
|
|
|
silent! let regs = @*
|
|
|
|
" mark the start of this s-exp
|
|
silent! norm! yl
|
|
if @0 != "("
|
|
call Sexp_Previous()
|
|
endif
|
|
|
|
"
|
|
" Find out if the previous s-exp is the parent of the current
|
|
"
|
|
" This by searching to the previous s-exp, doing a % and checking either
|
|
" of the following conditions:
|
|
"
|
|
" * prev_line2 == this_line2 && prev_col2 > this_col2
|
|
" * prev_line2 > this_line2
|
|
"
|
|
" where prev_line2/prev_col2 = the ) of the previous match, and
|
|
" this_line2/this_col2 = the ) of the current s-exp.
|
|
"
|
|
|
|
" so we can get back.
|
|
silent! norm! ma
|
|
let [b, this_line1, this_col1, o] = getpos('.')
|
|
|
|
" where does the *current* s-exp end?
|
|
silent! norm! %
|
|
let [b, this_line2, this_col2, o] = getpos('.')
|
|
silent! norm! %
|
|
|
|
" where does the previous s-exp end?
|
|
call Sexp_Previous()
|
|
silent! norm! mb
|
|
|
|
let [b, prev_line1, prev_col1, o] = getpos('.')
|
|
silent! norm! %
|
|
let [b, prev_line2, prev_col2, o] = getpos('.')
|
|
|
|
if (prev_line2 == this_line2 && prev_col2 > this_col2) || (prev_line2 > this_line2)
|
|
" For now, just do nothing
|
|
echom "Error: Trying to transpose s-exp backwards with parent."
|
|
silent! norm! `a
|
|
return
|
|
endif
|
|
|
|
" --------------------------------------------------------
|
|
|
|
" get the s-exps
|
|
silent! norm! `a
|
|
silent! norm! "ayab
|
|
silent! norm! `b
|
|
silent! norm! "byab
|
|
|
|
" copy and replace current s-exp with whitespace
|
|
let @c = Fill(" ", len(@b))
|
|
let @d = Fill(" ", len(@a))
|
|
|
|
silent! norm! `a"_dab
|
|
silent! norm! `a"cP
|
|
|
|
silent! norm! `b"_dab
|
|
silent! norm! `b"dP
|
|
|
|
if this_line1 == prev_line1
|
|
|
|
let diff = len(@a) - len(@b)
|
|
if diff > 0
|
|
let movement = ''.diff.'l'
|
|
elseif diff < 0
|
|
let movement = ''.(-diff).'h'
|
|
else
|
|
let movement = ''
|
|
endif
|
|
|
|
silent! norm! `b"aPl
|
|
silent! exe 'norm! '.len(@a).'x'
|
|
|
|
silent! exe 'norm! `a'.movement.'"bPl'
|
|
silent! exe 'norm! '.len(@b).'x'
|
|
|
|
silent! norm! `b
|
|
else
|
|
" different lines, so a simple paste will do
|
|
|
|
silent! norm! `a"bP
|
|
silent! exe 'norm! l'.len(@a).'x'
|
|
silent! norm! `b"aP
|
|
silent! exe 'norm! l'.len(@b).'x'
|
|
silent! norm! `b
|
|
endif
|
|
|
|
silent! let @* = regs
|
|
endfun
|
|
|
|
fun! Sexp_MoveForward()
|
|
" Inside an s-exp?
|
|
let [l, c] = searchpairpos('(', '', ')', 'bcnW')
|
|
if l == 0 || c == 0
|
|
" Nope,
|
|
return
|
|
endif
|
|
|
|
silent! let regs = @*
|
|
|
|
" mark the start of this s-exp
|
|
silent! norm! yl
|
|
if @0 != "("
|
|
call Sexp_Previous()
|
|
endif
|
|
|
|
"
|
|
" Find out if the next s-exp is the parent of the current.
|
|
"
|
|
" Search for the next ')', then see where the matching '(' ends.
|
|
" Check for any of the following conditions.
|
|
"
|
|
" * prev_line1 == this_line1 && prev_col1 < this_col1
|
|
" * prev_line1 < this_line1
|
|
"
|
|
" where prev_line1/prev_col1 = the ( of the previous match, and
|
|
" this_line1/this_col1 = the ( of the current s-exp.
|
|
"
|
|
|
|
" so we can get back.
|
|
silent! norm! ma
|
|
let [b, this_line1, this_col1, o] = getpos('.')
|
|
|
|
" where does the *current* s-exp end?
|
|
silent! norm! %
|
|
let [b, this_line2, this_col2, o] = getpos('.')
|
|
|
|
" where does the next s-exp end?
|
|
call search(')', 'W')
|
|
|
|
let [b, prev_line2, prev_col2, o] = getpos('.')
|
|
silent! norm! %
|
|
|
|
silent! norm! mb
|
|
let [b, prev_line1, prev_col1, o] = getpos('.')
|
|
|
|
if (prev_line1 == this_line1 && prev_col1 < this_col1) || (prev_line1 < this_line1)
|
|
" For now, just do nothing
|
|
echom "Error: Trying to transpose s-exp forward with parent."
|
|
silent! norm! `a
|
|
return
|
|
endif
|
|
|
|
" --------------------------------------------------------
|
|
|
|
" get the s-exps
|
|
silent! norm! `a
|
|
silent! norm! "ayab
|
|
silent! norm! `b
|
|
silent! norm! "byab
|
|
|
|
" copy and replace current s-exp with whitespace
|
|
let @c = Fill(" ", len(@b))
|
|
let @d = Fill(" ", len(@a))
|
|
|
|
silent! norm! `b"_dab
|
|
silent! norm! `b"dP
|
|
|
|
silent! norm! `a"_dab
|
|
silent! norm! `a"cP
|
|
|
|
if this_line1 == prev_line1
|
|
|
|
let diff = len(@a) - len(@b)
|
|
if diff > 0
|
|
let movement = ''.diff.'h'
|
|
elseif diff < 0
|
|
let movement = ''.(-diff).'l'
|
|
else
|
|
let movement = ''
|
|
endif
|
|
|
|
silent! norm! `a"bPl
|
|
silent! exe 'norm! '.len(@b).'x'
|
|
|
|
silent! exe 'norm! `b'.movement.'"aPl'
|
|
silent! exe 'norm! '.len(@a).'x'
|
|
|
|
silent! exe 'norm! `b'.movement
|
|
else
|
|
" different lines, so a simple paste will do
|
|
|
|
silent! norm! `a"bP
|
|
silent! exe 'norm! l'.len(@a).'x'
|
|
silent! norm! `b"aP
|
|
silent! exe 'norm! l'.len(@b).'x'
|
|
silent! norm! `b
|
|
endif
|
|
|
|
silent! let @* = regs
|
|
endfun
|
|
|
|
fun! Fill(c, n)
|
|
let s = ""
|
|
let n = a:n
|
|
while n > 0
|
|
let s = s.a:c
|
|
let n = n-1
|
|
endwhile
|
|
return s
|
|
endfun
|
|
|