Skip to content

Instantly share code, notes, and snippets.

@AndrewRadev
Created April 3, 2012 19:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AndrewRadev/2294916 to your computer and use it in GitHub Desktop.
Save AndrewRadev/2294916 to your computer and use it in GitHub Desktop.
Two useful text objects for coffeescript
" See http://andrewradev.com/2012/04/03/manipulating-coffeescript-with-vim-part-1-text-objects/
" for details on the implementation
onoremap ii :<c-u>call <SID>IndentTextObject()<cr>
onoremap аi :<c-u>call <SID>IndentTextObject()<cr>
xnoremap ii :<c-u>call <SID>IndentTextObject()<cr>
xnoremap ai :<c-u>call <SID>IndentTextObject()<cr>
function! s:IndentTextObject()
let upper = s:UpperIndentLimit(line('.'))
let lower = s:LowerIndentLimit(line('.'))
call s:MarkVisual('V', upper, lower)
endfunction
onoremap if :<c-u>call <SID>FunctionTextObject('i')<cr>
onoremap af :<c-u>call <SID>FunctionTextObject('a')<cr>
xnoremap if :<c-u>call <SID>FunctionTextObject('i')<cr>
xnoremap af :<c-u>call <SID>FunctionTextObject('a')<cr>
function! s:FunctionTextObject(type)
let function_start = search('\((.\{-})\)\=\s*[-=]>$', 'Wbc')
if function_start <= 0
return
endif
let body_start = function_start + 1
if body_start > line('$') || indent(nextnonblank(body_start)) <= indent(function_start)
if a:type == 'a'
normal! vg_
endif
return
endif
let indent_limit = s:LowerIndentLimit(body_start)
if a:type == 'i'
let start = body_start
else
let start = function_start
endif
call s:MarkVisual('v', start, indent_limit)
endfunction
function! s:LowerIndentLimit(lineno)
let base_indent = indent(a:lineno)
let current_line = a:lineno
let next_line = nextnonblank(current_line + 1)
while current_line < line('$') && indent(next_line) >= base_indent
let current_line = next_line
let next_line = nextnonblank(current_line + 1)
endwhile
return current_line
endfunction
function! s:UpperIndentLimit(lineno)
let base_indent = indent(a:lineno)
let current_line = a:lineno
let prev_line = prevnonblank(current_line - 1)
while current_line > 0 && indent(prev_line) >= base_indent
let current_line = prev_line
let prev_line = prevnonblank(current_line - 1)
endwhile
return current_line
endfunction
function! s:MarkVisual(command, start_line, end_line)
if a:start_line != line('.')
exe a:start_line
endif
if a:end_line > a:start_line
exe 'normal! '.a:command.(a:end_line - a:start_line).'jg_'
else
exe 'normal! '.a:command.'g_'
endif
endfunction
@lucapette
Copy link

Nice work. Are you familiar with https://github.com/kana/vim-textobj-indent ? Unless I'm missing something that plugin solves the same problem.

@AndrewRadev
Copy link
Author

To me, the interesting thing about the gist is the implementation -- it's fairly simple to build something useful like this on your own (it's a part of a blog post, where I explain it in a lot more detail). For an actual, battle-tested indent text object, I'm pretty sure Kana's plugin works better :). The function object is something a bit different that might be useful, though.

@lucapette
Copy link

Thanks for the explanation. I'll go reading the blog post ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment