From 3db97158a41b534758a2262cfdaa0eff37ec6ed2 Mon Sep 17 00:00:00 2001 From: Buddy Sandidge Date: Sat, 6 Dec 2014 15:54:03 -0800 Subject: [PATCH] Squashed 'vim/bundle/go/' content from commit 24cca9ae0 git-subtree-dir: vim/bundle/go git-subtree-split: 24cca9ae0af8fd495e993b6c5af57cd398555b18 --- .gitignore | 1 + README.md | 276 +++++++++++++++ addon-info.json | 6 + autoload/go/cmd.vim | 157 +++++++++ autoload/go/complete.vim | 144 ++++++++ autoload/go/def.vim | 105 ++++++ autoload/go/doc.vim | 163 +++++++++ autoload/go/errcheck.vim | 42 +++ autoload/go/fmt.vim | 141 ++++++++ autoload/go/import.vim | 236 +++++++++++++ autoload/go/lint.vim | 30 ++ autoload/go/oracle.vim | 224 ++++++++++++ autoload/go/package.vim | 152 ++++++++ autoload/go/play.vim | 94 +++++ autoload/go/rename.vim | 52 +++ autoload/go/tool.vim | 179 ++++++++++ autoload/go/ui.vim | 89 +++++ autoload/webapi/json.vim | 135 ++++++++ compiler/go.vim | 31 ++ doc/vim-go.txt | 574 +++++++++++++++++++++++++++++++ ftdetect/gofiletype.vim | 23 ++ ftplugin/go.vim | 39 +++ ftplugin/go/commands.vim | 92 +++++ ftplugin/go/snippets.vim | 36 ++ ftplugin/go/tagbar.vim | 55 +++ gosnippets/UltiSnips/go.snippets | 241 +++++++++++++ gosnippets/snippets/go.snip | 243 +++++++++++++ indent/go.vim | 65 ++++ plugin/go.vim | 182 ++++++++++ scripts/test.sh | 78 +++++ syntax/go.vim | 280 +++++++++++++++ syntax/godoc.vim | 47 +++ syntax/vimgo.vim | 11 + 33 files changed, 4223 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 addon-info.json 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/errcheck.vim create mode 100644 autoload/go/fmt.vim create mode 100644 autoload/go/import.vim create mode 100644 autoload/go/lint.vim create mode 100644 autoload/go/oracle.vim create mode 100644 autoload/go/package.vim create mode 100644 autoload/go/play.vim create mode 100644 autoload/go/rename.vim create mode 100644 autoload/go/tool.vim create mode 100644 autoload/go/ui.vim create mode 100644 autoload/webapi/json.vim create mode 100644 compiler/go.vim create mode 100644 doc/vim-go.txt create mode 100644 ftdetect/gofiletype.vim create mode 100644 ftplugin/go.vim create mode 100644 ftplugin/go/commands.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 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/vimgo.vim diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..926ccaa --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +doc/tags diff --git a/README.md b/README.md new file mode 100644 index 0000000..d42de04 --- /dev/null +++ b/README.md @@ -0,0 +1,276 @@ +# vim-go + +Go (golang) support for Vim. It comes with pre-defined sensible settings (like +auto gofmt on save), has autocomplete, snippet support, improved syntax +highlighting, go toolchain commands, etc... 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, such as Functions, Operators, Methods.. +* Auto completion support via `gocode` +* Better `gofmt` on save, 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 `goimports` +* Compile and `go build` your package, install it with `go install` +* `go run` quickly your current file/files +* Run `go test` and see any errors in quickfix window +* Create a coverage profile and display annotated source code in browser to see + which functions are covered. +* Lint your code with `golint` +* Run your code through `go vet` to catch static errors. +* Advanced source analysis tool with `oracle` +* Precise type-safe renaming of identifiers with `gorename` +* List all source files and dependencies +* Checking with `errcheck` for unchecked errors. +* Integrated and improved snippets. Supports `ultisnips` or `neosnippet` +* Share your current code to [play.golang.org](http://play.golang.org) +* On-the-fly type information about the word under the cursor +* Tagbar support to show tags of the source code in a sidebar with `gotags` + +## Install + +First of all, do not use it with other Go plugins. If you use pathogen, just +clone it into your bundle directory: + +```bash +$ cd ~/.vim/bundle +$ git clone https://github.com/fatih/vim-go.git +``` + +For Vundle add this line to your vimrc: + +```vimrc +Plugin 'fatih/vim-go' +``` +and execute `:PluginInstall` (or `:BundleInstall` for older versions of Vundle) + +Please be sure all necessary binaries are installed (such as `gocode`, `godef`, +`goimports`, etc..). You can easily install them with the included +`:GoInstallBinaries`. Those binaries will be automatically downloaded and +installed to your `$GOBIN` environment (if not set it will use `$GOPATH/bin`). +It requires `git` and `hg` for fetching the individual Go packages. + +### Optional + +* Autocompletion is enabled by default via ``, to get real-time +completion (completion by type) install: +[YCM](https://github.com/Valloric/YouCompleteMe) or +[neocomplete](https://github.com/Shougo/neocomplete.vim). +* To get displayed source code tag informations on a sidebar install +[tagbar](https://github.com/majutsushi/tagbar). +* For snippet feature install: +[ultisnips](https://github.com/SirVer/ultisnips) or +[neosnippet](https://github.com/Shougo/neosnippet.vim). +* Screenshot color scheme is a slightly modified molokai: [fatih/molokai](https://github.com/fatih/molokai). + +## Usage + +All [features](#features) are enabled by default. There are no additional +settings needed. Usage and commands are listed in `doc/vim-go.txt`. 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: + +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) +``` + +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) +``` + +Run commands, such as `go run` with `r` for the current file or `go +build` and `go test` for the current package with `b` and `t`. +Display a beautiful 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) +``` + +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`. + +Disable opening browser after posting to your snippet to `play.golang.org`: + +```vim +let g:go_play_open_browser = 0 +``` + +By default vim-go shows errors for the fmt command, to disable it: + +```vim +let g:go_fmt_fail_silently = 1 +``` + +Enable goimports to automatically insert import paths instead of gofmt: + +```vim +let g:go_fmt_command = "goimports" +``` + +Disable auto fmt on save: + +```vim +let g:go_fmt_autosave = 0 +``` + +By default 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 +``` + +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 +``` + +## Snippets + +Snippets are useful and very powerful. By default ultisnips is +used, however you can change it to neosnippet with: + +```vim +let g:go_snippet_engine = "neosnippet" +``` + +Snippet feature is enabled only if the snippet plugins are installed. Below are +some examples snippets and the corresponding trigger keywords, The `|` +character defines the cursor. Ultisnips has support for multiple cursors + + +`ff` is useful for debugging: + +```go +fmt.Printf(" | %+v\n", |) +``` + +`errn` expands to: + +```go +if err != nil { + return err +} +``` + +Use `gof` to quickly create a anonymous goroutine : + +```go +go func() { + | +}() +``` + +To add `json` tags to a struct field, use `json` trigger: + +``` +type foo struct { + bar string `json:"myField" + ^ type `json` here, hit tab and type "myField". It will expand to `json:"myField"` +} +``` + +... + +And many more! For the full list have a look at the +[included snippets](https://github.com/fatih/vim-go/blob/master/gosnippets/): + +## 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/bash' + endif + +or + + set shell='/bin/bash' + + +## Why another plugin? + +This plugin/package is born mainly from frustration. I had to re-install my Vim +plugins and especially for Go I had to install a lot of separate different +plugins, setup the necessary binaries to make them work together and hope not +to lose them again. Lots of plugins out there lack proper settings. +This plugin is improved and contains all my fixes/changes that I'm using for +months under heavy go development environment. + +Give it a try. I hope you like it. Feel free to contribute to the project. + +## 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) 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/go/cmd.vim b/autoload/go/cmd.vim new file mode 100644 index 0000000..fb40054 --- /dev/null +++ b/autoload/go/cmd.vim @@ -0,0 +1,157 @@ +if !exists("g:go_jump_to_error") + let g:go_jump_to_error = 1 +endif + +function! go#cmd#Run(bang, ...) + let goFiles = '"' . join(go#tool#Files(), '" "') . '"' + + if IsWin() + exec '!go run ' . goFiles + 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 + + return + endif + + let default_makeprg = &makeprg + if !len(a:000) + let &makeprg = 'go run ' . goFiles + else + let &makeprg = "go run " . expand(a:1) + endif + + exe 'make!' + if !a:bang + cwindow + let errors = getqflist() + if !empty(errors) + if g:go_jump_to_error + cc 1 "jump to first error if there is any + endif + endif + endif + + let &makeprg = default_makeprg +endfunction + +function! go#cmd#Install(...) + let pkgs = join(a:000, '" "') + let command = 'go install "' . pkgs . '"' + let out = go#tool#ExecuteInDir(command) + if v:shell_error + call go#tool#ShowErrors(out) + cwindow + return + endif + + if exists("$GOBIN") + echon "vim-go: " | echohl Function | echon "installed to ". $GOBIN | echohl None + else + echon "vim-go: " | echohl Function | echon "installed to ". $GOPATH . "/bin" | echohl None + endif +endfunction + +function! go#cmd#Build(bang, ...) + let default_makeprg = &makeprg + let gofiles = join(go#tool#Files(), '" "') + if v:shell_error + let &makeprg = "go build . errors" + else + let &makeprg = "go build -o /dev/null " . join(a:000, ' ') . ' "' . gofiles . '"' + endif + + echon "vim-go: " | echohl Identifier | echon "building ..."| echohl None + silent! exe 'make!' + redraw! + if !a:bang + cwindow + let errors = getqflist() + if !empty(errors) + if g:go_jump_to_error + cc 1 "jump to first error if there is any + endif + else + redraws! | echon "vim-go: " | echohl Function | echon "[build] SUCCESS"| echohl None + endif + endif + + let &makeprg = default_makeprg +endfunction + +function! go#cmd#Test(...) + let command = "go test ." + if len(a:000) + let command = "go test " . expand(a:1) + endif + + echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None + let out = go#tool#ExecuteInDir(command) + if v:shell_error + call go#tool#ShowErrors(out) + else + call setqflist([]) + endif + cwindow + + let errors = getqflist() + if !empty(errors) + if g:go_jump_to_error + cc 1 "jump to first error if there is any + endif + else + redraw | echon "vim-go: " | echohl Function | echon "[test] PASS" | echohl None + endif +endfunction + +function! go#cmd#Coverage(...) + let l:tmpname=tempname() + + let command = "go test -coverprofile=".l:tmpname + + let out = go#tool#ExecuteInDir(command) + if v:shell_error + call go#tool#ShowErrors(out) + else + " clear previous quick fix window + call setqflist([]) + + let openHTML = 'go tool cover -html='.l:tmpname + call go#tool#ExecuteInDir(openHTML) + endif + cwindow + + let errors = getqflist() + if !empty(errors) + if g:go_jump_to_error + cc 1 "jump to first error if there is any + endif + endif + + call delete(l:tmpname) +endfunction + +function! go#cmd#Vet() + echon "vim-go: " | echohl Identifier | echon "calling vet..." | echohl None + let out = go#tool#ExecuteInDir('go vet') + if v:shell_error + call go#tool#ShowErrors(out) + else + call setqflist([]) + endif + cwindow + + let errors = getqflist() + if !empty(errors) + if g:go_jump_to_error + cc 1 "jump to first error if there is any + endif + else + redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None + endif +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..d8edc73 --- /dev/null +++ b/autoload/go/complete.vim @@ -0,0 +1,144 @@ +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 + +let s:vim_system = get(g:, 'gocomplete#system_function', 'system') + +fu! s:system(str, ...) + return call(s:vim_system, [a:str] + a:000) +endf + +fu! s:gocodeShellescape(arg) + 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#tool#BinPath(g:go_gocode_bin) + if empty(bin_path) + return + endif + + let result = s:system(printf('%s %s %s %s', bin_path, join(a:preargs), a:cmd, join(a:args))) + if v: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:gocodeCursor() + if &encoding != 'utf-8' + let sep = &l:fileformat == 'dos' ? "\r\n" : "\n" + let c = col('.') + let buf = line('.') == 1 ? "" : (join(getline(1, line('.')-1), sep) . sep) + let buf .= c == 1 ? "" : getline('.')[:c-2] + return printf('%d', len(iconv(buf, &encoding, "utf-8"))) + endif + return printf('%d', line2byte(line('.')) + (col('.')-2)) +endf + +fu! s:gocodeAutocomplete() + let filename = s:gocodeCurrentBuffer() + let result = s:gocodeCommand('autocomplete', + \ [s:gocodeCurrentBufferOpt(filename), '-f=vim'], + \ [expand('%:p'), s:gocodeCursor()]) + call delete(filename) + return result +endf + +function! go#complete#GetInfo() + let filename = s:gocodeCurrentBuffer() + let result = s:gocodeCommand('autocomplete', + \ [s:gocodeCurrentBufferOpt(filename), '-f=godit'], + \ [expand('%:p'), s:gocodeCursor()]) + 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() + let result = go#complete#GetInfo() + if !empty(result) + echo "vim-go: " | echohl Function | echon result | echohl None + endif +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 + 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..0be1f02 --- /dev/null +++ b/autoload/go/def.vim @@ -0,0 +1,105 @@ +if !exists("g:go_godef_bin") + let g:go_godef_bin = "godef" +endif + + +" modified and improved version of vim-godef +function! go#def#Jump(...) + if !len(a:000) + " gives us the offset of the word, basicall the position of the word under + " he cursor + let arg = s:getOffset() + else + let arg = a:1 + endif + + let bin_path = go#tool#BinPath(g:go_godef_bin) + if empty(bin_path) + return + endif + + let command = bin_path . " -f=" . expand("%:p") . " -i " . shellescape(arg) + + " get output of godef + let out=system(command, join(getbufline(bufnr('%'), 1, '$'), LineEnding())) + + " jump to it + call s:godefJump(out, "") +endfunction + + +function! go#def#JumpMode(mode) + let arg = s:getOffset() + + let bin_path = go#tool#BinPath(g:go_godef_bin) + if empty(bin_path) + return + endif + + let command = bin_path . " -f=" . expand("%:p") . " -i " . shellescape(arg) + + " get output of godef + let out=system(command, join(getbufline(bufnr('%'), 1, '$'), LineEnding())) + + call s:godefJump(out, a:mode) +endfunction + + +function! s:getOffset() + let pos = getpos(".")[1:2] + if &encoding == 'utf-8' + let offs = line2byte(pos[0]) + pos[1] - 2 + else + let c = pos[1] + let buf = line('.') == 1 ? "" : (join(getline(1, pos[0] - 1), LineEnding()) . LineEnding()) + let buf .= c == 1 ? "" : getline(pos[0])[:c-2] + let offs = len(iconv(buf, &encoding, "utf-8")) + endif + + let argOff = "-o=" . offs + return argOff +endfunction + + +function! s:godefJump(out, mode) + let old_errorformat = &errorformat + let &errorformat = "%f:%l:%c" + + if a:out =~ 'godef: ' + let out=substitute(a:out, 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..ec1b884 --- /dev/null +++ b/autoload/go/doc.vim @@ -0,0 +1,163 @@ +" 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! 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(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 || !len(content) + echo 'No documentation found for "' . pkg . '".' + return -1 + endif + + call s:GodocView(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(position, content) + " reuse existing buffer window if it exists otherwise create a new one + if !bufexists(s:buf_nr) + execute a:position + 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/errcheck.vim b/autoload/go/errcheck.vim new file mode 100644 index 0000000..e470f22 --- /dev/null +++ b/autoload/go/errcheck.vim @@ -0,0 +1,42 @@ +if !exists("g:go_errcheck_bin") + let g:go_errcheck_bin = "errcheck" +endif + +function! go#errcheck#Run(...) abort + if a:0 == 0 + let package = go#package#ImportPath(expand('%:p:h')) + else + let package = a:1 + end + + let bin_path = go#tool#BinPath(g:go_errcheck_bin) + if empty(bin_path) + return + endif + + let out = system(bin_path . ' ' . package) + if v:shell_error + let errors = [] + let mx = '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)' + for line in split(out, '\n') + let tokens = matchlist(line, mx) + + if !empty(tokens) + call add(errors, {"filename": expand(DefaultGoPath() . "/src/" . tokens[1]), + \"lnum": tokens[2], + \"col": tokens[3], + \"text": tokens[4]}) + endif + endfor + if empty(errors) + % | " Couldn't detect error format, output errors + endif + if !empty(errors) + call setqflist(errors, 'r') + endif + echohl Error | echomsg "GoErrCheck returned error" | echohl None + else + call setqflist([]) + endif + cwindow +endfunction diff --git a/autoload/go/fmt.vim b/autoload/go/fmt.vim new file mode 100644 index 0000000..ed1417e --- /dev/null +++ b/autoload/go/fmt.vim @@ -0,0 +1,141 @@ +" 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 + +let s:got_fmt_error = 0 + +" 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) + " save cursor position and many other things + let l:curw=winsaveview() + + " needed for testing if gofmt fails or not + let l:tmpname=tempname() + call writefile(getline(1,'$'), l:tmpname) + + " 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 + + " get the command first so we can test it + let fmt_command = g:go_fmt_command + if a:withGoimport == 1 + " check if the user has installed goimports + let bin_path = go#tool#BinPath(g:go_goimports_bin) + if empty(bin_path) + return + endif + + let fmt_command = bin_path + endif + + " populate the final command with user based fmt options + let command = fmt_command . ' ' . g:go_fmt_options + + " execute our command... + let out = system(command . " " . l:tmpname) + + "if there is no error on the temp file, gofmt again our original file + if v:shell_error == 0 + " remove undo point caused via BufWritePre + try | silent undojoin | catch | endtry + + " do not include stderr to the buffer, this is due to goimports/gofmt + " tha fails with a zero exit return value (sad yeah). + let default_srr = &srr + set srr=>%s + + " execufe gofmt on the current buffer and replace it + silent execute "%!" . command + + " only clear quickfix if it was previously set, this prevents closing + " other quickfixes + if s:got_fmt_error + let s:got_fmt_error = 0 + call setqflist([]) + cwindow + endif + + " put back the users srr setting + let &srr = default_srr + elseif g:go_fmt_fail_silently == 0 + "otherwise get the errors and put them to quickfix window + let errors = [] + for line in split(out, '\n') + 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 setqflist(errors, 'r') + echohl Error | echomsg "Gofmt returned error" | echohl None + endif + let s:got_fmt_error = 1 + cwindow + endif + + " restore our undo history + silent! exe 'rundo ' . tmpundofile + call delete(tmpundofile) + + " restore our cursor/windows positions + call delete(l:tmpname) + call winrestview(l:curw) +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..6f05488 --- /dev/null +++ b/autoload/go/import.vim @@ -0,0 +1,236 @@ +" 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. +" +" import.vim: Vim commands to import/drop Go packages. +" +" This filetype plugin adds three new commands for go buffers: +" +" :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. +" +" :GoImportAs {localname} {path} +" +" Same as Import, but uses a custom local name for the package. +" +" :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. +" +" If you would like to add shortcuts, you can do so by doing the following: +" +" Import fmt +" au Filetype go nnoremap f :Import fmt +" +" Drop fmt +" au Filetype go nnoremap F :Drop fmt +" +" Import the word under your cursor +" au Filetype go nnoremap k +" \ :exe 'Import ' . expand('') +" +" The backslash '\' is the default maplocalleader, so it is possible that +" your vim is set to use a different character (:help maplocalleader). +" +function! go#import#SwitchImport(enabled, localname, path) + let view = winsaveview() + let path = a:path + + " 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 path == '' + call s:Error('Import path not provided') + return + 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/lint.vim b/autoload/go/lint.vim new file mode 100644 index 0000000..89c103a --- /dev/null +++ b/autoload/go/lint.vim @@ -0,0 +1,30 @@ +" 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. +" +" lint.vim: Vim command to lint Go files with golint. +" +" https://github.com/golang/lint +" +" This filetype plugin add a new commands for go buffers: +" +" :GoLint +" +" Run golint for the current Go file. +" +if !exists("g:go_golint_bin") + let g:go_golint_bin = "golint" +endif + +function! go#lint#Run() abort + let bin_path = go#tool#BinPath(g:go_golint_bin) + if empty(bin_path) + return + endif + + silent cexpr system(bin_path . " " . shellescape(expand('%'))) + cwindow +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..2dc24b9 --- /dev/null +++ b/autoload/go/oracle.vim @@ -0,0 +1,224 @@ +" -*- text -*- +" 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 + +func! s:qflist(output) + let qflist = [] + " 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(qflist, item) + endfor + call setqflist(qflist) + cwindow +endfun + +func! s:getpos(l, c) + if &encoding != 'utf-8' + let buf = a:l == 1 ? '' : (join(getline(1, a:l-1), "\n") . "\n") + let buf .= a:c == 1 ? '' : getline('.')[:a:c-2] + return len(iconv(buf, &encoding, 'utf-8')) + endif + return line2byte(a:l) + (a:c-2) +endfun + +func! s:RunOracle(mode, selected) range abort + let fname = expand('%:p') + let dname = expand('%:p:h') + let pkg = go#package#ImportPath(dname) + + if exists('g:go_oracle_scope_file') + " let the user defines the scope + let sname = shellescape(get(g:, 'go_oracle_scope_file')) + elseif exists('g:go_oracle_include_tests') && pkg != -1 + " give import path so it includes all _test.go files too + let sname = shellescape(pkg) + else + " best usable way, only pass the package itself, without the test + " files + let sname = join(go#tool#Files(), ' ') + endif + + "return with a warning if the bin doesn't exist + let bin_path = go#tool#BinPath(g:go_oracle_bin) + if empty(bin_path) + return + endif + + if a:selected != -1 + let pos1 = s:getpos(line("'<"), col("'<")) + let pos2 = s:getpos(line("'>"), col("'>")) + let cmd = printf('%s -format json -pos=%s:#%d,#%d %s %s', + \ bin_path, + \ shellescape(fname), pos1, pos2, a:mode, sname) + else + let pos = s:getpos(line('.'), col('.')) + let cmd = printf('%s -format json -pos=%s:#%d %s %s', + \ bin_path, + \ shellescape(fname), pos, a:mode, sname) + endif + + echon "vim-go: " | echohl Identifier | echon "analysing ..." | echohl None + + let out = system(cmd) + 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. + " echo out + " redraw | echon 'vim-go: could not run static analyser (does it build?)' + redraw | echon "vim-go: " | echohl Statement | echon out | echohl None + return {} + else + let json_decoded = webapi#json#decode(out) + return json_decoded + endif +endfun + + +" Show 'implements' relation for selected package +function! go#oracle#Implements(selected) + let out = s:RunOracle('implements', a:selected) + if empty(out) + return + endif + + " be sure they exists before we retrieve them from the map + if !has_key(out, "implements") + return + endif + + if has_key(out.implements, "from") + let interfaces = out.implements.from + elseif has_key(out.implements, "fromptr") + let interfaces = out.implements.fromptr + else + redraw | echon "vim-go: " | echon "does not satisfy any interface"| echohl None + return + endif + + " get the type name from the type under the cursor + let typeName = out.implements.type.name + + " prepare the title + let title = typeName . " implements:" + + " start to populate our buffer content + let result = [title, ""] + + for interface in interfaces + " don't add runtime interfaces + if interface.name !~ '^runtime' + let line = interface.name . "\t" . interface.pos + call add(result, line) + endif + endfor + + " open a window and put the result + call go#ui#OpenWindow(result) + + " define some buffer related mappings: + " + " go to definition when hit enter + nnoremap :call go#ui#OpenDefinition() + " close the window when hit ctrl-c + nnoremap :call go#ui#CloseWindow() +endfunction + +" Describe selected syntax: definition, methods, etc +function! go#oracle#Describe(selected) + let out = s:RunOracle('describe', a:selected) + if empty(out) + return + endif + + echo out + return + + let detail = out["describe"]["detail"] + let desc = out["describe"]["desc"] + + echo '# detail: '. detail + " package, constant, variable, type, function or statement labe + if detail == "package" + echo desc + return + endif + + if detail == "value" + echo desc + echo out["describe"]["value"] + return + endif + + " the rest needs to be implemented + echo desc +endfunction + +" Show possible targets of selected function call +function! go#oracle#Callees(selected) + let out = s:RunOracle('callees', a:selected) + echo out +endfunction + +" Show possible callers of selected function +function! go#oracle#Callers(selected) + let out = s:RunOracle('callers', a:selected) + echo out +endfunction + +" Show the callgraph of the current program. +function! go#oracle#Callgraph(selected) + let out = s:RunOracle('callgraph', a:selected) + echo out +endfunction + +" Show path from callgraph root to selected function +function! go#oracle#Callstack(selected) + let out = s:RunOracle('callstack', a:selected) + echo out +endfunction + +" Show free variables of selection +function! go#oracle#Freevars(selected) + let out = s:RunOracle('freevars', a:selected) + echo out +endfunction + +" Show send/receive corresponding to selected channel op +function! go#oracle#Peers(selected) + let out = s:RunOracle('peers', a:selected) + echo out +endfunction + +" Show all refs to entity denoted by selected identifier +function! go#oracle#Referrers(selected) + let out = s:RunOracle('referrers', a:selected) + echo 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..fe32a2c --- /dev/null +++ b/autoload/go/package.vim @@ -0,0 +1,152 @@ +" 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 executable('go') + let goroot = substitute(system('go env GOROOT'), '\n', '', 'g') + if v:shell_error + echomsg '''go env GOROOT'' failed' + endif + else + let goroot = $GOROOT + endif + + if len(goroot) != 0 && isdirectory(goroot) + let dirs += [goroot] + endif + + let pathsep = ':' + if s:goos == 'windows' + let pathsep = ';' + endif + let workspaces = split($GOPATH, pathsep) + 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 + + return substitute(path, workspace . '/src/', '', '') +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) + if len(words) > 2 && words[0] != "GoImportAs" + " 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') + let ret[i] = i + endfor + endfor + endfor + return sort(keys(ret)) +endfunction + +" vim: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..9e401e3 --- /dev/null +++ b/autoload/go/rename.vim @@ -0,0 +1,52 @@ +if !exists("g:go_gorename_bin") + let g:go_gorename_bin = "gorename" +endif + +function! go#rename#Rename(...) + let to = "" + if a:0 == 0 + let ask = printf("vim-go: rename '%s' to: ", expand("")) + let to = input(ask) + redraw + else + let to = a:1 + endif + + + "return with a warning if the bin doesn't exist + let bin_path = go#tool#BinPath(g:go_gorename_bin) + if empty(bin_path) + return + endif + + let fname = expand('%:p:t') + let pos = s:getpos(line('.'), col('.')) + let cmd = printf('%s -offset %s:#%d -to %s', bin_path, shellescape(fname), pos, 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') + + if v:shell_error + redraw | echon "vim-go: " | echohl Statement | echon clean[0] | echohl None + else + redraw | echon "vim-go: " | echohl Function | echon clean[0] | echohl None + endif + + " refresh the buffer so we can see the new content + silent execute ":e" +endfunction + +func! s:getpos(l, c) + if &encoding != 'utf-8' + let buf = a:l == 1 ? '' : (join(getline(1, a:l-1), "\n") . "\n") + let buf .= a:c == 1 ? '' : getline('.')[:a:c-2] + return len(iconv(buf, &encoding, 'utf-8')) + endif + return line2byte(a:l) + (a:c-2) +endfun + +" vim:ts=4:sw=4:et +" diff --git a/autoload/go/tool.vim b/autoload/go/tool.vim new file mode 100644 index 0000000..c72b3d2 --- /dev/null +++ b/autoload/go/tool.vim @@ -0,0 +1,179 @@ +function! go#tool#Files() + if IsWin() + let command = 'go list -f "{{range $f := .GoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}"' + else + " let command = "go list -f $'{{range $f := .GoFiles}}{{$.Dir}}/{{$f}}\n{{end}}'" + let command = "go list -f '{{range $f := .GoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}'" + endif + let out = go#tool#ExecuteInDir(command) + return split(out, '\n') +endfunction + +function! go#tool#Deps() + if 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 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 package_name = fnamemodify(package_path, ":t") + let imports[package_name] = package_path + endfor + + return imports +endfunction + +function! go#tool#ShowErrors(out) + let errors = [] + for line in split(a:out, '\n') + let tokens = matchlist(line, '^\s*\(.\{-}\):\(\d\+\):\s*\(.*\)') + if !empty(tokens) + call add(errors, {"filename" : expand("%:p:h:") . "/" . tokens[1], + \"lnum": tokens[2], + \"text": tokens[3]}) + 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 + + if !empty(errors) + call setqflist(errors, 'r') + return + endif + + if empty(errors) + " Couldn't detect error format, output errors + echo a:out + endif +endfunction + +function! go#tool#ExecuteInDir(cmd) abort + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd.'`=expand("%:p:h")`' + let out = system(a:cmd) + finally + execute cd.'`=dir`' + endtry + 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 + +" BinPath 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#tool#BinPath(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 = GetBinPath() + 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_bin_path + + if !executable(binpath) + echo "vim-go: could not find '" . basename . "'. Run :GoInstallBinaries to fix it." + return "" + endif + + " restore back! + if go_bin_path + let $PATH = old_path + endif + + return go_bin_path . '/' . basename +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 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..56ac8c9 --- /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(content) + " reuse existing buffer window if it exists otherwise create a new one + if !bufexists(s:buf_nr) + execute 'botright new' + file `="[Implements]"` + 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 implements_height = 10 + if len(a:content) < implements_height + exe 'resize ' . implements_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() + let curline = getline('.') + + " don't touch our first line and any blank line + if curline =~ "implements" || 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/webapi/json.vim b/autoload/webapi/json.vim new file mode 100644 index 0000000..5d6e7ba --- /dev/null +++ b/autoload/webapi/json.vim @@ -0,0 +1,135 @@ +" json +" Last Change: 2012-03-08 +" Maintainer: Yasuhiro Matsumoto +" License: This file is placed in the public domain. +" Reference: +" +let s:save_cpo = &cpo +set cpo&vim + +function! webapi#json#null() + return 0 +endfunction + +function! webapi#json#true() + return 1 +endfunction + +function! webapi#json#false() + return 0 +endfunction + +function! s:nr2byte(nr) + if a:nr < 0x80 + return nr2char(a:nr) + elseif a:nr < 0x800 + return nr2char(a:nr/64+192).nr2char(a:nr%64+128) + else + return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128) + endif +endfunction + +function! s:nr2enc_char(charcode) + if &encoding == 'utf-8' + return nr2char(a:charcode) + endif + let char = s:nr2byte(a:charcode) + if strlen(char) > 1 + let char = strtrans(iconv(char, 'utf-8', &encoding)) + endif + return char +endfunction + +function! s:fixup(val, tmp) + if type(a:val) == 0 + return a:val + elseif type(a:val) == 1 + if a:val == a:tmp.'null' + return function('webapi#json#null') + elseif a:val == a:tmp.'true' + return function('webapi#json#true') + elseif a:val == a:tmp.'false' + return function('webapi#json#false') + endif + return a:val + elseif type(a:val) == 2 + return a:val + elseif type(a:val) == 3 + return map(a:val, 's:fixup(v:val, a:tmp)') + elseif type(a:val) == 4 + return map(a:val, 's:fixup(v:val, a:tmp)') + else + return string(a:val) + endif +endfunction + +function! webapi#json#decode(json) + let json = iconv(a:json, "utf-8", &encoding) + if get(g:, 'webapi#json#parse_strict', 1) == 1 && substitute(substitute(substitute( + \ json, + \ '\\\%(["\\/bfnrt]\|u[0-9a-fA-F]\{4}\)', '\@', 'g'), + \ '"[^\"\\\n\r]*\"\|true\|false\|null\|-\?\d\+' + \ . '\%(\.\d*\)\?\%([eE][+\-]\{-}\d\+\)\?', ']', 'g'), + \ '\%(^\|:\|,\)\%(\s*\[\)\+', '', 'g') !~ '^[\],:{} \t\n]*$' + throw json + endif + let json = substitute(json, '\n', '', 'g') + let json = substitute(json, '\\u34;', '\\"', 'g') + if v:version >= 703 && has('patch780') + let json = substitute(json, '\\u\(\x\x\x\x\)', '\=iconv(nr2char(str2nr(submatch(1), 16), 1), "utf-8", &encoding)', 'g') + else + let json = substitute(json, '\\u\(\x\x\x\x\)', '\=s:nr2enc_char("0x".submatch(1))', 'g') + endif + if get(g:, 'webapi#json#allow_nil', 0) != 0 + let tmp = '__WEBAPI_JSON__' + while 1 + if stridx(json, tmp) == -1 + break + endif + let tmp .= '_' + endwhile + let [null,true,false] = [ + \ tmp.'null', + \ tmp.'true', + \ tmp.'false'] + sandbox let ret = eval(json) + call s:fixup(ret, tmp) + else + let [null,true,false] = [0,1,0] + sandbox let ret = eval(json) + endif + return ret +endfunction + +function! webapi#json#encode(val) + if type(a:val) == 0 + return a:val + elseif type(a:val) == 1 + let json = '"' . escape(a:val, '\"') . '"' + let json = substitute(json, "\r", '\\r', 'g') + let json = substitute(json, "\n", '\\n', 'g') + let json = substitute(json, "\t", '\\t', 'g') + let json = substitute(json, '\([[:cntrl:]]\)', '\=printf("\x%02d", char2nr(submatch(1)))', 'g') + return iconv(json, &encoding, "utf-8") + elseif type(a:val) == 2 + let s = string(a:val) + if s == "function('webapi#json#null')" + return 'null' + elseif s == "function('webapi#json#true')" + return 'true' + elseif s == "function('webapi#json#false')" + return 'false' + endif + elseif type(a:val) == 3 + return '[' . join(map(copy(a:val), 'webapi#json#encode(v:val)'), ',') . ']' + elseif type(a:val) == 4 + return '{' . join(map(keys(a:val), 'webapi#json#encode(v:val).":".webapi#json#encode(a:val[v:val])'), ',') . '}' + else + return string(a:val) + endif +endfunction + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:set et: diff --git a/compiler/go.vim b/compiler/go.vim new file mode 100644 index 0000000..c5ae0bd --- /dev/null +++ b/compiler/go.vim @@ -0,0 +1,31 @@ +" 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 + +CompilerSet makeprg=go\ build +CompilerSet errorformat= + \%-G#\ %.%#, + \%-G%.%#panic:\ %m, + \%A%f:%l:%c:\ %m, + \%A%f:%l:\ %m, + \%C%*\\s%m, + \%-G%.%# + +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..39c3502 --- /dev/null +++ b/doc/vim-go.txt @@ -0,0 +1,574 @@ +*vim-go.txt* Go development plugin +*vim-go* + +=============================================================================== +# # +# ## ## #### ## ## ###### ####### # +# ## ## ## ### ### ## ## ## ## # +# ## ## ## #### #### ## ## ## # +# ## ## ## ## ### ## ####### ## #### ## ## # +# ## ## ## ## ## ## ## ## ## # +# ## ## ## ## ## ## ## ## ## # +# ### #### ## ## ###### ####### # +# # +=============================================================================== +CONTENTS *go-contents* + + 1. Intro........................................|go-intro| + 2. Install......................................|go-install| + 3. Commands.....................................|go-commands| + 4. Settings.....................................|go-settings| + 5. Mappings.....................................|go-mappings| + 6. Troubleshooting..............................|go-troubleshooting| + 7. 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, such as Functions, Operators, Methods.. + * Auto completion support via `gocode` + * Better `gofmt` on save, keeps cursor position and doesn't break your undo + history + * Go to symbol/declaration with `godef` + * Automatically import packages via `goimports` + * Compile and `go build` your package, install it with `go install` + * `go run` quickly your current file/files + * Run `go test` and see any errors in quickfix window + * Lint your code with `golint` + * Run your code trough `go vet` to catch static errors. + * Advanced source analysis tool with `oracle` + * Precise type-safe renaming of identifiers with `gorename` + * List all source files and dependencies + * Checking with `errcheck` for unchecked errors. + * Integrated and improved snippets. Supports `ultisnips` or `neosnippet` + * Share your current code to play.golang.org + * Type information about the underlying identifier + * Tagbar support to show tags of the source code in a sidebar with `gotags` + +=============================================================================== +INSTALL *go-install* + +If you use pathogen, just clone it into your bundle directory: > + + $ cd ~/.vim/bundle + $ git clone https://github.com/fatih/vim-go.git +< + +For Vundle add this line to your vimrc: +> + Plugin 'fatih/vim-go' +< +and execute `:PluginInstall` (or `:BundleInstall` for older versions of Vundle) + + +Please be sure all necessary binares are installed (such as `gocode`, `godef`, +`goimports`, etc..). You can easily install them with the included +|GoInstallBinaries|. Those binaries will be automatically downloaded and +installed to your `$GOBIN` environment (if not set it will use `$GOPATH/bin`). +It requires `git` and `hg` for fetching the individual Go packages. + +* Autocompletion is enabled by default via ``, to get real-time +completion (completion by type) install: +https://github.com/Valloric/YouCompleteMe or +https://github.com/Shougo/neocomplete.vim +* To get displayed source code tag informations on a sidebar install +https://github.com/majutsushi/tagbar. +* For snippet feature install: +https://github.com/SirVer/ultisnips or +https://github.com/Shougo/neosnippet.vim. + +=============================================================================== +COMMANDS *go-commands* + + *: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. + + *: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 + + Run golint for the current Go file. + + *: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 + + 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. + + *: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. + + If [!] is not given the first error is jumped to. + + *:GoBuild* +:GoBuild[!] [options] + + Build your package with `go build`. 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`. + + If [!] is not given the first error is jumped to. + + *:GoInfo* +:GoInfo + Show type information about the identifer 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 + + Install your package with `go install`. + + *:GoTest* +:GoTest [expand] + + Test your _test.go files via in your current directory. Errors are + populated in quickfix window. If an argument is passed, 'expand' is used + as file selector (useful for cases like `:GoTest ./...`). + + *:GoCoverage* +:GoCoverage + + Create a coverage profile and open a browser to display the annotated + source code of the current package. + + *:GoErrCheck* +:GoErrCheck + + Check for unchecked errors in you current package. Errors are populated in + quickfix window. + + *: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 under the cursor (or selected package) is shown in a custom + window. Hit `` to jump in a new tab or close it via ``. + + *: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. + +=============================================================================== +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|. Available keys are: + + *(go-run)* + +Calls `go run` for the current file + + + *(go-build)* + +Calls `go build` 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-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 + + + +=============================================================================== +SETTINGS *go-settings* + + *'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: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_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 quickfix window when |g:go_fmt_command| +fails. By default it's disabled. > + + let g:go_fmt_fail_silently = 0 +< + *'g:go_doc_keywordprg_enabled'* + +Use this option to change the enable GoDoc to run on words under the cursor +with the default K , keywordprg shortcut. This shortcut is by default set to +use the program man. However 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 enabled/ disable the default mapping (`gd`) for GoDef +enabled. Disabling it allows you to map something else to the mapping `gd`. +Default is enabled. > + + let g:go_def_mapping_enabled = 1 +< + *'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_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 don'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 +< +=============================================================================== +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 certainx 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 weirds errors during the startup~ + +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 offical 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..b658f6b --- /dev/null +++ b/ftdetect/gofiletype.vim @@ -0,0 +1,23 @@ +" 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() + let s:current_fileformats = &g:fileformats + let s:current_fileencodings = &g:fileencodings + set fileencodings=utf-8 fileformats=unix + setlocal filetype=go +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 setlocal filetype=go fileencoding=utf-8 fileformat=unix +au BufRead *.go call s:gofiletype_pre() +au BufReadPost *.go call s:gofiletype_post() diff --git a/ftplugin/go.vim b/ftplugin/go.vim new file mode 100644 index 0000000..15d3b74 --- /dev/null +++ b/ftplugin/go.vim @@ -0,0 +1,39 @@ +" 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_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..b56bb55 --- /dev/null +++ b/ftplugin/go/commands.vim @@ -0,0 +1,92 @@ +if exists("g:go_loaded_commands") + finish +endif +let g:go_loaded_commands = 1 + + +" Some handy plug mappings +nnoremap (go-run) :call go#cmd#Run(expand('%')) +nnoremap (go-build) :call go#cmd#Build('') +nnoremap (go-install) :call go#cmd#Install() +nnoremap (go-test) :call go#cmd#Test('') +nnoremap (go-coverage) :call go#cmd#Coverage('') +nnoremap (go-vet) :call go#cmd#Vet() +nnoremap (go-files) :call go#tool#Files() +nnoremap (go-deps) :call go#tool#Deps() +nnoremap (go-info) :call go#complete#Info() +nnoremap (go-import) :call go#import#SwitchImport(1, '', expand('')) + +nnoremap (go-implements) :call go#oracle#Implements(-1) + +nnoremap (go-rename) :call go#rename#Rename() + +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("leftabove new") +nnoremap (go-doc-tab) :call go#doc#Open("tabnew") +nnoremap (go-doc-vertical) :call go#doc#Open("vnew") +nnoremap (go-doc-split) :call go#doc#Open("split") +nnoremap (go-doc-browser) :call go#doc#OpenBrowser() + + +" gorename +command! -nargs=? GoRename call go#rename#Rename() + +" oracle +command! -range=% GoImplements call go#oracle#Implements() + +" tool +command! -nargs=0 GoFiles echo go#tool#Files() +command! -nargs=0 GoDeps echo go#tool#Deps() +command! -nargs=* GoInfo call go#complete#Info() + +" cmd +command! -nargs=* -bang GoRun call go#cmd#Run(0,) +command! -nargs=* -bang GoBuild call go#cmd#Build(0,) +command! -nargs=* GoInstall call go#cmd#Install() +command! -nargs=* GoTest call go#cmd#Test() +command! -nargs=* GoCoverage call go#cmd#Coverage() +command! -nargs=0 GoVet call go#cmd#Vet() + +" -- 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('leftabove new', ) +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 -complete=customlist,go#package#Complete GoImport call go#import#SwitchImport(1, '', ) +command! -nargs=* -complete=customlist,go#package#Complete GoImportAs call go#import#SwitchImport(1, ) + +" -- lint +command! GoLint call go#lint#Run() + +" -- errcheck +command! -nargs=? -complete=customlist,go#package#Complete GoErrCheck call go#errcheck#Run() + +" Disable all commands until they are fully integrated. +" +" command! -range=% GoOracleDescribe call go#oracle#Describe() +" command! -range=% GoOracleCallees call go#oracle#Callees() +" command! -range=% GoOracleCallers call go#oracle#Callers() +" command! -range=% GoOracleCallgraph call go#oracle#Callgraph() +" command! -range=% GoOracleCallstack call go#oracle#Callstack() +" command! -range=% GoOracleFreevars call go#oracle#Freevars() +" command! -range=% GoOraclePeers call go#oracle#Peers() +" command! -range=% GoOracleReferrers call go#oracle#Referrers() + +" vim:ts=4:sw=4:et +" + diff --git a/ftplugin/go/snippets.vim b/ftplugin/go/snippets.vim new file mode 100644 index 0000000..c831578 --- /dev/null +++ b/ftplugin/go/snippets.vim @@ -0,0 +1,36 @@ +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 + exec 'NeoSnippetSource' globpath(&rtp, 'gosnippets/snippets/go.snip') +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..5b22363 --- /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 pathgen + 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#tool#BinPath(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:1', + \ '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..02e07fb --- /dev/null +++ b/gosnippets/UltiSnips/go.snippets @@ -0,0 +1,241 @@ +# Snippets for Go + +priority -10 + +# when to abbriviate and when not? +# b doesn't work here, because it ignores whitespace +# optional local name? +snippet import "Import declaration" b +import ( + "${1:package}" +) +endsnippet + +snippet package "Package declaration" b +// Package $1 provides ${2:...} +package ${1:main} +${0:${VISUAL}} +endsnippet + +# Mostly converted from: https://github.com/AlanQuatermain/go-tmbundle +snippet /^cons/ "Constants declaration" r +const ( + ${1:constant}${2/(.+)/ /}${2:type} = ${0:value} +) +endsnippet + +snippet /^con/ "Constant declaration" r +const ${1:name}${2/(.+)/ /}${2:type} = ${0:value} +endsnippet + +snippet iota "Iota constant generator" b +const ( + ${1:constant}${2/(.+)/ /}${2:type} = iota +) +endsnippet + +snippet struct "Struct declaration" b +type ${1:Struct} struct { + ${0:${VISUAL}} +} +endsnippet + +snippet interface "Interface declaration" b +type ${1:Interface} interface { + ${0:${VISUAL}} +} +endsnippet + +# statements +snippet for "For loop" b +for ${1:condition} { + ${0:${VISUAL}} +} +endsnippet + +snippet fori "Integer for loop" b +for ${1:i} := 0; $1 < ${2:N}; $1++ { + ${0:${VISUAL}} +} +endsnippet + +snippet forr "For range loop" b +for ${2:name} := range ${1:collection} { + ${0:${VISUAL}} +} +endsnippet + +snippet if "If statement" b +if ${1:condition}${1/(.+)/ /}{ + ${0:${VISUAL}} +} +endsnippet + +snippet switch "Switch statement" b +switch ${1:expression}${1/(.+)/ /}{ +case${0} +} +endsnippet + +snippet select "Select statement" b +select { +case${0} +} +endsnippet + +snippet case "Case clause" b +case ${1:condition}: + ${0:${VISUAL}} +endsnippet + +snippet default "Default clause" b +default: + ${0:${VISUAL}} +endsnippet + +# functions + +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 + +snippet /^main/ "Main function" r +func main() { + ${0:${VISUAL}} +} +endsnippet + +snippet /^meth/ "Method" r +func (${1:receiver} ${2:type}) ${3:name}(${4:params})${5/(.+)/ /}`!p opening_par(snip, 5)`$5`!p closing_par(snip, 5)` { + ${0:${VISUAL}} +} +endsnippet + +snippet func "Function" b +func ${1:name}(${2:params})${3/(.+)/ /}`!p opening_par(snip, 3)`$3`!p closing_par(snip, 3)` { + ${0:${VISUAL}} +} +endsnippet + +snippet anon "Anonymous Function" !b +${1:fn} := func() { + ${2} +} +endsnippet + +# types and variables +snippet map "Map type" b +map[${1:keytype}]${2:valtype} +endsnippet + +snippet : "Short variable declaration :=" !b +${1:name} := ${0:value} +endsnippet + +snippet var "Variable declaration" b +var ${1:name}${2/(.+)/ /}${2:type}${3: = ${0:value}} +endsnippet + +snippet vars "Variables declaration" b +var ( + ${1:name}${2/(.+)/ /}${2:type}${3: = ${0:value} } +) +endsnippet + +snippet json "JSON field" +\`json:"${1:displayName}"\` +endsnippet + +snippet er "Error clause " !b +if err != nil { + ${0} +} +endsnippet + +snippet errn "Error return " !b +if err != nil { + return err +} +${0} +endsnippet + +snippet errn, "Error return with two return values" !b +if err != nil { + return ${1:nil}, err +} +${0} +endsnippet + +snippet errn,, "Error return with three return values" !b +if err != nil { + return ${1:nil}, ${2:nil}, err +} +${0} +endsnippet + +snippet ok "OK statement" !b +if !ok { + ${0:${VISUAL}} +} +endsnippet + +snippet gof "Anonymous Goroutine" !b +go func() { + ${1} +}() +endsnippet + +snippet def "Anonymous Defer" !b +defer func() { + ${1} +}() +endsnippet + +snippet ff "Fmt Printf debug" !b +fmt.Printf("${1} %+v\n", $1) +endsnippet + +snippet fn "Fmt Println debug" !b +fmt.Println("${1}") +endsnippet + +snippet lf "Log Printf debug" !b +log.Printf("${1} %+v\n", $1) +endsnippet + +snippet ln "Log Println debug" !b +log.Println("${1}") +endsnippet + +snippet make "make allocation" !b +make(${1:Type}, ${2:size})${0} +endsnippet + +snippet test "test function" b +func Test${1:Function}(t *testing.T) { + ${2} +} +endsnippet + +# vim:ft=snippets: diff --git a/gosnippets/snippets/go.snip b/gosnippets/snippets/go.snip new file mode 100644 index 0000000..d4b16c5 --- /dev/null +++ b/gosnippets/snippets/go.snip @@ -0,0 +1,243 @@ +# shorthand variable declaration +snippet v + ${1} := ${2} +# variable initialization +snippet vr + var ${1:t} ${0:string} +# variable declaration +snippet var + var ${1} ${2} = ${3} +# variables declaration +snippet vars + var ( + ${1} ${2} = ${3} + ) +# append +snippet ap + append(${1:slice}, ${0:value}) +# bool +snippet bl + bool +# byte +snippet bt + byte +# break +snippet br + break +# channel +snippet ch + chan ${0:int} +# case +snippet cs + case ${1:value}: + ${0} +# const +snippet c + const ${1:NAME} = ${0:0} +# constants with iota +snippet co + const ( + ${1:NAME1} = iota + ${0:NAME2} + ) +# continue +snippet cn + continue +# defer +snippet df + defer ${0:func}() +# defer recover +snippet dfr + 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} +# int +snippet i + int +# import +snippet im + import ( + "${1:package}" + ) +# interface +snippet in + interface{} +# full interface snippet +snippet inf + interface ${1:name} { + ${2:/* methods */} + } +# if condition +snippet if + if ${1:/* condition */} { + ${2} + } +# else snippet +snippet el + else { + ${1} + } +# error snippet +snippet ir + if err != nil { + return err + } + ${0} +# false +snippet f + false +# fallthrough +snippet ft + fallthrough +# float +snippet fl + float32 +# float32 +snippet f3 + float32 +# float64 +snippet f6 + float64 +# if else +snippet ie + if ${1:/* condition */} { + ${2} + } else { + ${3} + } + ${0} +# for loop +snippet fo + for ${2:i} := 0; $2 < ${1:count}; $2${3:++} { + ${4} + } + ${0} +# for range loop +snippet fr + for ${1:k}, ${2:v} := range ${3} { + ${4} + } + ${0} +# function simple +snippet fun + func ${1:funcName}(${2}) ${3:error} { + ${4} + } + ${0} +# function on receiver +snippet fum + func (self ${1:type}) ${2:funcName}(${3}) ${4:error} { + ${5} + } + ${0} +# Fmt Printf debug +snippet ff + fmt.Printf("${1} %+v\n", ${0}) +# Fmt Println debug +snippet fn + fmt.Println("${1}") +# log printf +snippet lf + log.Printf("%${1:s}", ${2:var}) +# log printf +snippet lp + log.Println("${1}") +# make +snippet mk + make(${1:[]string}, ${0:0}) +# map +snippet mp + map[${1:string}]${0:int} +# main() +snippet main + func main() { + ${1} + } + ${0} +# new +snippet nw + new(${0:type}) +# panic +snippet pn + panic("${0:msg}") +# print +snippet pr + fmt.Printf("%${1:s}\n", ${2:var}) +# range +snippet rn + range ${0} +# return +snippet rt + return ${0} +# result +snippet rs + result +# select +snippet sl + select { + case ${1:v1} := <-${2:chan1} + ${3} + case ${4:v2} := <-${5:chan2} + ${6} + default: + ${0} + } +# string +snippet sr + string +# struct +snippet st + struct ${1:name} { + ${2:/* data */} + } + ${0} +# switch +snippet sw + switch ${1:var} { + case ${2:value1}: + ${3} + case ${4:value2}: + ${5} + default: + ${0} + } +snippet sp + fmt.Sprintf("%${1:s}", ${2:var}) +# true +snippet t + true +# goroutine named function +snippet g + go ${1:funcName}(${0}) +# goroutine anonymous function +snippet ga + go func(${1} ${2:type}) { + ${3:/* code */} + }(${0}) +# test function +snippet test + func Test${1:Function}(t *testing.T) { + ${2} + } diff --git a/indent/go.vim b/indent/go.vim new file mode 100644 index 0000000..faf4d79 --- /dev/null +++ b/indent/go.vim @@ -0,0 +1,65 @@ +" 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 + +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 += &sw + endif + if prevl =~# '^\s*\(case .*\|default\):$' + " previous line is part of a switch statement + let ind += &sw + endif + " TODO: handle if the previous line is a label. + + if thisl =~ '^\s*[)}]' + " this line closed a block + let ind -= &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 -= &sw + endif + + return ind +endfunction diff --git a/plugin/go.vim b/plugin/go.vim new file mode 100644 index 0000000..778e214 --- /dev/null +++ b/plugin/go.vim @@ -0,0 +1,182 @@ +" 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", + \ "golang.org/x/tools/cmd/goimports", + \ "code.google.com/p/rog-go/exp/cmd/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", + \ ] + +" Commands +command! GoErrCheck call go#errcheck#Run() + +command! GoInstallBinaries call s:GoInstallBinaries(-1) +command! GoUpdateBinaries call s:GoInstallBinaries(1) + +" LineEnding returns the correct line ending, based on the current fileformat +function! 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! IsWin() + let win = ['win16', 'win32', 'win32unix', 'win64', 'win95'] + for w in win + if (has(w)) + return 1 + endif + endfor + + return 0 +endfunction + +" PathSep returns the appropriate path separator based on OS. +function! PathSep() + if IsWin() + return ";" + endif + + return ":" +endfunction + +" DefaultGoPath returns the default GOPATH. +" If there is only one GOPATH it returns that, otherwise it returns the first one. +function! DefaultGoPath() + let go_paths = split($GOPATH, PathSep()) + + if len(go_paths) == 1 + return $GOPATH + endif + + return go_paths[0] +endfunction + +" GetBinPath returns the binary path of installed go tools +function! GetBinPath() + 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(DefaultGoPath() . "/bin/") + else + " could not find anything + endif + + return bin_path +endfunction + +" 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 = GetBinPath() + + " 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_bin_path + + 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("go get -u -v ".shellescape(pkg)) + if v:shell_error + echo "Error installing ". pkg . ": " . out + endif + endif + endfor + + " restore back! + let $PATH = old_path +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 + + if !executable('hg') + echohl Error | echomsg "vim.go: hg (mercurial) executable not found." | echohl None + return -1 + endif +endfunction + +" Autocommands +" ============================================================================ + +augroup vim-go + autocmd! + + " GoInfo automatic update + if get(g:, "go_auto_type_info", 0) + autocmd CursorHold *.go nested call go#complete#Info() + endif + + " code formatting on save + if get(g:, "go_fmt_autosave", 1) + autocmd BufWritePre *.go call go#fmt#Format(-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..c2d48f4 --- /dev/null +++ b/syntax/go.vim @@ -0,0 +1,280 @@ +" 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 and structs. +" +" - 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. + +" 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 = 1 +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 + +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 keyword goBuiltins append cap close complex copy delete imag len +syn keyword goBuiltins make new panic print println real recover +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=@goCommentGroup,@Spell + +hi def link goComment Comment +hi def link goTodo Todo + +" 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 start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup +syn region goRawString start=+`+ end=+`+ +syn match goFormatSpecifier /%[#0\-\ \+\*]*[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 "\<0x\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 Integer Number + +" Floating point +syn match goFloat "\<\d\+\.\d*\([Ee][-+]\d\+\)\?\>" +syn match goFloat "\<\.\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\+\.\d*\([Ee][-+]\d\+\)\?i\>" +syn match goImaginary "\<\.\d\+\([Ee][-+]\d\+\)\?i\>" +syn match goImaginary "\<\d\+[Ee][-+]\d\+i\>" + +hi def link goImaginary Number + +" 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 + syn match goSpaceError display "\(<-\)\@<=\s\+\(chan\>\)\@=" + " send-only annotation on chan type + syn match goSpaceError display "\(\/ + 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 + syn match goOperator /:=/ + syn match goOperator />=/ + syn match goOperator /<=/ + syn match goOperator /==/ + syn match goOperator /!=/ + syn match goOperator /+=/ + syn match goOperator /-=/ + syn match goOperator /\s>\s/ + syn match goOperator /\s<\s/ + syn match goOperator /\s+\s/ + syn match goOperator /\s-\s/ + syn match goOperator /\s\*\s/ + syn match goOperator /\s\/\s/ + syn match goOperator /\s%\s/ +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 + +" 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/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