├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── PATENTS ├── README.md ├── after └── ftplugin │ └── javascript.vim ├── autoload └── flowcomplete.vim ├── doc └── vim-flow.txt └── plugin └── flow.vim /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please [read the full text](https://code.fb.com/codeofconduct) so that you can understand what actions will and will not be tolerated. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to vim-flow 2 | We want to make contributing to this project as easy and transparent as 3 | possible. 4 | 5 | ## Pull Requests 6 | We actively welcome your pull requests. 7 | 8 | 1. Fork the repo and create your branch from `master`. 9 | 2. If you've added code that should be tested, add tests. 10 | 3. If you've changed APIs, update the documentation. 11 | 4. Ensure the test suite passes. 12 | 5. Make sure your code lints. 13 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 14 | 15 | ## Contributor License Agreement ("CLA") 16 | In order to accept your pull request, we need you to submit a CLA. You only need 17 | to do this once to work on any of Facebook's open source projects. 18 | 19 | Complete your CLA here: 20 | 21 | ## Issues 22 | We use GitHub issues to track public bugs. Please ensure your description is 23 | clear and has sufficient instructions to be able to reproduce the issue. 24 | 25 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 26 | disclosure of security bugs. In those cases, please go through the process 27 | outlined on that page and do not file a public issue. 28 | 29 | ## License 30 | By contributing to vim-flow, you agree that your contributions will be licensed 31 | under the LICENSE file in the root directory of this source tree. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD License 2 | 3 | For vim-flow software 4 | 5 | Copyright (c) 2013, Facebook, Inc. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without modification, 8 | are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | * Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | * Neither the name Facebook nor the names of its contributors may be used to 18 | endorse or promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /PATENTS: -------------------------------------------------------------------------------- 1 | Additional Grant of Patent Rights 2 | 3 | "Software" means the vim-flow software distributed by Facebook, Inc. 4 | 5 | Facebook hereby grants you a perpetual, worldwide, royalty-free, non-exclusive, 6 | irrevocable (subject to the termination provision below) license under any 7 | rights in any patent claims owned by Facebook, to make, have made, use, sell, 8 | offer to sell, import, and otherwise transfer the Software. For avoidance of 9 | doubt, no license is granted under Facebook's rights in any patent claims that 10 | are infringed by (i) modifications to the Software made by you or a third party, 11 | or (ii) the Software in combination with any software or other technology 12 | provided by you or a third party. 13 | 14 | The license granted hereunder will terminate, automatically and without notice, 15 | for anyone that makes any claim (including by filing any lawsuit, assertion or 16 | other action) alleging (a) direct, indirect, or contributory infringement or 17 | inducement to infringe any patent: (i) by Facebook or any of its subsidiaries or 18 | affiliates, whether or not such claim is related to the Software, (ii) by any 19 | party if such claim arises in whole or in part from any software, product or 20 | service of Facebook or any of its subsidiaries or affiliates, whether or not 21 | such claim is related to the Software, or (iii) by any party relating to the 22 | Software; or (b) that any right in any patent claim of Facebook is invalid or 23 | unenforceable. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim-flow 2 | 3 | A vim plugin for [Flow][flow]. 4 | 5 | - Adds completions to `omnifunc` 6 | - Checks JavaScript files for type errors on save 7 | 8 | ## Requirements 9 | 10 | - Requires [Flow][flow] to be installed and available on your path 11 | - Requires the project to be initialised with `flow init` 12 | - Requires JavaScript files to be marked with `/* @flow */` or `/* @flow weak */` at the top 13 | 14 | ## Installation 15 | 16 | ### [Pathogen][pathogen] 17 | 18 | cd ~/.vim/bundle 19 | git clone git://github.com/flowtype/vim-flow.git 20 | 21 | ### [NeoBundle][neobundle] 22 | 23 | Add this to your `~/.vimrc` 24 | 25 | ```VimL 26 | NeoBundleLazy 'flowtype/vim-flow', { 27 | \ 'autoload': { 28 | \ 'filetypes': 'javascript' 29 | \ }} 30 | ``` 31 | 32 | #### With [Flow][flow] build step, using [flow-bin][flowbin] 33 | 34 | ```VimL 35 | NeoBundleLazy 'flowtype/vim-flow', { 36 | \ 'autoload': { 37 | \ 'filetypes': 'javascript' 38 | \ }, 39 | \ 'build': { 40 | \ 'mac': 'npm install -g flow-bin', 41 | \ 'unix': 'npm install -g flow-bin' 42 | \ }} 43 | ``` 44 | ## Usage 45 | 46 | Unless [disabled manually][gflowenable], vim-flow will check JavaScript and JSX files on save. 47 | 48 | ## Commands 49 | 50 | #### `FlowMake` 51 | 52 | Triggers a type check for the current file. 53 | 54 | #### `FlowToggle` 55 | 56 | Turns automatic checks on save on or off. 57 | 58 | #### `FlowType` 59 | 60 | Display the type of the variable under the cursor. 61 | 62 | #### `FlowJumpToDef` 63 | 64 | Jump to the definition of the variable under the cursor. 65 | 66 | ## Configuration 67 | 68 | #### `g:flow#autoclose` 69 | 70 | If this is set to `1`, the quickfix window will not be opened when there are 71 | no errors, and will be automatically closed when previous errors are cleared. 72 | 73 | Default is `0`. 74 | 75 | #### `g:flow#enable` 76 | 77 | Typechecking is done automatically on `:w` if set to `1`. 78 | 79 | To disable this, set to `0` in your ~/.vimrc, like so: 80 | 81 | ```VimL 82 | let g:flow#enable = 0 83 | ``` 84 | 85 | Default is `1`. 86 | 87 | #### `g:flow#errjmp` 88 | 89 | Jump to errors after typechecking if set to `1`. 90 | 91 | Default is `0`. 92 | 93 | #### `g:flow#flowpath` 94 | 95 | Leave this as default to use the flow executable defined on your path. To use 96 | a custom flow executable, set this like so: 97 | 98 | ```VimL 99 | let g:flow#flowpath = /your/flow-path/flow 100 | ``` 101 | 102 | #### `g:flow#omnifunc` 103 | 104 | By default `omnifunc` will be set to provide omni completion. To disable it 105 | (prevent overwriting an existed omnifunc), set this value to 0: 106 | 107 | ```VimL 108 | let g:flow#omnifunc = 0 109 | ``` 110 | 111 | #### `g:flow#timeout` 112 | 113 | By default `timeout` will be set to 2 seconds. If you are working on a larger 114 | codebase, you may want to increase this to avoid errors when Flow initializes. 115 | 116 | ```VimL 117 | let g:flow#timeout = 4 118 | ``` 119 | 120 | #### `g:flow#qfsize` 121 | 122 | Leave this as default to let the plugin decide on the quickfix window size. 123 | 124 | #### `g:flow#showquickfix` 125 | 126 | By default, results are shown in a quickfix window. Setting this to 0 will 127 | stop the window from being shown. This is useful if you want to use 128 | vim-flow for the omnicomplete functionality, but are already using 129 | something like [ale](https://github.com/w0rp/ale). 130 | 131 | ```VimL 132 | let g:flow#showquickfix = 0 133 | ``` 134 | 135 | [gflowenable]: https://github.com/flowtype/vim-flow#gflowenable 136 | [flow]: https://github.com/facebook/flow 137 | [flowbin]: https://github.com/sindresorhus/flow-bin 138 | [pathogen]: https://github.com/tpope/vim-pathogen 139 | [neobundle]: https://github.com/Shougo/neobundle.vim 140 | -------------------------------------------------------------------------------- /after/ftplugin/javascript.vim: -------------------------------------------------------------------------------- 1 | " Vim filetype plugin 2 | 3 | " Require the flow executable. 4 | if !executable(g:flow#flowpath) 5 | finish 6 | endif 7 | 8 | " Omnicompletion. 9 | if !exists("g:flow#omnifunc") 10 | let g:flow#omnifunc = 1 11 | endif 12 | 13 | if exists('&omnifunc') && g:flow#omnifunc 14 | setl omnifunc=flowcomplete#Complete 15 | endif 16 | -------------------------------------------------------------------------------- /autoload/flowcomplete.vim: -------------------------------------------------------------------------------- 1 | " Vim completion script 2 | " 3 | " This source code is licensed under the BSD-style license found in the 4 | " LICENSE file in the toplevel directory of this source tree. An additional 5 | " grant of patent rights can be found in the PATENTS file in the same 6 | " directory. 7 | 8 | " Magical flow autocomplete token. 9 | let s:autotok = 'AUTO332' 10 | 11 | " Omni findstart phase. 12 | function! s:FindStart() 13 | let line = getline('.') 14 | let start = col('.') - 1 15 | 16 | while start >= 0 && line[start - 1] =~ '[a-zA-Z_0-9\x7f-\xff$]' 17 | let start -= 1 18 | endwhile 19 | return start 20 | endfunction 21 | 22 | function! flowcomplete#Complete(findstart, base) 23 | if a:findstart 24 | return s:FindStart() 25 | endif 26 | 27 | let lnum = line('.') 28 | let cnum = col('.') 29 | let lines = getline(1, '$') 30 | 31 | " Insert the base and magic token into the current line. 32 | let curline = lines[lnum - 1] 33 | let lines[lnum - 1] = curline[:cnum - 1] . a:base . s:autotok . curline[cnum :] 34 | 35 | " Pass the buffer to flow. 36 | let buffer = join(lines, "\n") 37 | let command = g:flow#flowpath.' autocomplete "'.expand('%:p').'"' 38 | let result = system(command, buffer) 39 | 40 | if result =~ '^Error: not enough type information to autocomplete' || 41 | \ result =~ '^Could not find file or directory' 42 | return [] 43 | endif 44 | 45 | let matches = [] 46 | 47 | " Parse the flow output. 48 | for line in split(result, "\n") 49 | if empty(line) | continue | endif 50 | 51 | let entry = {} 52 | let space = stridx(line, ' ') 53 | let word = line[:space - 1] 54 | let type = line[space + 1 :] 55 | 56 | " Skip matches that don't start with the base" 57 | if (stridx(word, a:base) != 0) | continue | endif 58 | 59 | " This is pretty hacky. We're using regexes to recognize the different 60 | " kind of matches. Really in the future we should somehow consume the json 61 | " output 62 | if type =~ '^(.*) =>' 63 | let entry = { 'word': word, 'kind': a:base, 'menu': type } 64 | elseif type =~ '^[class:' 65 | let entry = { 'word': word, 'kind': 'c', 'menu': type } 66 | else 67 | let entry = { 'word': word, 'kind': 'v', 'menu': type } 68 | endif 69 | 70 | call add(matches, entry) 71 | endfor 72 | 73 | return matches 74 | endfunction 75 | -------------------------------------------------------------------------------- /doc/vim-flow.txt: -------------------------------------------------------------------------------- 1 | *vim-flow.txt* 2 | *vim-flow* 3 | 4 | ============================================================================== 5 | CONTENTS *vim-flow-contents* 6 | 7 | Variables |vim-flow-variables| 8 | 9 | ------------------------------------------------------------------------------ 10 | VARIABLES *vim-flow-variables* 11 | 12 | g:flow#autoclose *g:flow#autoclose* 13 | 14 | If this is set to 1, the |quickfix| window will not be opened when there are 15 | no errors, and will be automatically closed when previous errors are cleared. 16 | 17 | Default is 0. 18 | 19 | g:flow#enable *g:flow#enable* 20 | 21 | Typechecking is done automatically on :w if set to 1. 22 | 23 | To disable this, set to 0 in your ~/.vimrc, like so: 24 | let g:flow#enable = 0 25 | 26 | Default is 1. 27 | 28 | g:flow#errjmp *g:flow#errjmp* 29 | 30 | Jump to errors after typechecking if set to 1. 31 | 32 | Default is 0. 33 | 34 | g:flow#flowpath *g:flow#flowpath* 35 | 36 | Leave this as default to use the flow executable defined on your path. To use 37 | a custom flow executable, set this like so: 38 | 39 | let g:flow#flowpath = /your/flow-path/flow 40 | 41 | g:flow#omnifunc *g:flow#omnifunc* 42 | 43 | By default 'omnifunc' will be set to provide omni completion. To disable it 44 | (prevent overwriting an existed omnifunc), set this value to 0: 45 | 46 | let g:flow#omnifunc = 0 47 | 48 | g:flow#qfsize *g:flow#qfsize* 49 | 50 | Leave this as default to let the plugin decide on the |quickfix| window size. 51 | 52 | vim:tw=78:ts=8:ft=help:norl: 53 | -------------------------------------------------------------------------------- /plugin/flow.vim: -------------------------------------------------------------------------------- 1 | " flow.vim - Flow typechecker integration for vim 2 | 3 | if exists("g:loaded_flow") 4 | finish 5 | endif 6 | let g:loaded_flow = 1 7 | 8 | " Configuration switches: 9 | " - enable: Typechecking is done on :w. 10 | " - autoclose: Quickfix window closes automatically when there are no errors. 11 | " - errjmp: Jump to errors after typechecking; default off. 12 | " - qfsize: Let the plugin control the quickfix window size. 13 | " - flowpath: Path to the flow executable - default is flow in path 14 | " - showquickfix Show the quickfix window 15 | if !exists("g:flow#enable") 16 | let g:flow#enable = 1 17 | endif 18 | if !exists("g:flow#autoclose") 19 | let g:flow#autoclose = 0 20 | endif 21 | if !exists("g:flow#errjmp") 22 | let g:flow#errjmp = 0 23 | endif 24 | if !exists("g:flow#qfsize") 25 | let g:flow#qfsize = 1 26 | endif 27 | if !exists("g:flow#flowpath") 28 | let g:flow#flowpath = "flow" 29 | endif 30 | if !exists("g:flow#timeout") 31 | let g:flow#timeout = 2 32 | endif 33 | if !exists("g:flow#showquickfix") 34 | let g:flow#showquickfix = 1 35 | endif 36 | 37 | " Require the flow executable. 38 | if !executable(g:flow#flowpath) 39 | finish 40 | endif 41 | 42 | " flow error format. 43 | let s:flow_errorformat = '%EFile "%f"\, line %l\, characters %c-%.%#,%Z%m,' 44 | " flow from editor. 45 | let s:flow_from = '--from vim' 46 | 47 | 48 | " Call wrapper for flow. 49 | function! FlowClientCall(cmd, suffix, ...) 50 | " Invoke typechecker. 51 | " We also concatenate with the empty string because otherwise 52 | " cgetexpr complains about not having a String argument, even though 53 | " type(flow_result) == 1. 54 | let command = g:flow#flowpath.' '.a:cmd.' '.s:flow_from.' '.a:suffix 55 | 56 | let flow_result = a:0 > 0 ? system(command, a:1) : system(command) 57 | 58 | " Handle the server still initializing 59 | if v:shell_error == 1 60 | echohl WarningMsg 61 | echomsg 'Flow server is still initializing...' 62 | echohl None 63 | cclose 64 | return 0 65 | endif 66 | 67 | " Handle timeout 68 | if v:shell_error == 3 69 | echohl WarningMsg 70 | echomsg 'Flow timed out, please try again!' 71 | echohl None 72 | cclose 73 | return 0 74 | endif 75 | 76 | return flow_result 77 | endfunction 78 | 79 | " Main interface functions. 80 | function! flow#typecheck() 81 | " Flow current outputs errors to stderr and gets fancy with single character 82 | " files 83 | let flow_result = FlowClientCall('--timeout '.g:flow#timeout.' --retry-if-init false "'.expand('%:p').'"', '2> /dev/null') 84 | let old_fmt = &errorformat 85 | let &errorformat = s:flow_errorformat 86 | 87 | if g:flow#errjmp 88 | cexpr flow_result 89 | else 90 | cgetexpr flow_result 91 | endif 92 | 93 | if g:flow#showquickfix 94 | if g:flow#autoclose 95 | botright cwindow 96 | else 97 | botright copen 98 | endif 99 | endif 100 | let &errorformat = old_fmt 101 | endfunction 102 | 103 | " Get the Flow type at the current cursor position. 104 | function! flow#get_type() 105 | let pos = line('.').' '.col('.') 106 | let path = ' --path '.fnameescape(expand('%')) 107 | let cmd = g:flow#flowpath.' type-at-pos '.pos.path 108 | let stdin = join(getline(1,'$'), "\n") 109 | 110 | let output = 'FlowType: '.system(cmd, stdin) 111 | let output = substitute(output, '\n$', '', '') 112 | echo output 113 | endfunction 114 | 115 | " Toggle auto-typecheck. 116 | function! flow#toggle() 117 | if g:flow#enable 118 | let g:flow#enable = 0 119 | else 120 | let g:flow#enable = 1 121 | endif 122 | endfunction 123 | 124 | " Jump to Flow definition for the current cursor position 125 | function! flow#jump_to_def() 126 | let pos = line('.').' '.col('.') 127 | let path = ' --path '.fnameescape(expand('%')) 128 | let stdin = join(getline(1,'$'), "\n") 129 | let flow_result = FlowClientCall('get-def --quiet '.pos.path, '', stdin) 130 | " Output format is: 131 | " File: "/path/to/file", line 1, characters 1-11 132 | 133 | " Flow returns a single line-feed if no result 134 | if strlen(flow_result) == 1 135 | echo 'No definition found' 136 | return 1 137 | endif 138 | 139 | let parts = split(flow_result, ",") 140 | if len(parts) < 2 141 | echo 'cannot find definition' 142 | return 1 143 | endif 144 | 145 | " File: "/path/to/file" => /path/to/file 146 | let file = substitute(substitute(parts[0], '"', '', 'g'), 'File ', '', '') 147 | 148 | " line 1 => 1 149 | let row = split(parts[1], " ")[1] 150 | 151 | " characters 1-11 => 1 152 | let col = 0 153 | if len(parts) == 3 154 | let col = split(split(parts[2], " ")[1], "-")[0] 155 | endif 156 | 157 | " File - means current file 158 | if filereadable(file) || file == '-' 159 | if file != '-' 160 | execute 'edit' file 161 | endif 162 | call cursor(row, col) 163 | end 164 | endfunction 165 | 166 | " Open importers of current file in quickfix window 167 | function! flow#get_importers() 168 | let flow_result = FlowClientCall('get-importers "'.expand('%').'" --strip-root', '') 169 | let importers = split(flow_result, '\n')[1:1000] 170 | 171 | let l:flow_errorformat = '%f' 172 | let old_fmt = &errorformat 173 | let &errorformat = l:flow_errorformat 174 | 175 | if g:flow#errjmp 176 | cexpr importers 177 | else 178 | cgetexpr importers 179 | endif 180 | 181 | if g:flow#autoclose 182 | botright cwindow 183 | else 184 | botright copen 185 | endif 186 | let &errorformat = old_fmt 187 | endfunction 188 | 189 | 190 | " Commands and auto-typecheck. 191 | command! FlowToggle call flow#toggle() 192 | command! FlowMake call flow#typecheck() 193 | command! FlowType call flow#get_type() 194 | command! FlowJumpToDef call flow#jump_to_def() 195 | command! FlowGetImporters call flow#get_importers() 196 | 197 | au BufWritePost *.js,*.jsx if g:flow#enable | call flow#typecheck() | endif 198 | 199 | 200 | " Keep quickfix window at an adjusted height. 201 | function! AdjustWindowHeight(minheight, maxheight) 202 | exe max([min([line("$"), a:maxheight]), a:minheight]) . "wincmd _" 203 | endfunction 204 | 205 | au FileType qf if g:flow#qfsize | call AdjustWindowHeight(3, 10) | endif 206 | --------------------------------------------------------------------------------