From abb5c708307e3d1c77c6708b79affed1a807d6ea Mon Sep 17 00:00:00 2001 From: Buddy Sandidge Date: Wed, 8 Mar 2017 14:57:36 -0800 Subject: [PATCH] Squashed 'vim/bundle/flow/' content from commit 3bd879dd7 git-subtree-dir: vim/bundle/flow git-subtree-split: 3bd879dd7060f13a78e9238669c2e1731e098607 --- .gitignore | 1 + LICENSE | 30 ++++++ PATENTS | 23 ++++ README.md | 128 +++++++++++++++++++++++ after/ftplugin/javascript.vim | 15 +++ autoload/flowcomplete.vim | 74 +++++++++++++ doc/vim-flow.txt | 52 +++++++++ plugin/flow.vim | 192 ++++++++++++++++++++++++++++++++++ 8 files changed, 515 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 PATENTS create mode 100644 README.md create mode 100644 after/ftplugin/javascript.vim create mode 100644 autoload/flowcomplete.vim create mode 100644 doc/vim-flow.txt create mode 100644 plugin/flow.vim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..926ccaa --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +doc/tags diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..2140666 --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +BSD License + +For vim-flow software + +Copyright (c) 2013, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +* Neither the name Facebook nor the names of its contributors may be used to +endorse or promote products derived from this software without specific +prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/PATENTS b/PATENTS new file mode 100644 index 0000000..a37cb02 --- /dev/null +++ b/PATENTS @@ -0,0 +1,23 @@ +Additional Grant of Patent Rights + +"Software" means the vim-flow software distributed by Facebook, Inc. + +Facebook hereby grants you a perpetual, worldwide, royalty-free, non-exclusive, +irrevocable (subject to the termination provision below) license under any +rights in any patent claims owned by Facebook, to make, have made, use, sell, +offer to sell, import, and otherwise transfer the Software. For avoidance of +doubt, no license is granted under Facebook's rights in any patent claims that +are infringed by (i) modifications to the Software made by you or a third party, +or (ii) the Software in combination with any software or other technology +provided by you or a third party. + +The license granted hereunder will terminate, automatically and without notice, +for anyone that makes any claim (including by filing any lawsuit, assertion or +other action) alleging (a) direct, indirect, or contributory infringement or +inducement to infringe any patent: (i) by Facebook or any of its subsidiaries or +affiliates, whether or not such claim is related to the Software, (ii) by any +party if such claim arises in whole or in part from any software, product or +service of Facebook or any of its subsidiaries or affiliates, whether or not +such claim is related to the Software, or (iii) by any party relating to the +Software; or (b) that any right in any patent claim of Facebook is invalid or +unenforceable. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e22f7d7 --- /dev/null +++ b/README.md @@ -0,0 +1,128 @@ +# vim-flow + +A vim plugin for [Flow][flow]. + + - Adds completions to `omnifunc` + - Checks JavaScript files for type errors on save + +## Requirements + + - Requires [Flow][flow] to be installed and available on your path + - Requires the project to be initialised with `flow init` + - Requires JavaScript files to be marked with `/* @flow */` or `/* @flow weak */` at the top + +## Installation + +### [Pathogen][pathogen] + + cd ~/.vim/bundle + git clone git://github.com/flowtype/vim-flow.git + +### [NeoBundle][neobundle] + +Add this to your `~/.vimrc` + +```VimL + NeoBundleLazy 'flowtype/vim-flow', { + \ 'autoload': { + \ 'filetypes': 'javascript' + \ }} +``` + +#### With [Flow][flow] build step, using [flow-bin][flowbin] + +```VimL + NeoBundleLazy 'flowtype/vim-flow', { + \ 'autoload': { + \ 'filetypes': 'javascript' + \ }, + \ 'build': { + \ 'mac': 'npm install -g flow-bin', + \ 'unix': 'npm install -g flow-bin' + \ }} +``` +## Usage + +Unless [disabled manually][gflowenable], vim-flow will check JavaScript and JSX files on save. + +## Commands + +#### `FlowMake` + +Triggers a type check for the current file. + +#### `FlowToggle` + +Turns automatic checks on save on or off. + +#### `FlowType` + +Display the type of the variable under the cursor. + +#### `FlowJumpToDef` + +Jump to the definition of the variable under the cursor. + +## Configuration + +#### `g:flow#autoclose` + +If this is set to `1`, the |quickfix| window opened when the plugin finds an error +will close automatically. + +Default is `0`. + +#### `g:flow#enable` + +Typechecking is done automatically on `:w` if set to `1`. + +To disable this, set to `0` in your ~/.vimrc, like so: + +```VimL +let g:flow#enable = 0 +``` + +Default is `1`. + +#### `g:flow#errjmp` + +Jump to errors after typechecking if set to `1`. + +Default is `0`. + +#### `g:flow#flowpath` + +Leave this as default to use the flow executable defined on your path. To use +a custom flow executable, set this like so: + +```VimL +let g:flow#flowpath = /your/flow-path/flow +``` + +#### `g:flow#omnifunc` + +By default `omnifunc` will be set to provide omni completion. To disable it +(prevent overwriting an existed omnifunc), set this value to 0: + +```VimL +let g:flow#omnifunc = 0 +``` + +#### `g:flow#timeout` + +By default `timeout` will be set to 2 seconds. If you are working on a larger +codebase, you may want to increase this to avoid errors when Flow initializes. + +```VimL +let g:flow#timeout = 4 +``` + +#### `g:flow#qfsize` + +Leave this as default to let the plugin decide on the quickfix window size. + +[gflowenable]: https://github.com/flowtype/vim-flow#gflowenable +[flow]: https://github.com/facebook/flow +[flowbin]: https://github.com/sindresorhus/flow-bin +[pathogen]: https://github.com/tpope/vim-pathogen +[neobundle]: https://github.com/Shougo/neobundle.vim diff --git a/after/ftplugin/javascript.vim b/after/ftplugin/javascript.vim new file mode 100644 index 0000000..782f763 --- /dev/null +++ b/after/ftplugin/javascript.vim @@ -0,0 +1,15 @@ +" Vim filetype plugin + +" Require the flow executable. +if !executable('flow') + finish +endif + +" Omnicompletion. +if !exists("g:flow#omnifunc") + let g:flow#omnifunc = 1 +endif + +if exists('&omnifunc') && g:flow#omnifunc + setl omnifunc=flowcomplete#Complete +endif diff --git a/autoload/flowcomplete.vim b/autoload/flowcomplete.vim new file mode 100644 index 0000000..21c7d6e --- /dev/null +++ b/autoload/flowcomplete.vim @@ -0,0 +1,74 @@ +" Vim completion script +" +" This source code is licensed under the BSD-style license found in the +" LICENSE file in the toplevel directory of this source tree. An additional +" grant of patent rights can be found in the PATENTS file in the same +" directory. + +" Magical flow autocomplete token. +let s:autotok = 'AUTO332' + +" Omni findstart phase. +function! s:FindStart() + let line = getline('.') + let start = col('.') - 1 + + while start >= 0 && line[start - 1] =~ '[a-zA-Z_0-9\x7f-\xff$]' + let start -= 1 + endwhile + return start +endfunction + +function! flowcomplete#Complete(findstart, base) + if a:findstart + return s:FindStart() + endif + + let lnum = line('.') + let cnum = col('.') + let lines = getline(1, '$') + + " Insert the base and magic token into the current line. + let curline = lines[lnum - 1] + let lines[lnum - 1] = curline[:cnum - 1] . a:base . s:autotok . curline[cnum :] + + " Pass the buffer to flow. + let buffer = join(lines, "\n") + let command = g:flow#flowpath.' autocomplete '.expand('%:p') + let result = system(command, buffer) + + if result =~ '^Error: not enough type information to autocomplete' || + \ result =~ '^Could not find file or directory' + return [] + endif + + let matches = [] + + " Parse the flow output. + for line in split(result, "\n") + if empty(line) | continue | endif + + let entry = {} + let space = stridx(line, ' ') + let word = line[:space - 1] + let type = line[space + 1 :] + + " Skip matches that don't start with the base" + if (stridx(word, a:base) != 0) | continue | endif + + " This is pretty hacky. We're using regexes to recognize the different + " kind of matches. Really in the future we should somehow consume the json + " output + if type =~ '^(.*) =>' + let entry = { 'word': word, 'kind': a:base, 'menu': type } + elseif type =~ '^[class:' + let entry = { 'word': word, 'kind': 'c', 'menu': type } + else + let entry = { 'word': word, 'kind': 'v', 'menu': type } + endif + + call add(matches, entry) + endfor + + return matches +endfunction diff --git a/doc/vim-flow.txt b/doc/vim-flow.txt new file mode 100644 index 0000000..235c9b0 --- /dev/null +++ b/doc/vim-flow.txt @@ -0,0 +1,52 @@ +*vim-flow.txt* + *vim-flow* + +============================================================================== +CONTENTS *vim-flow-contents* + +Variables |vim-flow-variables| + +------------------------------------------------------------------------------ +VARIABLES *vim-flow-variables* + +g:flow#autoclose *g:flow#autoclose* + +If this is set to 1, the |quickfix| window opened when the plugin finds an error +will close automatically. + +Default is 0. + +g:flow#enable *g:flow#enable* + +Typechecking is done automatically on :w if set to 1. + +To disable this, set to 0 in your ~/.vimrc, like so: +let g:flow#enable = 0 + +Default is 1. + +g:flow#errjmp *g:flow#errjmp* + +Jump to errors after typechecking if set to 1. + +Default is 0. + +g:flow#flowpath *g:flow#flowpath* + +Leave this as default to use the flow executable defined on your path. To use +a custom flow executable, set this like so: + +let g:flow#flowpath = /your/flow-path/flow + +g:flow#omnifunc *g:flow#omnifunc* + +By default 'omnifunc' will be set to provide omni completion. To disable it +(prevent overwriting an existed omnifunc), set this value to 0: + +let g:flow#omnifunc = 0 + +g:flow#qfsize *g:flow#qfsize* + +Leave this as default to let the plugin decide on the |quickfix| window size. + +vim:tw=78:ts=8:ft=help:norl: diff --git a/plugin/flow.vim b/plugin/flow.vim new file mode 100644 index 0000000..f92ab14 --- /dev/null +++ b/plugin/flow.vim @@ -0,0 +1,192 @@ +" 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! 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 = 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 = 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 = 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! AdjustWindowHeight(minheight, maxheight) + exe max([min([line("$"), a:maxheight]), a:minheight]) . "wincmd _" +endfunction + +au FileType qf if g:flow#qfsize | call AdjustWindowHeight(3, 10) | endif