You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

205 lines
5.3 KiB
VimL

if !exists("g:go_textobj_enabled")
let g:go_textobj_enabled = 1
endif
if !exists("g:go_textobj_include_function_doc")
let g:go_textobj_include_function_doc = 1
endif
if !exists("g:go_textobj_include_variable")
let g:go_textobj_include_variable = 1
endif
" ( ) motions
" { } motions
" s for sentence
" p for parapgrah
" < >
" t for tag
" Select a function in visual mode.
function! go#textobj#Function(mode) abort
let offset = go#util#OffsetCursor()
let fname = shellescape(expand("%:p"))
if &modified
" Write current unsaved buffer to a temp file and use the modified content
let l:tmpname = tempname()
call writefile(go#util#GetLines(), l:tmpname)
let fname = l:tmpname
endif
let bin_path = go#path#CheckBinPath('motion')
if empty(bin_path)
return
endif
let command = printf("%s -format vim -file %s -offset %s", bin_path, fname, offset)
let command .= " -mode enclosing"
if g:go_textobj_include_function_doc
let command .= " -parse-comments"
endif
let out = go#util#System(command)
if go#util#ShellError() != 0
call go#util#EchoError(out)
return
endif
" if exists, delete it as we don't need it anymore
if exists("l:tmpname")
call delete(l:tmpname)
endif
" convert our string dict representation into native Vim dictionary type
let result = eval(out)
if type(result) != 4 || !has_key(result, 'fn')
return
endif
let info = result.fn
if a:mode == 'a'
" anonymous functions doesn't have associated doc. Also check if the user
" want's to include doc comments for function declarations
if has_key(info, 'doc') && g:go_textobj_include_function_doc
call cursor(info.doc.line, info.doc.col)
elseif info['sig']['name'] == '' && g:go_textobj_include_variable
" one liner anonymous functions
if info.lbrace.line == info.rbrace.line
" jump to first nonblack char, to get the correct column
call cursor(info.lbrace.line, 0 )
normal! ^
call cursor(info.func.line, col("."))
else
call cursor(info.func.line, info.rbrace.col)
endif
else
call cursor(info.func.line, info.func.col)
endif
normal! v
call cursor(info.rbrace.line, info.rbrace.col)
return
endif
" rest is inner mode, a:mode == 'i'
" if the function is a one liner we need to select only that portion
if info.lbrace.line == info.rbrace.line
call cursor(info.lbrace.line, info.lbrace.col+1)
normal! v
call cursor(info.rbrace.line, info.rbrace.col-1)
return
endif
call cursor(info.lbrace.line+1, 1)
normal! V
call cursor(info.rbrace.line-1, 1)
endfunction
" Get the location of the previous or next function.
function! go#textobj#FunctionLocation(direction, cnt) abort
let offset = go#util#OffsetCursor()
let fname = shellescape(expand("%:p"))
if &modified
" Write current unsaved buffer to a temp file and use the modified content
let l:tmpname = tempname()
call writefile(go#util#GetLines(), l:tmpname)
let fname = l:tmpname
endif
let bin_path = go#path#CheckBinPath('motion')
if empty(bin_path)
return
endif
let command = printf("%s -format vim -file %s -offset %s", bin_path, fname, offset)
let command .= ' -shift ' . a:cnt
if a:direction == 'next'
let command .= ' -mode next'
else " 'prev'
let command .= ' -mode prev'
endif
if g:go_textobj_include_function_doc
let command .= " -parse-comments"
endif
let out = go#util#System(command)
if go#util#ShellError() != 0
call go#util#EchoError(out)
return
endif
" if exists, delete it as we don't need it anymore
if exists("l:tmpname")
call delete(l:tmpname)
endif
let l:result = json_decode(out)
if type(l:result) != 4 || !has_key(l:result, 'fn')
return 0
endif
return l:result
endfunction
function! go#textobj#FunctionJump(mode, direction) abort
" get count of the motion. This should be done before all the normal
" expressions below as those reset this value(because they have zero
" count!). We abstract -1 because the index starts from 0 in motion.
let l:cnt = v:count1 - 1
" set context mark so we can jump back with '' or ``
normal! m'
" select already previously selected visual content and continue from there.
" If it's the first time starts with the visual mode. This is needed so
" after selecting something in visual mode, every consecutive motion
" continues.
if a:mode == 'v'
normal! gv
endif
let l:result = go#textobj#FunctionLocation(a:direction, l:cnt)
if l:result is 0
return
endif
" we reached the end and there are no functions. The usual [[ or ]] jumps to
" the top or bottom, we'll do the same.
if type(result) == 4 && has_key(result, 'err') && result.err == "no functions found"
if a:direction == 'next'
keepjumps normal! G
else " 'prev'
keepjumps normal! gg
endif
return
endif
let info = result.fn
" if we select something ,select all function
if a:mode == 'v' && a:direction == 'next'
keepjumps call cursor(info.rbrace.line, 1)
return
endif
if a:mode == 'v' && a:direction == 'prev'
if has_key(info, 'doc') && g:go_textobj_include_function_doc
keepjumps call cursor(info.doc.line, 1)
else
keepjumps call cursor(info.func.line, 1)
endif
return
endif
keepjumps call cursor(info.func.line, 1)
endfunction
" vim: sw=2 ts=2 et