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.

193 lines
4.7 KiB
VimL

" flow.vim - Flow typechecker integration for vim
if exists("g:loaded_flow")
finish
endif
let g:loaded_flow = 1
" Configuration switches:
" - enable: Typechecking is done on :w.
" - autoclose: Quickfix window closes automatically.
" - errjmp: Jump to errors after typechecking; default off.
" - qfsize: Let the plugin control the quickfix window size.
" - flowpath: Path to the flow executable - default is flow in path
if !exists("g:flow#enable")
let g:flow#enable = 1
endif
if !exists("g:flow#autoclose")
let g:flow#autoclose = 0
endif
if !exists("g:flow#errjmp")
let g:flow#errjmp = 0
endif
if !exists("g:flow#qfsize")
let g:flow#qfsize = 1
endif
if !exists("g:flow#flowpath")
let g:flow#flowpath = "flow"
endif
if !exists("g:flow#timeout")
let g:flow#timeout = 2
endif
" Require the flow executable.
if !executable(g:flow#flowpath)
finish
endif
" flow error format.
let s:flow_errorformat = '%EFile "%f"\, line %l\, characters %c-%.%#,%Z%m,'
" flow from editor.
let s:flow_from = '--from vim'
" Call wrapper for flow.
function! <SID>FlowClientCall(cmd, suffix)
" Invoke typechecker.
" We also concatenate with the empty string because otherwise
" cgetexpr complains about not having a String argument, even though
" type(flow_result) == 1.
let command = g:flow#flowpath.' '.a:cmd.' '.s:flow_from.' '.a:suffix
let flow_result = system(command)
" Handle the server still initializing
if v:shell_error == 1
echohl WarningMsg
echomsg 'Flow server is still initializing...'
echohl None
cclose
return 0
endif
" Handle timeout
if v:shell_error == 3
echohl WarningMsg
echomsg 'Flow timed out, please try again!'
echohl None
cclose
return 0
endif
return flow_result
endfunction
" Main interface functions.
function! flow#typecheck()
" Flow current outputs errors to stderr and gets fancy with single character
" files
let flow_result = <SID>FlowClientCall('--timeout '.g:flow#timeout.' --retry-if-init false'.expand('%:p'), '2> /dev/null')
let old_fmt = &errorformat
let &errorformat = s:flow_errorformat
if g:flow#errjmp
cexpr flow_result
else
cgetexpr flow_result
endif
if g:flow#autoclose
botright cwindow
else
botright copen
endif
let &errorformat = old_fmt
endfunction
" Get the Flow type at the current cursor position.
function! flow#get_type()
let pos = fnameescape(expand('%')).' '.line('.').' '.col('.')
let cmd = g:flow#flowpath.' type-at-pos '.pos
let output = 'FlowType: '.system(cmd)
let output = substitute(output, '\n$', '', '')
echo output
endfunction
" Toggle auto-typecheck.
function! flow#toggle()
if g:flow#enable
let g:flow#enable = 0
else
let g:flow#enable = 1
endif
endfunction
" Jump to Flow definition for the current cursor position
function! flow#jump_to_def()
let pos = fnameescape(expand('%')).' '.line('.').' '.col('.')
let flow_result = <SID>FlowClientCall('get-def '.pos, '')
" Output format is:
" File: "/path/to/file", line 1, characters 1-11
" Flow returns a single line-feed if no result
if strlen(flow_result) == 1
echo 'No definition found'
return
endif
let parts = split(flow_result, ",")
if len(parts) < 2
echo 'cannot find definition'
return
endif
" File: "/path/to/file" => /path/to/file
let file = substitute(substitute(parts[0], '"', '', 'g'), 'File ', '', '')
" line 1 => 1
let row = split(parts[1], " ")[1]
" characters 1-11 => 1
let col = 0
if len(parts) == 3
let col = split(split(parts[2], " ")[1], "-")[0]
endif
if filereadable(file)
execute 'edit' file
call cursor(row, col)
end
endfunction
" Open importers of current file in quickfix window
function! flow#get_importers()
let flow_result = <SID>FlowClientCall('get-importers '.expand('%').' --strip-root', '')
let importers = split(flow_result, '\n')[1:1000]
let l:flow_errorformat = '%f'
let old_fmt = &errorformat
let &errorformat = l:flow_errorformat
if g:flow#errjmp
cexpr importers
else
cgetexpr importers
endif
if g:flow#autoclose
botright cwindow
else
botright copen
endif
let &errorformat = old_fmt
endfunction
" Commands and auto-typecheck.
command! FlowToggle call flow#toggle()
command! FlowMake call flow#typecheck()
command! FlowType call flow#get_type()
command! FlowJumpToDef call flow#jump_to_def()
command! FlowGetImporters call flow#get_importers()
au BufWritePost *.js,*.jsx if g:flow#enable | call flow#typecheck() | endif
" Keep quickfix window at an adjusted height.
function! <SID>AdjustWindowHeight(minheight, maxheight)
exe max([min([line("$"), a:maxheight]), a:minheight]) . "wincmd _"
endfunction
au FileType qf if g:flow#qfsize | call <SID>AdjustWindowHeight(3, 10) | endif