From c9ca63a8197001d146e52aa9259aad22f5bba326 Mon Sep 17 00:00:00 2001 From: Buddy Sandidge Date: Fri, 1 Apr 2016 20:32:25 -0700 Subject: [PATCH] Squashed 'vim/bundle/go/' content from commit 3efc952a7 git-subtree-dir: vim/bundle/go git-subtree-split: 3efc952a7fb754b43b4c21474046126562f83efc --- .github/CONTRIBUTING.md | 9 + .github/ISSUE_TEMPLATE.md | 28 + .gitignore | 1 + LICENSE | 58 ++ README.md | 282 ++++++++ addon-info.json | 6 + autoload/ctrlp/decls.vim | 158 ++++ autoload/go/alternate.vim | 30 + autoload/go/asmfmt.vim | 52 ++ autoload/go/cmd.vim | 365 ++++++++++ autoload/go/complete.vim | 170 +++++ autoload/go/def.vim | 111 +++ autoload/go/doc.vim | 171 +++++ autoload/go/fmt.vim | 210 ++++++ autoload/go/import.vim | 213 ++++++ autoload/go/jobcontrol.vim | 179 +++++ autoload/go/lint.vim | 199 +++++ autoload/go/list.vim | 126 ++++ autoload/go/oracle.vim | 233 ++++++ autoload/go/package.vim | 160 +++++ autoload/go/path.vim | 174 +++++ autoload/go/play.vim | 94 +++ autoload/go/rename.vim | 69 ++ autoload/go/term.vim | 128 ++++ autoload/go/textobj.vim | 180 +++++ autoload/go/tool.vim | 182 +++++ autoload/go/ui.vim | 89 +++ autoload/go/util.vim | 123 ++++ autoload/go/vimproc.vim | 21 + compiler/go.vim | 41 ++ doc/vim-go.txt | 1159 ++++++++++++++++++++++++++++++ ftdetect/gofiletype.vim | 31 + ftplugin/asm.vim | 17 + ftplugin/go.vim | 57 ++ ftplugin/go/commands.vim | 65 ++ ftplugin/go/mappings.vim | 60 ++ ftplugin/go/snippets.vim | 46 ++ ftplugin/go/tagbar.vim | 55 ++ gosnippets/UltiSnips/go.snippets | 401 +++++++++++ gosnippets/snippets/go.snip | 325 +++++++++ indent/go.vim | 78 ++ indent/gohtmltmpl.vim | 44 ++ plugin/go.vim | 173 +++++ scripts/test.sh | 78 ++ syntax/go.vim | 359 +++++++++ syntax/godoc.vim | 47 ++ syntax/gohtmltmpl.vim | 15 + syntax/gotexttmpl.vim | 85 +++ syntax/vimgo.vim | 11 + 49 files changed, 6968 insertions(+) create mode 100644 .github/CONTRIBUTING.md create mode 100644 .github/ISSUE_TEMPLATE.md create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 addon-info.json create mode 100644 autoload/ctrlp/decls.vim create mode 100644 autoload/go/alternate.vim create mode 100644 autoload/go/asmfmt.vim create mode 100644 autoload/go/cmd.vim create mode 100644 autoload/go/complete.vim create mode 100644 autoload/go/def.vim create mode 100644 autoload/go/doc.vim create mode 100644 autoload/go/fmt.vim create mode 100644 autoload/go/import.vim create mode 100644 autoload/go/jobcontrol.vim create mode 100644 autoload/go/lint.vim create mode 100644 autoload/go/list.vim create mode 100644 autoload/go/oracle.vim create mode 100644 autoload/go/package.vim create mode 100644 autoload/go/path.vim create mode 100644 autoload/go/play.vim create mode 100644 autoload/go/rename.vim create mode 100644 autoload/go/term.vim create mode 100644 autoload/go/textobj.vim create mode 100644 autoload/go/tool.vim create mode 100644 autoload/go/ui.vim create mode 100644 autoload/go/util.vim create mode 100644 autoload/go/vimproc.vim create mode 100644 compiler/go.vim create mode 100644 doc/vim-go.txt create mode 100644 ftdetect/gofiletype.vim create mode 100644 ftplugin/asm.vim create mode 100644 ftplugin/go.vim create mode 100644 ftplugin/go/commands.vim create mode 100644 ftplugin/go/mappings.vim create mode 100644 ftplugin/go/snippets.vim create mode 100644 ftplugin/go/tagbar.vim create mode 100644 gosnippets/UltiSnips/go.snippets create mode 100644 gosnippets/snippets/go.snip create mode 100644 indent/go.vim create mode 100644 indent/gohtmltmpl.vim create mode 100644 plugin/go.vim create mode 100755 scripts/test.sh create mode 100644 syntax/go.vim create mode 100644 syntax/godoc.vim create mode 100644 syntax/gohtmltmpl.vim create mode 100644 syntax/gotexttmpl.vim create mode 100644 syntax/vimgo.vim diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..7266cce --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,9 @@ +Thanks for improving vim-go! Before you dive in please read the following: + +1. Please read our + [FAQ](https://github.com/fatih/vim-go/wiki/FAQ-Troubleshooting), it might + have answers for your problem +2. If you add a new feature please don't forget to update the documentation: + [doc/vim-go.txt](doc/vim-go.txt) +3. If it's a breaking change or exceed +100 lines please open an issue first + and describe the changes you want to make. diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..0adaff3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,28 @@ +### Actual behavior + +Write here what's happening ... + +### Expected behavior + +Write here what you're expecting ... + +### Steps to reproduce: + +Please create a reproducible case of your problem. Re produce it +with a minimal `vimrc` with all plugins disabled and only `vim-go` +enabled: + +1. +2. +3. + +### Configuration + +Add here your current configuration and additional information that might be +useful, such as: + +* `vimrc` you used to reproduce +* vim version: +* vim-go version +* go version + 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..e9cc0dc --- /dev/null +++ b/LICENSE @@ -0,0 +1,58 @@ +Copyright (c) 2015, Fatih Arslan +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 of vim-go 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. + +This software includes some portions from Go. Go is used under the terms of the +BSD like license. + +Copyright (c) 2012 The Go Authors. 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 of Google Inc. 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 +OWNER 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/README.md b/README.md new file mode 100644 index 0000000..8614d87 --- /dev/null +++ b/README.md @@ -0,0 +1,282 @@ +# vim-go + +Go (golang) support for Vim, which comes with pre-defined sensible settings (like +auto gofmt on save), with autocomplete, snippet support, improved syntax +highlighting, go toolchain commands, and more. If needed vim-go installs all +necessary binaries for providing seamless Vim integration with current +commands. It's highly customizable and each individual feature can be +disabled/enabled easily. + +![vim-go](https://dl.dropboxusercontent.com/u/174404/vim-go-2.png) + + + +## Features + +* Improved Syntax highlighting with items such as Functions, Operators, Methods. +* Auto completion support via `gocode` +* Better `gofmt` on save, which keeps cursor position and doesn't break your undo + history +* Go to symbol/declaration with `:GoDef` +* Look up documentation with `:GoDoc` inside Vim or open it in browser +* Automatically import packages via `:GoImport` or plug it into autosave +* Compile your package with `:GoBuild`, install it with `:GoInstall` or test + them with `:GoTest` (also supports running single tests via `:GoTestFunc`) +* Quickly execute your current file/files with `:GoRun` +* Automatic `GOPATH` detection based on the directory structure (i.e. `gb` + projects, `godep` vendored projects) +* Change or display `GOPATH` with `:GoPath` +* Create a coverage profile and display annotated source code in browser to see + which functions are covered with `:GoCoverage` +* Call `gometalinter` with `:GoMetaLinter`, which invokes all possible linters + (golint, vet, errcheck, deadcode, etc..) and shows the warnings/errors +* Lint your code with `:GoLint` +* Run your code through `:GoVet` to catch static errors +* Advanced source analysis tools utilizing oracle, such as `:GoImplements`, + `:GoCallees`, and `:GoReferrers` +* Precise type-safe renaming of identifiers with `:GoRename` +* List all source files and dependencies +* Unchecked error checking with `:GoErrCheck` +* Integrated and improved snippets, supporting `ultisnips` or `neosnippet` +* Share your current code to [play.golang.org](http://play.golang.org) with `:GoPlay` +* On-the-fly type information about the word under the cursor. Plug it into + your custom vim function. +* Go asm formatting on save +* Tagbar support to show tags of the source code in a sidebar with `gotags` +* Custom vim text objects such as `a function` or `inner function` + list. +* A async launcher for the go command is implemented for Neovim, fully async + building and testing (beta). +* Integrated with the Neovim terminal, launch `:GoRun` and other go commands + in their own new terminal. (beta) +* Alternate between implementation and test code with `:GoAlternate` + +## Donation + +People have asked for this for a long time, now you can be a fully supporter by [being a patron](https://www.patreon.com/fatih)! This is fully optional and is just a way to support vim-go's ongoing development directly. Thanks! + +[https://www.patreon.com/fatih](https://www.patreon.com/fatih) + +## Install + +Vim-go follows the standard runtime path structure, so I highly recommend to +use a common and well known plugin manager to install vim-go. Do not use vim-go +with other Go oriented vim plugins. For Pathogen just clone the repo. For other +plugin managers add the appropriate lines and execute the plugin's install +command. + +* [Pathogen](https://github.com/tpope/vim-pathogen) + * `git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go` +* [vim-plug](https://github.com/junegunn/vim-plug) + * `Plug 'fatih/vim-go'` +* [NeoBundle](https://github.com/Shougo/neobundle.vim) + * `NeoBundle 'fatih/vim-go'` +* [Vundle](https://github.com/gmarik/vundle) + * `Plugin 'fatih/vim-go'` + +Please be sure all necessary binaries are installed (such as `gocode`, `godef`, +`goimports`, etc.). You can easily install them with the included +`:GoInstallBinaries` command. If invoked, all necessary binaries will be +automatically downloaded and installed to your `$GOBIN` environment (if not set +it will use `$GOPATH/bin`). Note that this command requires `git` for fetching +the individual Go packages. Additionally, use `:GoUpdateBinaries` to update the +installed binaries. + +### Optional + +* Autocompletion is enabled by default via ``. To get real-time +completion (completion by type) install: +[neocomplete](https://github.com/Shougo/neocomplete.vim) for Vim or +[deoplete](https://github.com/Shougo/deoplete.nvim) and +[deoplete-go](https://github.com/zchee/deoplete-go) for NeoVim +* To display source code tag information on a sidebar install +[tagbar](https://github.com/majutsushi/tagbar). +* For snippet features install: +[neosnippet](https://github.com/Shougo/neosnippet.vim) or +[ultisnips](https://github.com/SirVer/ultisnips). +* Screenshot color scheme is a slightly modified molokai: + [fatih/molokai](https://github.com/fatih/molokai). +* For a better documentation viewer checkout: + [go-explorer](https://github.com/garyburd/go-explorer). + +## Usage + +Many of the plugin's [features](#features) are enabled by default. There are no +additional settings needed. All usages and commands are listed in +`doc/vim-go.txt`. Note that help tags needs to be populated. Check your plugin +manager settings to generate the documentation (some do it automatically). +After that just open the help page to see all commands: + + :help vim-go + +## Mappings + +vim-go has several `` mappings which can be used to create custom +mappings. Below are some examples you might find useful: + +Run commands such as `go run` for the current file with `r` or `go +build` and `go test` for the current package with `b` and `t` +respectively. Display beautifully annotated source code to see which functions +are covered with `c`. + +```vim +au FileType go nmap r (go-run) +au FileType go nmap b (go-build) +au FileType go nmap t (go-test) +au FileType go nmap c (go-coverage) +``` + +By default the mapping `gd` is enabled, which opens the target identifier in +current buffer. You can also open the definition/declaration, in a new vertical, +horizontal, or tab, for the word under your cursor: + +```vim +au FileType go nmap ds (go-def-split) +au FileType go nmap dv (go-def-vertical) +au FileType go nmap dt (go-def-tab) +``` + +Open the relevant Godoc for the word under the cursor with `gd` or open +it vertically with `gv` + +```vim +au FileType go nmap gd (go-doc) +au FileType go nmap gv (go-doc-vertical) +``` + +Or open the Godoc in browser + +```vim +au FileType go nmap gb (go-doc-browser) +``` + +Show a list of interfaces which is implemented by the type under your cursor +with `s` + +```vim +au FileType go nmap s (go-implements) +``` + +Show type info for the word under your cursor with `i` (useful if you +have disabled auto showing type info via `g:go_auto_type_info`) + +```vim +au FileType go nmap i (go-info) +``` + +Rename the identifier under the cursor to a new name + +```vim +au FileType go nmap e (go-rename) +``` + +More `` mappings can be seen with `:he go-mappings`. Also these are just +recommendations, you are free to create more advanced mappings or functions +based on `:he go-commands`. + +## Settings +Below are some settings you might find useful. For the full list see `:he +go-settings`. + +By default syntax-highlighting for Functions, Methods and Structs is disabled. +To change it: +```vim +let g:go_highlight_functions = 1 +let g:go_highlight_methods = 1 +let g:go_highlight_structs = 1 +let g:go_highlight_interfaces = 1 +let g:go_highlight_operators = 1 +let g:go_highlight_build_constraints = 1 +``` + +Enable goimports to automatically insert import paths instead of gofmt: + +```vim +let g:go_fmt_command = "goimports" +``` + +By default vim-go shows errors for the fmt command, to disable it: + +```vim +let g:go_fmt_fail_silently = 1 +``` + +Disable auto fmt on save: + +```vim +let g:go_fmt_autosave = 0 +``` + +Disable opening browser after posting your snippet to `play.golang.org`: + +```vim +let g:go_play_open_browser = 0 +``` + +By default when `:GoInstallBinaries` is called, the binaries are installed to +`$GOBIN` or `$GOPATH/bin`. To change it: + +```vim +let g:go_bin_path = expand("~/.gotools") +let g:go_bin_path = "/home/fatih/.mypath" "or give absolute path +``` + +### Using with Neovim (beta) + +Note: Neovim currently is not a first class citizen for vim-go. You are free +to open bugs but I'm not going to look at them. Even though I'm using Neovim +myself, Neovim itself is still alpha. So vim-go might not work well as good as +in Vim. I'm happy to accept pull requests or very detailed bug reports. + + +Run `:GoRun` in a new tab, horizontal split or vertical split terminal + +```vim +au FileType go nmap rt (go-run-tab) +au FileType go nmap rs (go-run-split) +au FileType go nmap rv (go-run-vertical) +``` + +By default new terminals are opened in a vertical split. To change it + +```vim +let g:go_term_mode = "split" +``` + +By default the testing commands run asynchronously in the background and +display results with `go#jobcontrol#Statusline()`. To make them run in a new +terminal + +```vim +let g:go_term_enabled = 1 +``` + +### Using with Syntastic +Sometimes when using both `vim-go` and `syntastic` Vim will start lagging while +saving and opening files. The following fixes this: + +```vim +let g:syntastic_go_checkers = ['golint', 'govet', 'errcheck'] +let g:syntastic_mode_map = { 'mode': 'active', 'passive_filetypes': ['go'] } +``` + +## More info + +Check out the [Wiki](https://github.com/fatih/vim-go/wiki) page for more +information. It includes +[Screencasts](https://github.com/fatih/vim-go/wiki/Screencasts), an [FAQ +section](https://github.com/fatih/vim-go/wiki/FAQ-Troubleshooting), and many +other [various pieces](https://github.com/fatih/vim-go/wiki) of information. + +## Credits + +* Go Authors for official vim plugins +* Gocode, Godef, Golint, Oracle, Goimports, Gotags, Errcheck projects and + authors of those projects. +* Other vim-plugins, thanks for inspiration (vim-golang, go.vim, vim-gocode, + vim-godef) +* [Contributors](https://github.com/fatih/vim-go/graphs/contributors) of vim-go + +## License + +The BSD 3-Clause License - see `LICENSE` for more details diff --git a/addon-info.json b/addon-info.json new file mode 100644 index 0000000..145d961 --- /dev/null +++ b/addon-info.json @@ -0,0 +1,6 @@ +{ + "name": "vim-go", + "description": "Full featured Go (golang) support for Vim.", + "author": "Fatih Arslan ", + "repository" : {"type": "git", "url": "https://github.com/fatih/vim-go.git"} +} diff --git a/autoload/ctrlp/decls.vim b/autoload/ctrlp/decls.vim new file mode 100644 index 0000000..a57d09d --- /dev/null +++ b/autoload/ctrlp/decls.vim @@ -0,0 +1,158 @@ +let s:go_decls_var = { + \ 'init': 'ctrlp#decls#init()', + \ 'exit': 'ctrlp#decls#exit()', + \ 'enter': 'ctrlp#decls#enter()', + \ 'accept': 'ctrlp#decls#accept', + \ 'lname': 'declarations', + \ 'sname': 'decls', + \ 'type': 'tabs', + \} + +if exists('g:ctrlp_ext_vars') && !empty(g:ctrlp_ext_vars) + let g:ctrlp_ext_vars = add(g:ctrlp_ext_vars, s:go_decls_var) +else + let g:ctrlp_ext_vars = [s:go_decls_var] +endif + +function! ctrlp#decls#init() + cal s:enable_syntax() + return s:decls +endfunction + +function! ctrlp#decls#exit() + unlet! s:decls s:current_dir s:target +endfunction + +" The action to perform on the selected string +" Arguments: +" a:mode the mode that has been chosen by pressing or +" the values are 'e', 'v', 't' and 'h', respectively +" a:str the selected string +function! ctrlp#decls#accept(mode, str) + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + " we jump to the file directory so we can get the fullpath via fnamemodify + " below + execute cd . s:current_dir + + let vals = matchlist(a:str, '|\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)|') + + " i.e: main.go + let filename = vals[1] + let line = vals[2] + let col = vals[3] + + " i.e: /Users/fatih/vim-go/main.go + let filepath = fnamemodify(filename, ":p") + + " acceptile is a very versatile method, + call ctrlp#acceptfile(a:mode, filepath) + call cursor(line, col) + silent! norm! zvzz + finally + "jump back to old dir + execute cd . fnameescape(dir) + endtry +endfunction + +function! ctrlp#decls#enter() + let s:current_dir = fnameescape(expand('%:p:h')) + let s:decls = [] + + let bin_path = go#path#CheckBinPath('motion') + if empty(bin_path) + return + endif + let command = printf("%s -format vim -mode decls", bin_path) + let command .= " -include ". get(g:, "go_decls_includes", "func,type") + + call go#cmd#autowrite() + + if s:mode == 0 + " current file mode + let fname = expand("%:p") + if exists('s:target') + let fname = s:target + endif + + let command .= printf(" -file %s", fname) + else + " all functions mode + let dir = expand("%:p:h") + if exists('s:target') + let dir = s:target + endif + + let command .= printf(" -dir %s", dir) + endif + + let out = system(command) + if v:shell_error != 0 + call go#util#EchoError(out) + return + endif + + if exists("l:tmpname") + call delete(l:tmpname) + endif + + let result = eval(out) + if type(result) != 4 || !has_key(result, 'decls') + return + endif + + let decls = result.decls + + " find the maximum function name + let max_len = 0 + for decl in decls + if len(decl.ident)> max_len + let max_len = len(decl.ident) + endif + endfor + + for decl in decls + " paddings + let space = " " + for i in range(max_len - len(decl.ident)) + let space .= " " + endfor + + call add(s:decls, printf("%s\t%s |%s:%s:%s|\t%s", + \ decl.ident . space, + \ decl.keyword, + \ fnamemodify(decl.filename, ":t"), + \ decl.line, + \ decl.col, + \ decl.full, + \)) + endfor +endfunc + +function! s:enable_syntax() + if !(has('syntax') && exists('g:syntax_on')) + return + endif + + syntax match CtrlPIdent '\zs\h\+\ze\s' + syntax match CtrlPKeyword '\zs[^\t|]\+\ze|[^|]\+:\d\+:\d\+|' + syntax match CtrlPFilename '|\zs[^|]\+:\d\+:\d\+\ze|' + syntax match CtrlPSignature '\zs\t.*\ze$' contains=CtrlPKeyWord,CtrlPFilename + + highlight link CtrlPIdent Function + highlight link CtrlPKeyword Keyword + highlight link CtrlPFilename SpecialComment + highlight link CtrlPSignature Comment +endfunction + +let s:id = g:ctrlp_builtins + len(g:ctrlp_ext_vars) + +function! ctrlp#decls#cmd(mode, ...) + let s:mode = a:mode + if a:0 && !empty(a:1) + let s:target = a:1 + endif + return s:id +endfunction + diff --git a/autoload/go/alternate.vim b/autoload/go/alternate.vim new file mode 100644 index 0000000..1de6a69 --- /dev/null +++ b/autoload/go/alternate.vim @@ -0,0 +1,30 @@ +" By default use edit (current buffer view) to switch +if !exists("g:go_alternate_mode") + let g:go_alternate_mode = "edit" +endif + +" Test alternates between the implementation of code and the test code. +function! go#alternate#Switch(bang, cmd) + let file = expand('%') + if empty(file) + call go#util#EchoError("no buffer name") + return + elseif file =~# '^\f\+_test\.go$' + let l:root = split(file, '_test.go$')[0] + let l:alt_file = l:root . ".go" + elseif file =~# '^\f\+\.go$' + let l:root = split(file, ".go$")[0] + let l:alt_file = l:root . '_test.go' + else + call go#util#EchoError("not a go file") + return + endif + if !filereadable(alt_file) && !bufexists(alt_file) && !a:bang + call go#util#EchoError("couldn't find ".alt_file) + return + elseif empty(a:cmd) + execute ":" . g:go_alternate_mode . " " . alt_file + else + execute ":" . a:cmd . " " . alt_file + endif +endfunction diff --git a/autoload/go/asmfmt.vim b/autoload/go/asmfmt.vim new file mode 100644 index 0000000..0b42794 --- /dev/null +++ b/autoload/go/asmfmt.vim @@ -0,0 +1,52 @@ +" asmfmt.vim: Vim command to format Go asm files with asmfmt +" (github.com/klauspost/asmfmt). +" +" This filetype plugin adds new commands for asm buffers: +" +" :Fmt +" +" Filter the current asm buffer through asmfmt. +" It tries to preserve cursor position and avoids +" replacing the buffer with stderr output. +" +" Options: +" +" g:go_asmfmt_autosave [default=1] +" +" Flag to automatically call :Fmt when file is saved. + +let s:got_fmt_error = 0 + +" This is a trimmed-down version of the logic in fmt.vim. + +function! go#asmfmt#Format() + " Save state. + let l:curw = winsaveview() + + " Write the current buffer to a tempfile. + let l:tmpname = tempname() + call writefile(getline(1, '$'), l:tmpname) + + " Run asmfmt. + let path = go#path#CheckBinPath("asmfmt") + if empty(path) + return + endif + let out = system(path . ' -w ' . l:tmpname) + + " If there's no error, replace the current file with the output. + if v:shell_error == 0 + " Remove undo point caused by BufWritePre. + try | silent undojoin | catch | endtry + + " Replace the current file with the temp file; then reload the buffer. + let old_fileformat = &fileformat + call rename(l:tmpname, expand('%')) + silent edit! + let &fileformat = old_fileformat + let &syntax = &syntax + endif + + " Restore the cursor/window positions. + call winrestview(l:curw) +endfunction diff --git a/autoload/go/cmd.vim b/autoload/go/cmd.vim new file mode 100644 index 0000000..c90a053 --- /dev/null +++ b/autoload/go/cmd.vim @@ -0,0 +1,365 @@ +if !exists("g:go_dispatch_enabled") + let g:go_dispatch_enabled = 0 +endif + +function! go#cmd#autowrite() + if &autowrite == 1 + silent wall + endif +endfunction + + +" Build builds the source code without producting any output binary. We live in +" an editor so the best is to build it to catch errors and fix them. By +" default it tries to call simply 'go build', but it first tries to get all +" dependent files for the current folder and passes it to go build. +function! go#cmd#Build(bang, ...) + " expand all wildcards(i.e: '%' to the current file name) + let goargs = map(copy(a:000), "expand(v:val)") + + " escape all shell arguments before we pass it to make + let goargs = go#util#Shelllist(goargs, 1) + + " create our command arguments. go build discards any results when it + " compiles multiple packages. So we pass the `errors` package just as an + " placeholder with the current folder (indicated with '.') + let args = ["build"] + goargs + [".", "errors"] + + " if we have nvim, call it asynchronously and return early ;) + if has('nvim') + call go#util#EchoProgress("building dispatched ...") + call go#jobcontrol#Spawn(a:bang, "build", args) + return + endif + + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + let default_makeprg = &makeprg + let &makeprg = "go " . join(args, ' ') + + let l:listtype = go#list#Type("quickfix") + " execute make inside the source folder so we can parse the errors + " correctly + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd . fnameescape(expand("%:p:h")) + if g:go_dispatch_enabled && exists(':Make') == 2 + call go#util#EchoProgress("building dispatched ...") + silent! exe 'Make' + elseif l:listtype == "locationlist" + silent! exe 'lmake!' + else + silent! exe 'make!' + endif + redraw! + finally + execute cd . fnameescape(dir) + endtry + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + + if !empty(errors) + if !a:bang + call go#list#JumpToFirst(l:listtype) + endif + else + call go#util#EchoSuccess("[build] SUCCESS") + endif + + let &makeprg = default_makeprg + let $GOPATH = old_gopath +endfunction + + +" Run runs the current file (and their dependencies if any) in a new terminal. +function! go#cmd#RunTerm(bang, mode, files) + if empty(a:files) + let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) + else + let cmd = "go run ". go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1) + endif + call go#term#newmode(a:bang, cmd, a:mode) +endfunction + +" Run runs the current file (and their dependencies if any) and outputs it. +" This is intented to test small programs and play with them. It's not +" suitable for long running apps, because vim is blocking by default and +" calling long running apps will block the whole UI. +function! go#cmd#Run(bang, ...) + if has('nvim') + call go#cmd#RunTerm(a:bang, '', a:000) + return + endif + + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + if go#util#IsWin() + exec '!go run ' . go#util#Shelljoin(go#tool#Files()) + if v:shell_error + redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None + else + redraws! | echon "vim-go: [run] " | echohl Function | echon "SUCCESS"| echohl None + endif + + let $GOPATH = old_gopath + return + endif + + " :make expands '%' and '#' wildcards, so they must also be escaped + let default_makeprg = &makeprg + if a:0 == 0 + let &makeprg = 'go run ' . go#util#Shelljoin(go#tool#Files(), 1) + else + let &makeprg = "go run " . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) + endif + + let l:listtype = go#list#Type("quickfix") + + if g:go_dispatch_enabled && exists(':Make') == 2 + silent! exe 'Make' + elseif l:listtype == "locationlist" + exe 'lmake!' + else + exe 'make!' + endif + + let items = go#list#Get(l:listtype) + let errors = go#tool#FilterValids(items) + + call go#list#Populate(l:listtype, errors) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + endif + + let $GOPATH = old_gopath + let &makeprg = default_makeprg +endfunction + +" Install installs the package by simple calling 'go install'. If any argument +" is given(which are passed directly to 'go install') it tries to install those +" packages. Errors are populated in the location window. +function! go#cmd#Install(bang, ...) + let default_makeprg = &makeprg + + " :make expands '%' and '#' wildcards, so they must also be escaped + let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) + let &makeprg = "go install " . goargs + + let l:listtype = go#list#Type("quickfix") + " execute make inside the source folder so we can parse the errors + " correctly + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd . fnameescape(expand("%:p:h")) + if g:go_dispatch_enabled && exists(':Make') == 2 + call go#util#EchoProgress("building dispatched ...") + silent! exe 'Make' + elseif l:listtype == "locationlist" + silent! exe 'lmake!' + else + silent! exe 'make!' + endif + redraw! + finally + execute cd . fnameescape(dir) + endtry + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) + if !a:bang + call go#list#JumpToFirst(l:listtype) + endif + else + redraws! | echon "vim-go: " | echohl Function | echon "installed to ". $GOPATH | echohl None + endif + + let &makeprg = default_makeprg +endfunction + +" Test runs `go test` in the current directory. If compile is true, it'll +" compile the tests instead of running them (useful to catch errors in the +" test files). Any other argument is appendend to the final `go test` command +function! go#cmd#Test(bang, compile, ...) + let args = ["test"] + + " don't run the test, only compile it. Useful to capture and fix errors or + " to create a test binary. + if a:compile + call add(args, "-c") + endif + + if a:0 + " expand all wildcards(i.e: '%' to the current file name) + let goargs = map(copy(a:000), "expand(v:val)") + + call extend(args, goargs, 1) + else + " only add this if no custom flags are passed + let timeout = get(g:, 'go_test_timeout', '10s') + call add(args, printf("-timeout=%s", timeout)) + endif + + if a:compile + echon "vim-go: " | echohl Identifier | echon "compiling tests ..." | echohl None + else + echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None + endif + + if has('nvim') + if get(g:, 'go_term_enabled', 0) + call go#term#new(a:bang, ["go"] + args) + else + call go#jobcontrol#Spawn(a:bang, "test", args) + endif + return + endif + + call go#cmd#autowrite() + redraw + + let command = "go " . join(args, ' ') + + let out = go#tool#ExecuteInDir(command) + + let l:listtype = "quickfix" + + if v:shell_error + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd fnameescape(expand("%:p:h")) + let errors = go#tool#ParseErrors(split(out, '\n')) + let errors = go#tool#FilterValids(errors) + finally + execute cd . fnameescape(dir) + endtry + + call go#list#Populate(l:listtype, errors) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + elseif empty(errors) + " failed to parse errors, output the original content + call go#util#EchoError(out) + endif + echon "vim-go: " | echohl ErrorMsg | echon "[test] FAIL" | echohl None + else + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + + if a:compile + echon "vim-go: " | echohl Function | echon "[test] SUCCESS" | echohl None + else + echon "vim-go: " | echohl Function | echon "[test] PASS" | echohl None + endif + endif +endfunction + +" Testfunc runs a single test that surrounds the current cursor position. +" Arguments are passed to the `go test` command. +function! go#cmd#TestFunc(bang, ...) + " search flags legend (used only) + " 'b' search backward instead of forward + " 'c' accept a match at the cursor position + " 'n' do Not move the cursor + " 'W' don't wrap around the end of the file + " + " for the full list + " :help search + let test = search("func Test", "bcnW") + + if test == 0 + echo "vim-go: [test] no test found immediate to cursor" + return + end + + let line = getline(test) + let name = split(split(line, " ")[1], "(")[0] + let args = [a:bang, 0, "-run", name . "$"] + + if a:0 + call extend(args, a:000) + endif + + call call('go#cmd#Test', args) +endfunction + +" Coverage creates a new cover profile with 'go test -coverprofile' and opens +" a new HTML coverage page from that profile. +function! go#cmd#Coverage(bang, ...) + let l:tmpname=tempname() + + let command = "go test -coverprofile=" . l:tmpname . ' ' . go#util#Shelljoin(a:000) + + + let l:listtype = "quickfix" + call go#cmd#autowrite() + let out = go#tool#ExecuteInDir(command) + if v:shell_error + let errors = go#tool#ParseErrors(split(out, '\n')) + call go#list#Populate(l:listtype, errors) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + endif + else + " clear previous location list + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + + let openHTML = 'go tool cover -html='.l:tmpname + call go#tool#ExecuteInDir(openHTML) + endif + + call delete(l:tmpname) +endfunction + +" Generate runs 'go generate' in similar fashion to go#cmd#Build() +function! go#cmd#Generate(bang, ...) + let default_makeprg = &makeprg + + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + " :make expands '%' and '#' wildcards, so they must also be escaped + let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) + if v:shell_error + let &makeprg = "go generate " . goargs + else + let gofiles = go#util#Shelljoin(go#tool#Files(), 1) + let &makeprg = "go generate " . goargs . ' ' . gofiles + endif + + let l:listtype = go#list#Type("quickfix") + + echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None + if g:go_dispatch_enabled && exists(':Make') == 2 + silent! exe 'Make' + elseif l:listtype == "locationlist" + silent! exe 'lmake!' + else + silent! exe 'make!' + endif + redraw! + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) + if !a:bang + call go#list#JumpToFirst(l:listtype) + endif + else + redraws! | echon "vim-go: " | echohl Function | echon "[generate] SUCCESS"| echohl None + endif + + let &makeprg = default_makeprg + let $GOPATH = old_gopath +endfunction + +" vim:ts=4:sw=4:et diff --git a/autoload/go/complete.vim b/autoload/go/complete.vim new file mode 100644 index 0000000..9937e5d --- /dev/null +++ b/autoload/go/complete.vim @@ -0,0 +1,170 @@ +if !exists("g:go_gocode_bin") + let g:go_gocode_bin = "gocode" +endif + + +fu! s:gocodeCurrentBuffer() + let buf = getline(1, '$') + if &encoding != 'utf-8' + let buf = map(buf, 'iconv(v:val, &encoding, "utf-8")') + endif + if &l:fileformat == 'dos' + " XXX: line2byte() depend on 'fileformat' option. + " so if fileformat is 'dos', 'buf' must include '\r'. + let buf = map(buf, 'v:val."\r"') + endif + let file = tempname() + call writefile(buf, file) + + return file +endf + +if go#vimproc#has_vimproc() + let s:vim_system = get(g:, 'gocomplete#system_function', 'vimproc#system2') + let s:vim_shell_error = get(g:, 'gocomplete#shell_error_function', 'vimproc#get_last_status') +else + let s:vim_system = get(g:, 'gocomplete#system_function', 'system') + let s:vim_shell_error = '' +endif + +fu! s:shell_error() + if empty(s:vim_shell_error) + return v:shell_error + endif + return call(s:vim_shell_error, []) +endf + +fu! s:system(str, ...) + return call(s:vim_system, [a:str] + a:000) +endf + +fu! s:gocodeShellescape(arg) + if go#vimproc#has_vimproc() + return vimproc#shellescape(a:arg) + endif + try + let ssl_save = &shellslash + set noshellslash + return shellescape(a:arg) + finally + let &shellslash = ssl_save + endtry +endf + +fu! s:gocodeCommand(cmd, preargs, args) + for i in range(0, len(a:args) - 1) + let a:args[i] = s:gocodeShellescape(a:args[i]) + endfor + for i in range(0, len(a:preargs) - 1) + let a:preargs[i] = s:gocodeShellescape(a:preargs[i]) + endfor + + let bin_path = go#path#CheckBinPath(g:go_gocode_bin) + if empty(bin_path) + return + endif + + " we might hit cache problems, as gocode doesn't handle well different + " GOPATHS: https://github.com/nsf/gocode/issues/239 + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + let result = s:system(printf('%s %s %s %s', s:gocodeShellescape(bin_path), join(a:preargs), s:gocodeShellescape(a:cmd), join(a:args))) + + let $GOPATH = old_gopath + + if s:shell_error() != 0 + return "[\"0\", []]" + else + if &encoding != 'utf-8' + let result = iconv(result, 'utf-8', &encoding) + endif + return result + endif +endf + +fu! s:gocodeCurrentBufferOpt(filename) + return '-in=' . a:filename +endf + +fu! s:gocodeAutocomplete() + let filename = s:gocodeCurrentBuffer() + let result = s:gocodeCommand('autocomplete', + \ [s:gocodeCurrentBufferOpt(filename), '-f=vim'], + \ [expand('%:p'), go#util#OffsetCursor()]) + call delete(filename) + return result +endf + +function! go#complete#GetInfo() + let offset = go#util#OffsetCursor()+1 + let filename = s:gocodeCurrentBuffer() + let result = s:gocodeCommand('autocomplete', + \ [s:gocodeCurrentBufferOpt(filename), '-f=godit'], + \ [expand('%:p'), offset]) + call delete(filename) + + " first line is: Charcount,,NumberOfCandidates, i.e: 8,,1 + " following lines are candiates, i.e: func foo(name string),,foo( + let out = split(result, '\n') + + " no candidates are found + if len(out) == 1 + return "" + endif + + " only one candiate is found + if len(out) == 2 + return split(out[1], ',,')[0] + endif + + " to many candidates are available, pick one that maches the word under the + " cursor + let infos = [] + for info in out[1:] + call add(infos, split(info, ',,')[0]) + endfor + + let wordMatch = '\<' . expand("") . '\>' + " escape single quotes in wordMatch before passing it to filter + let wordMatch = substitute(wordMatch, "'", "''", "g") + let filtered = filter(infos, "v:val =~ '".wordMatch."'") + + if len(filtered) == 1 + return filtered[0] + endif + + return "" +endfunction + +function! go#complete#Info(auto) + " auto is true if we were called by g:go_auto_type_info's autocmd + let result = go#complete#GetInfo() + if !empty(result) + " if auto, and the result is a PANIC by gocode, hide it + if a:auto && result ==# 'PANIC PANIC PANIC' | return | endif + echo "vim-go: " | echohl Function | echon result | echohl None + endif +endfunction + +function! s:trim_bracket(val) + let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '') + return a:val +endfunction + +fu! go#complete#Complete(findstart, base) + "findstart = 1 when we need to get the text length + if a:findstart == 1 + execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete() + return col('.') - g:gocomplete_completions[0] - 1 + "findstart = 0 when we need to return the list of completions + else + let s = getline(".")[col('.') - 1] + if s =~ '[(){}\{\}]' + return map(copy(g:gocomplete_completions[1]), 's:trim_bracket(v:val)') + endif + return g:gocomplete_completions[1] + endif +endf + +" vim:ts=4:sw=4:et diff --git a/autoload/go/def.vim b/autoload/go/def.vim new file mode 100644 index 0000000..0928702 --- /dev/null +++ b/autoload/go/def.vim @@ -0,0 +1,111 @@ +if !exists("g:go_godef_bin") + let g:go_godef_bin = "godef" +endif + +if go#vimproc#has_vimproc() + let s:vim_system = get(g:, 'gocomplete#system_function', 'vimproc#system2') +else + let s:vim_system = get(g:, 'gocomplete#system_function', 'system') +endif + +fu! s:system(str, ...) + return call(s:vim_system, [a:str] + a:000) +endf + +" modified and improved version of vim-godef +function! go#def#Jump(...) + if !len(a:000) + let arg = "-o=" . go#util#OffsetCursor() + else + let arg = a:1 + endif + + let bin_path = go#path#CheckBinPath(g:go_godef_bin) + if empty(bin_path) + return + endif + + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + let command = bin_path . " -f=" . shellescape(fname) . " -i " . shellescape(arg) + + " get output of godef + let out = s:system(command, join(getbufline(bufnr('%'), 1, '$'), go#util#LineEnding())) + + " jump to it + call s:godefJump(out, "") + let $GOPATH = old_gopath +endfunction + + +function! go#def#JumpMode(mode) + let arg = "-o=" . go#util#OffsetCursor() + + let bin_path = go#path#CheckBinPath(g:go_godef_bin) + if empty(bin_path) + return + endif + + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + let command = bin_path . " -f=" . shellescape(fname) . " -i " . shellescape(arg) + + " get output of godef + let out = s:system(command, join(getbufline(bufnr('%'), 1, '$'), go#util#LineEnding())) + + call s:godefJump(out, a:mode) + let $GOPATH = old_gopath +endfunction + + +function! s:getOffset() + return "-o=" . go#util#OffsetCursor() +endfunction + + +function! s:godefJump(out, mode) + let old_errorformat = &errorformat + let &errorformat = "%f:%l:%c" + + if a:out =~ 'godef: ' + let out = substitute(a:out, go#util#LineEnding() . '$', '', '') + echom out + else + let parts = split(a:out, ':') + " parts[0] contains filename + let fileName = parts[0] + + " put the error format into location list so we can jump automatically to + " it + lgetexpr a:out + + " needed for restoring back user setting this is because there are two + " modes of switchbuf which we need based on the split mode + let old_switchbuf = &switchbuf + + if a:mode == "tab" + let &switchbuf = "usetab" + + if bufloaded(fileName) == 0 + tab split + endif + else + if a:mode == "split" + split + elseif a:mode == "vsplit" + vsplit + endif + endif + + " jump to file now + sil ll 1 + normal! zz + + let &switchbuf = old_switchbuf + end + let &errorformat = old_errorformat +endfunction diff --git a/autoload/go/doc.vim b/autoload/go/doc.vim new file mode 100644 index 0000000..ab10725 --- /dev/null +++ b/autoload/go/doc.vim @@ -0,0 +1,171 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" godoc.vim: Vim command to see godoc. +" +" +" Commands: +" +" :GoDoc +" +" Open the relevant Godoc for either the word[s] passed to the command or +" the, by default, the word under the cursor. +" +" Options: +" +" g:go_godoc_commands [default=1] +" +" Flag to indicate whether to enable the commands listed above. + +let s:buf_nr = -1 + +if !exists("g:go_doc_command") + let g:go_doc_command = "godoc" +endif + +if !exists("g:go_doc_options") + let g:go_doc_options = "" +endif + +" returns the package and exported name. exported name might be empty. +" ie: fmt and Println +" ie: github.com/fatih/set and New +function! s:godocWord(args) + if !executable('godoc') + echohl WarningMsg + echo "godoc command not found." + echo " install with: go get golang.org/x/tools/cmd/godoc" + echohl None + return [] + endif + + if !len(a:args) + let oldiskeyword = &iskeyword + setlocal iskeyword+=. + let word = expand('') + let &iskeyword = oldiskeyword + let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g') + let words = split(word, '\.\ze[^./]\+$') + else + let words = a:args + endif + + if !len(words) + return [] + endif + + let pkg = words[0] + if len(words) == 1 + let exported_name = "" + else + let exported_name = words[1] + endif + + let packages = go#tool#Imports() + + if has_key(packages, pkg) + let pkg = packages[pkg] + endif + + return [pkg, exported_name] +endfunction + +function! s:godocNotFound(content) + if len(a:content) == 0 + return 1 + endif + + return a:content =~# '^.*: no such file or directory\n$' +endfunction + +function! go#doc#OpenBrowser(...) + let pkgs = s:godocWord(a:000) + if empty(pkgs) + return + endif + + let pkg = pkgs[0] + let exported_name = pkgs[1] + + " example url: https://godoc.org/github.com/fatih/set#Set + let godoc_url = "https://godoc.org/" . pkg . "#" . exported_name + call go#tool#OpenBrowser(godoc_url) +endfunction + +function! go#doc#Open(newmode, mode, ...) + let pkgs = s:godocWord(a:000) + if empty(pkgs) + return + endif + + let pkg = pkgs[0] + let exported_name = pkgs[1] + + let command = g:go_doc_command . ' ' . g:go_doc_options . ' ' . pkg + + silent! let content = system(command) + if v:shell_error || s:godocNotFound(content) + echo 'No documentation found for "' . pkg . '".' + return -1 + endif + + call s:GodocView(a:newmode, a:mode, content) + + if exported_name == '' + silent! normal! gg + return -1 + endif + + " jump to the specified name + if search('^func ' . exported_name . '(') + silent! normal! zt + return -1 + endif + + if search('^type ' . exported_name) + silent! normal! zt + return -1 + endif + + if search('^\%(const\|var\|type\|\s\+\) ' . pkg . '\s\+=\s') + silent! normal! zt + return -1 + endif + + " nothing found, jump to top + silent! normal! gg +endfunction + +function! s:GodocView(newposition, position, content) + " reuse existing buffer window if it exists otherwise create a new one + if !bufexists(s:buf_nr) + execute a:newposition + sil file `="[Godoc]"` + let s:buf_nr = bufnr('%') + elseif bufwinnr(s:buf_nr) == -1 + execute a:position + execute s:buf_nr . 'buffer' + elseif bufwinnr(s:buf_nr) != bufwinnr('%') + execute bufwinnr(s:buf_nr) . 'wincmd w' + endif + + setlocal filetype=godoc + setlocal bufhidden=delete + setlocal buftype=nofile + setlocal noswapfile + setlocal nobuflisted + setlocal nocursorline + setlocal nocursorcolumn + setlocal iskeyword+=: + setlocal iskeyword-=- + + setlocal modifiable + %delete _ + call append(0, split(a:content, "\n")) + sil $delete _ + setlocal nomodifiable +endfunction + + +" vim:ts=4:sw=4:et diff --git a/autoload/go/fmt.vim b/autoload/go/fmt.vim new file mode 100644 index 0000000..56c6ee7 --- /dev/null +++ b/autoload/go/fmt.vim @@ -0,0 +1,210 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" fmt.vim: Vim command to format Go files with gofmt. +" +" This filetype plugin add a new commands for go buffers: +" +" :Fmt +" +" Filter the current Go buffer through gofmt. +" It tries to preserve cursor position and avoids +" replacing the buffer with stderr output. +" +" Options: +" +" g:go_fmt_command [default="gofmt"] +" +" Flag naming the gofmt executable to use. +" +" g:go_fmt_autosave [default=1] +" +" Flag to auto call :Fmt when saved file +" + +if !exists("g:go_fmt_command") + let g:go_fmt_command = "gofmt" +endif + +if !exists("g:go_goimports_bin") + let g:go_goimports_bin = "goimports" +endif + +if !exists('g:go_fmt_fail_silently') + let g:go_fmt_fail_silently = 0 +endif + +if !exists('g:go_fmt_options') + let g:go_fmt_options = '' +endif + +if !exists("g:go_fmt_experimental") + let g:go_fmt_experimental = 0 +endif + +" we have those problems : +" http://stackoverflow.com/questions/12741977/prevent-vim-from-updating-its-undo-tree +" http://stackoverflow.com/questions/18532692/golang-formatter-and-vim-how-to-destroy-history-record?rq=1 +" +" The below function is an improved version that aims to fix all problems. +" it doesn't undo changes and break undo history. If you are here reading +" this and have VimL experience, please look at the function for +" improvements, patches are welcome :) +function! go#fmt#Format(withGoimport) + if g:go_fmt_experimental == 1 + " Using winsaveview to save/restore cursor state has the problem of + " closing folds on save: + " https://github.com/fatih/vim-go/issues/502 + " One fix is to use mkview instead. Unfortunately, this sometimes causes + " other bad side effects: + " https://github.com/fatih/vim-go/issues/728 + " and still closes all folds if foldlevel>0: + " https://github.com/fatih/vim-go/issues/732 + let l:curw = {} + try + mkview! + catch + let l:curw=winsaveview() + endtry + else + " Save cursor position and many other things. + let l:curw=winsaveview() + endif + + " Write current unsaved buffer to a temp file + let l:tmpname = tempname() + call writefile(getline(1, '$'), l:tmpname) + + if g:go_fmt_experimental == 1 + " save our undo file to be restored after we are done. This is needed to + " prevent an additional undo jump due to BufWritePre auto command and also + " restore 'redo' history because it's getting being destroyed every + " BufWritePre + let tmpundofile=tempname() + exe 'wundo! ' . tmpundofile + endif + + " get the command first so we can test it + let fmt_command = g:go_fmt_command + if a:withGoimport == 1 + let fmt_command = g:go_goimports_bin + endif + + " check if the user has installed command binary. + " For example if it's goimports, let us check if it's installed, + " if not the user get's a warning via go#path#CheckBinPath() + let bin_path = go#path#CheckBinPath(fmt_command) + if empty(bin_path) + return + endif + + if fmt_command != "gofmt" + " change GOPATH too, so goimports can pick up the correct library + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + let fmt_command = bin_path + endif + + " populate the final command with user based fmt options + let command = fmt_command . ' -w ' + if a:withGoimport != 1 + let command = command . g:go_fmt_options + endif + + if fmt_command == "goimports" + if !exists('b:goimports_vendor_compatible') + let out = system("goimports --help") + if out !~ "-srcdir" + echohl WarningMsg + echomsg "vim-go: goimports does not support srcdir." + echomsg " update with: :GoUpdateBinaries" + echohl None + else + let b:goimports_vendor_compatible = 1 + endif + endif + + if exists('b:goimports_vendor_compatible') && b:goimports_vendor_compatible + let command = command . '-srcdir ' . fnameescape(expand("%:p:h")) + endif + endif + + " execute our command... + let out = system(command . " " . l:tmpname) + + if fmt_command != "gofmt" + let $GOPATH = old_gopath + endif + + let l:listtype = "locationlist" + "if there is no error on the temp file replace the output with the current + "file (if this fails, we can always check the outputs first line with: + "splitted =~ 'package \w\+') + if v:shell_error == 0 + " remove undo point caused via BufWritePre + try | silent undojoin | catch | endtry + + " Replace current file with temp file, then reload buffer + let old_fileformat = &fileformat + call rename(l:tmpname, expand('%')) + silent edit! + let &fileformat = old_fileformat + let &syntax = &syntax + + " clean up previous location list, but only if it's due to fmt + if exists('b:got_fmt_error') && b:got_fmt_error + let b:got_fmt_error = 0 + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + endif + elseif g:go_fmt_fail_silently == 0 + let splitted = split(out, '\n') + "otherwise get the errors and put them to location list + let errors = [] + for line in splitted + let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)') + if !empty(tokens) + call add(errors, {"filename": @%, + \"lnum": tokens[2], + \"col": tokens[3], + \"text": tokens[4]}) + endif + endfor + if empty(errors) + % | " Couldn't detect gofmt error format, output errors + endif + if !empty(errors) + call go#list#Populate(l:listtype, errors) + echohl Error | echomsg "Gofmt returned error" | echohl None + endif + + let b:got_fmt_error = 1 + call go#list#Window(l:listtype, len(errors)) + + " We didn't use the temp file, so clean up + call delete(l:tmpname) + endif + + if g:go_fmt_experimental == 1 + " restore our undo history + silent! exe 'rundo ' . tmpundofile + call delete(tmpundofile) + endif + + if g:go_fmt_experimental == 1 + " Restore our cursor/windows positions, folds, etc. + if empty(l:curw) + silent! loadview + else + call winrestview(l:curw) + endif + else + " Restore our cursor/windows positions. + call winrestview(l:curw) + endif +endfunction + + +" vim:ts=4:sw=4:et diff --git a/autoload/go/import.vim b/autoload/go/import.vim new file mode 100644 index 0000000..2660099 --- /dev/null +++ b/autoload/go/import.vim @@ -0,0 +1,213 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" Check out the docs for more information at /doc/vim-go.txt +" +function! go#import#SwitchImport(enabled, localname, path, bang) + let view = winsaveview() + let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '') + + " Quotes are not necessary, so remove them if provided. + if path[0] == '"' + let path = strpart(path, 1) + endif + if path[len(path)-1] == '"' + let path = strpart(path, 0, len(path) - 1) + endif + + " if given a trailing slash, eg. `github.com/user/pkg/`, remove it + if path[len(path)-1] == '/' + let path = strpart(path, 0, len(path) - 1) + endif + + if path == '' + call s:Error('Import path not provided') + return + endif + + if a:bang == "!" + let out = system("go get -u -v ".shellescape(path)) + if v:shell_error + call s:Error("Can't find import: " . path . ":" . out) + endif + endif + let exists = go#tool#Exists(path) + if exists == -1 + call s:Error("Can't find import: " . path) + return + endif + + " Extract any site prefix (e.g. github.com/). + " If other imports with the same prefix are grouped separately, + " we will add this new import with them. + " Only up to and including the first slash is used. + let siteprefix = matchstr(path, "^[^/]*/") + + let qpath = '"' . path . '"' + if a:localname != '' + let qlocalpath = a:localname . ' ' . qpath + else + let qlocalpath = qpath + endif + let indentstr = 0 + let packageline = -1 " Position of package name statement + let appendline = -1 " Position to introduce new import + let deleteline = -1 " Position of line with existing import + let linesdelta = 0 " Lines added/removed + + " Find proper place to add/remove import. + let line = 0 + while line <= line('$') + let linestr = getline(line) + + if linestr =~# '^package\s' + let packageline = line + let appendline = line + + elseif linestr =~# '^import\s\+(' + let appendstr = qlocalpath + let indentstr = 1 + let appendline = line + let firstblank = -1 + let lastprefix = "" + while line <= line("$") + let line = line + 1 + let linestr = getline(line) + let m = matchlist(getline(line), '^\()\|\(\s\+\)\(\S*\s*\)"\(.\+\)"\)') + if empty(m) + if siteprefix == "" && a:enabled + " must be in the first group + break + endif + " record this position, but keep looking + if firstblank < 0 + let firstblank = line + endif + continue + endif + if m[1] == ')' + " if there's no match, add it to the first group + if appendline < 0 && firstblank >= 0 + let appendline = firstblank + endif + break + endif + let lastprefix = matchstr(m[4], "^[^/]*/") + if a:localname != '' && m[3] != '' + let qlocalpath = printf('%-' . (len(m[3])-1) . 's %s', a:localname, qpath) + endif + let appendstr = m[2] . qlocalpath + let indentstr = 0 + if m[4] == path + let appendline = -1 + let deleteline = line + break + elseif m[4] < path + " don't set candidate position if we have a site prefix, + " we've passed a blank line, and this doesn't share the same + " site prefix. + if siteprefix == "" || firstblank < 0 || match(m[4], "^" . siteprefix) >= 0 + let appendline = line + endif + elseif siteprefix != "" && match(m[4], "^" . siteprefix) >= 0 + " first entry of site group + let appendline = line - 1 + break + endif + endwhile + break + + elseif linestr =~# '^import ' + if appendline == packageline + let appendstr = 'import ' . qlocalpath + let appendline = line - 1 + endif + let m = matchlist(linestr, '^import\(\s\+\)\(\S*\s*\)"\(.\+\)"') + if !empty(m) + if m[3] == path + let appendline = -1 + let deleteline = line + break + endif + if m[3] < path + let appendline = line + endif + if a:localname != '' && m[2] != '' + let qlocalpath = printf("%s %" . len(m[2])-1 . "s", a:localname, qpath) + endif + let appendstr = 'import' . m[1] . qlocalpath + endif + + elseif linestr =~# '^\(var\|const\|type\|func\)\>' + break + + endif + let line = line + 1 + endwhile + + " Append or remove the package import, as requested. + if a:enabled + if deleteline != -1 + call s:Error(qpath . ' already being imported') + elseif appendline == -1 + call s:Error('No package line found') + else + if appendline == packageline + call append(appendline + 0, '') + call append(appendline + 1, 'import (') + call append(appendline + 2, ')') + let appendline += 2 + let linesdelta += 3 + let appendstr = qlocalpath + let indentstr = 1 + endif + call append(appendline, appendstr) + execute appendline + 1 + if indentstr + execute 'normal! >>' + endif + let linesdelta += 1 + endif + else + if deleteline == -1 + call s:Error(qpath . ' not being imported') + else + execute deleteline . 'd' + let linesdelta -= 1 + + if getline(deleteline-1) =~# '^import\s\+(' && getline(deleteline) =~# '^)' + " Delete empty import block + let deleteline -= 1 + execute deleteline . "d" + execute deleteline . "d" + let linesdelta -= 2 + endif + + if getline(deleteline) == '' && getline(deleteline - 1) == '' + " Delete spacing for removed line too. + execute deleteline . "d" + let linesdelta -= 1 + endif + endif + endif + + " Adjust view for any changes. + let view.lnum += linesdelta + let view.topline += linesdelta + if view.topline < 0 + let view.topline = 0 + endif + + " Put buffer back where it was. + call winrestview(view) + +endfunction + + +function! s:Error(s) + echohl Error | echo a:s | echohl None +endfunction + + +" vim:ts=4:sw=4:et diff --git a/autoload/go/jobcontrol.vim b/autoload/go/jobcontrol.vim new file mode 100644 index 0000000..777b054 --- /dev/null +++ b/autoload/go/jobcontrol.vim @@ -0,0 +1,179 @@ +" s:jobs is a global reference to all jobs started with Spawn() or with the +" internal function s:spawn +let s:jobs = {} + +" Spawn is a wrapper around s:spawn. It can be executed by other files and +" scripts if needed. Desc defines the description for printing the status +" during the job execution (useful for statusline integration). +function! go#jobcontrol#Spawn(bang, desc, args) + " autowrite is not enabled for jobs + call go#cmd#autowrite() + + let job = s:spawn(a:bang, a:desc, a:args) + return job.id +endfunction + +" Statusline returns the current status of the job +function! go#jobcontrol#Statusline() abort + if empty(s:jobs) + return '' + endif + + let import_path = go#package#ImportPath(expand('%:p:h')) + + for job in values(s:jobs) + if job.importpath != import_path + continue + endif + + if job.state == "SUCCESS" + return '' + endif + + return printf("%s ... [%s]", job.desc, job.state) + endfor + + return '' +endfunction + +" spawn spawns a go subcommand with the name and arguments with jobstart. Once +" a job is started a reference will be stored inside s:jobs. spawn changes the +" GOPATH when g:go_autodetect_gopath is enabled. The job is started inside the +" current files folder. +function! s:spawn(bang, desc, args) + let job = { + \ 'desc': a:desc, + \ 'bang': a:bang, + \ 'winnr': winnr(), + \ 'importpath': go#package#ImportPath(expand('%:p:h')), + \ 'state': "RUNNING", + \ 'stderr' : [], + \ 'stdout' : [], + \ 'on_stdout': function('s:on_stdout'), + \ 'on_stderr': function('s:on_stderr'), + \ 'on_exit' : function('s:on_exit'), + \ } + + " modify GOPATH if needed + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + " execute go build in the files directory + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + + " cleanup previous jobs for this file + for jb in values(s:jobs) + if jb.importpath == job.importpath + unlet s:jobs[jb.id] + endif + endfor + + let dir = getcwd() + let jobdir = fnameescape(expand("%:p:h")) + execute cd . jobdir + + " append the subcommand, such as 'build' + let argv = ['go'] + a:args + + " run, forrest, run! + let id = jobstart(argv, job) + let job.id = id + let job.dir = jobdir + let s:jobs[id] = job + + execute cd . fnameescape(dir) + + " restore back GOPATH + let $GOPATH = old_gopath + + return job +endfunction + +" on_exit is the exit handler for jobstart(). It handles cleaning up the job +" references and also displaying errors in the quickfix window collected by +" on_stderr handler. If there are no errors and a quickfix window is open, +" it'll be closed. +function! s:on_exit(job_id, exit_status) + let std_combined = self.stderr + self.stdout + if a:exit_status == 0 + call go#list#Clean(0) + call go#list#Window(0) + + let self.state = "SUCCESS" + call go#util#EchoSuccess("SUCCESS") + return + endif + + let self.state = "FAILED" + call go#util#EchoError("FAILED") + + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd self.dir + let errors = go#tool#ParseErrors(std_combined) + let errors = go#tool#FilterValids(errors) + finally + execute cd . fnameescape(dir) + endtry + + if !len(errors) + " failed to parse errors, output the original content + call go#util#EchoError(std_combined[0]) + return + endif + + " if we are still in the same windows show the list + if self.winnr == winnr() + let l:listtype = "locationlist" + call go#list#Populate(l:listtype, errors) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !self.bang + call go#list#JumpToFirst(l:listtype) + endif + endif +endfunction + +" on_stdout is the stdout handler for jobstart(). It collects the output of +" stderr and stores them to the jobs internal stdout list. +function! s:on_stdout(job_id, data) + call extend(self.stdout, a:data) +endfunction + +" on_stderr is the stderr handler for jobstart(). It collects the output of +" stderr and stores them to the jobs internal stderr list. +function! s:on_stderr(job_id, data) + call extend(self.stderr, a:data) +endfunction + +" abort_all aborts all current jobs created with s:spawn() +function! s:abort_all() + if empty(s:jobs) + return + endif + + for id in keys(s:jobs) + if id > 0 + silent! call jobstop(id) + endif + endfor + + let s:jobs = {} +endfunction + +" abort aborts the job with the given name, where name is the first argument +" passed to s:spawn() +function! s:abort(path) + if empty(s:jobs) + return + endif + + for job in values(s:jobs) + if job.importpath == path && job.id > 0 + silent! call jobstop(job.id) + unlet s:jobs['job.id'] + endif + endfor +endfunction + +" vim:ts=2:sw=2:et diff --git a/autoload/go/lint.vim b/autoload/go/lint.vim new file mode 100644 index 0000000..91247ad --- /dev/null +++ b/autoload/go/lint.vim @@ -0,0 +1,199 @@ +if !exists("g:go_metalinter_command") + let g:go_metalinter_command = "" +endif + +if !exists("g:go_metalinter_autosave_enabled") + let g:go_metalinter_autosave_enabled = ['vet', 'golint'] +endif + +if !exists("g:go_metalinter_enabled") + let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck'] +endif + +if !exists("g:go_metalinter_deadline") + let g:go_metalinter_deadline = "5s" +endif + +if !exists("g:go_golint_bin") + let g:go_golint_bin = "golint" +endif + +if !exists("g:go_errcheck_bin") + let g:go_errcheck_bin = "errcheck" +endif + +function! go#lint#Gometa(autosave, ...) abort + if a:0 == 0 + let goargs = expand('%:p:h') + else + let goargs = go#util#Shelljoin(a:000) + endif + + let meta_command = "gometalinter --disable-all" + if a:autosave || empty(g:go_metalinter_command) + let bin_path = go#path#CheckBinPath("gometalinter") + if empty(bin_path) + return + endif + + if a:autosave + " include only messages for the active buffer + let meta_command .= " --include='^" . expand('%:p') . ".*$'" + endif + + " linters + let linters = a:autosave ? g:go_metalinter_autosave_enabled : g:go_metalinter_enabled + for linter in linters + let meta_command .= " --enable=".linter + endfor + + " deadline + let meta_command .= " --deadline=" . g:go_metalinter_deadline + + " path + let meta_command .= " " . goargs + else + " the user wants something else, let us use it. + let meta_command = g:go_metalinter_command + endif + + " comment out the following two lines for debugging + " echo meta_command + " return + + let out = go#tool#ExecuteInDir(meta_command) + + let l:listtype = "quickfix" + if v:shell_error == 0 + redraw | echo + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None + else + " GoMetaLinter can output one of the two, so we look for both: + " ::[]: () + " ::: () + " This can be defined by the following errorformat: + let errformat = "%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m" + + " Parse and populate our location list + call go#list#ParseFormat(l:listtype, errformat, split(out, "\n")) + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + + if !a:autosave + call go#list#JumpToFirst(l:listtype) + endif + endif +endfunction + +" Golint calls 'golint' on the current directory. Any warnings are populated in +" the location list +function! go#lint#Golint(...) abort + let bin_path = go#path#CheckBinPath(g:go_golint_bin) + if empty(bin_path) + return + endif + + if a:0 == 0 + let goargs = shellescape(expand('%')) + else + let goargs = go#util#Shelljoin(a:000) + endif + + let out = system(bin_path . " " . goargs) + if empty(out) + echon "vim-go: " | echohl Function | echon "[lint] PASS" | echohl None + return + endif + + let l:listtype = "quickfix" + call go#list#Parse(l:listtype, out) + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + call go#list#JumpToFirst(l:listtype) +endfunction + +" Vet calls 'go vet' on the current directory. Any warnings are populated in +" the location list +function! go#lint#Vet(bang, ...) + call go#cmd#autowrite() + echon "vim-go: " | echohl Identifier | echon "calling vet..." | echohl None + if a:0 == 0 + let out = go#tool#ExecuteInDir('go vet') + else + let out = go#tool#ExecuteInDir('go tool vet ' . go#util#Shelljoin(a:000)) + endif + + let l:listtype = "quickfix" + if v:shell_error + let errors = go#tool#ParseErrors(split(out, '\n')) + call go#list#Populate(l:listtype, errors) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + endif + echon "vim-go: " | echohl ErrorMsg | echon "[vet] FAIL" | echohl None + else + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None + endif +endfunction + +" ErrCheck calls 'errcheck' for the given packages. Any warnings are populated in +" the location list +function! go#lint#Errcheck(...) abort + if a:0 == 0 + let goargs = go#package#ImportPath(expand('%:p:h')) + if goargs == -1 + echohl Error | echomsg "vim-go: package is not inside GOPATH src" | echohl None + return + endif + else + let goargs = go#util#Shelljoin(a:000) + endif + + let bin_path = go#path#CheckBinPath(g:go_errcheck_bin) + if empty(bin_path) + return + endif + + echon "vim-go: " | echohl Identifier | echon "errcheck analysing ..." | echohl None + redraw + + let command = bin_path . ' -abspath ' . goargs + let out = go#tool#ExecuteInDir(command) + + let l:listtype = "quickfix" + if v:shell_error + let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m" + + " Parse and populate our location list + call go#list#ParseFormat(l:listtype, errformat, split(out, "\n")) + + let errors = go#list#Get(l:listtype) + + if empty(errors) + echohl Error | echomsg "GoErrCheck returned error" | echohl None + echo out + return + endif + + if !empty(errors) + call go#list#Populate(l:listtype, errors) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) + call go#list#JumpToFirst(l:listtype) + endif + endif + else + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None + endif + +endfunction + +" vim:ts=4:sw=4:et diff --git a/autoload/go/list.vim b/autoload/go/list.vim new file mode 100644 index 0000000..d91cfb1 --- /dev/null +++ b/autoload/go/list.vim @@ -0,0 +1,126 @@ +if !exists("g:go_list_type") + let g:go_list_type = "" +endif + +" Window opens the list with the given height up to 10 lines maximum. +" Otherwise g:go_loclist_height is used. If no or zero height is given it +" closes the window +function! go#list#Window(listtype, ...) + let l:listtype = go#list#Type(a:listtype) + " we don't use lwindow to close the location list as we need also the + " ability to resize the window. So, we are going to use lopen and lclose + " for a better user experience. If the number of errors in a current + " location list increases/decreases, cwindow will not resize when a new + " updated height is passed. lopen in the other hand resizes the screen. + if !a:0 || a:1 == 0 + if l:listtype == "locationlist" + lclose + else + cclose + endif + return + endif + + let height = get(g:, "go_list_height", 0) + if height == 0 + " prevent creating a large location height for a large set of numbers + if a:1 > 10 + let height = 10 + else + let height = a:1 + endif + endif + + if l:listtype == "locationlist" + exe 'lopen ' . height + else + exe 'copen ' . height + endif +endfunction + + +" Get returns the current list of items from the location list +function! go#list#Get(listtype) + let l:listtype = go#list#Type(a:listtype) + if l:listtype == "locationlist" + return getloclist(0) + else + return getqflist() + endif +endfunction + +" Populate populate the location list with the given items +function! go#list#Populate(listtype, items) + let l:listtype = go#list#Type(a:listtype) + if l:listtype == "locationlist" + call setloclist(0, a:items, 'r') + else + call setqflist(a:items, 'r') + endif +endfunction + +function! go#list#PopulateWin(winnr, items) + call setloclist(a:winnr, a:items, 'r') +endfunction + +" Parse parses the given items based on the specified errorformat nad +" populates the location list. +function! go#list#ParseFormat(listtype, errformat, items) + let l:listtype = go#list#Type(a:listtype) + " backup users errorformat, will be restored once we are finished + let old_errorformat = &errorformat + + " parse and populate the location list + let &errorformat = a:errformat + if l:listtype == "locationlist" + lgetexpr a:items + else + cgetexpr a:items + endif + + "restore back + let &errorformat = old_errorformat +endfunction + +" Parse parses the given items based on the global errorformat and +" populates the location list. +function! go#list#Parse(listtype, items) + let l:listtype = go#list#Type(a:listtype) + if l:listtype == "locationlist" + lgetexpr a:items + else + cgetexpr a:items + endif +endfunction + +" JumpToFirst jumps to the first item in the location list +function! go#list#JumpToFirst(listtype) + let l:listtype = go#list#Type(a:listtype) + if l:listtype == "locationlist" + ll 1 + else + cc 1 + endif +endfunction + +" Clean cleans the location list +function! go#list#Clean(listtype) + let l:listtype = go#list#Type(a:listtype) + if l:listtype == "locationlist" + lex [] + else + cex [] + endif +endfunction + +function! go#list#Type(listtype) + if g:go_list_type == "locationlist" + return "locationlist" + elseif g:go_list_type == "quickfix" + return "quickfix" + else + return a:listtype + endif +endfunction + +" vim:ts=4:sw=4:et diff --git a/autoload/go/oracle.vim b/autoload/go/oracle.vim new file mode 100644 index 0000000..490b6b4 --- /dev/null +++ b/autoload/go/oracle.vim @@ -0,0 +1,233 @@ +" oracle.vim -- Vim integration for the Go oracle. +" +" Part of this plugin was taken directly from the oracle repo, however it's +" massively changed for a better integration into vim-go. Thanks Alan Donovan +" for the first iteration based on quickfix! - Fatih Arslan +" + +if !exists("g:go_oracle_bin") + let g:go_oracle_bin = "oracle" +endif + +" Parses (via regex) Oracle's 'plain' format output and puts them into a +" location list +func! s:loclist(output) + let llist = [] + " Parse GNU-style 'file:line.col-line.col: message' format. + let mx = '^\(\a:[\\/][^:]\+\|[^:]\+\):\(\d\+\):\(\d\+\):\(.*\)$' + for line in split(a:output, "\n") + let ml = matchlist(line, mx) + + " Ignore non-match lines or warnings + if ml == [] || ml[4] =~ '^ warning:' + continue + endif + + let item = { + \ 'filename': ml[1], + \ 'text': ml[4], + \ 'lnum': ml[2], + \ 'col': ml[3], + \} + let bnr = bufnr(fnameescape(ml[1])) + if bnr != -1 + let item['bufnr'] = bnr + endif + call add(llist, item) + endfor + call go#list#Populate("locationlist", llist) + call go#list#Window("locationlist", len(llist)) +endfun + +" This uses Vim's errorformat to parse the output from Oracle's 'plain output +" and put it into location list. I believe using errorformat is much more +" easier to use. If we need more power we can always switch back to parse it +" via regex. +func! s:loclistSecond(output) + " backup users errorformat, will be restored once we are finished + let old_errorformat = &errorformat + + " match two possible styles of errorformats: + " + " 'file:line.col-line2.col2: message' + " 'file:line:col: message' + " + " We discard line2 and col2 for the first errorformat, because it's not + " useful and location only has the ability to show one line and column + " number + let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m" + call go#list#ParseFormat("locationlist", errformat, split(a:output, "\n")) + + let errors = go#list#Get("locationlist") + call go#list#Window("locationlist", len(errors)) +endfun + +func! s:RunOracle(mode, selected, needs_package) range abort + let fname = expand('%:p') + let dname = expand('%:p:h') + let pkg = go#package#ImportPath(dname) + + if exists('g:go_oracle_scope') + " let the user defines the scope, must be a space separated string, + " example: 'fmt math net/http' + let scopes = split(get(g:, 'go_oracle_scope')) + elseif a:needs_package || exists('g:go_oracle_include_tests') && pkg != -1 + " give import path so it includes all _test.go files too + let scopes = [pkg] + else + " best usable way, only pass the package itself, without the test + " files + let scopes = go#tool#Files() + endif + + "return with a warning if the bin doesn't exist + let bin_path = go#path#CheckBinPath(g:go_oracle_bin) + if empty(bin_path) + return + endif + + if exists('g:go_oracle_tags') + let tags = get(g:, 'go_oracle_tags') + else + let tags = "" + endif + + if a:selected != -1 + let pos1 = go#util#Offset(line("'<"), col("'<")) + let pos2 = go#util#Offset(line("'>"), col("'>")) + let cmd = printf('%s -format plain -pos=%s:#%d,#%d -tags=%s %s', + \ bin_path, + \ shellescape(fname), pos1, pos2, tags, a:mode) + else + let pos = go#util#OffsetCursor() + let cmd = printf('%s -format plain -pos=%s:#%d -tags=%s %s', + \ bin_path, + \ shellescape(fname), pos, tags, a:mode) + endif + + " strip trailing slashes for each path in scoped. bug: + " https://github.com/golang/go/issues/14584 + let scopes = go#util#StripTrailingSlash(scopes) + + " now append each scope to the end as Oracle's scope parameter. It can be + " a packages or go files, dependent on the User's own choice. For more + " info check Oracle's User Manual section about scopes: + " https://docs.google.com/document/d/1SLk36YRjjMgKqe490mSRzOPYEDe0Y_WQNRv-EiFYUyw/view#heading=h.nwso96pj07q8 + let cmd .= ' ' . go#util#Shelljoin(scopes) + + echon "vim-go: " | echohl Identifier | echon "analysing ..." | echohl None + + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + let out = system(cmd) + + let $GOPATH = old_gopath + + if v:shell_error + " unfortunaly oracle outputs a very long stack trace that is not + " parsable to show the real error. But the main issue is usually the + " package which doesn't build. + redraw | echon "vim-go: " | echohl Statement | echon out | echohl None + return "" + endif + + return out +endfunc + +function! go#oracle#Scope(...) + if a:0 + if a:0 == 1 && a:1 == '""' + unlet g:go_oracle_scope + echon "vim-go: " | echohl Function | echon "oracle scope is cleared"| echohl None + else + let g:go_oracle_scope = join(a:000, ' ') + echon "vim-go: " | echohl Function | echon "oracle scope changed to: '". g:go_oracle_scope ."'" | echohl None + endif + + return + endif + + if !exists('g:go_oracle_scope') + echon "vim-go: " | echohl Function | echon "oracle scope is not set"| echohl None + else + echon "vim-go: " | echohl Function | echon "current oracle scope: '". g:go_oracle_scope ."'" | echohl None + endif +endfunction + +function! go#oracle#Tags(...) + if a:0 + if a:0 == 1 && a:1 == '""' + unlet g:go_oracle_tags + echon "vim-go: " | echohl Function | echon "oracle tags is cleared"| echohl None + else + let g:go_oracle_tags = a:1 + echon "vim-go: " | echohl Function | echon "oracle tags changed to: '". g:go_oracle_tags ."'" | echohl None + endif + + return + endif + + if !exists('g:go_oracle_tags') + echon "vim-go: " | echohl Function | echon "oracle tags is not set"| echohl None + else + echon "vim-go: " | echohl Function | echon "current oracle tags: '". g:go_oracle_tags ."'" | echohl None + endif +endfunction + +" Show 'implements' relation for selected package +function! go#oracle#Implements(selected) + let out = s:RunOracle('implements', a:selected, 0) + call s:loclistSecond(out) +endfunction + +" Describe selected syntax: definition, methods, etc +function! go#oracle#Describe(selected) + let out = s:RunOracle('describe', a:selected, 0) + call s:loclistSecond(out) +endfunction + +" Show possible targets of selected function call +function! go#oracle#Callees(selected) + let out = s:RunOracle('callees', a:selected, 1) + call s:loclistSecond(out) +endfunction + +" Show possible callers of selected function +function! go#oracle#Callers(selected) + let out = s:RunOracle('callers', a:selected, 1) + call s:loclistSecond(out) +endfunction + +" Show path from callgraph root to selected function +function! go#oracle#Callstack(selected) + let out = s:RunOracle('callstack', a:selected, 1) + call s:loclistSecond(out) +endfunction + +" Show free variables of selection +function! go#oracle#Freevars(selected) + " Freevars requires a selection + if a:selected == -1 + echon "vim-go: " | echohl Statement | echon "GoFreevars requires a selection (range) of code "| echohl None + return + endif + + let out = s:RunOracle('freevars', a:selected, 0) + call s:loclistSecond(out) +endfunction + +" Show send/receive corresponding to selected channel op +function! go#oracle#ChannelPeers(selected) + let out = s:RunOracle('peers', a:selected, 1) + call s:loclistSecond(out) +endfunction + +" Show all refs to entity denoted by selected identifier +function! go#oracle#Referrers(selected) + let out = s:RunOracle('referrers', a:selected, 0) + call s:loclistSecond(out) +endfunction + +" vim:ts=4:sw=4:et +" diff --git a/autoload/go/package.vim b/autoload/go/package.vim new file mode 100644 index 0000000..32b38ae --- /dev/null +++ b/autoload/go/package.vim @@ -0,0 +1,160 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" This file provides a utility function that performs auto-completion of +" package names, for use by other commands. + +let s:goos = $GOOS +let s:goarch = $GOARCH + +if len(s:goos) == 0 + if exists('g:golang_goos') + let s:goos = g:golang_goos + elseif has('win32') || has('win64') + let s:goos = 'windows' + elseif has('macunix') + let s:goos = 'darwin' + else + let s:goos = '*' + endif +endif + +if len(s:goarch) == 0 + if exists('g:golang_goarch') + let s:goarch = g:golang_goarch + else + let s:goarch = '*' + endif +endif + +function! go#package#Paths() + let dirs = [] + + if !exists("s:goroot") + if executable('go') + let s:goroot = substitute(system('go env GOROOT'), '\n', '', 'g') + if v:shell_error + echomsg '''go env GOROOT'' failed' + endif + else + let s:goroot = $GOROOT + endif + endif + + if len(s:goroot) != 0 && isdirectory(s:goroot) + let dirs += [s:goroot] + endif + + let workspaces = split($GOPATH, go#util#PathListSep()) + if workspaces != [] + let dirs += workspaces + endif + + return dirs +endfunction + +function! go#package#ImportPath(arg) + let path = fnamemodify(resolve(a:arg), ':p') + let dirs = go#package#Paths() + + for dir in dirs + if len(dir) && match(path, dir) == 0 + let workspace = dir + endif + endfor + + if !exists('workspace') + return -1 + endif + + let srcdir = substitute(workspace . '/src/', '//', '/', '') + return substitute(path, srcdir, '', '') +endfunction + +function! go#package#FromPath(arg) + let path = fnamemodify(resolve(a:arg), ':p') + let dirs = go#package#Paths() + + for dir in dirs + if len(dir) && match(path, dir) == 0 + let workspace = dir + endif + endfor + + if !exists('workspace') + return -1 + endif + + if isdirectory(path) + return substitute(path, workspace . 'src/', '', '') + else + return substitute(substitute(path, workspace . 'src/', '', ''), + \ '/' . fnamemodify(path, ':t'), '', '') + endif +endfunction + +function! go#package#CompleteMembers(package, member) + silent! let content = system('godoc ' . a:package) + if v:shell_error || !len(content) + return [] + endif + let lines = filter(split(content, "\n"),"v:val !~ '^\\s\\+$'") + try + let mx1 = '^\s\+\(\S+\)\s\+=\s\+.*' + let mx2 = '^\%(const\|var\|type\|func\) \([A-Z][^ (]\+\).*' + let candidates = map(filter(copy(lines), 'v:val =~ mx1'), + \ 'substitute(v:val, mx1, "\\1", "")') + \ + map(filter(copy(lines), 'v:val =~ mx2'), + \ 'substitute(v:val, mx2, "\\1", "")') + return filter(candidates, '!stridx(v:val, a:member)') + catch + return [] + endtry +endfunction + +function! go#package#Complete(ArgLead, CmdLine, CursorPos) + let words = split(a:CmdLine, '\s\+', 1) + + " do not complete package members for these commands + let neglect_commands = ["GoImportAs", "GoOracleScope"] + + if len(words) > 2 && index(neglect_commands, words[0]) == -1 + " Complete package members + return go#package#CompleteMembers(words[1], words[2]) + endif + + let dirs = go#package#Paths() + + if len(dirs) == 0 + " should not happen + return [] + endif + + let ret = {} + for dir in dirs + " this may expand to multiple lines + let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n") + call add(root, expand(dir . '/src')) + for r in root + for i in split(globpath(r, a:ArgLead.'*'), "\n") + if isdirectory(i) + let i .= '/' + elseif i !~ '\.a$' + continue + endif + let i = substitute(substitute(i[len(r)+1:], '[\\]', '/', 'g'), + \ '\.a$', '', 'g') + + " without this the result can have duplicates in form of + " 'encoding/json' and '/encoding/json/' + let i = go#util#StripPathSep(i) + + let ret[i] = i + endfor + endfor + endfor + return sort(keys(ret)) +endfunction + +" vim:sw=4:et diff --git a/autoload/go/path.vim b/autoload/go/path.vim new file mode 100644 index 0000000..295e103 --- /dev/null +++ b/autoload/go/path.vim @@ -0,0 +1,174 @@ +" initial_go_path is used to store the initial GOPATH that was set when Vim +" was started. It's used with :GoPathClear to restore the GOPATH when the user +" changed it explicitly via :GoPath. Initially it's empty. It's being set when +" :GoPath is used +let s:initial_go_path = "" + +" GoPath sets or returns the current GOPATH. If no arguments are passed it +" echoes the current GOPATH, if an argument is passed it replaces the current +" GOPATH with it. If two double quotes are passed (the empty string in go), +" it'll clear the GOPATH and will restore to the initial GOPATH. +function! go#path#GoPath(...) + " we have an argument, replace GOPATH + if len(a:000) + " clears the current manually set GOPATH and restores it to the + " initial GOPATH, which was set when Vim was started. + if len(a:000) == 1 && a:1 == '""' + if !empty(s:initial_go_path) + let $GOPATH = s:initial_go_path + let s:initial_go_path = "" + endif + + echon "vim-go: " | echohl Function | echon "GOPATH restored to ". $GOPATH | echohl None + return + endif + + echon "vim-go: " | echohl Function | echon "GOPATH changed to ". a:1 | echohl None + let s:initial_go_path = $GOPATH + let $GOPATH = a:1 + return + endif + + echo go#path#Detect() +endfunction + +" Default returns the default GOPATH. If there is a single GOPATH it returns +" it. For multiple GOPATHS separated with a the OS specific separator, only +" the first one is returned +function! go#path#Default() + let go_paths = split($GOPATH, go#util#PathListSep()) + + if len(go_paths) == 1 + return $GOPATH + endif + + return go_paths[0] +endfunction + +" HasPath checks whether the given path exists in GOPATH environment variable +" or not +function! go#path#HasPath(path) + let go_paths = split($GOPATH, go#util#PathListSep()) + let last_char = strlen(a:path) - 1 + + " check cases of '/foo/bar/' and '/foo/bar' + if a:path[last_char] == go#util#PathSep() + let withSep = a:path + let noSep = strpart(a:path, 0, last_char) + else + let withSep = a:path . go#util#PathSep() + let noSep = a:path + endif + + let hasA = index(go_paths, withSep) != -1 + let hasB = index(go_paths, noSep) != -1 + return hasA || hasB +endfunction + +" Detect returns the current GOPATH. If a package manager is used, such as +" Godeps, GB, it will modify the GOPATH so those directories take precedence +" over the current GOPATH. It also detects diretories whose are outside +" GOPATH. +function! go#path#Detect() + let gopath = $GOPATH + + " don't lookup for godeps if autodetect is disabled. + if !get(g:, "go_autodetect_gopath", 1) + return gopath + endif + + let current_dir = fnameescape(expand('%:p:h')) + + " TODO(arslan): this should be changed so folders or files should be + " fetched from a customizable list. The user should define any new package + " management tool by it's own. + + " src folder outside $GOPATH + let src_root = finddir("src", current_dir .";") + if !empty(src_root) + let src_path = fnamemodify(src_root, ':p:h:h') . go#util#PathSep() + + " gb vendor plugin + " (https://github.com/constabulary/gb/tree/master/cmd/gb-vendor) + let gb_vendor_root = src_path . "vendor" . go#util#PathSep() + if isdirectory(gb_vendor_root) && !go#path#HasPath(gb_vendor_root) + let gopath = gb_vendor_root . go#util#PathListSep() . gopath + endif + + if !go#path#HasPath(src_path) + let gopath = src_path . go#util#PathListSep() . gopath + endif + endif + + " Godeps + let godeps_root = finddir("Godeps", current_dir .";") + if !empty(godeps_root) + let godeps_path = join([fnamemodify(godeps_root, ':p:h:h'), "Godeps", "_workspace" ], go#util#PathSep()) + + if !go#path#HasPath(godeps_path) + let gopath = godeps_path . go#util#PathListSep() . gopath + endif + endif + + return gopath +endfunction + + +" BinPath returns the binary path of installed go tools. +function! go#path#BinPath() + let bin_path = "" + + " check if our global custom path is set, if not check if $GOBIN is set so + " we can use it, otherwise use $GOPATH + '/bin' + if exists("g:go_bin_path") + let bin_path = g:go_bin_path + elseif $GOBIN != "" + let bin_path = $GOBIN + elseif $GOPATH != "" + let bin_path = expand(go#path#Default() . "/bin/") + else + " could not find anything + endif + + return bin_path +endfunction + +" CheckBinPath checks whether the given binary exists or not and returns the +" path of the binary. It returns an empty string doesn't exists. +function! go#path#CheckBinPath(binpath) + " remove whitespaces if user applied something like 'goimports ' + let binpath = substitute(a:binpath, '^\s*\(.\{-}\)\s*$', '\1', '') + + " if it's in PATH just return it + if executable(binpath) + return binpath + endif + + " just get the basename + let basename = fnamemodify(binpath, ":t") + + " check if we have an appropriate bin_path + let go_bin_path = go#path#BinPath() + if empty(go_bin_path) + echo "vim-go: could not find '" . basename . "'. Run :GoInstallBinaries to fix it." + return "" + endif + + " append our GOBIN and GOPATH paths and be sure they can be found there... + " let us search in our GOBIN and GOPATH paths + let old_path = $PATH + let $PATH = $PATH . go#util#PathListSep() .go_bin_path + + if !executable(basename) + echo "vim-go: could not find '" . basename . "'. Run :GoInstallBinaries to fix it." + " restore back! + let $PATH = old_path + return "" + endif + + let $PATH = old_path + + return go_bin_path . go#util#PathSep() . basename +endfunction + +" vim:ts=4:sw=4:et diff --git a/autoload/go/play.vim b/autoload/go/play.vim new file mode 100644 index 0000000..4b26fc1 --- /dev/null +++ b/autoload/go/play.vim @@ -0,0 +1,94 @@ +if !exists("g:go_play_open_browser") + let g:go_play_open_browser = 1 +endif + + +function! go#play#Share(count, line1, line2) + if !executable('curl') + echohl ErrorMsg | echomsg "vim-go: require 'curl' command" | echohl None + return + endif + + let content = join(getline(a:line1, a:line2), "\n") + let share_file = tempname() + call writefile(split(content, "\n"), share_file, "b") + + let command = "curl -s -X POST http://play.golang.org/share --data-binary '@".share_file."'" + let snippet_id = system(command) + + " we can remove the temp file because it's now posted. + call delete(share_file) + + if v:shell_error + echo 'A error has occured. Run this command to see what the problem is:' + echo command + return + endif + + let url = "http://play.golang.org/p/".snippet_id + + " copy to clipboard + if has('unix') && !has('xterm_clipboard') && !has('clipboard') + let @" = url + else + let @+ = url + endif + + if g:go_play_open_browser != 0 + call go#tool#OpenBrowser(url) + endif + + echo "vim-go: snippet uploaded: ".url +endfunction + + +function! s:get_visual_content() + let save_regcont = @" + let save_regtype = getregtype('"') + silent! normal! gvy + let content = @" + call setreg('"', save_regcont, save_regtype) + return content +endfunction + +" modified version of +" http://stackoverflow.com/questions/1533565/how-to-get-visually-selected-text-in-vimscript +" another function that returns the content of visual selection, it's not used +" but might be useful in the future +function! s:get_visual_selection() + let [lnum1, col1] = getpos("'<")[1:2] + let [lnum2, col2] = getpos("'>")[1:2] + + " check if the the visual mode is used before + if lnum1 == 0 || lnum2 == 0 || col1 == 0 || col2 == 0 + return + endif + + let lines = getline(lnum1, lnum2) + let lines[-1] = lines[-1][: col2 - (&selection == 'inclusive' ? 1 : 2)] + let lines[0] = lines[0][col1 - 1:] + return join(lines, "\n") +endfunction + +" following two functions are from: https://github.com/mattn/gist-vim +" thanks @mattn +function! s:get_browser_command() + let go_play_browser_command = get(g:, 'go_play_browser_command', '') + if go_play_browser_command == '' + if has('win32') || has('win64') + let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%' + elseif has('mac') || has('macunix') || has('gui_macvim') || system('uname') =~? '^darwin' + let go_play_browser_command = 'open %URL%' + elseif executable('xdg-open') + let go_play_browser_command = 'xdg-open %URL%' + elseif executable('firefox') + let go_play_browser_command = 'firefox %URL% &' + else + let go_play_browser_command = '' + endif + endif + return go_play_browser_command +endfunction + + +" vim:ts=4:sw=4:et diff --git a/autoload/go/rename.vim b/autoload/go/rename.vim new file mode 100644 index 0000000..98c5fa3 --- /dev/null +++ b/autoload/go/rename.vim @@ -0,0 +1,69 @@ +if !exists("g:go_gorename_bin") + let g:go_gorename_bin = "gorename" +endif + +if !exists("g:go_gorename_prefill") + let g:go_gorename_prefill = 1 +endif + +function! go#rename#Rename(bang, ...) + let to = "" + if a:0 == 0 + let from = expand("") + let ask = printf("vim-go: rename '%s' to: ", from) + if g:go_gorename_prefill + let to = input(ask, from) + else + let to = input(ask) + endif + redraw! + if empty(to) + return + endif + else + let to = a:1 + endif + + "return with a warning if the bin doesn't exist + let bin_path = go#path#CheckBinPath(g:go_gorename_bin) + if empty(bin_path) + return + endif + + let fname = expand('%:p') + let pos = go#util#OffsetCursor() + let cmd = printf('%s -offset %s -to %s', shellescape(bin_path), shellescape(printf('%s:#%d', fname, pos)), shellescape(to)) + + let out = go#tool#ExecuteInDir(cmd) + + " strip out newline on the end that gorename puts. If we don't remove, it + " will trigger the 'Hit ENTER to continue' prompt + let clean = split(out, '\n') + + let l:listtype = "quickfix" + if v:shell_error + let errors = go#tool#ParseErrors(split(out, '\n')) + call go#list#Populate(l:listtype, errors) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + elseif empty(errors) + " failed to parse errors, output the original content + call go#util#EchoError(out) + endif + return + else + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + redraw | echon "vim-go: " | echohl Function | echon clean[0] | echohl None + endif + + " refresh the buffer so we can see the new content + " TODO(arslan): also find all other buffers and refresh them too. For this + " we need a way to get the list of changes from gorename upon an success + " change. + silent execute ":e" +endfunction + +" vim:ts=4:sw=4:et +" diff --git a/autoload/go/term.vim b/autoload/go/term.vim new file mode 100644 index 0000000..693041e --- /dev/null +++ b/autoload/go/term.vim @@ -0,0 +1,128 @@ +if has('nvim') && !exists("g:go_term_mode") + let g:go_term_mode = 'vsplit' +endif + +" s:jobs is a global reference to all jobs started with new() +let s:jobs = {} + +" new creates a new terminal with the given command. Mode is set based on the +" global variable g:go_term_mode, which is by default set to :vsplit +function! go#term#new(bang, cmd) + return go#term#newmode(a:bang, a:cmd, g:go_term_mode) +endfunction + +" new creates a new terminal with the given command and window mode. +function! go#term#newmode(bang, cmd, mode) + let mode = a:mode + if empty(mode) + let mode = g:go_term_mode + endif + + " modify GOPATH if needed + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + " execute go build in the files directory + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + + execute cd . fnameescape(expand("%:p:h")) + + execute mode.' __go_term__' + + setlocal filetype=goterm + setlocal bufhidden=delete + setlocal winfixheight + setlocal noswapfile + setlocal nobuflisted + + let job = { + \ 'stderr' : [], + \ 'stdout' : [], + \ 'bang' : a:bang, + \ 'on_stdout': function('s:on_stdout'), + \ 'on_stderr': function('s:on_stderr'), + \ 'on_exit' : function('s:on_exit'), + \ } + + let id = termopen(a:cmd, job) + + execute cd . fnameescape(dir) + + " restore back GOPATH + let $GOPATH = old_gopath + + let job.id = id + startinsert + + " resize new term if needed. + let height = get(g:, 'go_term_height', winheight(0)) + let width = get(g:, 'go_term_width', winwidth(0)) + + " we are careful how to resize. for example it's vertical we don't change + " the height. The below command resizes the buffer + if a:mode == "split" + exe 'resize ' . height + elseif a:mode == "vertical" + exe 'vertical resize ' . width + endif + + " we also need to resize the pty, so there you go... + call jobresize(id, width, height) + + let s:jobs[id] = job + return id +endfunction + +function! s:on_stdout(job_id, data) + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + call extend(job.stdout, a:data) +endfunction + +function! s:on_stderr(job_id, data) + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + call extend(job.stderr, a:data) +endfunction + +function! s:on_exit(job_id, data) + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + let l:listtype = "locationlist" + " usually there is always output so never branch into this clause + if empty(job.stdout) + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + else + let errors = go#tool#ParseErrors(job.stdout) + let errors = go#tool#FilterValids(errors) + if !empty(errors) + " close terminal we don't need it + close + + call go#list#Populate(l:listtype, errors) + call go#list#Window(l:listtype, len(errors)) + if !self.bang + call go#list#JumpToFirst(l:listtype) + endif + else + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + endif + + endif + + unlet s:jobs[a:job_id] +endfunction + +" vim:ts=4:sw=4:et diff --git a/autoload/go/textobj.vim b/autoload/go/textobj.vim new file mode 100644 index 0000000..a8e91a2 --- /dev/null +++ b/autoload/go/textobj.vim @@ -0,0 +1,180 @@ +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 + +" ( ) motions +" { } motions +" s for sentence +" p for parapgrah +" < > +" t for tag + +function! go#textobj#Function(mode) + let offset = go#util#OffsetCursor() + + let fname = expand("%:p") + if &modified + " Write current unsaved buffer to a temp file and use the modified content + let l:tmpname = tempname() + call writefile(getline(1, '$'), 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 = system(command) + if v:shell_error != 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) + 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 + +function! go#textobj#FunctionJump(mode, direction) + " 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 offset = go#util#OffsetCursor() + + let fname = expand("%:p") + if &modified + " Write current unsaved buffer to a temp file and use the modified content + let l:tmpname = tempname() + call writefile(getline(1, '$'), 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 ' . l: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 = system(command) + if v:shell_error != 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 + + " 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:ts=2:sw=2:et diff --git a/autoload/go/tool.vim b/autoload/go/tool.vim new file mode 100644 index 0000000..d378b5a --- /dev/null +++ b/autoload/go/tool.vim @@ -0,0 +1,182 @@ +function! go#tool#Files() + if go#util#IsWin() + let command = 'go list -f "{{range $f := .GoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}"' + else + let command = "go list -f '{{range $f := .GoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}'" + endif + let out = go#tool#ExecuteInDir(command) + return split(out, '\n') +endfunction + +function! go#tool#Deps() + if go#util#IsWin() + let command = 'go list -f "{{range $f := .Deps}}{{$f}}{{printf \"\n\"}}{{end}}"' + else + let command = "go list -f $'{{range $f := .Deps}}{{$f}}\n{{end}}'" + endif + let out = go#tool#ExecuteInDir(command) + return split(out, '\n') +endfunction + +function! go#tool#Imports() + let imports = {} + if go#util#IsWin() + let command = 'go list -f "{{range $f := .Imports}}{{$f}}{{printf \"\n\"}}{{end}}"' + else + let command = "go list -f $'{{range $f := .Imports}}{{$f}}\n{{end}}'" + endif + let out = go#tool#ExecuteInDir(command) + if v:shell_error + echo out + return imports + endif + + for package_path in split(out, '\n') + let cmd = "go list -f {{.Name}} " . package_path + let package_name = substitute(go#tool#ExecuteInDir(cmd), '\n$', '', '') + let imports[package_name] = package_path + endfor + + return imports +endfunction + +function! go#tool#ParseErrors(lines) + let errors = [] + + for line in a:lines + let fatalerrors = matchlist(line, '^\(fatal error:.*\)$') + let tokens = matchlist(line, '^\s*\(.\{-}\):\(\d\+\):\s*\(.*\)') + + if !empty(fatalerrors) + call add(errors, {"text": fatalerrors[1]}) + elseif !empty(tokens) + " strip endlines of form ^M + let out=substitute(tokens[3], '\r$', '', '') + + call add(errors, { + \ "filename" : fnamemodify(tokens[1], ':p'), + \ "lnum" : tokens[2], + \ "text" : out, + \ }) + elseif !empty(errors) + " Preserve indented lines. + " This comes up especially with multi-line test output. + if match(line, '^\s') >= 0 + call add(errors, {"text": line}) + endif + endif + endfor + + return errors +endfunction + +"FilterValids filters the given items with only items that have a valid +"filename. Any non valid filename is filtered out. +function! go#tool#FilterValids(items) + " Remove any nonvalid filename from the location list to avoid opening an + " empty buffer. See https://github.com/fatih/vim-go/issues/287 for + " details. + let filtered = [] + let is_readable = {} + + for item in a:items + if has_key(item, 'bufnr') + let filename = bufname(item.bufnr) + elseif has_key(item, 'filename') + let filename = item.filename + else + " nothing to do, add item back to the list + call add(filtered, item) + continue + endif + + if !has_key(is_readable, filename) + let is_readable[filename] = filereadable(filename) + endif + if is_readable[filename] + call add(filtered, item) + endif + endfor + + for k in keys(filter(is_readable, '!v:val')) + echo "vim-go: " | echohl Identifier | echon "[run] Dropped " | echohl Constant | echon '"' . k . '"' + echohl Identifier | echon " from location list (nonvalid filename)" | echohl None + endfor + + return filtered +endfunction + +function! go#tool#ExecuteInDir(cmd) abort + let old_gopath = $GOPATH + let $GOPATH = go#path#Detect() + + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd . fnameescape(expand("%:p:h")) + let out = system(a:cmd) + finally + execute cd . fnameescape(dir) + endtry + + let $GOPATH = old_gopath + return out +endfunction + +" Exists checks whether the given importpath exists or not. It returns 0 if +" the importpath exists under GOPATH. +function! go#tool#Exists(importpath) + let command = "go list ". a:importpath + let out = go#tool#ExecuteInDir(command) + + if v:shell_error + return -1 + endif + + return 0 +endfunction + + +" following two functions are from: https://github.com/mattn/gist-vim +" thanks @mattn +function! s:get_browser_command() + let go_play_browser_command = get(g:, 'go_play_browser_command', '') + if go_play_browser_command == '' + if go#util#IsWin() + let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%' + elseif has('mac') || has('macunix') || has('gui_macvim') || system('uname') =~? '^darwin' + let go_play_browser_command = 'open %URL%' + elseif executable('xdg-open') + let go_play_browser_command = 'xdg-open %URL%' + elseif executable('firefox') + let go_play_browser_command = 'firefox %URL% &' + else + let go_play_browser_command = '' + endif + endif + return go_play_browser_command +endfunction + +function! go#tool#OpenBrowser(url) + let cmd = s:get_browser_command() + if len(cmd) == 0 + redraw + echohl WarningMsg + echo "It seems that you don't have general web browser. Open URL below." + echohl None + echo a:url + return + endif + if cmd =~ '^!' + let cmd = substitute(cmd, '%URL%', '\=shellescape(a:url)', 'g') + silent! exec cmd + elseif cmd =~ '^:[A-Z]' + let cmd = substitute(cmd, '%URL%', '\=a:url', 'g') + exec cmd + else + let cmd = substitute(cmd, '%URL%', '\=shellescape(a:url)', 'g') + call system(cmd) + endif +endfunction + +" vim:ts=4:sw=4:et diff --git a/autoload/go/ui.vim b/autoload/go/ui.vim new file mode 100644 index 0000000..c658141 --- /dev/null +++ b/autoload/go/ui.vim @@ -0,0 +1,89 @@ +let s:buf_nr = -1 + +"OpenWindow opens a new scratch window and put's the content into the window +function! go#ui#OpenWindow(title, content) + " reuse existing buffer window if it exists otherwise create a new one + if !bufexists(s:buf_nr) + execute 'botright new' + file `="[" . a:title . "]"` + let s:buf_nr = bufnr('%') + elseif bufwinnr(s:buf_nr) == -1 + execute 'botright new' + execute s:buf_nr . 'buffer' + elseif bufwinnr(s:buf_nr) != bufwinnr('%') + execute bufwinnr(s:buf_nr) . 'wincmd w' + endif + + + " Keep minimum height to 10, if there is more just increase it that it + " occupies all results + let buffer_height = 10 + if len(a:content) < buffer_height + exe 'resize ' . buffer_height + else + exe 'resize ' . len(a:content) + endif + + " some sane default values for a readonly buffer + setlocal filetype=vimgo + setlocal bufhidden=delete + setlocal buftype=nofile + setlocal noswapfile + setlocal nobuflisted + setlocal winfixheight + setlocal cursorline " make it easy to distinguish + + " we need this to purge the buffer content + setlocal modifiable + + "delete everything first from the buffer + %delete _ + + " add the content + call append(0, a:content) + + " delete last line that comes from the append call + $delete _ + + " set it back to non modifiable + setlocal nomodifiable +endfunction + + +" CloseWindow closes the current window +function! go#ui#CloseWindow() + close + echo "" +endfunction + +" OpenDefinition parses the current line and jumps to it by openening a new +" tab +function! go#ui#OpenDefinition(filter) + let curline = getline('.') + + " don't touch our first line or any blank line + if curline =~ a:filter || curline =~ "^$" + " supress information about calling this function + echo "" + return + endif + + " format: 'interface file:lnum:coln' + let mx = '^\(^\S*\)\s*\(.\{-}\):\(\d\+\):\(\d\+\)' + + " parse it now into the list + let tokens = matchlist(curline, mx) + + " convert to: 'file:lnum:coln' + let expr = tokens[2] . ":" . tokens[3] . ":" . tokens[4] + + " jump to it in a new tab, we use explicit lgetexpr so we can later change + " the behaviour via settings (like opening in vsplit instead of tab) + lgetexpr expr + tab split + ll 1 + + " center the word + norm! zz +endfunction + diff --git a/autoload/go/util.vim b/autoload/go/util.vim new file mode 100644 index 0000000..24aa254 --- /dev/null +++ b/autoload/go/util.vim @@ -0,0 +1,123 @@ +" PathSep returns the appropriate OS specific path separator. +function! go#util#PathSep() + if go#util#IsWin() + return '\' + endif + return '/' +endfunction + +" PathListSep returns the appropriate OS specific path list separator. +function! go#util#PathListSep() + if go#util#IsWin() + return ";" + endif + return ":" +endfunction + +" LineEnding returns the correct line ending, based on the current fileformat +function! go#util#LineEnding() + if &fileformat == 'dos' + return "\r\n" + elseif &fileformat == 'mac' + return "\r" + endif + + return "\n" +endfunction + +" IsWin returns 1 if current OS is Windows or 0 otherwise +function! go#util#IsWin() + let win = ['win16', 'win32', 'win64', 'win95'] + for w in win + if (has(w)) + return 1 + endif + endfor + + return 0 +endfunction + +" StripPath strips the path's last character if it's a path separator. +" example: '/foo/bar/' -> '/foo/bar' +function! go#util#StripPathSep(path) + let last_char = strlen(a:path) - 1 + if a:path[last_char] == go#util#PathSep() + return strpart(a:path, 0, last_char) + endif + + return a:path +endfunction + +" StripTrailingSlash strips the trailing slash from the given path list. +" example: ['/foo/bar/'] -> ['/foo/bar'] +function! go#util#StripTrailingSlash(paths) + return map(copy(a:paths), 'go#util#StripPathSep(v:val)') +endfunction + +" Shelljoin returns a shell-safe string representation of arglist. The +" {special} argument of shellescape() may optionally be passed. +function! go#util#Shelljoin(arglist, ...) + try + let ssl_save = &shellslash + set noshellslash + if a:0 + return join(map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')'), ' ') + endif + + return join(map(copy(a:arglist), 'shellescape(v:val)'), ' ') + finally + let &shellslash = ssl_save + endtry +endfunction + +" Shelllist returns a shell-safe representation of the items in the given +" arglist. The {special} argument of shellescape() may optionally be passed. +function! go#util#Shelllist(arglist, ...) + try + let ssl_save = &shellslash + set noshellslash + if a:0 + return map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')') + endif + return map(copy(a:arglist), 'shellescape(v:val)') + finally + let &shellslash = ssl_save + endtry +endfunction + +" Returns the byte offset for line and column +function! go#util#Offset(line, col) + if &encoding != 'utf-8' + let sep = go#util#LineEnding() + let buf = a:line == 1 ? '' : (join(getline(1, a:line-1), sep) . sep) + let buf .= a:col == 1 ? '' : getline('.')[:a:col-2] + return len(iconv(buf, &encoding, 'utf-8')) + endif + return line2byte(a:line) + (a:col-2) +endfunction +" +" Returns the byte offset for the cursor +function! go#util#OffsetCursor() + return go#util#Offset(line('.'), col('.')) +endfunction + +" TODO(arslan): I couldn't parameterize the highlight types. Check if we can +" simplify the following functions + +function! go#util#EchoSuccess(msg) + redraws! | echon "vim-go: " | echohl Function | echon a:msg | echohl None +endfunction + +function! go#util#EchoError(msg) + redraws! | echon "vim-go: " | echohl ErrorMsg | echon a:msg | echohl None +endfunction + +function! go#util#EchoWarning(msg) + redraws! | echon "vim-go: " | echohl WarningMsg | echon a:msg | echohl None +endfunction + +function! go#util#EchoProgress(msg) + redraws! | echon "vim-go: " | echohl Identifier | echon a:msg | echohl None +endfunction + +" vim:ts=4:sw=4:et diff --git a/autoload/go/vimproc.vim b/autoload/go/vimproc.vim new file mode 100644 index 0000000..42d1392 --- /dev/null +++ b/autoload/go/vimproc.vim @@ -0,0 +1,21 @@ +"Check if has vimproc +function! go#vimproc#has_vimproc() + if !exists('g:go#use_vimproc') + if go#util#IsWin() + try + call vimproc#version() + let exists_vimproc = 1 + catch + let exists_vimproc = 0 + endtry + else + let exists_vimproc = 0 + endif + + let g:go#use_vimproc = exists_vimproc + endif + + return g:go#use_vimproc +endfunction + +" vim:ts=4:sw=4:et diff --git a/compiler/go.vim b/compiler/go.vim new file mode 100644 index 0000000..7b5a025 --- /dev/null +++ b/compiler/go.vim @@ -0,0 +1,41 @@ +" Copyright 2013 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" compiler/go.vim: Vim compiler file for Go. + +if exists("current_compiler") + finish +endif +let current_compiler = "go" + +if exists(":CompilerSet") != 2 + command -nargs=* CompilerSet setlocal +endif + +let s:save_cpo = &cpo +set cpo-=C +if filereadable("makefile") || filereadable("Makefile") + CompilerSet makeprg=make +else + CompilerSet makeprg=go\ build +endif + +" Define the patterns that will be recognized by QuickFix when parsing the +" output of Go command that use this errorforamt (when called make, cexpr or +" lmake, lexpr). This is the global errorformat, however some command might +" use a different output, for those we define them directly and modify the +" errorformat ourselves. More information at: +" http://vimdoc.sourceforge.net/htmldoc/quickfix.html#errorformat +CompilerSet errorformat =%-G#\ %.%# " Ignore lines beginning with '#' ('# command-line-arguments' line sometimes appears?) +CompilerSet errorformat+=%-G%.%#panic:\ %m " Ignore lines containing 'panic: message' +CompilerSet errorformat+=%Ecan\'t\ load\ package:\ %m " Start of multiline error string is 'can\'t load package' +CompilerSet errorformat+=%A%f:%l:%c:\ %m " Start of multiline unspecified string is 'filename:linenumber:columnnumber:' +CompilerSet errorformat+=%A%f:%l:\ %m " Start of multiline unspecified string is 'filename:linenumber:' +CompilerSet errorformat+=%C%*\\s%m " Continuation of multiline error message is indented +CompilerSet errorformat+=%-G%.%# " All lines not matching any of the above patterns are ignored + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:ts=4:sw=4:et diff --git a/doc/vim-go.txt b/doc/vim-go.txt new file mode 100644 index 0000000..08ad5bc --- /dev/null +++ b/doc/vim-go.txt @@ -0,0 +1,1159 @@ +*vim-go.txt* Go development plugin +*vim-go* + +=============================================================================== +# # +# ## ## #### ## ## ###### ####### # +# ## ## ## ### ### ## ## ## ## # +# ## ## ## #### #### ## ## ## # +# ## ## ## ## ### ## ####### ## #### ## ## # +# ## ## ## ## ## ## ## ## ## # +# ## ## ## ## ## ## ## ## ## # +# ### #### ## ## ###### ####### # +# # +=============================================================================== +CONTENTS *go-contents* + + 1. Intro........................................|go-intro| + 2. Install......................................|go-install| + 3. Commands.....................................|go-commands| + 4. Mappings.....................................|go-mappings| + 5. Text Objects.................................|go-text-objects| + 6. Functions....................................|go-functions| + 7. Settings.....................................|go-settings| + 8. Troubleshooting..............................|go-troubleshooting| + 9. Credits......................................|go-credits| + +=============================================================================== +INTRO *go-intro* + +Go (golang) support for Vim. vim-go installs automatically all necessary +binaries for providing seamless Vim integration. It comes with pre-defined +sensible settings (like auto gofmt on save), has autocomplete, snippet +support, improved syntax highlighting, go toolchain commands, etc... It's +highly customizable and each individual feature can be disabled/enabled +easily. + + * Improved Syntax highlighting with items such as Functions, Operators, Methods. + * Auto completion support via `gocode` + * Better `gofmt` on save, which keeps cursor position and doesn't break your undo + history + * Go to symbol/declaration with `:GoDef` + * Look up documentation with `:GoDoc` inside Vim or open it in browser + * Automatically import packages via `:GoImport` or plug it into autosave + * Compile your package with `:GoBuild`, install it with `:GoInstall` or test + them with `:GoTest` (also supports running single tests via `:GoTestFunc`) + * Quickly execute your current file/files with `:GoRun` + * Automatic `GOPATH` detection based on the directory structure (i.e. `gb` + projects, `godep` vendored projects) + * Change or display `GOPATH` with `:GoPath` + * Create a coverage profile and display annotated source code in browser to see + which functions are covered with `:GoCoverage` + * Call `gometalinter` with `:GoMetaLinter`, which invokes all possible linters + (golint, vet, errcheck, deadcode, etc..) and shows the warnings/errors + * Lint your code with `:GoLint` + * Run your code through `:GoVet` to catch static errors + * Advanced source analysis tools utilizing oracle, such as `:GoImplements`, + `:GoCallees`, and `:GoReferrers` + * Precise type-safe renaming of identifiers with `:GoRename` + * List all source files and dependencies + * Unchecked error checking with `:GoErrCheck` + * Integrated and improved snippets, supporting `ultisnips` or `neosnippet` + * Share your current code to [play.golang.org](http://play.golang.org) with `:GoPlay` + * On-the-fly type information about the word under the cursor. Plug it into + your custom vim function. + * Go asm formatting on save + * Tagbar support to show tags of the source code in a sidebar with `gotags` + * Custom vim text objects such as `a function` or `inner function` + * A async launcher for the go command is implemented for neovim, fully async + building and testing. + * Integrated with the neovim terminal, launch `:GoRun` and other go commands + in their own new terminal. + * Alternate between implementation and test code with `:GoAlternate` + +=============================================================================== +INSTALL *go-install* + +Vim-go follows the standard runtime path structure, so I highly recommend to use +a common and well known plugin manager to install vim-go. Do not use vim-go with +other Go plugins. For Pathogen just clone the repo, for other plugin managers +add the appropriate lines and execute the plugin's install command. + +* https://github.com/tpope/vim-pathogen > + + git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go +< + +* https://github.com/junegunn/vim-plug > + + Plug 'fatih/vim-go' + +< +* https://github.com/Shougo/neobundle.vim > + + NeoBundle 'fatih/vim-go' +< + +* https://github.com/gmarik/vundle > + + Plugin 'fatih/vim-go' + +< +* Manual > + + Copy all of the files into your `~/.vim` directory +< + +Please be sure all necessary binaries are installed (such as `gocode`, `godef`, +`goimports`, etc..). You can easily install them with the included +|GoInstallBinaries| command. If you invoke it, all necessary binaries will be +automatically downloaded and installed to your `$GOBIN` environment (if not set +it will use `$GOPATH/bin`). It requires `git` for fetching the individual Go +packages. + +* Autocompletion is enabled by default via ``, to get real-time +completion (completion by type) install: +https://github.com/Shougo/neocomplete.vim for Vim or +https://github.com/Shougo/deoplete.nvim and +https://github.com/zchee/deoplete-go for Neovim +* To get displayed source code tag informations on a sidebar install +https://github.com/majutsushi/tagbar. +* For snippet feature install: +https://github.com/Shougo/neosnippet.vim or +https://github.com/SirVer/ultisnips. +* For a better documentation viewer checkout: https://github.com/garyburd/go-explorer + +=============================================================================== +COMMANDS *go-commands* + + *:GoPath* +:GoPath [path] + + GoPath sets and overrides GOPATH with the given {path}. If no {path} is + given it shows the current GOPATH. If `""` is given as path, it clears + current `GOPATH` which was set with |GoPath| and restores `GOPATH` back to + the initial value which was sourced when Vim was started. + + *:GoImport* +:GoImport[!] [path] + + Import ensures that the provided package {path} is imported in the current + Go buffer, using proper style and ordering. If {path} is already being + imported, an error will be displayed and the buffer will be untouched. + + If [!] is given it will download the package with `go get` + + *:GoImportAs* +:GoImportAs [localname] [path] + + Same as Import, but uses a custom local name for the package. + + *:GoDrop* +:GoDrop [path] + + Remove the import line for the provided package {path}, if present in the + current Go buffer. If {path} is not being imported, an error will be + displayed and the buffer will be untouched. + + *:GoLint* +:GoLint [packages] + + Run golint for the current Go file, or for given packages. + + *:GoDoc* +:GoDoc [word] + + Open the relevant GoDoc in split window for either the word[s] passed to + the command or by default, the word under the cursor. + + *:GoDocBrowser* +:GoDocBrowser [word] + + Open the relevant GoDoc in browser for either the word[s] passed to the + command or by default, the word under the cursor. + + *:GoFmt* +:GoFmt + + Filter the current Go buffer through gofmt. It tries to preserve cursor + position and avoids replacing the buffer with stderr output. + + *:GoImports* +:GoImports + + Filter the current Go buffer through goimports (needs to be installed). + `goimports` automatically discards/add import path based on the code. Like + |GoFmt|, It tries to preserve cursor position and avoids replacing the + buffer with stderr output. + + *:GoPlay* +:[range]GoPlay + + Share snippet to play.golang.org. If no [range] is given it shares + the whole file, otherwise the selected lines are shared. Snippet URL + is copied to system clipboard if Vim is compiled with 'clipboard' or + 'xterm-clipboard' otherwise it's get yanked into the `""` register. + + *:GoVet* +:GoVet[!] [options] + + Run `go vet` for the directory under your current file. Vet examines Go + source code and reports suspicious constructs, such as Printf calls whose + arguments do not align with the format string. Vet uses heuristics that do not + guarantee all reports are genuine problems, but it can find errors not caught + by the compilers. + + You may optionally pass any valid go tool vet flags/options. In this case, + `go tool vet` is run in place of `go vet`. For a full list please see + `go tool vet -h`. + + If [!] is not given the first error is jumped to. + + *:GoDef* +:GoDef [identifier] + + Goto declaration/definition for the given [identifier]. If no argument is + given, it will jump to the declaration under the cursor. By default the + mapping `gd` is enabled to invoke GoDef for the identifier under the cursor. + See |g:go_def_mapping_enabled| to disable it. + + *:GoRun* +:GoRun[!] [expand] + + Build and run your current main package. By default all main files for the + current file is used. If an argument is passed, 'expand' is used as file + selector. For example use `:GoRun %` to select the current file only. + + You may optionally pass any valid go run flags/options. For a full list + please see `go help run`. + + If [!] is not given the first error is jumped to. + + If using neovim then `:GoRun` will run in a new terminal according to + |g:go_term_mode|. + + *:GoBuild* +:GoBuild[!] [expand] + + Build your package with `go build`. Errors are populated in the quickfix + window. It automatically builds only the files that depends on the current + file. `:GoBuild` doesn't produce a result file. + Use 'make' to create a result file. + + You may optionally pass any valid go build flags/options. For a full list + please see `go help build`. Options are expanded with 'expand'. + + If [!] is not given the first error is jumped to. + + If using neovim then this command is fully async, it does not block the + UI. + + *:GoGenerate* +:GoGenerate[!] [expand] + + Creates or updates your auto-generated source files by running `go + generate`. + + You may optionally pass any valid go generate flags/options. For a full list + please see `go help generate`. Options are expanded with 'expand'. + + If [!] is not given the first error is jumped to. + + *:GoInfo* +:GoInfo + Show type information about the identifier under the cursor. For example + putting it above a function call is going to show the full function + signature. It uses gocode to get the type informations. + + + *:GoInstall* +:GoInstall[!] [options] + + Install your package with `go install`. + + You may optionally pass any valid go install flags/options. For a full list + please see `go help install`. + + If [!] is not given the first error is jumped to. + + *:GoTest* +:GoTest[!] [expand] + + Run the tests on your _test.go files via in your current directory. Errors + are populated in the quickfix window. If an argument is passed, 'expand' + is used as file selector (useful for cases like `:GoTest ./...`). + + You may optionally pass any valid go test flags/options. For a full list + please see `go help test`. + + GoTest timesout automatically after 10 seconds. To customize the timeout + use |g:go_test_timeout|. This feature is disabled if any arguments are + passed to the `:GoTest` command. + + If [!] is not given the first error is jumped to. + + If using neovim `:GoTest` will run in a new terminal or run asynchronously + in the background according to |g:go_term_enabled|. You can set the mode of + the new terminal with |g:go_term_mode|. + + *:GoTestFunc* +:GoTestFunc[!] [expand] + + Runs :GoTest, but only on the single test function immediate to your + cursor using 'go test's '-run' flag. + + Lookup is done starting at the cursor (including that line) moving up till + a matching `func Test` pattern is found or top of file is reached. Search + will not wrap around when at the top of the file. + + If [!] is not given the first error is jumped to. + + If using neovim `:GoTestFunc` will run in a new terminal or run asynchronously + in the background according to |g:go_term_enabled|. You can set the mode of + the new terminal with |g:go_term_mode|. + + *:GoTestCompile* +:GoTestCompile[!] [expand] + + Compile your _test.go files via in your current directory. Errors are + populated in the quickfix window. If an argument is passed, 'expand' is + used as file selector (useful for cases like `:GoTest ./...`). Useful to + not run the tests and capture/fix errors before running the tests or to + create test binary. + + If [!] is not given the first error is jumped to. + + If using neovim `:GoTestCompile` will run in a new terminal or run asynchronously + in the background according to |g:go_term_enabled|. You can set the mode of + the new terminal with |g:go_term_mode|. + + *:GoCoverage* +:GoCoverage[!] [options] + + Create a coverage profile and open a browser to display the annotated + source code of the current package. + + You may optionally pass any valid go test flags/options, such as + `-covermode set,count,atomic`. For a full list please see `go help test`. + + If [!] is not given the first error is jumped to. + + *:GoErrCheck* +:GoErrCheck [options] + + Check for unchecked errors in you current package. Errors are populated in + the quickfix window. + + You may optionally pass any valid errcheck flags/options. For a full list + please see `errcheck -h`. + + *:GoFiles* +:GoFiles + + Show source files that depends for the current package + + *:GoDeps* +:GoDeps + + Show dependencies for the current package + + *:GoInstallBinaries* +:GoInstallBinaries + + Download and Install all necessary Go tool binaries such as `godef`, + `goimports`, `gocode`, etc.. under `g:go_bin_path` + + *:GoUpdateBinaries* +:GoUpdateBinaries + + Download and Update previously installed Go tool binaries such as `godef`, + `goimports`, `gocode`, etc.. under `g:go_bin_path`. This can be used to + update the necessary Go binaries. + + *:GoImplements* +:GoImplements + + Show 'implements' relation for a selected package. A list of interfaces + for the type that implements an interface under the cursor (or selected + package) is shown in a location list. + *:GoRename* +:GoRename[!] [to] + + Rename the identifier under the cursor to the desired new name. If no + argument is given a prompt will ask for the desired identifier. + + If [!] is not given the first error is jumped to. + + + *:GoOracleScope* +:GoOracleScope [path1] [path2] ... + + Changes the custom |g:go_oracle_scope| setting and overrides it with the + given import paths. The custom scope is cleared (unset) if `""` is given + as the only path. If no arguments is given it prints the current custom + scope. + + *:GoCallees* +:GoCallees + + Show 'callees' relation for a selected package. A list of possible call + targets for the type under the cursor (or selected package) is shown in a + location list. + + *:GoCallers* +:GoCallers + + Show 'callers' relation for a selected function. A list of possible + callers for the selected function under the cursor is shown in a location + list. + + *:GoDescribe* +:GoDescribe + + Shows various properties of the selected syntax: its syntactic kind, its + type (for an expression), its value (for a constant expression), its size, + alignment, method set and interfaces (for a type), its declaration (for an + identifier), etc. Almost any piece of syntax may be described, and the + oracle will try to print all the useful information it can. + + *:GoCallstack* +:GoCallstack + + Shows 'callstack' relation for the selected function. An arbitrary path + from the root of the callgraph to the selected function is shown in a + location list. This may be useful to understand how the function is + reached in a given program. + + *:GoFreevars* +:GoFreevars + + Enumerates the free variables of the selection. “Free variables” is a + technical term meaning the set of variables that are referenced but not + defined within the selection, or loosely speaking, its inputs. + + This information is useful if you’re considering whether to refactor the + selection into a function of its own, as the free variables would be the + necessary parameters of that function. It’s also useful when you want to + understand what the inputs are to a complex block of code even if you + don’t plan to change it. + + *:GoChannelPeers* +:GoChannelPeers + + Shows the set of possible sends/receives on the channel operand of the + selected send or receive operation; the selection must be a <- token. + + For example, visually select a channel operand in the form of: + + "done <- true" + + and call |GoChannelPeers| on it. It will show where it was allocated, and + the sending and receiving endings. + + *:GoReferrers* +:GoReferrers + + The referrers query shows the set of identifiers that refer to the same + object as does the selected identifier, within any package in the analysis + scope. + + *:GoMetaLinter* +:GoMetaLinter [path] + + Calls the underlying `gometalinter` tool and displays all warnings and + errors in the quickfix window. By default the following linters are + enabled: "'vet', 'golint', 'errcheck'". This can be changed with the + |g:go_metalinter_enabled| variable. To override the command completely use + the variable |g:go_metalinter_command|. To override the maximum linters + execution time use |g:go_metalinter_deadline| variable. + + *:GoOracleTags* +:GoOracleTags [tags] + + Changes the custom |g:go_oracle_tags| setting and overrides it with the + given build tags. This command cooperate with GoReferrers command when + there exist mulitiple build tags in your project, then you can set one + of the build tags for GoReferrers to find more accurate. + The custom build tags is cleared (unset) if `""` is given. If no arguments + is given it prints the current custom build tags. + + *:AsmFmt* +:AsmFmt + + Filter the current Go asm buffer through asmfmt. It tries to preserve cursor + position and avoids replacing the buffer with stderr output. + + + *:GoAlternate* +:GoAlternate[!] + + Alternates between the implementation and test code. For example if in main.go, + switch to main_test.go. Uses the |g:go_alternate_mode| setting as the command + to open the file. + + If [!] is given then it switches to the new file even if it does not exist. + + If you would like to override the traditional commands for alternating, add + the following to your .vimrc: +> + augroup go + autocmd! + autocmd Filetype go command! -bang A call go#alternate#Switch(0, 'edit') + autocmd Filetype go command! -bang AV call go#alternate#Switch(0, 'vsplit') + autocmd Filetype go command! -bang AS call go#alternate#Switch(0, 'split') + augroup END +< + *:GoDecls* +:GoDecls [file] + + Only enabled if `ctrlp.vim` is installed. If run shows all function and + type declarations for the current file. If [file] is non empty it parses + the given file. + By default `type` and `func` declarations are being showed. This can be + changed via |g:go_decls_includes|, which accepts a comma delimited list of + definitions. By default set to: `"func,type"`. Possible options are: + `{func,type}` + + *:GoDeclsDir* +:GoDeclsDir [dir] + + Only enabled if `ctrlp.vim` is installed. If run shows all function and + type declarations for the current directory. If [dir] is given it parses + the given directory. + By default `type` and `func` declarations are being showed. This can be + changed via |g:go_decls_includes|, which accepts a comma delimited list of + definitions. By default set to: `"func,type"`. Possible options are: + `{func,type}` + +=============================================================================== +MAPPINGS *go-mappings* + +vim-go has several keys which can be used to create custom mappings +For example, to create a mapping that `go run` the current file create a +mapping for the `(go-run)`: > + + au FileType go nmap r (go-run) + +As always one is free to create more advanced mappings or functions based with +|go-commands|. For more information please check out the mappings command +documentation in the |go-commands| section. Available keys are: + + *(go-run)* + +Calls `go run` for the current file + + *(go-run-tab)* + +Calls `go run` for the current file in a new terminal tab +This option is neovim only. + + *(go-run-split)* + +Calls `go run` for the current file in a new terminal horizontal split +This option is neovim only. + + *(go-run-vertical)* + +Calls `go run` for the current file in a new terminal vertical split +This option is neovim only. + + + *(go-build)* + +Calls `go build` for the current package + + *(go-generate)* + +Calls `go generate` for the current package + + + *(go-info)* + +Shows type information for the word under the cursor + + + *(go-install)* + +Calls `go install` for the current package + + + *(go-test)* + +Calls `go test` for the current package + + *(go-test-func)* + +Calls `go test -run '...'` for the test function immediate to cursor + + *(go-test-compile)* + +Calls `go test -c` for the current package + + *(go-coverage)* + +Calls `go test -coverprofile-temp.out` for the current package + + *(go-vet)* + +Calls `go vet` for the current package + + + *(go-files)* + +Show source files that depends for the current package + + + *(go-deps)* + +Show dependencies for the current package + + *(go-doc)* + +Show the relevant GoDoc for the word under the cursor in a split window +leftabove (default mode). + + *(go-doc-split)* + +Show the relevant GoDoc for the word under the cursor in a split window. + + + *(go-doc-vertical)* + +Show the relevant GoDoc for the word under the cursor in a vertical split +window. + + + *(go-doc-tab)* + +Show the relevant GoDoc for the word under the cursor in a tab window. + + + *(go-doc-browser)* + +Show the relevant GoDoc for the word under in browser + + *(go-def)* + +Goto declaration/definition. Results are shown in the current buffer. + + + *(go-def-split)* + +Goto declaration/definition. Results are shown in a split window. + + + *(go-def-vertical)* + +Goto declaration/definition. Results are shown in a vertical split window. + + + *(go-def-tab)* + +Goto declaration/definition. Results are shown in a tab window. + + *(go-implements)* + +Show the interfaces that the type under the cursor implements. + + *(go-rename)* + +Rename the identifier under the cursor to the desired new name + + *(go-callees)* + +Show the call targets for the type under the cursor + + *(go-callers)* + +Show possible callers of selected function + + *(go-describe)* + +Describe selected syntax: definition, methods, etc + + + *(go-callstack)* + +Show path from callgraph root to selected function + + *(go-freevars)* + +Show free variables of selection + + *(go-channelpeers)* + +Show send/receive corresponding to selected channel op + + *(go-referrers)* + +Show all refs to entity denoted by selected identifier + + *(go-metalinter)* + +Calls `go-metalinter` for the current directory + + *(go-alternate-edit)* + +Alternates between the implementation and test code in the current window + + *(go-alternate-split)* + +Alternates between the implementation and test code in a new horizontal split + + *(go-alternate-vertical)* + +Alternates between the implementation and test code in a new vertical split + +=============================================================================== +TEXT OBJECTS *go-text-objects* + +vim-go comes with several custom |text-objects| that can be used to operate +upon regions of text. vim-go currently defines the following text objects: + + *go-v_af* *go-af* +af "a function", select contents from a function definition to the + closing bracket. If |g:go_textobj_include_function_doc| is + enabled it also includes the comment doc for a function + declaration. This text-object also supports literal functions. + + *go-v_if* *go-if* +if "inside a function", select contents of a function, + excluding the function definition and the closing bracket. This + text-object also supports literal functions + + +vim-go also defines the following text motion objects: + + *go-v_]]* *go-]]* +]] [count] forward to next function declaration. If + |g:go_textobj_include_function_doc| is enabled and if your + on a comment, it skips the function which the comment + belongs and forwards to the next function declaration. + + *go-v_[[* *go-[[* +[[ [count] backward to previous function declaration. + + + +=============================================================================== +FUNCTIONS *go-functions* + + *go#jobcontrol#Statusline()* + +Shows the status of a job running asynchronously. Can be used to plug into the +statusline. It works to show the status per package instead of per +file. Assume you have three files open, all belonging to the same package, +if the package build (`:GoBuild`) is successful, all statusline's will be empty +(means SUCCESS), if you it fails all file's statusline will show FAILED. + + *go#complete#GetInfo()* + +Returns the description of the identifer under the cursor. Can be used to plug +into the statusline. This function is also used for |g:go_auto_type_info|. + +=============================================================================== +SETTINGS *go-settings* + + *'g:go_test_timeout'* + +Use this option to change the test timeout of |:GoTest|. By default it is +set to 10 seconds . > + + let g:go_test_timeout= '10s' +< + + *'g:go_play_browser_command'* + +Use this option to change the browser that is used to open the snippet url +posted to play.golang.org with |:GoPlay| or for the relevant documentation +used with |:GoDocBrowser|. By default it tries to find it automatically for +the current OS. > + + let g:go_play_browser_command = '' +< + *'g:go_play_open_browser'* + +Use this option to open browser after posting the snippet to play.golang.org +with |:GoPlay|. By default it's enabled. > + + let g:go_play_open_browser = 1 +< + *'g:go_auto_type_info'* + +Use this option to show the type info (|:GoInfo|) for the word under the cursor +automatically. Whenever the cursor changes the type info will be updated. +By default it's disabled > + + let g:go_auto_type_info = 0 +< + + *'g:go_jump_to_error'* + +Use this option to enable/disable passing the bang attribute to the mappings +|(go-build)|, |(go-run)|, etc.. When enabled it will jump to the first error +automatically (means it will NOT pass the bang attribute to the appropriate +command, i.e: (go-run) -> :GoRun ). Note, that calling this doesn't have any +affect on calling the commands manually. This setting is only useful for +changing the behaviour of our custom static mappings. By default it's enabled. +> + let g:go_jump_to_error = 1 +< + *'g:go_fmt_autosave'* + +Use this option to auto |:GoFmt| on save. By default it's enabled > + + let g:go_fmt_autosave = 1 +< + *'g:go_fmt_command'* + +Use this option to define which tool is used to gofmt. By default `gofmt` is +used > + + let g:go_fmt_command = "gofmt" +< + *'g:go_fmt_options'* + +Use this option to add additional options to the |g:go_fmt_command|. Default +is empty. > + + let g:go_fmt_options = '' +< + + *'g:go_fmt_fail_silently'* + +Use this option to disable showing a location list when |g:go_fmt_command| +fails. By default the location list is shown. > + + let g:go_fmt_fail_silently = 0 +< + + *'g:go_fmt_experimental'* + +Use this option to enable fmt's experimental mode. This experimental mode is +superior to the current mode as it fully saves the undo history, so undo/redo +doesn't break. However it's slows (creates/deletes a file for every save) and +it's causing problems on some Vim versions. By default it's disabled. > + + let g:go_fmt_experimental = 0 +< + *'g:go_doc_keywordprg_enabled'* + +Use this option to run `godoc` on words under the cursor with the default +K , keywordprg shortcut. Usually this shortcut is set to use the program `man`. +In Go, using `godoc` is more idiomatic. Default is enabled. > + + let g:go_doc_keywordprg_enabled = 1 +< + *'g:go_def_mapping_enabled'* + +Use this option to enable/disable the default mapping of (`gd`) for GoDef. +Disabling it allows you to map something else to `gd`. Default is enabled. > + + let g:go_def_mapping_enabled = 1 +< + *'g:go_dispatch_enabled'* + +Use this option to enable/disable the use of Dispatch to execute the +`:GoRun`, `:GoBuild` and `:GoGenerate` commands. More information about Dispatch +is available at https://github.com/tpope/vim-dispatch. Default is disabled. > + + let g:go_dispatch_enabled = 0 +< + *'g:go_doc_command'* + +Use this option to define which tool is used to godoc. By default `godoc` is +used > + + let g:go_doc_command = "godoc" +< + *'g:go_doc_options'* + +Use this option to add additional options to the |g:go_doc_command|. Default +is empty. > + + let g:go_doc_options = '' + +< *'g:go_bin_path'* + +Use this option to change default path for vim-go tools when using +|GoInstallBinaries| and |GoUpdateBinaries|. If not set `$GOBIN` or +`$GOPATH/bin` is used. > + + let g:go_bin_path = "" +< + *'g:go_snippet_engine'* + +Use this option to define the default snippet engine. By default "ultisnips" +is used. Use "neosnippet" for neosnippet.vim: > + + let g:go_snippet_engine = "ultisnips" +< + + *'g:go_oracle_scope'* + +Use this option to define the scope of the analysis to be passed for oracle +related commands, such as |GoImplements|, |GoCallers|, etc. By default it's +not set, so only the current package's go files are passed as scope. You can +change it on-the-fly with |GoOracleScope|. For more info, please have a look +at oracle's user manual: +https://golang.org/s/oracle-user-manual#heading=h.nwso96pj07q8 > + + let g:go_oracle_scope = '' +< + + *'g:go_highlight_array_whitespace_error'* + +Highlights white space after "[]". > + + let g:go_highlight_array_whitespace_error = 1 +< + + *'g:go_highlight_chan_whitespace_error'* + +Highlights white space around the communications operator (`<-`) that doesn't +follow the standard style. > + + let g:go_highlight_chan_whitespace_error = 1 +< + + *'g:go_highlight_extra_types'* + +Highlights commonly used library types (io.Reader, etc.). > + + let g:go_highlight_extra_types = 1 +< + + *'g:go_highlight_space_tab_error'* + +Highlights instances of tabs following spaces. > + + let g:go_highlight_space_tab_error = 1 +< + *'g:go_highlight_trailing_whitespace_error'* + +Highlights trailing white space. > + + let g:go_highlight_trailing_whitespace_error = 1 + +< + *'g:go_highlight_operators'* + +Highlights operators such as `:=` , `==`, `-=`, etc. By default it's +disabled. > + + let g:go_highlight_operators = 0 +< + *'g:go_highlight_functions'* + +Highlights function names. By default it's disabled. > + + let g:go_highlight_functions = 0 +< + *'g:go_highlight_methods'* + +Highlights method names. By default it's disabled. > + + let g:go_highlight_methods = 0 +< + *'g:go_highlight_structs'* + +Highlights struct names. By default it's disabled. > + + let g:go_highlight_structs = 0 +< + *'g:go_highlight_interfaces'* + +Highlights interface names. By default it's disabled. > + + let g:go_highlight_interfaces = 0 +< + *'g:go_highlight_build_constraints'* + +Highlights build constraints. By default it's disabled. > + + let g:go_highlight_build_constraints = 0 +< + *'g:go_highlight_string_spellcheck* + +Use this option to highlight spelling errors in strings when |spell| is +also enabled. By default it's enabled. > + + let g:go_highlight_string_spellcheck = 1 +< + + *'g:go_autodetect_gopath'* + +Automatically modifies GOPATH for certain directory structures, such as for +the tool `godep` which has his own dependencies via the `Godeps` folder. What +this means is that all tools are now working with the newly modified GOPATH. +So |GoDef| for example jumps to the source inside the `Godeps` (vendored) +source. Currently `godep` and `gb` is supported, in the near future more tool +supports will be added. By default it's enabled. > + + let g:go_autodetect_gopath = 1 +< + *'g:go_textobj_enabled'* + +Adds custom text objects. By default it's enabled. > + + let g:go_textobj_enabled = 1 +< + *'g:go_metalinter_autosave'* + +Use this option to auto |:GoMetaLinter| on save. Only linter messages for +the active buffer will be shown. By default it's disabled > + + let g:go_metalinter_autosave = 0 +< + *'g:go_metalinter_autosave_enabled'* + +Specifies the enabled linters for auto |GoMetaLinter| on save. By +default it's using `vet` and `golint`. +> + let g:go_metalinter_autosave_enabled = ['vet', 'golint'] +< + *'g:go_metalinter_enabled'* + +Specifies the currently enabled linters for the |GoMetaLinter| command. By +default it's using `vet`, `golint` and `errcheck`. +> + let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck'] +< + *'g:go_metalinter_command'* + +Overrides the command to be executed when |GoMetaLinter| is called. This is +an advanced settings and is for users who want to have a complete control +over how `gometalinter` should be executed. By default it's empty. +> + let g:go_metalinter_command = "" +< + *'g:go_metalinter_deadline'* + +Overrides the maximum time the linters have to complete. By default it's 5 +seconds. +> + let g:go_metalinter_deadline = "5s" +< + *'g:go_list_height'* + +Specifies the window height for the quickfix and location list windows. The +default value (empty) automatically sets the height to the number of items +(maximum up to 10 items to prevent large heights). Setting the value +explicitly overrides this behavior. For standard Vim behavior, set it to 10. +> + let g:go_list_height = 0 +< + *'g:go_list_type'* + +Specifies the type of list to use. The default value (empty) will use the +appropriate kind of list for the command that was called. Supported values are +"", "quickfix", and "locationlist". > + + let g:go_list_type = "" +< + *'g:go_asmfmt_autosave'* + +Use this option to auto |:AsmFmt| on save. By default it's enabled. > + + let g:go_asmfmt_autosave = 1 +< + *g:go_term_mode* + +This option is Neovim only. Use it to change the default command used to +open a new terminal for go commands such as |:GoRun|. +The default is vsplit. +> + let g:go_term_mode = "vsplit" +< + *g:go_term_height* + *g:go_term_width* + +These options are Neovim only. Use them to control the height and width of +a terminal split. By default these are not set, meaning that the height and +width are set automatically by Neovim. The height only applies to a +horizontal split and width only applies to a vertical split. + +For example here is how to set each to 30. +> + let g:go_term_height = 30 + let g:go_term_width = 30 +< + *g:go_term_enabled* + +This option is Neovim only. Use it to change the behavior of the test +commands. If set to 1 it opens the test commands inside a new terminal +according to |g:go_term_mode|, otherwise it will run them in the background +just like `:GoBuild` and then display the status with |go#jobcontrol#Statusline()|. +By default it is disabled. +> + let g:go_term_enabled = 0 +< + *g:go_alternate_mode* + +Specifies the command that |:GoAlternate| uses to open the alternate file. +By default it is set to edit. +> + let g:go_alternate_mode = "edit" +< + *g:go_gorename_prefill* + +Specifies whether |:GoRename| prefills the new identifier name with the +word under the cursor. By default is is enabled. +> + let g:go_gorename_prefill = 1 +< + + +=============================================================================== +TROUBLESHOOTING *go-troubleshooting* + +I'm using Fish shell but have some problems using vim-go~ + +First environment variables in Fish are applied differently, it should be like: +> + set -x GOPATH /your/own/gopath +< +Second, Vim needs a POSIX compatible shell (more info here: +https://github.com/dag/vim-fish#teach-a-vim-to-fish). If you use Fish to open +Vim, it will make certain shell based commands fail (means vim-go will fail +too). To overcome this problem change the default shell by adding the +following into your .vimrc (on the top of the file): +> + if $SHELL =~ 'fish' + set shell='/bin/sh' + endif +< +or +> + set shell='/bin/sh' +> + +I'm seeing weird errors during installation of binaries with +GoInstallBinaries: + +If you see errors like this: +> + Error installing code.google.com/p/go.tools/cmd/goimports: + Error installing code.google.com/p/rog-go/exp/cmd/godef: +< +that means your local Go setup is broken or the remote website is down. For +example sometimes code.google.com times out. To test, just execute a simple go +get: + +> + go get code.google.com/p/go.tools/cmd/goimports +< +You'll see a more detailed error. If this works, vim-go will work too. + +=============================================================================== +CREDITS *go-credits* + +* Go Authors for official vim plugins +* Gocode, Godef, Golint, Oracle, Goimports, Errcheck projects and authors of + those projects. +* Other vim-plugins, thanks for inspiration (vim-golang, go.vim, vim-gocode, + vim-godef) +* vim-go contributors: https://github.com/fatih/vim-go/graphs/contributors + + +vim:ft=help:et:ts=2:sw=2:sts=2:norl diff --git a/ftdetect/gofiletype.vim b/ftdetect/gofiletype.vim new file mode 100644 index 0000000..b5578a6 --- /dev/null +++ b/ftdetect/gofiletype.vim @@ -0,0 +1,31 @@ +" We take care to preserve the user's fileencodings and fileformats, +" because those settings are global (not buffer local), yet we want +" to override them for loading Go files, which are defined to be UTF-8. +let s:current_fileformats = '' +let s:current_fileencodings = '' + +" define fileencodings to open as utf-8 encoding even if it's ascii. +function! s:gofiletype_pre(type) + let s:current_fileformats = &g:fileformats + let s:current_fileencodings = &g:fileencodings + set fileencodings=utf-8 fileformats=unix + let &l:filetype = a:type +endfunction + +" restore fileencodings as others +function! s:gofiletype_post() + let &g:fileformats = s:current_fileformats + let &g:fileencodings = s:current_fileencodings +endfunction + +au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix +au BufRead *.go call s:gofiletype_pre("go") +au BufReadPost *.go call s:gofiletype_post() + +au BufNewFile *.s setfiletype asm | setlocal fileencoding=utf-8 fileformat=unix +au BufRead *.s call s:gofiletype_pre("asm") +au BufReadPost *.s call s:gofiletype_post() + +au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl + +" vim:ts=4:sw=4:et diff --git a/ftplugin/asm.vim b/ftplugin/asm.vim new file mode 100644 index 0000000..0ac3594 --- /dev/null +++ b/ftplugin/asm.vim @@ -0,0 +1,17 @@ +" asm.vim: Vim filetype plugin for Go assembler. + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl fo< com< cms<" + +setlocal formatoptions-=t + +setlocal comments=s1:/*,mb:*,ex:*/,:// +setlocal commentstring=//\ %s + +setlocal noexpandtab + +command! -nargs=0 AsmFmt call go#asmfmt#Format() diff --git a/ftplugin/go.vim b/ftplugin/go.vim new file mode 100644 index 0000000..3b317d2 --- /dev/null +++ b/ftplugin/go.vim @@ -0,0 +1,57 @@ +" Copyright 2013 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" go.vim: Vim filetype plugin for Go. + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl fo< com< cms<" + +setlocal formatoptions-=t + +setlocal comments=s1:/*,mb:*,ex:*/,:// +setlocal commentstring=//\ %s + +setlocal noexpandtab + +compiler go + +" Set gocode completion +setlocal omnifunc=go#complete#Complete + +if get(g:, "go_doc_keywordprg_enabled", 1) + " keywordprg doesn't allow to use vim commands, override it + nnoremap K :GoDoc +endif + +if get(g:, "go_def_mapping_enabled", 1) + nnoremap gd :GoDef +endif + +if get(g:, "go_textobj_enabled", 1) + onoremap af :call go#textobj#Function('a') + onoremap if :call go#textobj#Function('i') + + xnoremap af :call go#textobj#Function('a') + xnoremap if :call go#textobj#Function('i') + + " Remap ]] and [[ to jump betweeen functions as they are useless in Go + nnoremap ]] :call go#textobj#FunctionJump('n', 'next') + nnoremap [[ :call go#textobj#FunctionJump('n', 'prev') + + onoremap ]] :call go#textobj#FunctionJump('o', 'next') + onoremap [[ :call go#textobj#FunctionJump('o', 'prev') + + xnoremap ]] :call go#textobj#FunctionJump('v', 'next') + xnoremap [[ :call go#textobj#FunctionJump('v', 'prev') +endif + +if get(g:, "go_auto_type_info", 0) + setlocal updatetime=800 +endif + +" vim:ts=4:sw=4:et diff --git a/ftplugin/go/commands.vim b/ftplugin/go/commands.vim new file mode 100644 index 0000000..4c4f070 --- /dev/null +++ b/ftplugin/go/commands.vim @@ -0,0 +1,65 @@ +" gorename +command! -nargs=? GoRename call go#rename#Rename(0,) + +" oracle +command! -nargs=* -complete=customlist,go#package#Complete GoOracleScope call go#oracle#Scope() +command! -range=% GoImplements call go#oracle#Implements() +command! -range=% GoCallees call go#oracle#Callees() +command! -range=% GoDescribe call go#oracle#Describe() +command! -range=% GoCallers call go#oracle#Callers() +command! -range=% GoCallstack call go#oracle#Callstack() +command! -range=% GoFreevars call go#oracle#Freevars() +command! -range=% GoChannelPeers call go#oracle#ChannelPeers() +command! -range=% GoReferrers call go#oracle#Referrers() +command! -nargs=? GoOracleTags call go#oracle#Tags() + +" tool +command! -nargs=0 GoFiles echo go#tool#Files() +command! -nargs=0 GoDeps echo go#tool#Deps() +command! -nargs=* GoInfo call go#complete#Info(0) + +" cmd +command! -nargs=* -bang GoBuild call go#cmd#Build(0,) +command! -nargs=* -bang GoGenerate call go#cmd#Generate(0,) +command! -nargs=* -bang -complete=file GoRun call go#cmd#Run(0,) +command! -nargs=* -bang GoInstall call go#cmd#Install(0, ) +command! -nargs=* -bang GoTest call go#cmd#Test(0, 0, ) +command! -nargs=* -bang GoTestFunc call go#cmd#TestFunc(0, ) +command! -nargs=* -bang GoTestCompile call go#cmd#Test(0, 1, ) +command! -nargs=* -bang GoCoverage call go#cmd#Coverage(0, ) + +" -- play +command! -nargs=0 -range=% GoPlay call go#play#Share(, , ) + +" -- def +command! -nargs=* -range GoDef :call go#def#Jump() + +" -- doc +command! -nargs=* -range -complete=customlist,go#package#Complete GoDoc call go#doc#Open('new', 'split', ) +command! -nargs=* -range -complete=customlist,go#package#Complete GoDocBrowser call go#doc#OpenBrowser() + +" -- fmt +command! -nargs=0 GoFmt call go#fmt#Format(-1) +command! -nargs=0 GoImports call go#fmt#Format(1) + +" -- import +command! -nargs=? -complete=customlist,go#package#Complete GoDrop call go#import#SwitchImport(0, '', , '') +command! -nargs=1 -bang -complete=customlist,go#package#Complete GoImport call go#import#SwitchImport(1, '', , '') +command! -nargs=* -bang -complete=customlist,go#package#Complete GoImportAs call go#import#SwitchImport(1, , '') + +" -- linters +command! -nargs=* GoMetaLinter call go#lint#Gometa(0, ) +command! -nargs=* GoLint call go#lint#Golint() +command! -nargs=* -bang GoVet call go#lint#Vet(0, ) +command! -nargs=* -complete=customlist,go#package#Complete GoErrCheck call go#lint#Errcheck() + +" -- alternate +command! -bang GoAlternate call go#alternate#Switch(0, '') + +" -- ctrlp +if globpath(&rtp, 'plugin/ctrlp.vim') != "" + command! -nargs=? -complete=file GoDecls call ctrlp#init(ctrlp#decls#cmd(0, )) + command! -nargs=? -complete=dir GoDeclsDir call ctrlp#init(ctrlp#decls#cmd(1, )) +endif + +" vim:ts=4:sw=4:et diff --git a/ftplugin/go/mappings.vim b/ftplugin/go/mappings.vim new file mode 100644 index 0000000..2529a84 --- /dev/null +++ b/ftplugin/go/mappings.vim @@ -0,0 +1,60 @@ +" go_jump_to_error defines whether we should pass the bang attribute to the +" command or not. This is only used for mappings, because the user can't pass +" the bang attribute to the plug mappings below. So instead of hardcoding it +" as 0 (no '!' attribute) or 1 (with '!' attribute) we pass the user setting, +" which by default is enabled. For commands the user has the ability to pass +" the '!', such as :GoBuild or :GoBuild! +if !exists("g:go_jump_to_error") + let g:go_jump_to_error = 1 +endif + +" Some handy plug mappings +nnoremap (go-run) :call go#cmd#Run(!g:go_jump_to_error) + +if has("nvim") + nnoremap (go-run-vertical) :call go#cmd#RunTerm(!g:go_jump_to_error, 'vsplit', []) + nnoremap (go-run-split) :call go#cmd#RunTerm(!g:go_jump_to_error, 'split', []) + nnoremap (go-run-tab) :call go#cmd#RunTerm(!g:go_jump_to_error, 'tabe', []) +endif + +nnoremap (go-build) :call go#cmd#Build(!g:go_jump_to_error) +nnoremap (go-generate) :call go#cmd#Generate(!g:go_jump_to_error) +nnoremap (go-install) :call go#cmd#Install(!g:go_jump_to_error) +nnoremap (go-test) :call go#cmd#Test(!g:go_jump_to_error, 0) +nnoremap (go-test-func) :call go#cmd#TestFunc(!g:go_jump_to_error) +nnoremap (go-test-compile) :call go#cmd#Test(!g:go_jump_to_error, 1) +nnoremap (go-coverage) :call go#cmd#Coverage(!g:go_jump_to_error) + +nnoremap (go-files) :call go#tool#Files() +nnoremap (go-deps) :call go#tool#Deps() +nnoremap (go-info) :call go#complete#Info(0) +nnoremap (go-import) :call go#import#SwitchImport(1, '', expand(''), '') + +nnoremap (go-implements) :call go#oracle#Implements(-1) +nnoremap (go-callees) :call go#oracle#Callees(-1) +nnoremap (go-callers) :call go#oracle#Callers(-1) +nnoremap (go-describe) :call go#oracle#Describe(-1) +nnoremap (go-callstack) :call go#oracle#Callstack(-1) +nnoremap (go-freevars) :call go#oracle#Freevars(-1) +nnoremap (go-channelpeers) :call go#oracle#ChannelPeers(-1) +nnoremap (go-referrers) :call go#oracle#Referrers(-1) + +nnoremap (go-rename) :call go#rename#Rename(!g:go_jump_to_error) + +nnoremap (go-def) :call go#def#Jump() +nnoremap (go-def-vertical) :call go#def#JumpMode("vsplit") +nnoremap (go-def-split) :call go#def#JumpMode("split") +nnoremap (go-def-tab) :call go#def#JumpMode("tab") + +nnoremap (go-doc) :call go#doc#Open("new", "split") +nnoremap (go-doc-tab) :call go#doc#Open("tabnew", "tabe") +nnoremap (go-doc-vertical) :call go#doc#Open("vnew", "vsplit") +nnoremap (go-doc-split) :call go#doc#Open("new", "split") +nnoremap (go-doc-browser) :call go#doc#OpenBrowser() + +nnoremap (go-metalinter) :call go#lint#Gometa(0) +nnoremap (go-vet) :call go#lint#Vet(!g:go_jump_to_error) + +nnoremap (go-alternate-edit) :call go#alternate#Switch(0, "edit") +nnoremap (go-alternate-vertical) :call go#alternate#Switch(0, "vsplit") +nnoremap (go-alternate-split) :call go#alternate#Switch(0, "split") diff --git a/ftplugin/go/snippets.vim b/ftplugin/go/snippets.vim new file mode 100644 index 0000000..3bf13d2 --- /dev/null +++ b/ftplugin/go/snippets.vim @@ -0,0 +1,46 @@ +if exists("g:go_loaded_gosnippets") + finish +endif +let g:go_loaded_gosnippets = 1 + +" by default UltiSnips +if !exists("g:go_snippet_engine") + let g:go_snippet_engine = "ultisnips" +endif + +function! s:GoUltiSnips() + if globpath(&rtp, 'plugin/UltiSnips.vim') == "" + return + endif + + if !exists("g:UltiSnipsSnippetDirectories") + let g:UltiSnipsSnippetDirectories = ["gosnippets/UltiSnips"] + else + let g:UltiSnipsSnippetDirectories += ["gosnippets/UltiSnips"] + endif +endfunction + +function! s:GoNeosnippet() + if globpath(&rtp, 'plugin/neosnippet.vim') == "" + return + endif + + let g:neosnippet#enable_snipmate_compatibility = 1 + + let gosnippets_dir = globpath(&rtp, 'gosnippets/snippets') + if type(g:neosnippet#snippets_directory) == type([]) + let g:neosnippet#snippets_directory += [gosnippets_dir] + elseif type(g:neosnippet#snippets_directory) == type("") + if strlen(g:neosnippet#snippets_directory) > 0 + let g:neosnippet#snippets_directory = g:neosnippet#snippets_directory . "," . gosnippets_dir + else + let g:neosnippet#snippets_directory = gosnippets_dir + endif + endif +endfunction + +if g:go_snippet_engine == "ultisnips" + call s:GoUltiSnips() +elseif g:go_snippet_engine == "neosnippet" + call s:GoNeosnippet() +endif diff --git a/ftplugin/go/tagbar.vim b/ftplugin/go/tagbar.vim new file mode 100644 index 0000000..ca5b184 --- /dev/null +++ b/ftplugin/go/tagbar.vim @@ -0,0 +1,55 @@ +" Check if tagbar is installed under plugins or is directly under rtp +" this covers pathogen + Vundle/Bundle +" +" Also make sure the ctags command exists +" +if !executable('ctags') + finish +elseif globpath(&rtp, 'plugin/tagbar.vim') == "" + finish +endif + +if !exists("g:go_gotags_bin") + let g:go_gotags_bin = "gotags" +endif + + +function! s:SetTagbar() + let bin_path = go#path#CheckBinPath(g:go_gotags_bin) + if empty(bin_path) + return + endif + + if !exists("g:tagbar_type_go") + let g:tagbar_type_go = { + \ 'ctagstype' : 'go', + \ 'kinds' : [ + \ 'p:package', + \ 'i:imports', + \ 'c:constants', + \ 'v:variables', + \ 't:types', + \ 'n:interfaces', + \ 'w:fields', + \ 'e:embedded', + \ 'm:methods', + \ 'r:constructor', + \ 'f:functions' + \ ], + \ 'sro' : '.', + \ 'kind2scope' : { + \ 't' : 'ctype', + \ 'n' : 'ntype' + \ }, + \ 'scope2kind' : { + \ 'ctype' : 't', + \ 'ntype' : 'n' + \ }, + \ 'ctagsbin' : expand(bin_path), + \ 'ctagsargs' : '-sort -silent' + \ } + endif +endfunction + + +call s:SetTagbar() diff --git a/gosnippets/UltiSnips/go.snippets b/gosnippets/UltiSnips/go.snippets new file mode 100644 index 0000000..d08d72e --- /dev/null +++ b/gosnippets/UltiSnips/go.snippets @@ -0,0 +1,401 @@ +# Snippets for Go + +priority -10 + +# shorthand variable declaration +snippet : "v := value" +${1} := ${0} +endsnippet + +# anonymous function +snippet anon "fn := func() { ... }" +${1:fn} := func() { + ${2:${VISUAL}} +} +${0} +endsnippet + +# append +snippet ap "append(slice, value)" +append(${1:slice}, ${0:value}) +endsnippet + +# append assignment +snippet ap= "a = append(a, value)" +${1:slice} = append($1, ${0:value}) +endsnippet + +# break +snippet br "break" +break +endsnippet + +# channel +snippet ch "chan Type" +chan ${0:int} +endsnippet + +# case +snippet case "case ...:" +case ${1:value}: + ${0:${VISUAL}} +endsnippet + +# constant +snippet con "const XXX Type = ..." +const ${1:NAME} ${2:Type} = ${0:0} +endsnippet + +# constants +snippet cons "const ( ... )" +const ( + ${1:NAME} ${2:Type} = ${3:value} + ${0} +) +endsnippet + +# constants with iota +snippet iota "const ( ... = iota )" +const ( + ${1:NAME} ${2:Type} = iota + ${0} +) +endsnippet + +# continue +snippet cn "continue" +continue +endsnippet + +# default case +snippet default "default: ..." +default: + ${0:${VISUAL}} +endsnippet + +# defer +snippet df "defer someFunction()" +defer ${1:func}(${2}) +${0} +endsnippet + +snippet def "defer func() { ... }" +defer func() { + ${0:${VISUAL}} +}() +endsnippet + +# defer recover +snippet defr +defer func() { + if err := recover(); err != nil { + ${0:${VISUAL}} + } +}() +endsnippet + +# gpl +snippet gpl +/* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, see . +* +* Copyright (C) ${1:Author}, `strftime("%Y")` +*/ +${0} +endsnippet + +# import +snippet import "import ( ... )" +import ( + "${1:package}" +) +endsnippet + +# full interface snippet +snippet interface "interface I { ... }" +type ${1:Interface} interface { + ${2:/* TODO: add methods */} +} +endsnippet + +# if condition +snippet if "if ... { ... }" +if ${1:condition} { + ${0:${VISUAL}} +} +endsnippet + +# else snippet +snippet else +else { + ${0:${VISUAL}} +} +endsnippet + +# error snippet +snippet errn "Error return " !b +if err != nil { + return err +} +${0} +endsnippet + +# error snippet +snippet errt "Error test fatal " !b +if err != nil { + t.Fatal(err) +} +${0} +endsnippet + +snippet errn, "Error return with two return values" !b +if err != nil { + return ${1:nil}, err +} +${0} +endsnippet + +snippet errh "Error handle and return" !b +if err != nil { + ${1} + return +} +${0} +endsnippet + +snippet json "\`json:key\`" +\`json:"${1:keyName}"\` +endsnippet + +# fallthrough +snippet ft "fallthrough" +fallthrough +endsnippet + +# for loop +snippet for "for ... { ... }" +for ${1} { + ${0:${VISUAL}} +} +endsnippet + +# for integer loop +snippet fori "for 0..N-1 { ... }" +for ${1:i} := 0; $1 < ${2:N}; $1++ { + ${0:${VISUAL}} +} +endsnippet + +# for range loop +snippet forr "for k, v := range items { ... }" +for ${2:k}, ${3:v} := range ${1} { + ${0:${VISUAL}} +} +endsnippet + +# function +snippet func "func Function(...) [error] { ... }" +func ${1:name}(${2:params})${3/(.+)/ /}`!p opening_par(snip, 3)`$3`!p closing_par(snip, 3)` { + ${0:${VISUAL}} +} +endsnippet + +# Fmt Printf debug +snippet ff "fmt.Printf(...)" +fmt.Printf("${1:${VISUAL}} = %+v\n", $1) +endsnippet + +# Fmt Println debug +snippet fn "fmt.Println(...)" +fmt.Println("${1:${VISUAL}}") +endsnippet + +# log printf +snippet lf "log.Printf(...)" +log.Printf("${1:${VISUAL}} = %+v\n", $1) +endsnippet + +# log println +snippet ln "log.Println(...)" +log.Println("${1:${VISUAL}}") +endsnippet + +# make +snippet make "make(Type, size)" +make(${1:[]string}, ${2:0})${0} +endsnippet + +# map +snippet map "map[Type]Type" +map[${1:string}]${0:int} +endsnippet + +# main() +snippet main "func main() { ... }" +func main() { + ${0:${VISUAL}} +} +endsnippet + +# method +snippet meth "func (self Type) Method(...) [error] { ... }" +func (${1:receiver} ${2:type}) ${3:name}(${4:params})${5/(.+)/ /}`!p opening_par(snip, 5)`$5`!p closing_par(snip, 5)` { + ${0:${VISUAL}} +} +endsnippet + +# ok +snippet ok "if !ok { ... }" +if !ok { + ${0:${VISUAL}} +} +endsnippet + +# package +snippet package "package ..." +// Package $1 provides ${2:...} +package ${1:main} +${0} +endsnippet + +# panic +snippet pn "panic()" +panic("${0:msg}") +endsnippet + +# return +snippet rt "return" +return ${0:${VISUAL}} +endsnippet + +# select +snippet select "select { case a := <-chan: ... }" +select { +case ${1:v1} := <-${2:chan1} + ${0} +} +endsnippet + +# struct +snippet st "type T struct { ... }" +type ${1:Type} struct { +${0} +} +endsnippet + +# switch +snippet switch "switch x { ... }" +switch ${1:var} { +case ${2:value1}: + ${0} +} +endsnippet + +# sprintf +snippet sp "fmt.Sprintf(...)" +fmt.Sprintf("%${1:s}", ${2:var}) +endsnippet + +# goroutine named function +snippet go "go someFunc(...)" +go ${1:funcName}(${0}) +endsnippet + +# goroutine anonymous function +snippet gof "go func() { ... }()" +go func() { + ${1:${VISUAL}} +}() +${0} +endsnippet + +# test function +snippet test "func TestXYZ(t *testing.T) { ... }" +func Test${1:Function}(t *testing.T) { + ${0:${VISUAL}} +} +endsnippet + +# quick test server +snippet tsrv "httptest.NewServer" +ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, ${1:`response`}) +})) +defer ts.Close() + +${0:someUrl} = ts.URL +endsnippet + +# test error handling +snippet ter "if err != nil { t.Errorf(...) }" +if err != nil { + t.Errorf("${0:message}") +} +endsnippet + +# test fatal error +snippet terf "if err != nil { t.Fatalf(...) }" +if err != nil { + t.Fatalf("${0:message}") +} +endsnippet + +# variable declaration +snippet var "var x Type [= ...]" +var ${1:x} ${2:Type}${3: = ${0:value}} +endsnippet + +# variables declaration +snippet vars "var ( ... )" +var ( + ${1:x} ${2:Type}${3: = ${0:value}} +) +endsnippet + +# equals fails the test if exp is not equal to act. +snippet eq "equals: test two identifiers with DeepEqual" +if !reflect.DeepEqual(${1:expected}, ${2:actual}) { + _, file, line, _ := runtime.Caller(0) + fmt.Printf("%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\n\n", filepath.Base(file), line, $1, $2) + t.FailNow() +} +endsnippet + + +global !p + +import re + +# Automatically wrap return types with parentheses + +def return_values(s): + # remove everything wrapped in parentheses + s = re.sub("\(.*?\)|\([^)]*$", "", s) + return len(s.split(",")) + +def opening_par(snip, pos): + if return_values(t[pos]) > 1 and not t[pos].startswith("("): + snip.rv = "(" + else: + snip.rv = "" + +def closing_par(snip, pos): + if return_values(t[pos]) > 1: + snip.rv = ")" + else: + snip.rv = "" + +endglobal + +# vim:ft=snippets: diff --git a/gosnippets/snippets/go.snip b/gosnippets/snippets/go.snip new file mode 100644 index 0000000..f60aeb5 --- /dev/null +++ b/gosnippets/snippets/go.snip @@ -0,0 +1,325 @@ +# shorthand variable declaration +snippet : +abbr v := value + ${1} := ${0} +# anonymous function +snippet anon +abbr fn := func() { ... } + ${1:fn} := func() { + ${0} + } +# append +snippet ap +abbr append(slice, value) + append(${1:slice}, ${0:value}) +# append assign +snippet ap= +abbr slice = append(slice, value) + ${1:slice} = append($1, ${0:value}) +# break +snippet br +abbr break + break +# channel +snippet ch +abbr chan Type + chan ${0:int} +# case +snippet case +abbr case ...: + case ${1:value}: + ${0} +# constant +snippet con +abbr const XXX Type = ... + const ${1:NAME} ${2:Type} = ${0:0} +# constants +snippet cons +abbr const ( ... ) + const ( + ${1:NAME} ${2:Type} = ${3:value} + ${0} + ) +# constants with iota +snippet iota +abbr const ( ... = iota ) + const ( + ${1:NAME} ${2:Type} = iota + ${0} + ) +# continue +snippet cn +abbr continue + continue +# default case +snippet default +abbr default: ... + default: + ${0} + +# defer +snippet df +abbr defer someFunction() + defer ${1:func}(${2}) + ${0} +snippet def +abbr defer func() { ... } + defer func() { + ${0} + }() +# defer recover +snippet defr + defer func() { + if err := recover(); err != nil { + ${0} + } + }() +# gpl +snippet gpl + /* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Copyright (C) ${1:Author}, `strftime("%Y")` + */ + + ${0} +# import +snippet import +abbr import ( ... ) + import ( + "${1:package}" + ) +# full interface snippet +snippet interface +abbr interface I { ... } + type ${1:Interface} interface { + ${2:/* TODO: add methods */} + } +# if condition +snippet if +abbr if ... { ... } + if ${1:condition} { + ${0} + } +# else snippet +abbr else { ... } +snippet else + else { + ${0} + } +# error snippet +snippet errn +abbr if err != nil { ... } + if err != nil { + return err + } + ${0} +# error snippet in TestFunc +snippet errt +abbr if err != nil { ... } + if err != nil { + t.Fatal(err) + } + +# error snippet with two return values +snippet errn, +abbr if err != nil { return [...], err } + if err != nil { + return ${1:nil}, err + } + ${0} + +# error snippet handle and return +snippet errh +abbr if err != nil { return } + if err != nil { + ${1} + return + } + ${0} + +# json snippet +snippet json +abbr \`json:key\` + \`json:"${1:keyName}"\` + +# fallthrough +snippet ft +abbr fallthrough + fallthrough +# for loop +snippet for +abbr for ... { ... } + for ${1} { + ${0} + } +# for integer loop +snippet fori +abbr for 0..N-1 { ... } + for ${1:i} := 0; $1 < ${2:N}; $1++ { + ${0} + } +# for range loop +snippet forr +abbr for k, v := range items { ... } + for ${2:k}, ${3:v} := range ${1} { + ${0} + } +# function +snippet func +abbr func function(...) [error] { ... } + func ${1:function}(${2}) ${3:error }{ + ${0} + } +# Fmt Printf debug +snippet ff +abbr fmt.Printf(...) + fmt.Printf("${1} = %+v\n", $1) + ${0} +# Fmt Println debug +snippet fn +abbr fmt.Println(...) + fmt.Println("${1}") +# log printf +snippet lf +abbr log.Printf(...) + log.Printf("${1} = %+v\n", $1) +# log println +snippet ln +abbr log.Println(...) + log.Println("${1}") +# make +snippet make +abbr make(Type, size) + make(${1:[]string}, ${2:0})${0} +# map +snippet map +abbr map[Type]Type + map[${1:string}]${0:int} +# main() +snippet main +abbr func main() { ... } +options head + func main() { + ${0} + } +# method +snippet meth +abbr func (self Type) Method(...) [error] { ... } +regexp /^meth/ + func (${1:self} ${2:Type}) ${3:Do}(${4}) ${5:error }{ + ${0} + } +# ok +snippet ok +abbr if !ok { ... } + if !ok { + ${0} + } +# package +snippet package +abbr package ... + // Package $1 provides ${2:...} + package ${1:main} + ${0} +# panic +snippet panic +alias pn +abbr panic("...") + panic("${0}") +# return +snippet return +alias rt +abbr return ... + return ${0} +# select +snippet select +abbr select { case a := <-chan: ... } + select { + case ${1:v1} := <-${2:chan1} + ${0} + } +# struct +snippet st +abbr type T struct { ... } + type ${1:Type} struct { + ${0} + } +# switch +snippet switch +abbr switch x { ... } + switch ${1:var} { + case ${2:value1}: + ${0} + } +# sprintf +snippet sp +abbr fmt.Sprintf(...) + fmt.Sprintf("%${1:s}", ${2:var}) +# goroutine named function +snippet go +abbr go someFunc(...) + go ${1:funcName}(${0}) +# goroutine anonymous function +snippet gof +abbr go func(...) { ... }(...) + go func(${1}) { + ${3:/* TODO */} + }(${2}) +# test function +snippet test +abbr func TestXYZ(t *testing.T) { ... } + func Test${1:Function}(t *testing.T) { + ${0} + } +# test server +snippet tsrv +abbr ts := httptest.NewServer(...) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, ${1:`response`}) + })) + defer ts.Close() + + //Use testing server url (type string) somewhere + ${0:someUrl} = ts.URL +# test error +snippet ter +abbr if err != nil { t.Errorf(...) } + if err != nil { + t.Errorf("${1}") + } +# test fatal error +snippet terf +abbr if err != nil { t.Fatalf(...) } + if err != nil { + t.Fatalf("${1}") + } +# variable declaration +snippet var +abbr var x Type [= ...] + var ${1:x} ${2:Type}${3: = ${0:value\}} +# variables declaration +snippet vars +abbr var ( ... ) + var ( + ${1:x} ${2:Type}${3: = ${0:value\}} + ) +# equals fails the test if exp is not equal to act. +snippet eq +abbr equals: test two identifiers with DeepEqual + if !reflect.DeepEqual(${1:expected}, ${2:actual}) { + _, file, line, _ := runtime.Caller(0) + fmt.Printf("%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\n\n", filepath.Base(file), line, $1, $2) + t.FailNow() + } diff --git a/indent/go.vim b/indent/go.vim new file mode 100644 index 0000000..a3fa2b7 --- /dev/null +++ b/indent/go.vim @@ -0,0 +1,78 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" indent/go.vim: Vim indent file for Go. +" +" TODO: +" - function invocations split across lines +" - general line splits (line ends in an operator) + +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +" C indentation is too far off useful, mainly due to Go's := operator. +" Let's just define our own. +setlocal nolisp +setlocal autoindent +setlocal indentexpr=GoIndent(v:lnum) +setlocal indentkeys+=<:>,0=},0=) + +if exists("*GoIndent") + finish +endif + +" use shiftwidth function only if it's available +if exists('*shiftwidth') + func s:sw() + return shiftwidth() + endfunc +else + func s:sw() + return &sw + endfunc +endif + +function! GoIndent(lnum) + let prevlnum = prevnonblank(a:lnum-1) + if prevlnum == 0 + " top of file + return 0 + endif + + " grab the previous and current line, stripping comments. + let prevl = substitute(getline(prevlnum), '//.*$', '', '') + let thisl = substitute(getline(a:lnum), '//.*$', '', '') + let previ = indent(prevlnum) + + let ind = previ + + if prevl =~ '[({]\s*$' + " previous line opened a block + let ind += s:sw() + endif + if prevl =~# '^\s*\(case .*\|default\):$' + " previous line is part of a switch statement + let ind += s:sw() + endif + " TODO: handle if the previous line is a label. + + if thisl =~ '^\s*[)}]' + " this line closed a block + let ind -= s:sw() + endif + + " Colons are tricky. + " We want to outdent if it's part of a switch ("case foo:" or "default:"). + " We ignore trying to deal with jump labels because (a) they're rare, and + " (b) they're hard to disambiguate from a composite literal key. + if thisl =~# '^\s*\(case .*\|default\):$' + let ind -= s:sw() + endif + + return ind +endfunction + +" vim:ts=4:sw=4:et diff --git a/indent/gohtmltmpl.vim b/indent/gohtmltmpl.vim new file mode 100644 index 0000000..94ea135 --- /dev/null +++ b/indent/gohtmltmpl.vim @@ -0,0 +1,44 @@ +if exists("b:did_indent") + finish +endif + +runtime! indent/html.vim + +" Indent Golang HTML templates +setlocal indentexpr=GetGoHTMLTmplIndent(v:lnum) +setlocal indentkeys+==else,=end + +" Only define the function once. +if exists("*GetGoHTMLTmplIndent") + finish +endif + +function! GetGoHTMLTmplIndent(lnum) + " Get HTML indent + if exists('*HtmlIndent') + let ind = HtmlIndent() + else + let ind = HtmlIndentGet(a:lnum) + endif + + " The value of a single shift-width + if exists('*shiftwidth') + let sw = shiftwidth() + else + let sw = &sw + endif + + " If need to indent based on last line + let last_line = getline(a:lnum-1) + if last_line =~ '^\s*{{\s*\%(if\|else\|range\|with\|define\|block\).*}}' + let ind += sw + endif + + " End of FuncMap block + let current_line = getline(a:lnum) + if current_line =~ '^\s*{{\s*\%(else\|end\).*}}' + let ind -= sw + endif + + return ind +endfunction diff --git a/plugin/go.vim b/plugin/go.vim new file mode 100644 index 0000000..e03565c --- /dev/null +++ b/plugin/go.vim @@ -0,0 +1,173 @@ +" install necessary Go tools +if exists("g:go_loaded_install") + finish +endif +let g:go_loaded_install = 1 + + +" these packages are used by vim-go and can be automatically installed if +" needed by the user with GoInstallBinaries +let s:packages = [ + \ "github.com/nsf/gocode", + \ "github.com/alecthomas/gometalinter", + \ "golang.org/x/tools/cmd/goimports", + \ "github.com/rogpeppe/godef", + \ "golang.org/x/tools/cmd/oracle", + \ "golang.org/x/tools/cmd/gorename", + \ "github.com/golang/lint/golint", + \ "github.com/kisielk/errcheck", + \ "github.com/jstemmer/gotags", + \ "github.com/klauspost/asmfmt/cmd/asmfmt", + \ "github.com/fatih/motion", + \ ] + +" These commands are available on any filetypes +command! GoInstallBinaries call s:GoInstallBinaries(-1) +command! GoUpdateBinaries call s:GoInstallBinaries(1) +command! -nargs=? -complete=dir GoPath call go#path#GoPath() + + +" GoInstallBinaries downloads and install all necessary binaries stated in the +" packages variable. It uses by default $GOBIN or $GOPATH/bin as the binary +" target install directory. GoInstallBinaries doesn't install binaries if they +" exist, to update current binaries pass 1 to the argument. +function! s:GoInstallBinaries(updateBinaries) + if $GOPATH == "" + echohl Error + echomsg "vim.go: $GOPATH is not set" + echohl None + return + endif + + let err = s:CheckBinaries() + if err != 0 + return + endif + + let go_bin_path = go#path#BinPath() + + " change $GOBIN so go get can automatically install to it + let $GOBIN = go_bin_path + + " old_path is used to restore users own path + let old_path = $PATH + + " vim's executable path is looking in PATH so add our go_bin path to it + let $PATH = $PATH . go#util#PathListSep() .go_bin_path + + " when shellslash is set on MS-* systems, shellescape puts single quotes + " around the output string. cmd on Windows does not handle single quotes + " correctly. Unsetting shellslash forces shellescape to use double quotes + " instead. + let resetshellslash = 0 + if has('win32') && &shellslash + let resetshellslash = 1 + set noshellslash + endif + + let cmd = "go get -u -v " + + let s:go_version = matchstr(system("go version"), '\d.\d.\d') + + " https://github.com/golang/go/issues/10791 + if s:go_version > "1.4.0" && s:go_version < "1.5.0" + let cmd .= "-f " + endif + + for pkg in s:packages + let basename = fnamemodify(pkg, ":t") + let binname = "go_" . basename . "_bin" + + let bin = basename + if exists("g:{binname}") + let bin = g:{binname} + endif + + if !executable(bin) || a:updateBinaries == 1 + if a:updateBinaries == 1 + echo "vim-go: Updating ". basename .". Reinstalling ". pkg . " to folder " . go_bin_path + else + echo "vim-go: ". basename ." not found. Installing ". pkg . " to folder " . go_bin_path + endif + + + let out = system(cmd . shellescape(pkg)) + if v:shell_error + echo "Error installing ". pkg . ": " . out + endif + endif + endfor + + " restore back! + let $PATH = old_path + if resetshellslash + set shellslash + endif +endfunction + +" CheckBinaries checks if the necessary binaries to install the Go tool +" commands are available. +function! s:CheckBinaries() + if !executable('go') + echohl Error | echomsg "vim-go: go executable not found." | echohl None + return -1 + endif + + if !executable('git') + echohl Error | echomsg "vim-go: git executable not found." | echohl None + return -1 + endif +endfunction + +" Autocommands +" ============================================================================ +" +function! s:echo_go_info() + if !exists('v:completed_item') || empty(v:completed_item) + return + endif + let item = v:completed_item + + if !has_key(item, "info") + return + endif + + if empty(item.info) + return + endif + + redraws! | echo "vim-go: " | echohl Function | echon item.info | echohl None +endfunction + +augroup vim-go + autocmd! + + " GoInfo automatic update + if get(g:, "go_auto_type_info", 0) + autocmd CursorHold *.go nested call go#complete#Info(1) + endif + + " Echo the identifier information when completion is done. Useful to see + " the signature of a function, etc... + if exists('##CompleteDone') + autocmd CompleteDone *.go nested call s:echo_go_info() + endif + + " Go code formatting on save + if get(g:, "go_fmt_autosave", 1) + autocmd BufWritePre *.go call go#fmt#Format(-1) + endif + + " Go asm formatting on save + if get(g:, "go_asmfmt_autosave", 1) + autocmd BufWritePre *.s call go#asmfmt#Format() + endif + + " run gometalinter on save + if get(g:, "go_metalinter_autosave", 0) + autocmd BufWritePost *.go call go#lint#Gometa(1) + endif +augroup END + + +" vim:ts=4:sw=4:et diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 0000000..d8a5b89 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,78 @@ +#!/bin/bash -e +# +# Copyright 2012 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. +# +# Tests for import.vim. + +cd $(dirname $0) + +cat > base.go <&1 -n "$1: " + vim -e -s -u /dev/null -U /dev/null --noplugin -c "source import.vim" \ + -c "$1" -c 'wq! test.go' base.go + # ensure blank lines are treated correctly + if ! gofmt test.go | cmp test.go -; then + echo 2>&1 "gofmt conflict" + gofmt test.go | diff -u test.go - | sed "s/^/ /" 2>&1 + fail=1 + return + fi + if ! [[ $(cat test.go) =~ $2 ]]; then + echo 2>&1 "$2 did not match" + cat test.go | sed "s/^/ /" 2>&1 + fail=1 + return + fi + echo 2>&1 "ok" +} + +# Tests for Import + +test_one "Import baz" '"baz".*"bytes"' +test_one "Import io/ioutil" '"io".*"io/ioutil".*"net"' +test_one "Import myc" '"io".*"myc".*"net"' # prefix of a site prefix +test_one "Import nat" '"io".*"nat".*"net"' +test_one "Import net/http" '"net".*"net/http".*"mycorp/foo"' +test_one "Import zoo" '"net".*"zoo".*"mycorp/foo"' +test_one "Import mycorp/bar" '"net".*"mycorp/bar".*"mycorp/foo"' +test_one "Import mycorp/goo" '"net".*"mycorp/foo".*"mycorp/goo"' + +# Tests for Drop + +cat > base.go <&1 "FAIL" + exit 1 +fi +echo 2>&1 "PASS" diff --git a/syntax/go.vim b/syntax/go.vim new file mode 100644 index 0000000..484b400 --- /dev/null +++ b/syntax/go.vim @@ -0,0 +1,359 @@ +" Copyright 2009 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" go.vim: Vim syntax file for Go. +" +" Options: +" There are some options for customizing the highlighting; the recommended +" settings are the default values, but you can write: +" let OPTION_NAME = 0 +" in your ~/.vimrc file to disable particular options. You can also write: +" let OPTION_NAME = 1 +" to enable particular options. +" At present, all options default to on, except highlight of: +" functions, methods, structs, operators, build constraints and interfaces. +" +" - go_highlight_array_whitespace_error +" Highlights white space after "[]". +" - go_highlight_chan_whitespace_error +" Highlights white space around the communications operator that don't follow +" the standard style. +" - go_highlight_extra_types +" Highlights commonly used library types (io.Reader, etc.). +" - go_highlight_space_tab_error +" Highlights instances of tabs following spaces. +" - go_highlight_trailing_whitespace_error +" Highlights trailing white space. +" - go_highlight_string_spellcheck +" Specifies that strings should be spell checked + +" Quit when a (custom) syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +if !exists("g:go_highlight_array_whitespace_error") + let g:go_highlight_array_whitespace_error = 1 +endif + +if !exists("g:go_highlight_chan_whitespace_error") + let g:go_highlight_chan_whitespace_error = 1 +endif + +if !exists("g:go_highlight_extra_types") + let g:go_highlight_extra_types = 1 +endif + +if !exists("g:go_highlight_space_tab_error") + let g:go_highlight_space_tab_error = 1 +endif + +if !exists("g:go_highlight_trailing_whitespace_error") + let g:go_highlight_trailing_whitespace_error = 1 +endif + +if !exists("g:go_highlight_operators") + let g:go_highlight_operators = 0 +endif + +if !exists("g:go_highlight_functions") + let g:go_highlight_functions = 0 +endif + +if !exists("g:go_highlight_methods") + let g:go_highlight_methods = 0 +endif + +if !exists("g:go_highlight_structs") + let g:go_highlight_structs = 0 +endif + +if !exists("g:go_highlight_interfaces") + let g:go_highlight_interfaces = 0 +endif + +if !exists("g:go_highlight_build_constraints") + let g:go_highlight_build_constraints = 0 +endif + +if !exists("g:go_highlight_string_spellcheck") + let g:go_highlight_string_spellcheck = 1 +endif + +if !exists("g:go_highlight_generate_tags") + let g:go_highlight_generate_tags = 0 +endif + +syn case match + +syn keyword goDirective package import +syn keyword goDeclaration var const type +syn keyword goDeclType struct interface + +hi def link goDirective Statement +hi def link goDeclaration Keyword +hi def link goDeclType Keyword + +" Keywords within functions +syn keyword goStatement defer go goto return break continue fallthrough +syn keyword goConditional if else switch select +syn keyword goLabel case default +syn keyword goRepeat for range + +hi def link goStatement Statement +hi def link goConditional Conditional +hi def link goLabel Label +hi def link goRepeat Repeat + +" Predefined types +syn keyword goType chan map bool string error +syn keyword goSignedInts int int8 int16 int32 int64 rune +syn keyword goUnsignedInts byte uint uint8 uint16 uint32 uint64 uintptr +syn keyword goFloats float32 float64 +syn keyword goComplexes complex64 complex128 + +hi def link goType Type +hi def link goSignedInts Type +hi def link goUnsignedInts Type +hi def link goFloats Type +hi def link goComplexes Type + +" Treat func specially: it's a declaration at the start of a line, but a type +" elsewhere. Order matters here. +syn match goDeclaration /\/ + + +" Predefined functions and values +syn match goBuiltins /\<\v(append|cap|close|complex|copy|delete|imag|len)\ze\(/ +syn match goBuiltins /\<\v(make|new|panic|print|println|real|recover)\ze\(/ +syn keyword goBoolean iota true false nil + +hi def link goBuiltins Keyword +hi def link goBoolean Boolean + +" Comments; their contents +syn keyword goTodo contained TODO FIXME XXX BUG +syn cluster goCommentGroup contains=goTodo +syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell +syn region goComment start="//" end="$" contains=goGenerate,@goCommentGroup,@Spell + +hi def link goComment Comment +hi def link goTodo Todo + +if g:go_highlight_generate_tags != 0 + syn match goGenerateVariables contained /\(\$GOARCH\|\$GOOS\|\$GOFILE\|\$GOLINE\|\$GOPACKAGE\|\$DOLLAR\)\>/ + syn region goGenerate start="^\s*//go:generate" end="$" contains=goGenerateVariables + hi def link goGenerate PreProc + hi def link goGenerateVariables Special +endif + +" Go escapes +syn match goEscapeOctal display contained "\\[0-7]\{3}" +syn match goEscapeC display contained +\\[abfnrtv\\'"]+ +syn match goEscapeX display contained "\\x\x\{2}" +syn match goEscapeU display contained "\\u\x\{4}" +syn match goEscapeBigU display contained "\\U\x\{8}" +syn match goEscapeError display contained +\\[^0-7xuUabfnrtv\\'"]+ + +hi def link goEscapeOctal goSpecialString +hi def link goEscapeC goSpecialString +hi def link goEscapeX goSpecialString +hi def link goEscapeU goSpecialString +hi def link goEscapeBigU goSpecialString +hi def link goSpecialString Special +hi def link goEscapeError Error + +" Strings and their contents +syn cluster goStringGroup contains=goEscapeOctal,goEscapeC,goEscapeX,goEscapeU,goEscapeBigU,goEscapeError +if g:go_highlight_string_spellcheck != 0 + syn region goString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup,@Spell + syn region goRawString start=+`+ end=+`+ contains=@Spell +else + syn region goString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup + syn region goRawString start=+`+ end=+`+ +endif +syn match goFormatSpecifier /%[-#0 +]*\%(\*\|\d\+\)\=\%(\.\%(\*\|\d\+\)\)*[vTtbcdoqxXUeEfgGsp]/ contained containedin=goString + +hi def link goString String +hi def link goRawString String +hi def link goFormatSpecifier goSpecialString + +" Characters; their contents +syn cluster goCharacterGroup contains=goEscapeOctal,goEscapeC,goEscapeX,goEscapeU,goEscapeBigU +syn region goCharacter start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@goCharacterGroup + +hi def link goCharacter Character + +" Regions +syn region goBlock start="{" end="}" transparent fold +syn region goParen start='(' end=')' transparent + +" Integers +syn match goDecimalInt "\<-\=\d\+\%([Ee][-+]\=\d\+\)\=\>" +syn match goHexadecimalInt "\<-\=0[xX]\x\+\>" +syn match goOctalInt "\<-\=0\o\+\>" +syn match goOctalError "\<-\=0\o*[89]\d*\>" + +hi def link goDecimalInt Integer +hi def link goHexadecimalInt Integer +hi def link goOctalInt Integer +hi def link goOctalError Error +hi def link Integer Number + +" Floating point +syn match goFloat "\<-\=\d\+\.\d*\%([Ee][-+]\=\d\+\)\=\>" +syn match goFloat "\<-\=\.\d\+\%([Ee][-+]\=\d\+\)\=\>" + +hi def link goFloat Float + +" Imaginary literals +syn match goImaginary "\<-\=\d\+i\>" +syn match goImaginary "\<-\=\d\+[Ee][-+]\=\d\+i\>" +syn match goImaginaryFloat "\<-\=\d\+\.\d*\%([Ee][-+]\=\d\+\)\=i\>" +syn match goImaginaryFloat "\<-\=\.\d\+\%([Ee][-+]\=\d\+\)\=i\>" + +hi def link goImaginary Number +hi def link goImaginaryFloat Float + +" Spaces after "[]" +if g:go_highlight_array_whitespace_error != 0 + syn match goSpaceError display "\(\[\]\)\@<=\s\+" +endif + +" Spacing errors around the 'chan' keyword +if g:go_highlight_chan_whitespace_error != 0 + " receive-only annotation on chan type + " + " \(\\)\@\)\@\)\@=" + + " send-only annotation on chan type + " + " \(<-\)\@ (only pick chan when it doesn't come after an arrow) + " this prevents picking up '<-chan <-chan' but not 'chan <-' + syn match goSpaceError display "\(\(<-\)\@\)\@<=\s\+\(<-\)\@=" + + " value-ignoring receives in a few contexts + syn match goSpaceError display "\(\(^\|[={(,;]\)\s*<-\)\@<=\s\+" +endif + +" Extra types commonly seen +if g:go_highlight_extra_types != 0 + syn match goExtraType /\/ + syn match goExtraType /\/ + syn match goExtraType /\/ + syn match goExtraType /\/ +endif + +" Space-tab error +if g:go_highlight_space_tab_error != 0 + syn match goSpaceError display " \+\t"me=e-1 +endif + +" Trailing white space error +if g:go_highlight_trailing_whitespace_error != 0 + syn match goSpaceError display excludenl "\s\+$" +endif + +hi def link goExtraType Type +hi def link goSpaceError Error + + + +" included from: https://github.com/athom/more-colorful.vim/blob/master/after/syntax/go.vim +" +" Comments; their contents +syn keyword goTodo contained NOTE +hi def link goTodo Todo + + +" Operators; +if g:go_highlight_operators != 0 + " match single-char operators: - + % < > ! & | ^ * = + " and corresponding two-char operators: -= += %= <= >= != &= |= ^= *= == + syn match goOperator /[-+%<>!&|^*=]=\?/ + " match / and /= + syn match goOperator /\/\%(=\|\ze[^/*]\)/ + " match two-char operators: << >> &^ + " and corresponding three-char operators: <<= >>= &^= + syn match goOperator /\%(<<\|>>\|&^\)=\?/ + " match remaining two-char operators: := && || <- ++ -- + syn match goOperator /:=\|||\|<-\|++\|--/ + " match ... + syn match goOperator /\.\.\./ +endif +hi def link goOperator Operator + +" Functions; +if g:go_highlight_functions != 0 + syn match goFunction /\(func\s\+\)\@<=\w\+\((\)\@=/ + syn match goFunction /\()\s\+\)\@<=\w\+\((\)\@=/ +endif +hi def link goFunction Function + +" Methods; +if g:go_highlight_methods != 0 + syn match goMethod /\(\.\)\@<=\w\+\((\)\@=/ +endif +hi def link goMethod Type + +" Structs; +if g:go_highlight_structs != 0 + syn match goStruct /\(.\)\@<=\w\+\({\)\@=/ + syn match goStructDef /\(type\s\+\)\@<=\w\+\(\s\+struct\s\+{\)\@=/ +endif +hi def link goStruct Function +hi def link goStructDef Function + +" Interfaces; +if g:go_highlight_interfaces != 0 + syn match goInterface /\(.\)\@<=\w\+\({\)\@=/ + syn match goInterfaceDef /\(type\s\+\)\@<=\w\+\(\s\+interface\s\+{\)\@=/ +endif +hi def link goInterface Function +hi def link goInterfaceDef Function + +" Build Constraints +if g:go_highlight_build_constraints != 0 + syn match goBuildKeyword display contained "+build" + " Highlight the known values of GOOS, GOARCH, and other +build options. + syn keyword goBuildDirectives contained + \ android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 + \ solaris windows 386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 + \ ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc + \ s390 s390x sparc sparc64 cgo ignore race + + " Other words in the build directive are build tags not listed above, so + " avoid highlighting them as comments by using a matchgroup just for the + " start of the comment. + " The rs=s+2 option lets the \s*+build portion be part of the inner region + " instead of the matchgroup so it will be highlighted as a goBuildKeyword. + syn region goBuildComment matchgroup=goBuildCommentStart + \ start="//\s*+build\s"rs=s+2 end="$" + \ contains=goBuildKeyword,goBuildDirectives + hi def link goBuildCommentStart Comment + hi def link goBuildDirectives Type + hi def link goBuildKeyword PreProc + + " One or more line comments that are followed immediately by a "package" + " declaration are treated like package documentation, so these must be + " matched as comments to avoid looking like working build constraints. + " The he, me, and re options let the "package" itself be highlighted by + " the usual rules. + syn region goPackageComment start=/\v(\/\/.*\n)+\s*package/ + \ end=/\v\n\s*package/he=e-7,me=e-7,re=e-7 + \ contains=@goCommentGroup,@Spell + hi def link goPackageComment Comment +endif + + +" Search backwards for a global declaration to start processing the syntax. +"syn sync match goSync grouphere NONE /^\(const\|var\|type\|func\)\>/ + +" There's a bug in the implementation of grouphere. For now, use the +" following as a more expensive/less precise workaround. +syn sync minlines=500 + +let b:current_syntax = "go" diff --git a/syntax/godoc.vim b/syntax/godoc.vim new file mode 100644 index 0000000..0fba208 --- /dev/null +++ b/syntax/godoc.vim @@ -0,0 +1,47 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. + +if exists("b:current_syntax") + finish +endif + +syn case match + +syn match godocTitle "^\([A-Z][A-Z ]*\)$" +hi def link godocTitle Title + +" Single Line Definitions +syn match godocMethodRec /\i\+\ze)/ contained +syn match godocMethodName /) \zs\i\+\ze(/ contained +syn match godocMethod /^func \((\i\+ [^)]*)\) \i\+(/ contains=godocMethodRec,godocMethodName +syn match godocFunction /^func \zs\i\+\ze(/ + +syn match godocType /^type \zs\i\+\ze.*/ +syn match godocVar /^var \zs\i\+\ze.*/ +syn match godocConst /^const \zs\i\+\ze.*/ + +hi def link godocMethodRec Type +hi def link godocType Type +hi def link godocMethodName Function +hi def link godocFunction Function +hi def link godocVar Identifier +hi def link godocConst Identifier + +" Definition Blocks +syn region godocComment start="/\*" end="\*/" contained +syn region godocComment start="//" end="$" contained +syn match godocDefinition /^\s\+\i\+/ contained + +syn region godocVarBlock start=/^var (/ end=/^)/ contains=godocComment,godocDefinition +syn region godocConstBlock start=/^const (/ end=/^)/ contains=godocComment,godocDefinition +syn region godocTypeBlock start=/^type \i\+ \(interface\|struct\) {/ end=/^}/ matchgroup=godocType contains=godocComment,godocType + +hi def link godocComment Comment +hi def link godocDefinition Identifier + +syn sync minlines=500 + +let b:current_syntax = "godoc" + +" vim:ts=4 sts=2 sw=2: diff --git a/syntax/gohtmltmpl.vim b/syntax/gohtmltmpl.vim new file mode 100644 index 0000000..5aa6c98 --- /dev/null +++ b/syntax/gohtmltmpl.vim @@ -0,0 +1,15 @@ +if exists("b:current_syntax") + finish +endif + +if !exists("main_syntax") + let main_syntax = 'html' +endif + +runtime! syntax/gotexttmpl.vim +runtime! syntax/html.vim +unlet b:current_syntax + +let b:current_syntax = "gohtmltmpl" + +" vim:ts=4:sw=4:et diff --git a/syntax/gotexttmpl.vim b/syntax/gotexttmpl.vim new file mode 100644 index 0000000..f8f4e68 --- /dev/null +++ b/syntax/gotexttmpl.vim @@ -0,0 +1,85 @@ +" Copyright 2011 The Go Authors. All rights reserved. +" Use of this source code is governed by a BSD-style +" license that can be found in the LICENSE file. +" +" gotexttmpl.vim: Vim syntax file for Go templates. + +" Quit when a (custom) syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +syn case match + +" Go escapes +syn match goEscapeOctal display contained "\\[0-7]\{3}" +syn match goEscapeC display contained +\\[abfnrtv\\'"]+ +syn match goEscapeX display contained "\\x\x\{2}" +syn match goEscapeU display contained "\\u\x\{4}" +syn match goEscapeBigU display contained "\\U\x\{8}" +syn match goEscapeError display contained +\\[^0-7xuUabfnrtv\\'"]+ + +hi def link goEscapeOctal goSpecialString +hi def link goEscapeC goSpecialString +hi def link goEscapeX goSpecialString +hi def link goEscapeU goSpecialString +hi def link goEscapeBigU goSpecialString +hi def link goSpecialString Special +hi def link goEscapeError Error + +" Strings and their contents +syn cluster goStringGroup contains=goEscapeOctal,goEscapeC,goEscapeX,goEscapeU,goEscapeBigU,goEscapeError +syn region goString contained start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup +syn region goRawString contained start=+`+ end=+`+ + +hi def link goString String +hi def link goRawString String + +" Characters; their contents +syn cluster goCharacterGroup contains=goEscapeOctal,goEscapeC,goEscapeX,goEscapeU,goEscapeBigU +syn region goCharacter contained start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@goCharacterGroup + +hi def link goCharacter Character + +" Integers +syn match goDecimalInt contained "\<\d\+\([Ee]\d\+\)\?\>" +syn match goHexadecimalInt contained "\<0x\x\+\>" +syn match goOctalInt contained "\<0\o\+\>" +syn match goOctalError contained "\<0\o*[89]\d*\>" +syn cluster goInt contains=goDecimalInt,goHexadecimalInt,goOctalInt +" Floating point +syn match goFloat contained "\<\d\+\.\d*\([Ee][-+]\d\+\)\?\>" +syn match goFloat contained "\<\.\d\+\([Ee][-+]\d\+\)\?\>" +syn match goFloat contained "\<\d\+[Ee][-+]\d\+\>" +" Imaginary literals +syn match goImaginary contained "\<\d\+i\>" +syn match goImaginary contained "\<\d\+\.\d*\([Ee][-+]\d\+\)\?i\>" +syn match goImaginary contained "\<\.\d\+\([Ee][-+]\d\+\)\?i\>" +syn match goImaginary contained "\<\d\+[Ee][-+]\d\+i\>" + +hi def link goInt Number +hi def link goFloat Number +hi def link goImaginary Number + +" Token groups +syn cluster gotplLiteral contains=goString,goRawString,goCharacter,@goInt,goFloat,goImaginary +syn keyword gotplControl contained if else end range with template +syn keyword gotplFunctions contained and html index js len not or print printf println urlquery eq ne lt le gt ge +syn match gotplVariable contained /\$[a-zA-Z0-9_]*\>/ +syn match goTplIdentifier contained /\.[^\s}]+\>/ + +hi def link gotplControl Keyword +hi def link gotplFunctions Function +hi def link goTplVariable Special + +syn region gotplAction start="{{" end="}}" contains=@gotplLiteral,gotplControl,gotplFunctions,gotplVariable,goTplIdentifier display +syn region gotplAction start="\[\[" end="\]\]" contains=@gotplLiteral,gotplControl,gotplFunctions,gotplVariable display +syn region goTplComment start="{{/\*" end="\*/}}" display +syn region goTplComment start="\[\[/\*" end="\*/\]\]" display + +hi def link gotplAction PreProc +hi def link goTplComment Comment + +let b:current_syntax = "gotexttmpl" + +" vim:ts=4:sw=4:et diff --git a/syntax/vimgo.vim b/syntax/vimgo.vim new file mode 100644 index 0000000..d62791d --- /dev/null +++ b/syntax/vimgo.vim @@ -0,0 +1,11 @@ +if exists("b:current_syntax") + finish +endif + +let b:current_syntax = "vimgo" + +syn match goInterface /^\S*/ +syn region goTitle start="\%1l" end=":" + +hi def link goInterface Type +hi def link goTitle Label