diff --git a/vim/bundle/go/.coveragerc b/vim/bundle/go/.coveragerc new file mode 100644 index 0000000..9f46ed7 --- /dev/null +++ b/vim/bundle/go/.coveragerc @@ -0,0 +1,3 @@ +[run] +plugins = covimerage +data_file = .coverage.covimerage diff --git a/vim/bundle/go/.dockerignore b/vim/bundle/go/.dockerignore new file mode 100644 index 0000000..f12dac2 --- /dev/null +++ b/vim/bundle/go/.dockerignore @@ -0,0 +1,2 @@ +.local/ +.git/ diff --git a/vim/bundle/go/.editorconfig b/vim/bundle/go/.editorconfig new file mode 100644 index 0000000..2c22281 --- /dev/null +++ b/vim/bundle/go/.editorconfig @@ -0,0 +1,17 @@ +# http://EditorConfig.org + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + +[*.go] +indent_style = tab +indent_size = 4 + +[Makefile] +indent_style = tab +indent_size = 8 diff --git a/vim/bundle/go/.github/CONTRIBUTING.md b/vim/bundle/go/.github/CONTRIBUTING.md index 7266cce..edabf91 100644 --- a/vim/bundle/go/.github/CONTRIBUTING.md +++ b/vim/bundle/go/.github/CONTRIBUTING.md @@ -1,9 +1,12 @@ Thanks for improving vim-go! Before you dive in please read the following: 1. Please read our - [FAQ](https://github.com/fatih/vim-go/wiki/FAQ-Troubleshooting), it might - have answers for your problem -2. If you add a new feature please don't forget to update the documentation: - [doc/vim-go.txt](doc/vim-go.txt) -3. If it's a breaking change or exceed +100 lines please open an issue first - and describe the changes you want to make. + [Documentation](https://github.com/fatih/vim-go/blob/master/doc/vim-go.txt), + it might have a solution to your problem. +2. If you add a new feature then please don't forget to update the documentation: + [doc/vim-go.txt](https://github.com/fatih/vim-go/blob/master/doc/vim-go.txt). +3. If it's a breaking change or exceeds 100 lines of code then please open an + issue first and describe the changes you want to make. +4. See `:help go-development` for instructions on how to run and write tests. If + you add a new feature be sure you also include a test if feasible. + diff --git a/vim/bundle/go/.github/ISSUE_TEMPLATE.md b/vim/bundle/go/.github/ISSUE_TEMPLATE.md index 0adaff3..dd376ee 100644 --- a/vim/bundle/go/.github/ISSUE_TEMPLATE.md +++ b/vim/bundle/go/.github/ISSUE_TEMPLATE.md @@ -1,28 +1,22 @@ -### Actual behavior +### What did you do? (required. The issue will be **closed** when not provided.) -Write here what's happening ... -### Expected behavior +### What did you expect to happen? -Write here what you're expecting ... -### Steps to reproduce: +### What happened instead? -Please create a reproducible case of your problem. Re produce it -with a minimal `vimrc` with all plugins disabled and only `vim-go` -enabled: -1. -2. -3. +### Configuration (**MUST** fill this out): -### Configuration +* Vim version (first two lines from `:version`): -Add here your current configuration and additional information that might be -useful, such as: +* Go version (`go version`): + +* Go environment (`go env`): + +* vim-go version: + +* `vimrc` you used to reproduce (use a *minimal* vimrc with other plugins disabled; do not link to a 2,000 line vimrc): -* `vimrc` you used to reproduce -* vim version: -* vim-go version -* go version diff --git a/vim/bundle/go/.gitignore b/vim/bundle/go/.gitignore index 926ccaa..5c64849 100644 --- a/vim/bundle/go/.gitignore +++ b/vim/bundle/go/.gitignore @@ -1 +1,5 @@ -doc/tags +.DS_Store +/doc/tags +/.coverage.covimerage +/coverage.xml +*.pyc diff --git a/vim/bundle/go/.travis.yml b/vim/bundle/go/.travis.yml new file mode 100644 index 0000000..fe50e85 --- /dev/null +++ b/vim/bundle/go/.travis.yml @@ -0,0 +1,14 @@ +language: go +notifications: + email: false +matrix: + include: + - env: SCRIPT="test -c" VIM_VERSION=vim-7.4 + - env: SCRIPT="test -c" VIM_VERSION=vim-8.0 + - env: SCRIPT="test -c" VIM_VERSION=nvim + - env: SCRIPT=lint VIM_VERSION=vim-8.0 +install: + - ./scripts/install-vim $VIM_VERSION + - pip install --user vim-vint covimerage codecov +script: + - ./scripts/$SCRIPT $VIM_VERSION diff --git a/vim/bundle/go/.vintrc.yaml b/vim/bundle/go/.vintrc.yaml new file mode 100644 index 0000000..32d7dec --- /dev/null +++ b/vim/bundle/go/.vintrc.yaml @@ -0,0 +1,7 @@ +policies: + ProhibitUnnecessaryDoubleQuote: + enabled: false + ProhibitEqualTildeOperator: + enabled: false + ProhibitNoAbortFunction: + enabled: false diff --git a/vim/bundle/go/CHANGELOG.md b/vim/bundle/go/CHANGELOG.md new file mode 100644 index 0000000..17d9b46 --- /dev/null +++ b/vim/bundle/go/CHANGELOG.md @@ -0,0 +1,1093 @@ +## unplanned + +## 1.16 - (December 29, 2017) + +FEATURES: + +* Add `g:go_doc_url` to change the `godoc` server from `godoc.org` to a custom + private instance. Currently only `godoc -http` instances are supported. + [[GH-1957]](https://github.com/fatih/vim-go/pull/1957). +* New setting `g:go_test_prepend_name` (off by default) to add the failing test + name to the output of `:GoTest` + [[GH-1578]](https://github.com/fatih/vim-go/pull/1578). +* Support [denite.vim](https://github.com/Shougo/denite.nvim) for `:GoDecls[Dir]` + [[GH-1604]](https://github.com/fatih/vim-go/pull/1604). + +IMPROVEMENTS: + +* `:GoRename` is a bit smarter when automatically pre-filling values, and what + gets pre-filled can be configured with `g:go_gorename_prefill` option. + In addition `:GoRename ` now lists some common options. + [[GH-1465]](https://github.com/fatih/vim-go/pull/1465). +* Add support for `g:go_build_tags` to the `:GoTest` family of functions. + [[GH-1562]](https://github.com/fatih/vim-go/pull/1562). +* Pass `--tests` to gometalinter when autosaving and when a custom gometalinter + command has not been set. + [[GH-1563]](https://github.com/fatih/vim-go/pull/1563). +* Do not spam messages when command is run in a directory that does not exist. + [[GH-1527]](https://github.com/fatih/vim-go/pull/1527). +* Run `syntax sync fromstart` after `:GoFmt`; this should make syntax + highlighting break slightly less often after formatting code + [[GH-1582]](https://github.com/fatih/vim-go/pull/1582). +* `:GoDescribe` doesn't require a scope anymore + [[GH-1596]](https://github.com/fatih/vim-go/pull/1596). +* Add some standard snippets for + [vim-minisnip](https://github.com/joereynolds/vim-minisnip) + [[GH-1589]](https://github.com/fatih/vim-go/pull/1589). +* `g:go_snippet_engine` now defaults to `automatic` to use the first installed + snippet engine it can find. + [[GH-1589]](https://github.com/fatih/vim-go/pull/1589). +* Make sure temporary files created for `:GoFmt` end with `.go` suffix as this + is required by some Go formatting tools + [[GH-1601]](https://github.com/fatih/vim-go/pull/1601). + +BUG FIXES: + +* Fix compatibility with Vim version before 7.4.1546 + [[GH-1498]](https://github.com/fatih/vim-go/pull/1498). +* Don't resize godoc window if it's already visible + [[GH-1488]](https://github.com/fatih/vim-go/pull/1488). +* `:GoTestCompile` produces a test binary again. The test binary will be + written to a temporary directory to avoid polluting the user's working + directory. [[GH-1519]](https://github.com/fatih/vim-go/pull/1519) +* Fix incorrect `:GoSameIdsToggle` behavior when there were match groups + present, but none were goSameId. + [[GH-1538]](https://github.com/fatih/vim-go/pull/1538) +* Fix `gpl` snippet for UltiSnips. + [[GH-1535]](https://github.com/fatih/vim-go/pull/1535) +* Fix test output processing to correctly handle panics and log statements. + [[GH-1513]](https://github.com/fatih/vim-go/pull/1513) +* `:GoImpl` tab-completion would sometimes stop working + [[GH-1581]](https://github.com/fatih/vim-go/pull/1581). +* Add `g:go_highlight_function_arguments` to highlight function arguments. + [[GH-1587]](https://github.com/fatih/vim-go/pull/1587). +* Fix installation of `gocode` on MS-Windows. + [[GH-1606]](https://github.com/fatih/vim-go/pull/1606). +* Fix template creation for files in directories that don't exist yet. + [[GH-1618]](https://github.com/fatih/vim-go/pull/1618). +* Fix behavior of terminal windows and resize terminal windows correctly for + all valid `g:go_term_mode` values. + [[GH-1611]](https://github.com/fatih/vim-go/pull/1611). + +BACKWARDS INCOMPATIBILITIES: + +* Display a warning for Vim versions older than 7.4.1689. Older versions may + still work, but are not supported. You can use `let g:go_version_warning = 0` + to disable the warning. + [[GH-1524]](https://github.com/fatih/vim-go/pull/1524). +* `g:go_autodetect_gopath` is *disabled* by default, as support for `vendor` has + been in Go for a while.
+ Also change the implementation for `g:go_autodetect_gopath`; instead of manually + setting it before every command it will now be set with the `BufEnter` event, + and reset with the `BufLeave` event. This means that `$GOPATH` will be + changed for all commands run from Vim. + [[GH-1461]](https://github.com/fatih/vim-go/pull/1461) and + [[GH-1525]](https://github.com/fatih/vim-go/pull/1525). +* Update `:GoFillStruct` to check the current line (vs. the exact cursor + position) for a struct literal to fill. To support this, fillstruct made + [backwards imcompatible + changes](https://github.com/davidrjenni/reftools/pull/8). + [[GH-1607]](https://github.com/fatih/vim-go/pull/1607). + +## 1.15 - (October 3, 2017) + +FEATURES: + +* Add `:GoFillStruct` to fill a struct with all fields; uses + [`fillstruct`](https://github.com/davidrjenni/reftools/tree/master/cmd/fillstruct) + [[GH-1443]](https://github.com/fatih/vim-go/pull/1443). + +IMPROVEMENTS: + +* `:GoAddTags` and `:GoRemoveTags` now continue to process if there are + malformed individual struct tags (run `:GoUpdateBinaries` to update + `gomodifiytags` binary) [[GH-1401]](https://github.com/fatih/vim-go/pull/1401) +* `:GoAddTags` and `:GoRemoveTags` now shows a location list if there are + malformed struct tags (run `:GoUpdateBinaries` to update `gomodifiytags` + binary) [[GH-1401]](https://github.com/fatih/vim-go/pull/1401) +* Add folding of the package-level comment (enabled by default) and/or any + other comments (disabled by default) [[GH-1377]](https://github.com/fatih/vim-go/pull/1377). + [[GH-1428]](https://github.com/fatih/vim-go/pull/1428). +* Allow using :GoImpl on the type and struct parts too. Makes it a wee bit + easier to use [[GH-1386]](https://github.com/fatih/vim-go/pull/1386) +* `:GoDef` sets the path of new buffers as relative to the current directory + when appropriate, instead of always using the full path [[GH-1277]](https://github.com/fatih/vim-go/pull/1277). +* Syntax highlighting for variable declarations and assignments (disabled by default) + [[GH-1426]](https://github.com/fatih/vim-go/pull/1426) and + [[GH-1458]](https://github.com/fatih/vim-go/pull/1458). +* Add support for `:GoDecls[Dir]` in [unite.vim](https://github.com/Shougo/unite.vim) + [[GH-1391]](https://github.com/fatih/vim-go/pull/1391). +* Add support for [fzf.vim](https://github.com/junegunn/fzf.vim) in + `GoDecls[Dir]`. + [[GH-1437]](https://github.com/fatih/vim-go/pull/1437). +* Support relative imports for `:GoImpl` [[GH-1322]](https://github.com/fatih/vim-go/pull/1322). +* A new `g:go_list_type_commands` setting is added to individually set the list type for each command [[GH-1415]](https://github.com/fatih/vim-go/pull/1415). As en example: + + let g:go_list_type_commands = {"GoBuild": "quickfix", "GoTest": "locationlist"} +* Show unexpected errors better by expanding newlines and tabs + [[GH-1456]](https://github.com/fatih/vim-go/pull/1456). +* `:GoInstallBinaries` and `:GoUpdateBinaries` can now install/update only the + selected binaries (e.g. `:GoUpdateBinaries guru golint`) + [[GH-1467]](https://github.com/fatih/vim-go/pull/1467). + +BUG FIXES: + +* `:GoFmt` now (again) uses `locationlist` to show formatting errors instead of + `quickfix`. To change back to `locationlist` you can change it with the + setting `let g:go_list_type_commands = { "GoFmt": locationlist" }` [[GH-1415]](https://github.com/fatih/vim-go/pull/1415) +* Include comments in import block when folding is enabled [[GH-1387]](https://github.com/fatih/vim-go/pull/1387) +* Fix opening definitions in tabs [[GH-1400]](https://github.com/fatih/vim-go/pull/1400) +* Fix accidentally closing quickfix window from other commands if :GoFmt or autosave format was called [[GH-1407]](https://github.com/fatih/vim-go/pull/1407) +* Fix entering into insert mode after for term mode in nvim [[GH-1411]](https://github.com/fatih/vim-go/pull/1411) +* When using :GoImpl on type foo struct{} it would work, but with: + + type foo struct{ + } + + or with a struct with fields, it would create the generated methods inside the + struct [[GH-1386]](https://github.com/fatih/vim-go/pull/1386) +* `:GoImpl` output would include extra newline, and error would include + trailing newline from shell command: `vim-go: invalid receiver: "} *}"<00>`. + Fixed with [[GH-1386]](https://github.com/fatih/vim-go/pull/1386) +* Run `:GoMetaLinter` against the package of the open file [[GH-1414]](https://github.com/fatih/vim-go/pull/1414). +* The `g:go_doc_command` and `g:go_doc_options` to configure the command for + `:GoDoc` were documented but never referenced [[GH-1420]](https://github.com/fatih/vim-go/pull/1420). +* `go#package#FromPath()` didn't work correctly [[GH-1435]](https://github.com/fatih/vim-go/pull/1435). +* Fix race condition for `guru` based commands [[GH-1439]](https://github.com/fatih/vim-go/pull/1439). +* The `gohtmltmpl` filetype now sources the `html` ftplugin, so that `matchit`, + completion, and some other things work better. + [[GH-1442]](https://github.com/fatih/vim-go/pull/1442) +* Fix `:GoBuild` shell escaping [[GH-1450]](https://github.com/fatih/vim-go/pull/1450). +* Ensure fmt list gets closed when title cannot be checked [[GH-1474]](https://github.com/fatih/vim-go/pull/1474). + +BACKWARDS INCOMPATIBILITIES: + +* `:GoMetaLinter` now runs against the package of the open file instead of the + current working directory. This is so all commands behave the same relative + to the current open buffer. [[GH-1414]](https://github.com/fatih/vim-go/pull/1414) + +* `:GoImpl` now requires [`impl`](https://github.com/josharian/impl) version + 3fb19c2c or newer (released June 13, 2017); use `:GoUpdateBinaries` to make + sure that you've got a recent version [[GH-1322]](https://github.com/fatih/vim-go/pull/1322) + +## 1.14 - (August 6, 2017) + +FEATURES: + +* We now have folding support based on Go syntax. To enable it you have to set + the following Vim setting: `set foldmethod=syntax`. Currently it folds blocks + (`{ }`), `import`, `var`, and `const` blocks, and package-level comments. + These can be individually disabled/enabled if desired. For more info please + read the documentation for the `g:go_fold_enable` setting. [[GH-1339]](https://github.com/fatih/vim-go/pull/1339) + [[GH-1377]](https://github.com/fatih/vim-go/pull/1377) +* `:GoFiles` accepts now an argument to change the type of files it can show. + By default it shows`.go source files` but now it can be changed to show + various kind of files. The full list can be seen via `go list --help` under + the `// Source Files` section [[GH-1372]](https://github.com/fatih/vim-go/pull/1372) i.e: + +``` +:GoFiles CgoFiles // shows .go sources files that import "C" +:GoFiles TestGoFiles // shows _test.go files in package +:GoFiles IgnoredGoFiles // shows .go sources ignored due to build constraints +etc.. +``` + +IMPROVEMENTS + +* Files created with `_test.go` extension have a new template with a ready to + go test function. The template can be changed with the + `g:go_template_test_file` setting. [[GH-1318]](https://github.com/fatih/vim-go/pull/1318) +* Improve performance for highly used operations by caching `go env` calls [[GH-1320]](https://github.com/fatih/vim-go/pull/1320) +* `:GoCoverage` can accept arguments now. i.e: `:GoCoverage -run TestFoo` [[GH-1326]](https://github.com/fatih/vim-go/pull/1326) +* `:GoDecls` and `:GoDeclsDir` shows a warning if [ctrlp.vim](https://github.com/ctrlpvim/ctrlp.vim) is not installed +* `:GoBuild` now compiles the package with the `-i` flag added. This means that subsequent calls are much more faster due caching of packages [[GH-1330]](https://github.com/fatih/vim-go/pull/1330) +* `:GoCoverage` echos now the progress if `g:go_echo_command_info` is enabled [[GH-1333]](https://github.com/fatih/vim-go/pull/1333) +* Add `g:go_doc_max_height` setting to control the maximum height of the window created by `:GoDoc` and `K` mapping [[GH-1335]](https://github.com/fatih/vim-go/pull/1335) +* The `af` text object is able to include the assignment variable for anonymous functions. Can be disabled with `g:go_textobj_include_variable = 0` [[GH-1345]](https://github.com/fatih/vim-go/pull/1345) +* Add `g:go_list_autoclose` setting to prevent closing the quickfix/location list after zero items [[GH-1361]](https://github.com/fatih/vim-go/pull/1361) +* Cursor is now adjusted and locked to the correct line when `goimports` is used for autosave [[GH-1367]](https://github.com/fatih/vim-go/pull/1367) +* Complement the path of command for different situations of Cygwin environment [[GH-1394]](https://github.com/fatih/vim-go/pull/1394) +* Show message when using :GoDef and opening a new buffer [[GH-1385]](https://github.com/fatih/vim-go/pull/1385) + + +BUG FIXES: + +* Fix obtaining package's import path for the current directory. This fixes some issues we had if the user was using multiple GOPATH's [[GH-1321]](https://github.com/fatih/vim-go/pull/1321) +* Fix documentation for vim-go & syntastic integration for errcheck using [[GH-1323]](https://github.com/fatih/vim-go/pull/1323) +* Fix showing an output if a test has finished when `:GoTest` is called [[GH-1327]](https://github.com/fatih/vim-go/pull/1327) +* Fix warning when goimports doesn't support srcdir [[GH-1344]](https://github.com/fatih/vim-go/pull/1344) +* Fix broken code folding with go_highlight_types [[GH-1338]](https://github.com/fatih/vim-go/pull/1338) +* Fix blocking the ui when swapfile is enabled and `:GoFmt` is called (either manually or via autosave) [[GH-1362]](https://github.com/fatih/vim-go/pull/1362) +* Fix getting bin paths for binaries if GOPATH was not set and Go version =>1.7 was used [[GH-1363]](https://github.com/fatih/vim-go/pull/1363) +* Fix picking up the correct list type for showing `:GoFmt` errors [[GH-1365]](https://github.com/fatih/vim-go/pull/1365) +* Fix auto detecting of GOPATH for import paths with string 'src' (i.e: `GOPATH/src/github.com/foo/src/bar`) [[GH-1366]](https://github.com/fatih/vim-go/pull/1366) +* Fix showing an empty window if `gogetdoc` was not found [[GH-1379]](https://github.com/fatih/vim-go/pull/1379) +* Fix commands not being executed if paths would include spaces (binary name, GOPATH, file itself, etc..) [[GH-1374]](https://github.com/fatih/vim-go/pull/1374) +* Fix showing correct message when editing a new file [[GH-1371]](https://github.com/fatih/vim-go/pull/1371) +* Fix filepaths in the quickfix list for :GoVet [[GH-1381]](https://github.com/fatih/vim-go/pull/1381) +* Run :GoLint against the package of the open file [[GH-1382]](https://github.com/fatih/vim-go/pull/1382) + +BACKWARDS INCOMPATIBILITIES: + +* `:GoFmt` now uses `quickfix` to show formatting errors instead of + `locationlist`. To change back to `locationlist` you can change it with the + setting `let g:go_list_type = "locationlist"` [[GH-1365]](https://github.com/fatih/vim-go/pull/1365) +* `:GoLint` now runs against the package of the open file instead of the + current working directory. This is so all commands behave the same relative + to the current open buffer. For more info check the [comment + here](https://github.com/fatih/vim-go/issues/1375#issuecomment-317535953) + [[GH-1382]](https://github.com/fatih/vim-go/pull/1382) + +## 1.13 - (June 6, 2017) + +FEATURES: + +* New `:GoKeyify` command that turns unkeyed struct literals into keyed struct literals. [[GH-1258]](https://github.com/fatih/vim-go/pull/1258). i.e: + +``` +Example{"foo", "bar", "qux"} +``` + +will be converted to: + +``` +Example{ + foo: "foo", + bar: "bar", + qux: "qux", +} +``` + +Checkout the demo here: https://twitter.com/fatih/status/860410299714764802 + + +* New `g:go_addtags_transform` setting to change the transform rule (snakecase, camelcase, etc..) for `:GoAddTags` command [[GH-1275]](https://github.com/fatih/vim-go/pull/1275) +* New snippet shortcut assigned to `ife` that expands to `if err := foo(); err != nil { ... }` [[GH-1268]](https://github.com/fatih/vim-go/pull/1268) + +IMPROVEMENTS + +* :GoMetaLinter can now exclude linters with the new `g:go_metalinter_excludes` option [[GH-1253]](https://github.com/fatih/vim-go/pull/1253) +* Override `` mapping so `:GoDef` is used by default (as we do the same for `CTRL-]`, `gd`, etc. [[GH-1264]](https://github.com/fatih/vim-go/pull/1264) +* add support for `go_list_type` setting in `:GoFmt` and `:GoImports` commands [[GH-1304]](https://github.com/fatih/vim-go/pull/1304) +* add support for `go_list_type` setting in `:GoMetaLinter` commands [[GH-1309]](https://github.com/fatih/vim-go/pull/1309) +* `go_fmt_options` can be now a dictionary to allow us to specifcy the + options for multiple binaries [[GH-1308]](https://github.com/fatih/vim-go/pull/1308). i.e: + +``` + let g:go_fmt_options = { + \ 'gofmt': '-s', + \ 'goimports': '-local mycompany.com', + \ } +``` +* If win-vim(x64) with Cygwin is used, `cygpath` is used for constructing the paths [[GH-1092]](https://github.com/fatih/vim-go/pull/1092) + +BUG FIXES: + +* job: fix race between channel close and job exit [[GH-1247]](https://github.com/fatih/vim-go/pull/1247) +* internal: fix system calls when using tcsh [[GH-1276]](https://github.com/fatih/vim-go/pull/1276) +* path: return the unmodified GOPATH if autodetect is disabled [[GH-1280]](https://github.com/fatih/vim-go/pull/1280) +* fix jumping to quickfix window when autom gometalinter on save was enabled [[GH-1293]](https://github.com/fatih/vim-go/pull/1293) +* fix highlighting for `interface` and `structs` words when `go_highlight_types` is enabled [[GH-1301]](https://github.com/fatih/vim-go/pull/1301) +* fix cwd for running `:GoRun` when used with neovim [[GH-1296]](https://github.com/fatih/vim-go/pull/1296) +* `:GoFmt` handles files that are symlinked into GOPATH better (note that this behaviour is discouraged, but we're trying our best to handle all edge case :)) [[GH-1310]](https://github.com/fatih/vim-go/pull/1310) +* `:GoTest` is able to parse error messages that include a colon `:` [[GH-1316]](https://github.com/fatih/vim-go/pull/1316) +* `:GoTestCompile` under the hood doesn't produces a test binary anymore. Sometimes a race condition would happen which would not delete the test binary. [[GH-1317]](https://github.com/fatih/vim-go/pull/1317) +* `:GoDef` jumps now to definition for build tags defined with `:GoBuildTags` (only guru) [[GH-1319]](https://github.com/fatih/vim-go/pull/1319) + +BACKWARDS INCOMPATIBILITIES: + +* `:GoLint` works on the whole directory instead of the current file. To use it for the current file give it as an argument, i.e `:GoLint foo.go` [[GH-1295]](https://github.com/fatih/vim-go/pull/1295) +* `go_snippet_case_type` is removed in favor of the new `go_addtags_transform` setting [[GH-1299]](https://github.com/fatih/vim-go/pull/1299) +* `go_imports_bin` is removed to avoid confusion as it would lead to race + conditions when set to `gofmt` along with the usage of `go_fmt_command` + [[GH-1212]](https://github.com/fatih/vim-go/pull/1212) [[GH-1308]](https://github.com/fatih/vim-go/pull/1308) +* commands such as `:GoTest` has been refactored for easy maintainability. If + you use any custom script that was using the function `go#cmd#Test`, it + should be renamed to `go#test#Test` + +## 1.12 - (March 29, 2017) + +FEATURES: + +* New `:GoAddTags` and `:GoRemoveTags` command based on the tool + [gomodifytags](https://github.com/fatih/gomodifytags). This fixes many old + bugs that were due prior regexp based implementation. For the usage please + read the docs and checkout the demo at: + https://github.com/fatih/vim-go/pull/1204 [[GH-1204]](https://github.com/fatih/vim-go/pull/1204) +* Add new `errl` snippet that expands to [[GH-1185]](https://github.com/fatih/vim-go/pull/1185): + +``` +if err != nil { + log.Fatal(err) +} +``` +* New `:GoBuildTags` command to change build tags for tools such as `guru`, + `gorename`, etc ... There is also a new setting called `g:go_build_tags` + [[GH-1232]](https://github.com/fatih/vim-go/pull/1232) + +IMPROVEMENTS: + +* vim-go works now even if GOPATH is not set (starting with Go 1.8) [[GH-1248]](https://github.com/fatih/vim-go/pull/1248) +* Lowercase `` in mappings examples for consistent documentation across the README [[GH-1192]](https://github.com/fatih/vim-go/pull/1192) +* All of files should be written in utf-8 if the file will be passed to external command. [[GH-1184]](https://github.com/fatih/vim-go/pull/1184) +* `:GoAddTags` is now able to add options to existing tags with the syntax + `:GoAddTags key,option`, i.e: `:GoAddTags json,omitempty` [[GH-985]](https://github.com/fatih/vim-go/pull/985) +* Document 'noshowmode' requirement for echo_go_info [[GH-1197]](https://github.com/fatih/vim-go/pull/1197) +* Improve godoc view for vertical splits [[GH-1195]](https://github.com/fatih/vim-go/pull/1195) +* Set GOPATH for both possible go guru execution paths (sync and async) [[GH-1193]](https://github.com/fatih/vim-go/pull/1193) +* Improve docs for :GoDef usage [[GH-1242]](https://github.com/fatih/vim-go/pull/1242) +* Highlight trimming syntax for Go templates [[GH-1235]](https://github.com/fatih/vim-go/pull/1235) + +BUG FIXES: + +* Honor `g:go_echo_command_info` when dispatching builds in neovim [[GH-1176]](https://github.com/fatih/vim-go/pull/1176) +* Fix `:GoBuild` error in neovim due to invalid jobcontrol handler function + signatures (`s:on_stdout`, `s:on_stderr`)[[GH-1176]](https://github.com/fatih/vim-go/pull/1176) +* Update statusline before and after `go#jobcontrol#Spawn` command is executed [[GH-1176]](https://github.com/fatih/vim-go/pull/1176) +* Correctly report the value of the 'g:go_guru_tags' variable [[GH-1177]](https://github.com/fatih/vim-go/pull/1177) +* Ensure no trailing `:` exist in GOPATH detection if initial GOPATH is not set [[GH-1194]](https://github.com/fatih/vim-go/pull/1194) +* Fix `:GoAddTags` to allow modifying existing comments [[GH-984]](https://github.com/fatih/vim-go/pull/984) +* Fix `:GoAddTags` to work with nested structs [[GH-990]](https://github.com/fatih/vim-go/pull/990) +* Fix `:GoAddTags` adding tags twice for existing tags [[GH-1064]](https://github.com/fatih/vim-go/pull/1064) +* Fix `:GoAddTags` not working for fields of types `interface{}` [[GH-1091]](https://github.com/fatih/vim-go/pull/1091) +* Fix `:GoAddTags` not working for fields with one line comments [[GH-1181]](https://github.com/fatih/vim-go/pull/1181) +* Fix `:GoAddTags` not working if any field comment would contain `{}` [[GH-1189]](https://github.com/fatih/vim-go/pull/1189) +* Respect go_fmt_options when running goimports [[GH-1211]](https://github.com/fatih/vim-go/pull/1211) +* Set the filename in the location-list when there is an error with :GoFmt [[GH-1199]](https://github.com/fatih/vim-go/pull/1199) +* Fix `:GoInstall` to accept additional arguments if async mode was enabled [[GH-1246]](https://github.com/fatih/vim-go/pull/1246) + +BACKWARDS INCOMPATIBILITIES: + +* The command `:GoGuruTags` is removed in favour of the new command + `:GoBuildTags`. This command will be used now not just for `guru`, also for + all new commands such as `gorename` [[GH-1232]](https://github.com/fatih/vim-go/pull/1232) +* The setting `g:go_guru_tags` is removed in favour of the new setting + `g:go_build_tags` [[GH-1232]](https://github.com/fatih/vim-go/pull/1232) + + +## 1.11 - (January 9, 2017) + +FEATURES: + +* Travis test integration has been added. Now any file that is added as + `_test.vim` will be automatically tested in for every Pull Request + (just like how we add tests to Go with `_test.go`). Going forward this will + tremendously increase the stability and decrease the maintenance burden of + vim-go. [[GH-1157]](https://github.com/fatih/vim-go/pull/1157) +* Add new `g:go_updatetime` setting to change the default updatetime (which was hardcoded previously) [[GH-1055]](https://github.com/fatih/vim-go/pull/1055) +* Add new `g:go_template_use_pkg` setting to enable to use cwd as package name instead of basic template file [[GH-1124]](https://github.com/fatih/vim-go/pull/1124) + +IMPROVEMENTS: + +* Add `statusline` support for `:GoMetaLinter` [[GH-1120]](https://github.com/fatih/vim-go/pull/1120) +* Quickfix and Location lists contain now a descriptive title (requires at least Vim `7.4.2200`)[[GH-1004]](https://github.com/fatih/vim-go/pull/1004) +* Check `go env GOPATH` as well for `:GoInstallBinaries` as Go has now a default path for GOPATH ("~/go")starting with 1.8 [[GH-1152]](https://github.com/fatih/vim-go/pull/1152) +* `:GoDocBrowser` now also works on import paths [[GH-1174]](https://github.com/fatih/vim-go/pull/1174) + +BUG FIXES: + +* Always use full path to detect packages to be shown in statusline [[GH-1121]](https://github.com/fatih/vim-go/pull/1121) +* Use `echom` to persist errors in case of multiple echos [[GH-1122]](https://github.com/fatih/vim-go/pull/1122) +* Fix a race condition where a quickfix window was not closed if a job has succeeded [[GH-1123]](https://github.com/fatih/vim-go/pull/1123) +* Do not expand coverage arguments for non job execution of `:GoCoverage` [[GH-1127]](https://github.com/fatih/vim-go/pull/1127) +* `:GoCoverage` doesn't mess up custom syntax anymore [[GH-1128]](https://github.com/fatih/vim-go/pull/1128) +* Disable autoformat for `asm` files as they might be non Go ASM format [[GH-1141]](https://github.com/fatih/vim-go/pull/1141) +* Fix indentation broken when using a action with a minus sign like `{{-` [[GH-1143]](https://github.com/fatih/vim-go/pull/1143) +* Fix breaking Neovim change of passing less arguments to callbacks [[GH-1145]](https://github.com/fatih/vim-go/pull/1145) +* Fix `guru` commands if custom build tags were set [[GH-1136]](https://github.com/fatih/vim-go/pull/1136) +* Fix referencing a non defined variable for async commands when bang (!) was used +* Fix `:GoDef` failing for a modified buffer if `hidden` was not set [[GH-1132]](https://github.com/fatih/vim-go/pull/1132) +* Fix `:GoDefStack` to allow popping from jump list when buffer is modified [[GH-1133]](https://github.com/fatih/vim-go/pull/1133) +* Improve internal defining of functions and referencing them for async operations [[GH-1155]](https://github.com/fatih/vim-go/pull/1155) +* Fix `:GoMetaLinter` failing if `go_metalinter_command` is set. [[GH-1160]](https://github.com/fatih/vim-go/pull/1160) +* Fix `:GoMetaLinter`'s `go_metalinter_deadline` setting for async mode [[GH-1146]](https://github.com/fatih/vim-go/pull/1146) + +BACKWARDS INCOMPATIBILITIES: + +* The following syntax options are now disabled by default. If you're using them be sure to set them in your .vimrc [[GH-1167]](https://github.com/fatih/vim-go/pull/1167) + +```viml +g:go_highlight_array_whitespace_error +g:go_highlight_chan_whitespace_error +g:go_highlight_extra_types +g:go_highlight_space_tab_error +g:go_highlight_trailing_whitespace_error +``` + + + +## 1.10 (November 24, 2016) + +FEATURES: + +* **Vim 8.0 support!** This is the initial version to add Vim 8.0 based support to + all basic commands (check out below for more information). With time we'll + going to extend it to other commands. All the features are only enabled if + you have at least Vim 8.0.0087. Backwards compatible with Vim 7.4.x. + If you see any problems, please open an issue. + +* We have now a [logo for vim-go](https://github.com/fatih/vim-go/blob/master/assets/vim-go.png)! Thanks to @egonelbre for his work on this. +* `:GoBuild`, `:GoTest`, `:GoTestCompile`, `:GoInstall` commands are now fully + async. Async means it doesn't block your UI anymore. If the command finished + it echoes the status. For a better experience use the statusline information + (more info below) + +* `:GoCoverage` and `:GoCoverageBrowser` commands are fully async. +* `:GoDef` is fully async if `guru` is used as command. +* `:GoRename` is fully async . + +* `:GoMetaLinter` is fully asnyc. Also works with the current autosave linting + feature. As a reminder, to enable auto linting on save either call + `:GoMetaLinterAutoSaveToggle` (temporary) or add `let + g:go_metalinter_autosave = 1` (persistent) to your virmc). + +* All `guru` commands run asynchronously if Vim 8.0 is being used. Current + Commands: + * GoImplements + * GoWhicherrs + * GoCallees + * GoDescribe + * GoCallers + * GoCallstack + * GoFreevars + * GoChannelPeers + * GoReferrers + +* `:GoSameIds` also runs asynchronously. This makes it useful especially for + auto sameids mode. In this mode it constantly evaluates the identifier under the + cursor whenever it's in hold position and then calls :GoSameIds. As a + reminder, to enable auto info either call `:GoSameIdsAutoToggle`(temporary) + or add `let g:go_auto_sameids = 1` (persistent) to your vimrc. + +* `:GoInfo` is now non blocking and works in async mode if `guru` is used in + `g:go_info_mode`. This makes it useful especially for autoinfo mode. In this + mode it constantly evaluates the identifier under the cursor whenever it's in + hold position and then calls :GoInfo. As a reminder, to enable auto info + either call `:GoAutoTypeInfoToggle`(temporary) or add `let + g:go_auto_type_info = 1` (persistent) to your vimrc. To use `guru` instead of + `gocode` add following to your vimrc: `let g:go_info_mode = 'guru'` + + The `guru` is more accurate and reliabed due the usage of `guru` describe. It + doesn't rely on `pkg/` folder like `gocode` does. However it's slower than + `gocode` as there is no caching mechanism in `guru` yet. + +* **New**: Statusline function: `go#statusline#Show()` which can be plugged into + the statusline bar. Works only with vim 8.0. It shows all asynchronously + called functions status real time. Checkout it in action: + https://twitter.com/fatih/status/800473735467847680. To enable it add the + following to your `vimrc`. If you use lightline, airline, .. check out their + respective documentation on how to add a custom function: + +```viml +" go command status (requires vim-go) +set statusline+=%#goStatuslineColor# +set statusline+=%{go#statusline#Show()} +set statusline+=%* +``` + +IMPROVEMENTS: + +* **:GoDocBrowser** is now capable to to understand the identifier under the cursor (just like :GoDoc) +* Function calls are now highlighted as well when `g:go_highlight_functions` is enabled [[GH-1048]](https://github.com/fatih/vim-go/pull/1048) +* Add completion support for un-imported packages. This allows to complete even + if the package is not imported. By default it's disabled, enable by adding + `let g:go_gocode_unimported_packages = 1` [[GH-1084]](https://github.com/fatih/vim-go/pull/1084) +* Tools that embeds GOROOT into their binaries do not work when people update + their Go version and the GOROOT contains the vesion as part of their path + (i.e: `/usr/local/Cellar/go/1.7.2/libexec`, [more + info](https://blog.filippo.io/stale-goroot-and-gorebuild/)) . This is now + fixed by introducing automatic GOROOT set/unset before each tool invoke. + [[GH-954]](https://github.com/fatih/vim-go/pull/954) +* Added new setting `g:go_echo_go_info` to enable/disable printing identifier + information when completion is done [[GH-1101]](https://github.com/fatih/vim-go/pull/1101) +* Added new `go_echo_command_info` setting is added, which is enabled by + default. It's just a switch for disabling messages of commands, such as + `:GoBuild`, `:GoTest`, etc.. Useful to *disable* if `go#statusline#Show()` is + being used in Statusline, to prevent to see duplicates notifications. +* goSameId highlighting is now linked to `Search`, which is much more clear as + it changes according to the users colorscheme +* Add plug mapping `(go-lint)` for :GoLint [[GH-1089]](https://github.com/fatih/vim-go/pull/1089) + + +BUG FIXES: + +* Change back nil and iota highlighting color to the old type [[GH-1049]](https://github.com/fatih/vim-go/pull/1049) +* Fix passing arguments to `:GoBuild` while using NeoVim [[GH-1062]](https://github.com/fatih/vim-go/pull/1062) +* Do not open a split if `:GoDef` is used on a modified file [[GH-1083]](https://github.com/fatih/vim-go/pull/1083) +* Highlight nested structs correctly [[GH-1075]](https://github.com/fatih/vim-go/pull/1075) +* Highlight builtin functions correctly if `g:go_highlight_functions` is enabled [[GH-1070]](https://github.com/fatih/vim-go/pull/1070) +* Fix `:GoSameIds` highlighting if a new buffer is opened in the same window [[GH-1067]](https://github.com/fatih/vim-go/pull/1067) +* Internal: add `abort` to all vim function to return in case of errors [[GH-1100]](https://github.com/fatih/vim-go/pull/1100) +* Fix `:GoCoverage` to be executed if working dir is not inside the test dir [[GH-1033]](https://github.com/fatih/vim-go/pull/1033) + +BACKWARDS INCOMPATIBILITIES: + +* remove vim-dispatch and vimproc.vim support. vim 8.0 has now the necessary + API to invoke async jobs and timers. Going forward we should use those. Also + this will remove the burden to maintain compatibility with those plugins. + +* `go#jobcontrol#Statusline()` is removed in favor of the new, global and + extensible `go#statusline#Show()` + +## 1.9 (September 13, 2016) + +IMPROVEMENTS: + +* **guru** uses now the `-modified` flag, which allows us use guru on modified + buffers as well. This affects all commands where `guru` is used. Such as + `:GoDef`, `:GoReferrers`, etc.. [[GH-944]](https://github.com/fatih/vim-go/pull/944) +* **:GoDoc** uses now the `-modified` flag under the hood (for `gogetdoc), which allows us to get documentation for the identifier under the cursor ina modified buffer. [[GH-1014]](https://github.com/fatih/vim-go/pull/1014) +* Cleanup and improve documentation [[GH-987]](https://github.com/fatih/vim-go/pull/987) +* Add new `g:go_gocode_socket_type` setting to change the underlying socket type passed to `gocode`. Useful to fallback to `tcp` on cases such as Bash on Windows [[GH-1000]](https://github.com/fatih/vim-go/pull/1000) +* `:GoSameIds` is now automatically re-evaluated in cases of buffer reloads (such as `:GoRename`) [[GH-998]](https://github.com/fatih/vim-go/pull/998) +* Improve docs about `go_auto_sameids` [[GH-1017]](https://github.com/fatih/vim-go/pull/1017) +* Improve error message by printing the full path if an incompatible `goimports` is being used [[GH-1006]](https://github.com/fatih/vim-go/pull/1006) +* `iota` and `nil` are now highlighted correctly and are not treated as booleans [[GH-1030]](https://github.com/fatih/vim-go/pull/1030) + +BUG FIXES: + +* Fix system calls on Windows [[GH-988]](https://github.com/fatih/vim-go/pull/988) +* Fix :GoSameIds and :GoCoverage for light background and after changing color schemes [[GH-983]](https://github.com/fatih/vim-go/pull/983) +* Fix TagBar and `GoCallers` for Windows user [[GH-999]](https://github.com/fatih/vim-go/pull/999) +* Set updatetime for for `auto_sameids` feature as well [[GH-1016]](https://github.com/fatih/vim-go/pull/1016) +* Update docs about missing `go_highlight_generate_tags` setting [[GH-1023]](https://github.com/fatih/vim-go/pull/1023) +* Fix updating the jumplist if `:GoDef` is used [[GH-1029]](https://github.com/fatih/vim-go/pull/1029) +* Fix highlighting literal percent sign (`%%`) in strings [[GH-1011]](https://github.com/fatih/vim-go/pull/1011) +* Fix highlighting of nested fields [[GH-1007]](https://github.com/fatih/vim-go/pull/1007) +* Fix checking for `exepath` feature for the upcoming vim 8.0 release [[GH-1046]](https://github.com/fatih/vim-go/pull/1046) + +BACKWARDS INCOMPATIBILITIES: + +* Rename `GoMetalinterAutoSaveToggle` to `GoMetaLinterAutoSaveToggle` to make it compatible with the existing `:GoMetaLinter` command [[GH-1020]](https://github.com/fatih/vim-go/pull/1020) + +## 1.8 (July 31, 2016) + +FEATURES: +* New **`:GoAddTags`** command that adds field tags for the fields of a struct automatically based on the field names. Checkout the demo to see it in action: https://twitter.com/fatih/status/759822857773907968 [[GH-971]](https://github.com/fatih/vim-go/pull/971) +* The snippet expansion `json` is now much more smarter. It pre populates the placeholder according to the first word and it also applies `snake_case` or `camelCase` conversion. Together with `:GoAddTags` it gives `vim-go` users flexible ways of populating a field tag. Checkout the demo to see it in action: https://twitter.com/fatih/status/754477622042689536 [[GH-927]](https://github.com/fatih/vim-go/pull/927) +* New **`:GoSameIds`** command. When called highlights all same identifiers in the current file. Can be also enabled to highlight identifiers automatically (with `:GoSameIdsAutoToggle` or `g:go_auto_sameids`). Checkout the demo to see it in action: https://twitter.com/fatih/status/753673709278339072. [[GH-936]](https://github.com/fatih/vim-go/pull/936) +* New **`:GoWhicherrs`** command. It shows all possible values of the selected error variable. [[GH-948]](https://github.com/fatih/vim-go/pull/948) +* Add new `errp` snippet to expand an `if err != nil { panic() }` clause [[GH-926]](https://github.com/fatih/vim-go/pull/926) +* If you open a new buffer with a Go filename it get automatically populated based on the directory. If there are no Go files a simple main package is created, otherwise the file will include the package declaration line based on the package in the current directory. Checkout the demo to see it in action: https://twitter.com/fatih/status/748333086643994624. This is enabled by default. Can be disabled with `let g:go_template_autocreate = 0`. You can use your own template with `let g:go_template_file = "foo.go"` and putting the file under the `templates/` folder. [[GH-918]](https://github.com/fatih/vim-go/pull/918) +* Added new toggle commands to enable/disable feature that run for your + automatic. For example if you have `let g:go_auto_type_info = 1` enabled, you + can now easily enable/disable it on the fly. Support added with the following + commands: `:GoAutoTypeInfoToggle`, `:GoFmtAutoSaveToggle`, + `:GoAsmFmtAutoSaveToggle`, `:GoMetalinterAutoSaveToggle`, + `:GoTemplateAutoCreateToggle` [[GH-945]](https://github.com/fatih/vim-go/pull/945) + + +IMPROVEMENTS: +* `:GoDoc` accepts arguments now which are passed directly to `godoc`. So usages like `:GoDoc flag` works again (it was changed in previous versions [[GH-894]](https://github.com/fatih/vim-go/pull/894) +* `:GoDef` works now for modified files as well [[GH-910]](https://github.com/fatih/vim-go/pull/910) +* Internal: pass filename to the `--srcdir` flag to enable upcoming `goimports` features [[GH-957]](https://github.com/fatih/vim-go/pull/957) +* Internal: fix indentations on all files to **2-spaces/no tabs**. This is now the default vim-go style across all VimL files [[GH-915]](https://github.com/fatih/vim-go/pull/915) +* Internal: autocmd settings can be now dynamically enabled/disabled [[GH-939]](https://github.com/fatih/vim-go/pull/939) +* Internal: automatically detect `GOPATH` for :GoInstall [[GH-980]](https://github.com/fatih/vim-go/pull/980) +* Internal: shell executions uses now by default `sh` and then resets it back to the user preference. [[GH-967]](https://github.com/fatih/vim-go/pull/967) +* Syntax: improved syntax highglighting performance for methods, fields, structs and interface type declarations [[GH-917]](https://github.com/fatih/vim-go/pull/917) +* Syntax: moved `:GoCoverage` highlight definition into go's syntax file for more customizability [[GH-962]](https://github.com/fatih/vim-go/pull/962) + + +BUG FIXES: + +* Escape `#` characters when opening URL's, as it's handled as alternative file in vim [[GH-895]](https://github.com/fatih/vim-go/pull/895) +* Fix typos in `doc/vim-go.txt` about usages of syntax highglightings [[GH-897]](https://github.com/fatih/vim-go/pull/897) +* Fix `:GoCoverage` not running for Neovim [[GH-899]](https://github.com/fatih/vim-go/pull/899) +* Fix `:GoFmt` not picking up `-srcdir` if the command was set to use `goimports` [[GH-904]](https://github.com/fatih/vim-go/pull/904) +* Fix `:GoTestCompile` to not leave behind artifacts if the cwd and the test files's directory do not match [[GH-909]](https://github.com/fatih/vim-go/pull/909) +* Fix `:GoDocBrowser` to not fail if godoc doesn't exist [[GH-920]](https://github.com/fatih/vim-go/pull/920) +* Fix `:GoFmt` to not change the permissions of saved file. Now original file permissions are restored [[GH-922]](https://github.com/fatih/vim-go/pull/922) + +BACKWARDS INCOMPATIBILITIES: + +* `g:go_highlight_structs` and `g:go_highlight_interface` are removed in favor of `g:go_highlight_types` [[GH-917]](https://github.com/fatih/vim-go/pull/917) + + +## 1.7.1 (June 7, 2016) + +BUG FIXES: +* Fixed typo in `syntax/go.vim` file from `go:go_highlight_fields` to `g:go_highlight_fields` + +## 1.7 (June 7, 2016) + +FEATURES: + +* New **`:GoImpl`** command that generates method stubs for implementing an interface. Checkout the [demo](https://twitter.com/fatih/status/729991365581545472) to see how it works. [[GH-846]](https://github.com/fatih/vim-go/pull/846) +* `godef` support is added back as an optional setting. By default `:GoDef` still uses `guru`, but can be changed to `godef` by adding the option: `let g:go_def_mode = 'godef'` [[GH-888]](https://github.com/fatih/vim-go/pull/888) +* New `` and `]>` shortcuts to split current window and jumpt to the identifier under cursor. [[GH-838]](https://github.com/fatih/vim-go/pull/838) +* New syntax setting" `g:go_highlight_fields` that highlights struct field references [[GH-854]](https://github.com/fatih/vim-go/pull/854) + +IMPROVEMENTS: + +* Invoking `:GoRename` now reloads all files to reflect new changes automatically [[GH-855]](https://github.com/fatih/vim-go/pull/855) +* Calling `:GoTestCompile` does not create any temporary binary file anymore [[GH-879]](https://github.com/fatih/vim-go/pull/879) +* Enable passing the `-tags` flag to `:GoDef`. Now you can pass build tags to `:GoDef` via `:GoGuruTags` or `g:go_guru_tags` +* Internal refactoring to use custom `system()` function that wraps both the standard `system()` call and `vimproc`. Now all system calls will take advantage and will use `vimproc` if installed. [[GH-801]](https://github.com/fatih/vim-go/pull/801) +* Completion enables now `gocode`'s `autobuild` and `propose-builtins` flags automatically. With these settings packages will be automatically build to get the freshest completion candidates and builtin keywords will be showed as well. By defaults these settings are enabled. Settings can be disabled/enabled via `g:go_gocode_autobuild` and `g:go_gocode_propose_builtins`. [[GH-815]](https://github.com/fatih/vim-go/pull/815) +* Added new `http.HandlerFunc` snippets with `hf` and `hhf` shortcuts [[GH-816]](https://github.com/fatih/vim-go/pull/816) +* Added new `Example` and `Benchmark` snippets with `example` and `benchmark` shortcuts [[GH-836]](https://github.com/fatih/vim-go/pull/836) +* Search tool binaries first in `GOBIN` and then in `PATH` as most of vim-go users installs it to `GOBIN` mostly [[GH-823]](https://github.com/fatih/vim-go/pull/823) +* Improve `guru` based commands by providing automatically detected GOPATHS, such as `gb`, `godep` to be used if possible [[GH-861]](https://github.com/fatih/vim-go/pull/861) +* Add `(go-imports)` mapping to make it assignable to other keys [[GH-878]](https://github.com/fatih/vim-go/pull/878) +* Increase compatibility with tcsh [[GH-869]](https://github.com/fatih/vim-go/pull/869) +* Improve `:GoInstallBinaries` for GOPATH's which don't have packages that work well with `go get -u`. We have a new `g:go_get_update` setting to disable it. By default it's enabled. [[GH-883]](https://github.com/fatih/vim-go/pull/883) + + + +BUG FIXES: +* Fix `(go-freevars)` plug mapping to work as in visual mode instead of noncompatible normal mode [[GH-832]](https://github.com/fatih/vim-go/pull/832) +* Commands based on guru now shows a more meaningful error message instead of just showing the exit status (-1) +* Fix `:GoCoverage` accidentally enabling syntax highlighting for users who don't use syntax (i.e syntax off) [[GH-827]](https://github.com/fatih/vim-go/pull/827) +* Fix `:GoCoverage` colors to work for xterm as well [[GH-863]](https://github.com/fatih/vim-go/pull/863) +* Fix commenting out block of texts for Go templates (filetype gothtmltmpl) [[GH-813]](https://github.com/fatih/vim-go/pull/813) +* Fix `:GoImplements` failing because of an empty scope definition. Now we default to current package to make it usable. +* Fix `:GoPlay` posting to non HTTPS url. [[GH-847]](https://github.com/fatih/vim-go/pull/847) +* Fix escaping the filenames for lint and motion commands [[GH-862]](https://github.com/fatih/vim-go/pull/862) +* Fix escaping the filename to `:GoDef` completely for tcsh [[GH-868]](https://github.com/fatih/vim-go/pull/868) +* Fix showing SUCCESS for `go test` related commands if no test files are available [[GH-859]](https://github.com/fatih/vim-go/pull/859) + + + +## 1.6 (April 25, 2016) + +FEATURES: + +* New `CHANGELOG.md` file (which you're reading now). This will make it easier + for me to track changes and release versions +* **`:GoCoverage`** is now highlighting the current source file for + covered/uncovered lines. If called again it runs the tests and updates the + annotation. Use `:GoCoverageClear` to clear the coverage annotation. + This is a pretty good addition to vim-go and I suggest to check out the gif + that shows it in action: https://twitter.com/fatih/status/716722650383564800 + [[GH-786]](https://github.com/fatih/vim-go/pull/786) +* **`:GoCoverageToggle`** just like `:GoCoverage` but acts as a toggle. If run + again it clears the annotation. +* **`:GoCoverageBrowser`** opens a new annotated HTML page. This is the old + `:GoCoverage` behavior [[GH-786]](https://github.com/fatih/vim-go/pull/786) +* **`:GoDoc`** uses now [gogetdoc](https://github.com/zmb3/gogetdoc) to + lookup and display the comment documentation for the identifier under the + cursor. This is more superior as it support looking up dot imports, named + imports and imports where package name and file name are different [[GH-782]](https://github.com/fatih/vim-go/pull/782) +* **`guru support`**: `oracle` is replaced by the new tool `guru`. `oracle.vim` + is therefore renamed to `guru.vim`. I've also refactored the code to make it + much more easier to maintain and add additional features in future (such as + upcoming JSON decoding). vim-go is now fully compatible with `guru`. Please + be sure you have installed `guru`. You can easily do it with + `:GoInstallBinaries`. +* **`:GoDef`** uses now `guru definition` under the hood instead of `godef`. + This fixes the following issues: 1. dot imports 2. vendor imports 3. folder + != package name imports. The tool `godef` is also deprecated and not used + anymore. +* **`:GoDef`** does have now history of the call stack. This means you can + easily jump back to your last entry. This can be done with the new command + `:GoDefPop` or the mapping `CTRL-t`. To see the stack and jump between entries + you can use the new command `:GoDefStack`, which shows the list of all stack + entries. To reset the stack list anytime you can call `:GoDefStackClear` + [[GH-776]](https://github.com/fatih/vim-go/pull/776) + +IMPROVEMENTS: + +* **`:GoCoverage`** is executed asynchronously when used within Neovim [[GH-686]](https://github.com/fatih/vim-go/pull/686) +* **`:GoTestFunc`** supports now testable examples [[GH-794]](https://github.com/fatih/vim-go/pull/794) +* **`:GoDef`** can jump to existing buffers instead of opening a new window + (split, vsplit or tab). By default it's disabled to not break the old + behavior, can be enabled with `let g:go_def_reuse_buffer = 1` + +BUG FIXES: + +* Fix not showing documentation for dot, named and package/file name being different imports [[GH-332]](https://github.com/fatih/vim-go/pull/332) +* Term mode: fix closing location list if result is successful after a failed attempt [[GH-768]](https://github.com/fatih/vim-go/pull/768) +* Syntax: fix gotexttmpl identifier highlighting [[GH-778]](https://github.com/fatih/vim-go/pull/778) +* Doc: fix wrong wording for `go-run` mapping. It's for the whole main package, + not for the current file + +BACKWARDS INCOMPATIBILITIES: + +* `:GoDef` doesn't accept any identifier as an argument. This is not suported + via `guru definition` and also was not widely used either. Also with this, we + significantly simplified the existing def.vim code +* `:GoOracleScope` and `:GoOracleTags` are deprecated in favor of + `:GoGuruScope` and `:GoGuruTags`. Also `g:go_oracle_scope` is renamed to + `g:go_guru_scope` +* `g:go_guru_scope` accepts a variable in type of `list` instead of `string`. + i.g: `let g:go_guru_scope = ["github.com/fatih/structs", "golang.org/x/tools/..."]` + + +## 1.5 (Mar 16, 2016) + +FEATURES: +* Introducing code name "motion". A new whole way of moving + around and navigating [[GH-765]](https://github.com/fatih/vim-go/pull/765). Checkout the following new changes: + * A vim-go specific tool, called [motion](https://github.com/fatih/motion) is being developed which + provides us the underlying foundation for the following and upcoming + new features. + * `]]` and `[[` motions can be used to jump between functions + * `if` and `af` are improved and implement from scratch. It has now + support for literal functions, comments of functions, better cursor + position support and more stable. + * New `:GoDecls` and `:GoDeclsDir` commands that are available if + `ctrlp.vim` is installed. Once called one can easily jump to any generic declaration available. + * I wrote two blog posts about these new features in more detail. I recommend you to read it: [Treating Go types as objects in Vim](https://medium.com/@farslan/treating-go-types-as-objects-in-vim-ed6b3fad9287#.mbwaisevp) and [Navigation between functions and types in vim-go](https://medium.com/@farslan/navigation-between-functions-and-types-in-vim-go-f9dd7de8ca37#.2sdf8tbbe) +* A new `:GoAlternate` command that toggles to the test + file of the current file. It also has new appropriate mappings to open the + alternate file in split or tabs. [[GH-704]](https://github.com/fatih/vim-go/pull/704) +* Now commands can choose whether they want to open a + `quickfix` or a `location list` via the setting `g:go_list_type`. Also all + the commands have now some sensible settings, some will open a qf window, + some will open a location list [[GH-700]](https://github.com/fatih/vim-go/pull/700) + +IMPROVEMENTS: + +* Add support for goimport's new `-srcdir`. Goimports now succesfully suports `vendor/` folders with this release. [[GH-735]](https://github.com/fatih/vim-go/pull/735) +* Add `g:go_gorename_prefill` setting which disabled pre filling the argument for `:GoRename` [[GH-711]](https://github.com/fatih/vim-go/pull/711) +* Improve `:GoRun` to complete to filenames [[GH-742]](https://github.com/fatih/vim-go/pull/742) +* Highlight `//go:generate` comment directives [[GH-757]](https://github.com/fatih/vim-go/pull/757) +* Indent code in Go HTML templates [[GH-709]](https://github.com/fatih/vim-go/pull/709) +* Improve negative numbers of all types, octals, imaginary numbers with exponents [[GH-752]](https://github.com/fatih/vim-go/pull/752) +* Improved internal usage of retrieving offsets [[GH-762]](https://github.com/fatih/vim-go/pull/762) +* Improve by substitute all backslashes to slashes for filename [[GH-703]](https://github.com/fatih/vim-go/pull/703) +* Improve internal Go package path function [[GH-702]](https://github.com/fatih/vim-go/pull/702) +* Improved typo and grammar errors in docs [[GH-714]](https://github.com/fatih/vim-go/pull/714) +* Improved internal `:GoInfo` automatic call [[GH-759]](https://github.com/fatih/vim-go/pull/759) + +BUG FIXES: + +* Fix oracle scope not working if trailing slash exists in scope [[GH-751]](https://github.com/fatih/vim-go/pull/751) +* Fix `:GoErrCheck` checking abspath [[GH-671]](https://github.com/fatih/vim-go/pull/671) +* Fix `:GoInstall` correctly parsing errors [[GH-692]](https://github.com/fatih/vim-go/pull/692) +* Fix `:GoInstall` correctly parsing errors [[GH-692]](https://github.com/fatih/vim-go/pull/692) +* Fix `:GoTestFunc` for neovim [[GH-695]](https://github.com/fatih/vim-go/pull/695) +* Fix `:GoRun` accepting arguments for neovim [[GH-730]](https://github.com/fatih/vim-go/pull/730) +* Fix `go run` mappings not working [[GH-542]](https://github.com/fatih/vim-go/pull/542) +* Fix autodetect gopath picking up non existing GB vendor folder +* Fix gofmt errors showing per buffer instead of per script [[GH-721]](https://github.com/fatih/vim-go/pull/721) +* Fix some of the neosnippet snippets + +## 1.4 (Jan 18, 2016) + +FEATURES: + +* You waited for it for a long time. And here you have it: **Neovim support!** + This is a huge feature. It's fully compatible with Vim and kicks only in if + vim-go is being used within Neovim. Checkout the full list of changes + [[GH-607]](https://github.com/fatih/vim-go/pull/607): + * An async launcher and base foundation was implemented for the `go` command. + This will be used in the future for all upcoming subcommands of the `go` + tool. + * `:GoBuild` is now called asynchronously (it doesn't block the UI anymore). + * A new `go#jobcontrol#Statusline()` can be used to plug into the statusline. + This will show the status of the job running asynchronously. The statusline + is improved to show the status per package instead of file. Assume you have + three files open, all belonging to the same package, if the package build + (`:GoBuild`) is successful, all statusline's will be empty (means SUCCESS), + if it fails all files statusline's will show `FAILED`. + * `:GoRun` opens a new vertical terminal emulator inside Neovim and runs the + command there. The terminal mode can be changed with `g:go_term_mode`, + which is by default `vsplit`. Current options are `vsplit, split or tab`. + We also have three new mappings to open `:GoRun` command in different + terminal split modes: `(go-run-vertical)`, `(go-run-split)` + and `(go-run-tab)` + * `:GoTest`, `:GoTestFunc` and `:GoTestCompile` opens and runs in a new + terminal. The view mode (split,vertical, tab) is defined with + `g:go_term_mode`. The `g:go_term_enabled` setting can be use to change the + behavior of `:GoTestXXX` commands .If set to `1`, it opens the test + commands inside a terminal, if not it runs them in background just like + `:GoBuild` and displays the result in the statusline. + * We have two settings for terminal sizes: `g:go_term_height` and + `g:go_term_width`. By default a vertical or horizontal view is equally + splitted by vim automatically. However with these settings we can for + example have a terminal with a smaller height when we split it + horizontally. + * If a command inside the term fails (such as `go run`, `go test` ...) we + parse now the errors and list them inside a location list. +* Instead of quickfix window, vim-go now uses the `location list` feature of + Vim. These are associated with each window independently of each other. This + enables us to have multiple, independent location lists per window (example + usages: `:GoBuild` with errors that needs to be fixed, `:GoLint` with + warnings that we want to check, `:GoReferrers` with a list of referred + identifiers) [[GH-626]](https://github.com/fatih/vim-go/pull/626) +* a new **`:AsmFmt`** command which is integrated to work with [asmfmt](https://github.com/klauspost/asmfmt) [[GH-673]](https://github.com/fatih/vim-go/pull/673) +* the full identifier information of a completed identifier is echoed in + statusline. This is very useful to see a function signatures arguments. + [[GH-685]](https://github.com/fatih/vim-go/pull/685) + +IMPROVEMENTS: + +* Improve `:GoFmt` by checking if the binary is indeed installed on the system [[GH-617]](https://github.com/fatih/vim-go/pull/617) +* Improve `:GoMetaLinter` by adding the option to run the metalinter on save + and adding the option to limit the output to the currently active buffer. Set + `let g:go_metalinter_autosave = 1` to enable autosave and use `let + g:go_metalinter_autosave_enabled = ['vet', 'golint']` to change your options. + [[GH-631]](https://github.com/fatih/vim-go/pull/631) +* Improved `:GoDef`. If `vimproc` is installed `godef` will make use of it [[GH-670]](https://github.com/fatih/vim-go/pull/670) +* Improve completion of godoce when vimproc is used [[GH-620]](https://github.com/fatih/vim-go/pull/620) +* Improve internal error matching prodecure to not match false positives [[GH-618]](https://github.com/fatih/vim-go/pull/618) +* A new option to highlight interface variables with `go_highlight_interfaces` [[GH-681]](https://github.com/fatih/vim-go/pull/681) + +BUG FIXES + +* Fix `:GoFmt` changing the fileformat of the current buffer [[GH-615]](https://github.com/fatih/vim-go/pull/615) +* Fix `:GoRename` to output the original error if parsing fails [[GH-675]](https://github.com/fatih/vim-go/pull/675) +* Fix `:GoTest` to output the original error if parsing fails [[GH-676]](https://github.com/fatih/vim-go/pull/676) +* Fixed `fmt.Fprintln` not to highlight as builtin [[GH-628]](https://github.com/fatih/vim-go/pull/628) +* Fixed wrong highlighting of channels of channels [[GH-678]](https://github.com/fatih/vim-go/pull/678) + +## 1.3 (Nov 22, 2015) + +FEATURES: + +* A new `:GoOracleTags` command was added to pass build tags to Oracle's `-tags` flag. [[GH-573]](https://github.com/fatih/vim-go/pull/573) + +IMPROVEMENTS: + +* Change `:GoTest` command to timeout after 10 seconds. Vim UI is blocking and + tests with large running times makes Vim blocking for a long time. This is + also customizable with the new option `g:go_test_timeout`. [[GH-578]](https://github.com/fatih/vim-go/pull/578) +* Improve `:GoRename` to collect and populate quickfix window with errors. + [[GH-577]](https://github.com/fatih/vim-go/pull/577) +* Improve `:GoRun` by dropping bad filenames from quickfix window. This allows + us to have only valid entries which can be jumped to [[GH-547]](https://github.com/fatih/vim-go/pull/547) +* Improve `:GoMetaLinter` quickfix output by using absolute paths. This enables + us to jump to errors for all cases. [[GH-565]](https://github.com/fatih/vim-go/pull/565) +* Improve `:GoMetaLinter` command by adding a new option + `g:go_metalinter_deadline` which cancels the linters after 5 seconds + (previous default). [[GH-576]](https://github.com/fatih/vim-go/pull/576) +* Improve `:GoMetaLinter` by jumping to the first encountered error from the quickfix window. +* Automatically resize quickfix window based on the number of errors [[GH-602]](https://github.com/fatih/vim-go/pull/602) +* Improve build constraints to show invalid cases (such as `// +buildfoo`, not + having an empty line between the package statement, etc..). Also add missing + `GOARCH` values sucha s `arm64`. There are many other useful improvements, + for more detail please have a look at + [[GH-589]](https://github.com/fatih/vim-go/pull/589) +* Add support for all values of `GOARCH` [[GH-601]](https://github.com/fatih/vim-go/pull/601) +* Add note about Syntastic usage as this problem comes up a lot [[GH-580]](https://github.com/fatih/vim-go/pull/580) +* Add note about `:GoUpdateBinaries` [[GH-606]](https://github.com/fatih/vim-go/pull/606) + +BUG FIXES: + +* Fixed `:GoErrCheck` showing the correct output when executed inside the source folder [[GH-564]](https://github.com/fatih/vim-go/pull/564) +* Fixed `:GoBuild` by not using `/dev/null` anymore for build output (not + supported by `go`). We pass a temporary file now. [[GH-567]](https://github.com/fatih/vim-go/pull/567) +* Fixed `:GoFmt` passing `g:go_fmt_options` options to `goimports`. This option + is only valid with `gofmt`. [[GH-590]](https://github.com/fatih/vim-go/pull/590) +* Fix vim-go for `cygwin` users. [[GH-575]](https://github.com/fatih/vim-go/pull/575) +* Fixed identifier in template files to be highlighted correctly [[GH-559]](https://github.com/fatih/vim-go/pull/559) +* Fixed character region in template files to be highlighted correctly [[GH-603]](https://github.com/fatih/vim-go/pull/603) +* Fixed variables in template files to be highlighted correctly [[GH-611]](https://github.com/fatih/vim-go/pull/611) +* Do not treat builtins as keywords. Now `make` will not highlighted but + `make()` will be highlighted (gh-605) + +## 1.2 (Oct 2, 2015) + +FEATURES: + +* A new `:GoMetaLinter` command which invokes [gometalinter](https://github.com/alecthomas/gometalinter). Please check the PR [[GH-553]](https://github.com/fatih/vim-go/pull/553) for more detail on customizing and usage of `:GoMetaLinter`. + +IMPROVEMENTS: + +* Improve `:GoImport` to trim spaces when including import paths of form `"fmt "` +* Avoid setting `filetype` twice. Previously it was doing it twice, which was expensive +* Improve handling of GOPATH's with trailing `/` characters, such as `/home/user/go/` +* Add a new `g:go_highlight_string_spellcheck` feature, which is enabled by feature. Now if spell is enabled, go strings are also checked. +* Specify our limited but functional [gb](http://getgb.io/) support + +BUG FIXES: +* Fixed `:GoRun` to display errors when `g:go_dispatch_enabled` was enabled +* Fixed `:GoDrop` displaying "Not enough arguments" (regression) +* Fixed `:GoErrCheck` not showing `PASS` message if the command was successful +* Fixed `:GoErrCheck` not executing in the directory of the currently edited file +* Close quickfix window after a successful second round of `:GoInstall` +* Fix passing files rather than packages to certain oracle commands. +* Escape files passed to oracle command. This could lead to some serious things. +* Clear `g:go_oracle_scope` when the scope was reseted. Previously it was set to empty string, which was causing false positives. +* Correct various misspellings. + +## 1.1 (Jul 25, 2015) + +With this release the version will now increase in `minor` levels. So the next +release will be `1.2`, the other one `1.3`, etc.. This provides us more +flexibility (like releasing patch versions if needed). + +FEATURES: +* A new `:GoGenerate` command is now available which can be used to invoke `go generate` within vim +* Vim-go didn't had any license, now we use BSD 3-Clause License (the same as Go). This is needed for Linux distributions to package vim-go and is also something that people asked for. + +IMPROVEMENTS: +* Improve commands `GoRun, GoTest{,Func,Compile}, GoCoverage, + GoGenerate, GoErrcheck, GoLint, and GoVet` to handle multiple arguments. + Previously this feature was limited to only certain commands. What this means + is, for example `:GoVet . -all` will invoke `go tool vet . -all` + automatically instead of plan `go vet`. This is one of the big changes in + this release, so give it a try :) +* Improved `:GoFmt` command, which now uses the `-w` flag to + write to the source code directly, instead of outputting it to stdout. This + makes `:GoFmt` much more faster than the current implementation. This is one + of the big changes in this release, so feedback is welcome! +* Improve `:GoImport` to have a `!` feature. Now when when called + with a `!` appended it will go get it. i.e: `:GoImport! + github.com/fatih/color`. Useful if `:GoImport` fails and you want to download + it. +* Automatic GOPATH detections can now detect `gb` vendored folders. Some commands should now work without any problem when invoked on a `gb` project. +* All command arguments are now properly escaped for shell invocation. +* Added the `-f` flag to :GoInstallBinaries command to support `git url..insteadOf` configuration +* Improve width and precision highlighting, such as `%s %5s %-5s %5.5f %.5f` +* Show an error if a region is not selected when `:GoFreeVars` is called + +BUG FIXES: +* Fix`:GoDef` for files containing spaces. We know escape the files before passing to `:GoDef` +* Fix `:GoFmt` not picking up the correct GOPATH when the fmt command was set to `goimports` +* Fix and simplify README.md, add Wiki reference +* Fixed tagbar integration to show correct imports. + + +## 1.0.5 (May 26, 2015) + +FEATURES: +* A new `:GoOracleScope` is added to change the oracle scope on-the-fly. It + accepts import paths as arguments. If no arguments are passed it prints the + current custom oracle scope. `:GoOracleScope` also supports completion of + import paths, so it's very fast and handy to use. `:GoOracleScope ""` clears + the current custom scope. +* A new `:GoPath` command that displays the current `GOPATH`. A path can be + passed to change the `GOPATH` (i.e `:GoPath ~/foo/src`). `:GoPath ""` clears + and resets the `GOPATH` to the initial value. +* A new "autodetect GOPATH" feature is added. This automatically detects if the + project is using `godep` or is under a `src` root directory which is not in + `GOPATH` and changes/modifies the `GOPATH` so all commands work based on this + GOPATH. What this means is, commands such as `:GoDef`, `:GoBuild`, etc.. will + include the Godeps folder. For example any go-to-definition via `:GoDef` will + jump to the source code inside Godeps. This is enabled by default, but can + disabled with `let g:go_autodetect_gopath = 0`. This new feature is also the + foundation for other tools such as `gb` or `wgo`. + +IMPROVEMENTS: +* Improve `:GoFmt` (gofmt and goimports) speed. Now it's 2x faster than the previous implementation. +* Add Dispatch support for `:GoBuild` and `:GoRun`. For more info about + dispatch see https://github.com/tpope/vim-dispatch . By default it's + disabled, to enable it add `let g:go_dispatch_enabled = 1` to your vimrc. +* Add support for the bang `!` attribute for all `go` tool commands. What this + does it, if `:GoBuild` is called it will jump to the error. But `:GoBuild!` + will not jump to any error. This has the same behavior as the internal + `:make` command in vim. We had this feature already for `:GoBuild` and + `:GoRun`. But not for `:GoInstall`, `:GoTest`, etc.. Now all commands are + unified. +* Add autojump to error for `:GoInstall`. +* Add autowrite feature for `:GoInstall`, `:GoTestXXX` functions and `:GoVet` +* Support `git url..insteadOf` and custom import paths of binaries. This + improves the commands `:GoInstallBinaries` and `:GoUpdateBinaries`. +* Add support for highlighting go templates with `*.tmpl` extensions. Based on + the work from @cespare from https://github.com/cespare/vim-go-templates + +BUG FIXES: +* Fix clearing the status bar when `:GoErrCheck` is called +* Fix godocNotFound to not match 'os' pkg contents. This improves the command + `:GoDoc` +* Fix parsing and jumping to error locations when used Vim from a different + directory than the current buffer's directory +* Fix completion showing duplicates paths for completion results, such as + github.com/fatih/color and github.com/fatih/color/. + +## 1.0.4 (Apr 28, 2015) + +FEATURES: + +* A new `:GoTestFunc` command (with appropriate + mappings) is added. Run tests function which surrounds the current cursor + location. Useful to test single tests. +* Highlight all Go operators. Previously not all + operators were highlighted. As previously, to highlight options, enable it + with by setting `g:go_highlight_operators` to 1 in your vimrc. + +IMPROVEMENTS: + +* Improved certain `:GoDoc` usages to show a better error message +* Improved `:GoRename` to have a default value for rename input. Avoids retyping similar words. +* Synced with latest Oracle version. `callgraph` is removed. +* Removed our custom referrers mode. New version of oracle now displays the matching lines. + +BUG FIXES: + +* Fixed the internal `executeInDir` function which was failing when ignorelist was not set properly. +* Fixed trailing slash for package completion with `:GoImport` +* Fixed paths in error list for Windows users. +* Fixed not showing "import cycle not allowed" error message when called `:GoBuild` or `:GoRun` +* Fixed users using vimproc requiring arguments to functions to be escaped. +* Fixed depth for test snippets +* Fixed neosnippet support loading snippet files the second time if necessary. + +## 1.0.3 (Mar 7, 2015) + +FEATURES: +* A new `:GoTestCompile` command (with appropriate mappings) is added. Useful to compile a test binary or show/fix compile errors in quickfix window + +IMPROVEMENTS: +* `referrer` mode is improved to show referring lines in the quickfix window +* A new `errt` snippet is added, which expands to `if err != nil { t.Fatal(err) }` +* A new `errh` snippet is added, useful to be used in a `http.Handler` +* UltiSnips snippets are improved to take advance of Vim's `Visual` mode. For example selecting a block and typing `if` will create an if scope around the block. +* Cleanup README.md + +BUG FIXES: +* Fix trimming brackets if completion was invoked on a previous completion +* Fix Oracle scope settings. Added docs about usage. +* Fixed previously broken `var` and `vars` snippets +* Fix duplicate docs +* Fix fallback binary path for Windows users. The fallback mechanism is used to discover the necessary Go tools, such as `godef`, `gocode`, etc... + +## 1.0.2 (Feb 17, 2015) + +FEATURES: + +* New snippets are added, mostly for testing ( [changes](https://github.com/fatih/vim-go/pull/321/files)) + +IMPROVEMENTS: + +* Enable all Oracle commands. Docs, mappings and commands are also added. It uses Quickfix list instead of a custom UI. +* Clarify installation process in Readme, add instructions for vim-plug, NeoBundle and manual. + +BUG FIXES: + +* Fix shiftwidth parsing, it was broken in the previous release for old Vim versions +* Fix experimantal mode + + +## 1.0.1 (Feb 9, 2015) + +FEATURES: + +* New feature to highlight build constraints (disabled by default) + +IMPROVEMENTS: + +* Updated godef import path +* Updated Readme for possible problems with `csh` +* Documentation for text objects are updated, typo fixes are merged +* If vimproc is installed, Windows users will use it for autocompletion +* Improve UltiSnips snippets to pick Visual selection (demo: http://quick.as/0dvigz5) +* Packages with extensions, like "gopkg.in/yaml.v2" can be now displayed +* Packages with different import paths, like "github.com/bitly/go-simplejson" can be now displayed + +BUG FIXES: + +* Fatal errors are now parsed successfully and populated to quickfix list +* Shiftwidth is changed to use shiftwidth() function. Fixes usage with plugins like vim-sleuth and possible mis usage (like setting shiftwidth to zero) +* Added a new [Donation](https://github.com/fatih/vim-go#donations) section to Readme, for those who ask for it. +* Fix parsing of errcheck error syntax +* Fix consistency between Neosnippet and UltiSnips snippets + + +## 1.0 (Dec 24, 2014) + +We don't tag any changes or releases, so let's start with `1.0`. Our Windows +support is now in a good shape, tons of bugs are fixed, many new features and +improvements is being added and it's getting better with each day (thanks to +the community contributions). + +## 0.0 (Mar 24, 2014) + +Initial commit: https://github.com/fatih/vim-go/commit/78c5caa82c111c50e9c219f222d65b07694f8f5a + + diff --git a/vim/bundle/go/Dockerfile b/vim/bundle/go/Dockerfile new file mode 100644 index 0000000..e7ff629 --- /dev/null +++ b/vim/bundle/go/Dockerfile @@ -0,0 +1,20 @@ +FROM golang:1.9.2 + +RUN apt-get update -y && \ + apt-get install -y build-essential curl git libncurses5-dev python3-pip && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* + +RUN pip3 install vim-vint + +RUN useradd -ms /bin/bash -d /vim-go vim-go +USER vim-go + +COPY . /vim-go/ +WORKDIR /vim-go + +RUN scripts/install-vim vim-7.4 +RUN scripts/install-vim vim-8.0 +RUN scripts/install-vim nvim + +ENTRYPOINT ["make"] diff --git a/vim/bundle/go/LICENSE b/vim/bundle/go/LICENSE index e9cc0dc..d9493d4 100644 --- a/vim/bundle/go/LICENSE +++ b/vim/bundle/go/LICENSE @@ -25,7 +25,7 @@ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - + This software includes some portions from Go. Go is used under the terms of the BSD like license. @@ -56,3 +56,5 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The Go gopher was designed by Renee French. http://reneefrench.blogspot.com/ The design is licensed under the Creative Commons 3.0 Attributions license. Read this article for more details: https://blog.golang.org/gopher diff --git a/vim/bundle/go/Makefile b/vim/bundle/go/Makefile new file mode 100644 index 0000000..54978e8 --- /dev/null +++ b/vim/bundle/go/Makefile @@ -0,0 +1,29 @@ +VIMS ?= vim-7.4 vim-8.0 nvim + +all: install test lint + +install: + @echo "==> Installing Vims: $(VIMS)" + @for vim in $(VIMS); do \ + ./scripts/install-vim $$vim; \ + done + +test: + @echo "==> Running tests for $(VIMS)" + @for vim in $(VIMS); do \ + ./scripts/test $$vim; \ + done + +lint: + @echo "==> Running linting tools" + @./scripts/lint vim-8.0 + +docker: + @echo "==> Building/starting Docker container" + @./scripts/docker-test + +clean: + @echo "==> Cleaning /tmp/vim-go-test" + @rm -rf /tmp/vim-go-test + +.PHONY: all test install clean lint docker diff --git a/vim/bundle/go/README.md b/vim/bundle/go/README.md index 8614d87..8566224 100644 --- a/vim/bundle/go/README.md +++ b/vim/bundle/go/README.md @@ -1,282 +1,71 @@ -# vim-go - -Go (golang) support for Vim, which comes with pre-defined sensible settings (like -auto gofmt on save), with autocomplete, snippet support, improved syntax -highlighting, go toolchain commands, and more. If needed vim-go installs all -necessary binaries for providing seamless Vim integration with current -commands. It's highly customizable and each individual feature can be -disabled/enabled easily. - -![vim-go](https://dl.dropboxusercontent.com/u/174404/vim-go-2.png) - +# vim-go [![Build Status](http://img.shields.io/travis/fatih/vim-go.svg?style=flat-square)](https://travis-ci.org/fatih/vim-go) +

+ Vim-go logo +

## Features -* Improved Syntax highlighting with items such as Functions, Operators, Methods. -* Auto completion support via `gocode` -* Better `gofmt` on save, which keeps cursor position and doesn't break your undo - history -* Go to symbol/declaration with `:GoDef` -* Look up documentation with `:GoDoc` inside Vim or open it in browser -* Automatically import packages via `:GoImport` or plug it into autosave -* Compile your package with `:GoBuild`, install it with `:GoInstall` or test - them with `:GoTest` (also supports running single tests via `:GoTestFunc`) -* Quickly execute your current file/files with `:GoRun` -* Automatic `GOPATH` detection based on the directory structure (i.e. `gb` - projects, `godep` vendored projects) -* Change or display `GOPATH` with `:GoPath` -* Create a coverage profile and display annotated source code in browser to see - which functions are covered with `:GoCoverage` -* Call `gometalinter` with `:GoMetaLinter`, which invokes all possible linters - (golint, vet, errcheck, deadcode, etc..) and shows the warnings/errors -* Lint your code with `:GoLint` -* Run your code through `:GoVet` to catch static errors -* Advanced source analysis tools utilizing oracle, such as `:GoImplements`, - `:GoCallees`, and `:GoReferrers` -* Precise type-safe renaming of identifiers with `:GoRename` -* List all source files and dependencies -* Unchecked error checking with `:GoErrCheck` -* Integrated and improved snippets, supporting `ultisnips` or `neosnippet` -* Share your current code to [play.golang.org](http://play.golang.org) with `:GoPlay` -* On-the-fly type information about the word under the cursor. Plug it into - your custom vim function. -* Go asm formatting on save -* Tagbar support to show tags of the source code in a sidebar with `gotags` -* Custom vim text objects such as `a function` or `inner function` - list. -* A async launcher for the go command is implemented for Neovim, fully async - building and testing (beta). -* Integrated with the Neovim terminal, launch `:GoRun` and other go commands - in their own new terminal. (beta) -* Alternate between implementation and test code with `:GoAlternate` - -## Donation - -People have asked for this for a long time, now you can be a fully supporter by [being a patron](https://www.patreon.com/fatih)! This is fully optional and is just a way to support vim-go's ongoing development directly. Thanks! - -[https://www.patreon.com/fatih](https://www.patreon.com/fatih) +This plugin adds Go language support for Vim, with the following main features: + +* Compile your package with `:GoBuild`, install it with `:GoInstall` or test it + with `:GoTest`. Run a single tests with `:GoTestFunc`). +* Quickly execute your current file(s) with `:GoRun`. +* Improved syntax highlighting and folding. +* Completion support via `gocode`. +* `gofmt` or `goimports` on save keeps the cursor position and undo history. +* Go to symbol/declaration with `:GoDef`. +* Look up documentation with `:GoDoc` or `:GoDocBrowser`. +* Easily import packages via `:GoImport`, remove them via `:GoDrop`. +* Automatic `GOPATH` detection which works with `gb` and `godep`. Change or + display `GOPATH` with `:GoPath`. +* See which code is covered by tests with `:GoCoverage`. +* Add or remove tags on struct fields with `:GoAddTags` and `:GoRemoveTags`. +* Call `gometalinter` with `:GoMetaLinter` to invoke all possible linters + (`golint`, `vet`, `errcheck`, `deadcode`, etc.) and put the result in the + quickfix or location list. +* Lint your code with `:GoLint`, run your code through `:GoVet` to catch static + errors, or make sure errors are checked with `:GoErrCheck`. +* Advanced source analysis tools utilizing `guru`, such as `:GoImplements`, + `:GoCallees`, and `:GoReferrers`. +* Precise type-safe renaming of identifiers with `:GoRename`. +* ... and many more! Please see [doc/vim-go.txt](doc/vim-go.txt) for more + information. ## Install -Vim-go follows the standard runtime path structure, so I highly recommend to -use a common and well known plugin manager to install vim-go. Do not use vim-go -with other Go oriented vim plugins. For Pathogen just clone the repo. For other -plugin managers add the appropriate lines and execute the plugin's install -command. +The [**latest stable release**](https://github.com/fatih/vim-go/releases/latest) is the +recommended version to use. If you choose to use the master branch instead, +please do so with caution; it is a _development_ branch. + +vim-go follows the standard runtime path structure. Below are some helper lines +for popular package managers: -* [Pathogen](https://github.com/tpope/vim-pathogen) +* [Vim 8 packages](http://vimhelp.appspot.com/repeat.txt.html#packages) + * `git clone https://github.com/fatih/vim-go.git ~/.vim/pack/plugins/start/vim-go` +* [Pathogen](https://github.com/tpope/vim-pathogen) * `git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go` -* [vim-plug](https://github.com/junegunn/vim-plug) +* [vim-plug](https://github.com/junegunn/vim-plug) * `Plug 'fatih/vim-go'` -* [NeoBundle](https://github.com/Shougo/neobundle.vim) - * `NeoBundle 'fatih/vim-go'` -* [Vundle](https://github.com/gmarik/vundle) - * `Plugin 'fatih/vim-go'` -Please be sure all necessary binaries are installed (such as `gocode`, `godef`, -`goimports`, etc.). You can easily install them with the included -`:GoInstallBinaries` command. If invoked, all necessary binaries will be -automatically downloaded and installed to your `$GOBIN` environment (if not set -it will use `$GOPATH/bin`). Note that this command requires `git` for fetching -the individual Go packages. Additionally, use `:GoUpdateBinaries` to update the -installed binaries. +You will also need to install all the necessary binaries. vim-go makes it easy +to install all of them by providing a command, `:GoInstallBinaries`, which will +`go get` all the required binaries. -### Optional - -* Autocompletion is enabled by default via ``. To get real-time -completion (completion by type) install: -[neocomplete](https://github.com/Shougo/neocomplete.vim) for Vim or -[deoplete](https://github.com/Shougo/deoplete.nvim) and -[deoplete-go](https://github.com/zchee/deoplete-go) for NeoVim -* To display source code tag information on a sidebar install -[tagbar](https://github.com/majutsushi/tagbar). -* For snippet features install: -[neosnippet](https://github.com/Shougo/neosnippet.vim) or -[ultisnips](https://github.com/SirVer/ultisnips). -* Screenshot color scheme is a slightly modified molokai: - [fatih/molokai](https://github.com/fatih/molokai). -* For a better documentation viewer checkout: - [go-explorer](https://github.com/garyburd/go-explorer). +Check out the Install section in [the documentation](doc/vim-go.txt) for more +detailed instructions (`:help go-install`). ## Usage -Many of the plugin's [features](#features) are enabled by default. There are no -additional settings needed. All usages and commands are listed in -`doc/vim-go.txt`. Note that help tags needs to be populated. Check your plugin -manager settings to generate the documentation (some do it automatically). -After that just open the help page to see all commands: - - :help vim-go - -## Mappings - -vim-go has several `` mappings which can be used to create custom -mappings. Below are some examples you might find useful: - -Run commands such as `go run` for the current file with `r` or `go -build` and `go test` for the current package with `b` and `t` -respectively. Display beautifully annotated source code to see which functions -are covered with `c`. - -```vim -au FileType go nmap r (go-run) -au FileType go nmap b (go-build) -au FileType go nmap t (go-test) -au FileType go nmap c (go-coverage) -``` - -By default the mapping `gd` is enabled, which opens the target identifier in -current buffer. You can also open the definition/declaration, in a new vertical, -horizontal, or tab, for the word under your cursor: - -```vim -au FileType go nmap ds (go-def-split) -au FileType go nmap dv (go-def-vertical) -au FileType go nmap dt (go-def-tab) -``` - -Open the relevant Godoc for the word under the cursor with `gd` or open -it vertically with `gv` - -```vim -au FileType go nmap gd (go-doc) -au FileType go nmap gv (go-doc-vertical) -``` - -Or open the Godoc in browser - -```vim -au FileType go nmap gb (go-doc-browser) -``` - -Show a list of interfaces which is implemented by the type under your cursor -with `s` - -```vim -au FileType go nmap s (go-implements) -``` - -Show type info for the word under your cursor with `i` (useful if you -have disabled auto showing type info via `g:go_auto_type_info`) - -```vim -au FileType go nmap i (go-info) -``` - -Rename the identifier under the cursor to a new name - -```vim -au FileType go nmap e (go-rename) -``` - -More `` mappings can be seen with `:he go-mappings`. Also these are just -recommendations, you are free to create more advanced mappings or functions -based on `:he go-commands`. - -## Settings -Below are some settings you might find useful. For the full list see `:he -go-settings`. - -By default syntax-highlighting for Functions, Methods and Structs is disabled. -To change it: -```vim -let g:go_highlight_functions = 1 -let g:go_highlight_methods = 1 -let g:go_highlight_structs = 1 -let g:go_highlight_interfaces = 1 -let g:go_highlight_operators = 1 -let g:go_highlight_build_constraints = 1 -``` - -Enable goimports to automatically insert import paths instead of gofmt: - -```vim -let g:go_fmt_command = "goimports" -``` - -By default vim-go shows errors for the fmt command, to disable it: - -```vim -let g:go_fmt_fail_silently = 1 -``` - -Disable auto fmt on save: - -```vim -let g:go_fmt_autosave = 0 -``` - -Disable opening browser after posting your snippet to `play.golang.org`: - -```vim -let g:go_play_open_browser = 0 -``` - -By default when `:GoInstallBinaries` is called, the binaries are installed to -`$GOBIN` or `$GOPATH/bin`. To change it: - -```vim -let g:go_bin_path = expand("~/.gotools") -let g:go_bin_path = "/home/fatih/.mypath" "or give absolute path -``` - -### Using with Neovim (beta) - -Note: Neovim currently is not a first class citizen for vim-go. You are free -to open bugs but I'm not going to look at them. Even though I'm using Neovim -myself, Neovim itself is still alpha. So vim-go might not work well as good as -in Vim. I'm happy to accept pull requests or very detailed bug reports. - - -Run `:GoRun` in a new tab, horizontal split or vertical split terminal - -```vim -au FileType go nmap rt (go-run-tab) -au FileType go nmap rs (go-run-split) -au FileType go nmap rv (go-run-vertical) -``` - -By default new terminals are opened in a vertical split. To change it - -```vim -let g:go_term_mode = "split" -``` - -By default the testing commands run asynchronously in the background and -display results with `go#jobcontrol#Statusline()`. To make them run in a new -terminal - -```vim -let g:go_term_enabled = 1 -``` - -### Using with Syntastic -Sometimes when using both `vim-go` and `syntastic` Vim will start lagging while -saving and opening files. The following fixes this: - -```vim -let g:syntastic_go_checkers = ['golint', 'govet', 'errcheck'] -let g:syntastic_mode_map = { 'mode': 'active', 'passive_filetypes': ['go'] } -``` - -## More info - -Check out the [Wiki](https://github.com/fatih/vim-go/wiki) page for more -information. It includes -[Screencasts](https://github.com/fatih/vim-go/wiki/Screencasts), an [FAQ -section](https://github.com/fatih/vim-go/wiki/FAQ-Troubleshooting), and many -other [various pieces](https://github.com/fatih/vim-go/wiki) of information. +The full documentation can be found at [doc/vim-go.txt](doc/vim-go.txt). You can +display it from within Vim with `:help vim-go`. -## Credits +Depending on your installation method, you may have to generate the plugin's +[`help tags`](http://vimhelp.appspot.com/helphelp.txt.html#%3Ahelptags) +manually (e.g. `:helptags ALL`). -* Go Authors for official vim plugins -* Gocode, Godef, Golint, Oracle, Goimports, Gotags, Errcheck projects and - authors of those projects. -* Other vim-plugins, thanks for inspiration (vim-golang, go.vim, vim-gocode, - vim-godef) -* [Contributors](https://github.com/fatih/vim-go/graphs/contributors) of vim-go +We also have an [official vim-go tutorial](https://github.com/fatih/vim-go-tutorial). ## License -The BSD 3-Clause License - see `LICENSE` for more details +The BSD 3-Clause License - see [`LICENSE`](LICENSE) for more details diff --git a/vim/bundle/go/assets/screenshot.png b/vim/bundle/go/assets/screenshot.png new file mode 100644 index 0000000..8b2923d Binary files /dev/null and b/vim/bundle/go/assets/screenshot.png differ diff --git a/vim/bundle/go/assets/vim-go.png b/vim/bundle/go/assets/vim-go.png new file mode 100644 index 0000000..cb26e2c Binary files /dev/null and b/vim/bundle/go/assets/vim-go.png differ diff --git a/vim/bundle/go/assets/vim-go.svg b/vim/bundle/go/assets/vim-go.svg new file mode 100644 index 0000000..8471db5 --- /dev/null +++ b/vim/bundle/go/assets/vim-go.svg @@ -0,0 +1,821 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vim/bundle/go/autoload/ctrlp/decls.vim b/vim/bundle/go/autoload/ctrlp/decls.vim index a57d09d..00ecb89 100644 --- a/vim/bundle/go/autoload/ctrlp/decls.vim +++ b/vim/bundle/go/autoload/ctrlp/decls.vim @@ -1,26 +1,26 @@ let s:go_decls_var = { - \ 'init': 'ctrlp#decls#init()', - \ 'exit': 'ctrlp#decls#exit()', - \ 'enter': 'ctrlp#decls#enter()', - \ 'accept': 'ctrlp#decls#accept', - \ 'lname': 'declarations', - \ 'sname': 'decls', - \ 'type': 'tabs', - \} + \ 'init': 'ctrlp#decls#init()', + \ 'exit': 'ctrlp#decls#exit()', + \ 'enter': 'ctrlp#decls#enter()', + \ 'accept': 'ctrlp#decls#accept', + \ 'lname': 'declarations', + \ 'sname': 'decls', + \ 'type': 'tabs', + \} if exists('g:ctrlp_ext_vars') && !empty(g:ctrlp_ext_vars) - let g:ctrlp_ext_vars = add(g:ctrlp_ext_vars, s:go_decls_var) + let g:ctrlp_ext_vars = add(g:ctrlp_ext_vars, s:go_decls_var) else - let g:ctrlp_ext_vars = [s:go_decls_var] + let g:ctrlp_ext_vars = [s:go_decls_var] endif -function! ctrlp#decls#init() - cal s:enable_syntax() - return s:decls +function! ctrlp#decls#init() abort + cal s:enable_syntax() + return s:decls endfunction -function! ctrlp#decls#exit() - unlet! s:decls s:current_dir s:target +function! ctrlp#decls#exit() abort + unlet! s:decls s:current_dir s:target endfunction " The action to perform on the selected string @@ -28,131 +28,128 @@ endfunction " a:mode the mode that has been chosen by pressing or " the values are 'e', 'v', 't' and 'h', respectively " a:str the selected string -function! ctrlp#decls#accept(mode, str) - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - let dir = getcwd() - try - " we jump to the file directory so we can get the fullpath via fnamemodify - " below - execute cd . s:current_dir - - let vals = matchlist(a:str, '|\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)|') - - " i.e: main.go - let filename = vals[1] - let line = vals[2] - let col = vals[3] - - " i.e: /Users/fatih/vim-go/main.go - let filepath = fnamemodify(filename, ":p") - - " acceptile is a very versatile method, - call ctrlp#acceptfile(a:mode, filepath) - call cursor(line, col) - silent! norm! zvzz - finally - "jump back to old dir - execute cd . fnameescape(dir) - endtry +function! ctrlp#decls#accept(mode, str) abort + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + " we jump to the file directory so we can get the fullpath via fnamemodify + " below + execute cd . s:current_dir + + let vals = matchlist(a:str, '|\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)|') + + " i.e: main.go + let filename = vals[1] + let line = vals[2] + let col = vals[3] + + " i.e: /Users/fatih/vim-go/main.go + let filepath = fnamemodify(filename, ":p") + + " acceptile is a very versatile method, + call ctrlp#acceptfile(a:mode, filepath) + call cursor(line, col) + silent! norm! zvzz + finally + "jump back to old dir + execute cd . fnameescape(dir) + endtry endfunction -function! ctrlp#decls#enter() - let s:current_dir = fnameescape(expand('%:p:h')) - let s:decls = [] - - let bin_path = go#path#CheckBinPath('motion') - if empty(bin_path) - return - endif - let command = printf("%s -format vim -mode decls", bin_path) - let command .= " -include ". get(g:, "go_decls_includes", "func,type") - - call go#cmd#autowrite() - - if s:mode == 0 - " current file mode - let fname = expand("%:p") - if exists('s:target') - let fname = s:target - endif - - let command .= printf(" -file %s", fname) - else - " all functions mode - let dir = expand("%:p:h") - if exists('s:target') - let dir = s:target - endif - - let command .= printf(" -dir %s", dir) - endif - - let out = system(command) - if v:shell_error != 0 - call go#util#EchoError(out) - return - endif - - if exists("l:tmpname") - call delete(l:tmpname) - endif - - let result = eval(out) - if type(result) != 4 || !has_key(result, 'decls') - return - endif - - let decls = result.decls - - " find the maximum function name - let max_len = 0 - for decl in decls - if len(decl.ident)> max_len - let max_len = len(decl.ident) - endif - endfor - - for decl in decls - " paddings - let space = " " - for i in range(max_len - len(decl.ident)) - let space .= " " - endfor - - call add(s:decls, printf("%s\t%s |%s:%s:%s|\t%s", - \ decl.ident . space, - \ decl.keyword, - \ fnamemodify(decl.filename, ":t"), - \ decl.line, - \ decl.col, - \ decl.full, - \)) - endfor +function! ctrlp#decls#enter() abort + let s:current_dir = fnameescape(expand('%:p:h')) + let s:decls = [] + + let bin_path = go#path#CheckBinPath('motion') + if empty(bin_path) + return + endif + let command = printf("%s -format vim -mode decls", bin_path) + let command .= " -include ". get(g:, "go_decls_includes", "func,type") + + call go#cmd#autowrite() + + if s:mode == 0 + " current file mode + let fname = expand("%:p") + if exists('s:target') + let fname = s:target + endif + + let command .= printf(" -file %s", fname) + else + " all functions mode + let dir = expand("%:p:h") + if exists('s:target') + let dir = s:target + endif + + let command .= printf(" -dir %s", dir) + endif + + let out = go#util#System(command) + if go#util#ShellError() != 0 + call go#util#EchoError(out) + return + endif + + let result = eval(out) + if type(result) != 4 || !has_key(result, 'decls') + return + endif + + let decls = result.decls + + " find the maximum function name + let max_len = 0 + for decl in decls + if len(decl.ident)> max_len + let max_len = len(decl.ident) + endif + endfor + + for decl in decls + " paddings + let space = " " + for i in range(max_len - len(decl.ident)) + let space .= " " + endfor + + call add(s:decls, printf("%s\t%s |%s:%s:%s|\t%s", + \ decl.ident . space, + \ decl.keyword, + \ fnamemodify(decl.filename, ":t"), + \ decl.line, + \ decl.col, + \ decl.full, + \)) + endfor endfunc -function! s:enable_syntax() - if !(has('syntax') && exists('g:syntax_on')) - return - endif +function! s:enable_syntax() abort + if !(has('syntax') && exists('g:syntax_on')) + return + endif - syntax match CtrlPIdent '\zs\h\+\ze\s' - syntax match CtrlPKeyword '\zs[^\t|]\+\ze|[^|]\+:\d\+:\d\+|' - syntax match CtrlPFilename '|\zs[^|]\+:\d\+:\d\+\ze|' - syntax match CtrlPSignature '\zs\t.*\ze$' contains=CtrlPKeyWord,CtrlPFilename + syntax match CtrlPIdent '\zs\h\+\ze\s' + syntax match CtrlPKeyword '\zs[^\t|]\+\ze|[^|]\+:\d\+:\d\+|' + syntax match CtrlPFilename '|\zs[^|]\+:\d\+:\d\+\ze|' + syntax match CtrlPSignature '\zs\t.*\ze$' contains=CtrlPKeyWord,CtrlPFilename - highlight link CtrlPIdent Function - highlight link CtrlPKeyword Keyword - highlight link CtrlPFilename SpecialComment - highlight link CtrlPSignature Comment + highlight link CtrlPIdent Function + highlight link CtrlPKeyword Keyword + highlight link CtrlPFilename SpecialComment + highlight link CtrlPSignature Comment endfunction let s:id = g:ctrlp_builtins + len(g:ctrlp_ext_vars) -function! ctrlp#decls#cmd(mode, ...) - let s:mode = a:mode - if a:0 && !empty(a:1) - let s:target = a:1 - endif - return s:id +function! ctrlp#decls#cmd(mode, ...) abort + let s:mode = a:mode + if a:0 && !empty(a:1) + let s:target = a:1 + endif + return s:id endfunction +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/fzf/decls.vim b/vim/bundle/go/autoload/fzf/decls.vim new file mode 100644 index 0000000..255320e --- /dev/null +++ b/vim/bundle/go/autoload/fzf/decls.vim @@ -0,0 +1,150 @@ +function! s:code(group, attr) abort + let code = synIDattr(synIDtrans(hlID(a:group)), a:attr, "cterm") + if code =~ '^[0-9]\+$' + return code + endif +endfunction + +function! s:color(str, group) abort + let fg = s:code(a:group, "fg") + let bg = s:code(a:group, "bg") + let bold = s:code(a:group, "bold") + let italic = s:code(a:group, "italic") + let reverse = s:code(a:group, "reverse") + let underline = s:code(a:group, "underline") + let color = (empty(fg) ? "" : ("38;5;".fg)) . + \ (empty(bg) ? "" : (";48;5;".bg)) . + \ (empty(bold) ? "" : ";1") . + \ (empty(italic) ? "" : ";3") . + \ (empty(reverse) ? "" : ";7") . + \ (empty(underline) ? "" : ";4") + return printf("\x1b[%sm%s\x1b[m", color, a:str) +endfunction + +function! s:sink(str) abort + if len(a:str) < 2 + return + endif + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + " we jump to the file directory so we can get the fullpath via fnamemodify + " below + execute cd . fnameescape(s:current_dir) + + let vals = matchlist(a:str[1], '|\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)|') + + " i.e: main.go + let filename = vals[1] + let line = vals[2] + let col = vals[3] + + " i.e: /Users/fatih/vim-go/main.go + let filepath = fnamemodify(filename, ":p") + + let cmd = get({'ctrl-x': 'split', + \ 'ctrl-v': 'vertical split', + \ 'ctrl-t': 'tabe'}, a:str[0], 'e') + execute cmd fnameescape(filepath) + call cursor(line, col) + silent! norm! zvzz + finally + "jump back to old dir + execute cd . fnameescape(dir) + endtry +endfunction + +function! s:source(mode,...) abort + let s:current_dir = expand('%:p:h') + let ret_decls = [] + + let bin_path = go#path#CheckBinPath('motion') + if empty(bin_path) + return + endif + let command = printf("%s -format vim -mode decls", bin_path) + let command .= " -include ". get(g:, "go_decls_includes", "func,type") + + call go#cmd#autowrite() + + if a:mode == 0 + " current file mode + let fname = expand("%:p") + if a:0 && !empty(a:1) + let fname = a:1 + endif + + let command .= printf(" -file %s", shellescape(fname)) + else + " all functions mode + if a:0 && !empty(a:1) + let s:current_dir = a:1 + endif + + let command .= printf(" -dir %s", shellescape(s:current_dir)) + endif + + let out = go#util#System(command) + if go#util#ShellError() != 0 + call go#util#EchoError(out) + return + endif + + let result = eval(out) + if type(result) != 4 || !has_key(result, 'decls') + return ret_decls + endif + + let decls = result.decls + + " find the maximum function name + let max_len = 0 + for decl in decls + if len(decl.ident)> max_len + let max_len = len(decl.ident) + endif + endfor + + for decl in decls + " paddings + let space = " " + for i in range(max_len - len(decl.ident)) + let space .= " " + endfor + + let pos = printf("|%s:%s:%s|", + \ fnamemodify(decl.filename, ":t"), + \ decl.line, + \ decl.col + \) + call add(ret_decls, printf("%s\t%s %s\t%s", + \ s:color(decl.ident . space, "Function"), + \ s:color(decl.keyword, "Keyword"), + \ s:color(pos, "SpecialComment"), + \ s:color(decl.full, "Comment"), + \)) + endfor + + return ret_decls +endfunc + +function! fzf#decls#cmd(...) abort + let normal_fg = s:code("Normal", "fg") + let normal_bg = s:code("Normal", "bg") + let cursor_fg = s:code("CursorLine", "fg") + let cursor_bg = s:code("CursorLine", "bg") + let colors = printf(" --color %s%s%s%s%s", + \ &background, + \ empty(normal_fg) ? "" : (",fg:".normal_fg), + \ empty(normal_bg) ? "" : (",bg:".normal_bg), + \ empty(cursor_fg) ? "" : (",fg+:".cursor_fg), + \ empty(cursor_bg) ? "" : (",bg+:".cursor_bg), + \) + call fzf#run(fzf#wrap('GoDecls', { + \ 'source': call('source', a:000), + \ 'options': '-n 1 --ansi --prompt "GoDecls> " --expect=ctrl-t,ctrl-v,ctrl-x'.colors, + \ 'sink*': function('s:sink') + \ })) +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/alternate.vim b/vim/bundle/go/autoload/go/alternate.vim index 1de6a69..f2cb210 100644 --- a/vim/bundle/go/autoload/go/alternate.vim +++ b/vim/bundle/go/autoload/go/alternate.vim @@ -4,7 +4,7 @@ if !exists("g:go_alternate_mode") endif " Test alternates between the implementation of code and the test code. -function! go#alternate#Switch(bang, cmd) +function! go#alternate#Switch(bang, cmd) abort let file = expand('%') if empty(file) call go#util#EchoError("no buffer name") @@ -28,3 +28,5 @@ function! go#alternate#Switch(bang, cmd) execute ":" . a:cmd . " " . alt_file endif endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/asmfmt.vim b/vim/bundle/go/autoload/go/asmfmt.vim index 0b42794..cc8acd3 100644 --- a/vim/bundle/go/autoload/go/asmfmt.vim +++ b/vim/bundle/go/autoload/go/asmfmt.vim @@ -11,7 +11,7 @@ " " Options: " -" g:go_asmfmt_autosave [default=1] +" g:go_asmfmt_autosave [default=0] " " Flag to automatically call :Fmt when file is saved. @@ -19,29 +19,33 @@ let s:got_fmt_error = 0 " This is a trimmed-down version of the logic in fmt.vim. -function! go#asmfmt#Format() +function! go#asmfmt#Format() abort " Save state. let l:curw = winsaveview() " Write the current buffer to a tempfile. let l:tmpname = tempname() - call writefile(getline(1, '$'), l:tmpname) + call writefile(go#util#GetLines(), l:tmpname) " Run asmfmt. let path = go#path#CheckBinPath("asmfmt") if empty(path) return endif - let out = system(path . ' -w ' . l:tmpname) + let out = go#util#System(path . ' -w ' . l:tmpname) " If there's no error, replace the current file with the output. - if v:shell_error == 0 + if go#util#ShellError() == 0 " Remove undo point caused by BufWritePre. try | silent undojoin | catch | endtry " Replace the current file with the temp file; then reload the buffer. let old_fileformat = &fileformat + " save old file permissions + let original_fperm = getfperm(expand('%')) call rename(l:tmpname, expand('%')) + " restore old file permissions + call setfperm(expand('%'), original_fperm) silent edit! let &fileformat = old_fileformat let &syntax = &syntax @@ -50,3 +54,16 @@ function! go#asmfmt#Format() " Restore the cursor/window positions. call winrestview(l:curw) endfunction + +function! go#asmfmt#ToggleAsmFmtAutoSave() abort + if get(g:, "go_asmfmt_autosave", 0) + let g:go_asmfmt_autosave = 1 + call go#util#EchoProgress("auto asmfmt enabled") + return + end + + let g:go_asmfmt_autosave = 0 + call go#util#EchoProgress("auto asmfmt disabled") +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/cmd.vim b/vim/bundle/go/autoload/go/cmd.vim index c90a053..a867607 100644 --- a/vim/bundle/go/autoload/go/cmd.vim +++ b/vim/bundle/go/autoload/go/cmd.vim @@ -1,365 +1,311 @@ -if !exists("g:go_dispatch_enabled") - let g:go_dispatch_enabled = 0 -endif - -function! go#cmd#autowrite() - if &autowrite == 1 - silent wall - endif +function! go#cmd#autowrite() abort + if &autowrite == 1 + silent! wall + endif endfunction - -" Build builds the source code without producting any output binary. We live in +" Build builds the source code without producing any output binary. We live in " an editor so the best is to build it to catch errors and fix them. By " default it tries to call simply 'go build', but it first tries to get all " dependent files for the current folder and passes it to go build. -function! go#cmd#Build(bang, ...) - " expand all wildcards(i.e: '%' to the current file name) - let goargs = map(copy(a:000), "expand(v:val)") - - " escape all shell arguments before we pass it to make - let goargs = go#util#Shelllist(goargs, 1) +function! go#cmd#Build(bang, ...) abort + " Create our command arguments. go build discards any results when it + " compiles multiple packages. So we pass the `errors` package just as an + " placeholder with the current folder (indicated with '.'). We also pass -i + " that tries to install the dependencies, this has the side effect that it + " caches the build results, so every other build is faster. + let args = + \ ["build"] + + \ map(copy(a:000), "expand(v:val)") + + \ ["-i", ".", "errors"] + + " Vim async. + if go#util#has_job() + if get(g:, 'go_echo_command_info', 1) + call go#util#EchoProgress("building dispatched ...") + endif - " create our command arguments. go build discards any results when it - " compiles multiple packages. So we pass the `errors` package just as an - " placeholder with the current folder (indicated with '.') - let args = ["build"] + goargs + [".", "errors"] + call s:cmd_job({ + \ 'cmd': ['go'] + args, + \ 'bang': a:bang, + \ 'for': 'GoBuild', + \}) - " if we have nvim, call it asynchronously and return early ;) - if has('nvim') - call go#util#EchoProgress("building dispatched ...") - call go#jobcontrol#Spawn(a:bang, "build", args) - return + " Nvim async. + elseif has('nvim') + if get(g:, 'go_echo_command_info', 1) + call go#util#EchoProgress("building dispatched ...") endif - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() + call go#jobcontrol#Spawn(a:bang, "build", "GoBuild", args) + + " Vim 7.4 without async + else let default_makeprg = &makeprg - let &makeprg = "go " . join(args, ' ') + let &makeprg = "go " . join(go#util#Shelllist(args), ' ') - let l:listtype = go#list#Type("quickfix") + let l:listtype = go#list#Type("GoBuild") " execute make inside the source folder so we can parse the errors " correctly let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let dir = getcwd() try - execute cd . fnameescape(expand("%:p:h")) - if g:go_dispatch_enabled && exists(':Make') == 2 - call go#util#EchoProgress("building dispatched ...") - silent! exe 'Make' - elseif l:listtype == "locationlist" - silent! exe 'lmake!' - else - silent! exe 'make!' - endif - redraw! + execute cd . fnameescape(expand("%:p:h")) + if l:listtype == "locationlist" + silent! exe 'lmake!' + else + silent! exe 'make!' + endif + redraw! finally - execute cd . fnameescape(dir) + execute cd . fnameescape(dir) endtry let errors = go#list#Get(l:listtype) call go#list#Window(l:listtype, len(errors)) - - if !empty(errors) - if !a:bang - call go#list#JumpToFirst(l:listtype) - endif + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) else - call go#util#EchoSuccess("[build] SUCCESS") + call go#util#EchoSuccess("[build] SUCCESS") endif let &makeprg = default_makeprg - let $GOPATH = old_gopath + endif endfunction -" Run runs the current file (and their dependencies if any) in a new terminal. -function! go#cmd#RunTerm(bang, mode, files) - if empty(a:files) - let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) +" BuildTags sets or shows the current build tags used for tools +function! go#cmd#BuildTags(bang, ...) abort + if a:0 + if a:0 == 1 && a:1 == '""' + unlet g:go_build_tags + call go#util#EchoSuccess("build tags are cleared") else - let cmd = "go run ". go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1) + let g:go_build_tags = a:1 + call go#util#EchoSuccess("build tags are changed to: ". a:1) endif - call go#term#newmode(a:bang, cmd, a:mode) -endfunction -" Run runs the current file (and their dependencies if any) and outputs it. -" This is intented to test small programs and play with them. It's not -" suitable for long running apps, because vim is blocking by default and -" calling long running apps will block the whole UI. -function! go#cmd#Run(bang, ...) - if has('nvim') - call go#cmd#RunTerm(a:bang, '', a:000) - return - endif + return + endif - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() - - if go#util#IsWin() - exec '!go run ' . go#util#Shelljoin(go#tool#Files()) - if v:shell_error - redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None - else - redraws! | echon "vim-go: [run] " | echohl Function | echon "SUCCESS"| echohl None - endif + if !exists('g:go_build_tags') + call go#util#EchoSuccess("build tags are not set") + else + call go#util#EchoSuccess("current build tags: ". g:go_build_tags) + endif +endfunction - let $GOPATH = old_gopath - return - endif - " :make expands '%' and '#' wildcards, so they must also be escaped - let default_makeprg = &makeprg - if a:0 == 0 - let &makeprg = 'go run ' . go#util#Shelljoin(go#tool#Files(), 1) - else - let &makeprg = "go run " . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) - endif - - let l:listtype = go#list#Type("quickfix") +" Run runs the current file (and their dependencies if any) in a new terminal. +function! go#cmd#RunTerm(bang, mode, files) abort + if empty(a:files) + let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) + else + let cmd = "go run ". go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1) + endif + call go#term#newmode(a:bang, cmd, a:mode) +endfunction - if g:go_dispatch_enabled && exists(':Make') == 2 - silent! exe 'Make' - elseif l:listtype == "locationlist" - exe 'lmake!' +" Run runs the current file (and their dependencies if any) and outputs it. +" This is intended to test small programs and play with them. It's not +" suitable for long running apps, because vim is blocking by default and +" calling long running apps will block the whole UI. +function! go#cmd#Run(bang, ...) abort + if has('nvim') + call go#cmd#RunTerm(a:bang, '', a:000) + return + endif + + if go#util#has_job() + " NOTE(arslan): 'term': 'open' case is not implement for +jobs. This means + " executions waiting for stdin will not work. That's why we don't do + " anything. Once this is implemented we're going to make :GoRun async + endif + + if go#util#IsWin() + exec '!go run ' . go#util#Shelljoin(go#tool#Files()) + if v:shell_error + redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None else - exe 'make!' + redraws! | echon "vim-go: [run] " | echohl Function | echon "SUCCESS"| echohl None endif - let items = go#list#Get(l:listtype) - let errors = go#tool#FilterValids(items) - - call go#list#Populate(l:listtype, errors) - call go#list#Window(l:listtype, len(errors)) - if !empty(errors) && !a:bang - call go#list#JumpToFirst(l:listtype) - endif + return + endif - let $GOPATH = old_gopath - let &makeprg = default_makeprg -endfunction + " :make expands '%' and '#' wildcards, so they must also be escaped + let default_makeprg = &makeprg + if a:0 == 0 + let &makeprg = 'go run ' . go#util#Shelljoin(go#tool#Files(), 1) + else + let &makeprg = "go run " . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) + endif -" Install installs the package by simple calling 'go install'. If any argument -" is given(which are passed directly to 'go install') it tries to install those -" packages. Errors are populated in the location window. -function! go#cmd#Install(bang, ...) - let default_makeprg = &makeprg + let l:listtype = go#list#Type("GoRun") - " :make expands '%' and '#' wildcards, so they must also be escaped - let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) - let &makeprg = "go install " . goargs + if l:listtype == "locationlist" + exe 'lmake!' + else + exe 'make!' + endif - let l:listtype = go#list#Type("quickfix") - " execute make inside the source folder so we can parse the errors - " correctly - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - let dir = getcwd() - try - execute cd . fnameescape(expand("%:p:h")) - if g:go_dispatch_enabled && exists(':Make') == 2 - call go#util#EchoProgress("building dispatched ...") - silent! exe 'Make' - elseif l:listtype == "locationlist" - silent! exe 'lmake!' - else - silent! exe 'make!' - endif - redraw! - finally - execute cd . fnameescape(dir) - endtry + let items = go#list#Get(l:listtype) + let errors = go#tool#FilterValids(items) - let errors = go#list#Get(l:listtype) - call go#list#Window(l:listtype, len(errors)) - if !empty(errors) - if !a:bang - call go#list#JumpToFirst(l:listtype) - endif - else - redraws! | echon "vim-go: " | echohl Function | echon "installed to ". $GOPATH | echohl None - endif + call go#list#Populate(l:listtype, errors, &makeprg) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + endif - let &makeprg = default_makeprg + let &makeprg = default_makeprg endfunction -" Test runs `go test` in the current directory. If compile is true, it'll -" compile the tests instead of running them (useful to catch errors in the -" test files). Any other argument is appendend to the final `go test` command -function! go#cmd#Test(bang, compile, ...) - let args = ["test"] - - " don't run the test, only compile it. Useful to capture and fix errors or - " to create a test binary. - if a:compile - call add(args, "-c") - endif - - if a:0 - " expand all wildcards(i.e: '%' to the current file name) - let goargs = map(copy(a:000), "expand(v:val)") - - call extend(args, goargs, 1) - else - " only add this if no custom flags are passed - let timeout = get(g:, 'go_test_timeout', '10s') - call add(args, printf("-timeout=%s", timeout)) - endif - - if a:compile - echon "vim-go: " | echohl Identifier | echon "compiling tests ..." | echohl None - else - echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None - endif +" Install installs the package by simple calling 'go install'. If any argument +" is given(which are passed directly to 'go install') it tries to install +" those packages. Errors are populated in the location window. +function! go#cmd#Install(bang, ...) abort + " use vim's job functionality to call it asynchronously + if go#util#has_job() + " expand all wildcards(i.e: '%' to the current file name) + let goargs = map(copy(a:000), "expand(v:val)") - if has('nvim') - if get(g:, 'go_term_enabled', 0) - call go#term#new(a:bang, ["go"] + args) - else - call go#jobcontrol#Spawn(a:bang, "test", args) - endif - return + if get(g:, 'go_echo_command_info', 1) + call go#util#EchoProgress("installing dispatched ...") endif - call go#cmd#autowrite() - redraw - - let command = "go " . join(args, ' ') - - let out = go#tool#ExecuteInDir(command) - - let l:listtype = "quickfix" - - if v:shell_error - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - let dir = getcwd() - try - execute cd fnameescape(expand("%:p:h")) - let errors = go#tool#ParseErrors(split(out, '\n')) - let errors = go#tool#FilterValids(errors) - finally - execute cd . fnameescape(dir) - endtry - - call go#list#Populate(l:listtype, errors) - call go#list#Window(l:listtype, len(errors)) - if !empty(errors) && !a:bang - call go#list#JumpToFirst(l:listtype) - elseif empty(errors) - " failed to parse errors, output the original content - call go#util#EchoError(out) - endif - echon "vim-go: " | echohl ErrorMsg | echon "[test] FAIL" | echohl None + call s:cmd_job({ + \ 'cmd': ['go', 'install'] + goargs, + \ 'bang': a:bang, + \ 'for': 'GoInstall', + \}) + return + endif + + let default_makeprg = &makeprg + + " :make expands '%' and '#' wildcards, so they must also be escaped + let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) + let &makeprg = "go install " . goargs + + let l:listtype = go#list#Type("GoInstall") + " execute make inside the source folder so we can parse the errors + " correctly + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd . fnameescape(expand("%:p:h")) + if l:listtype == "locationlist" + silent! exe 'lmake!' else - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) - - if a:compile - echon "vim-go: " | echohl Function | echon "[test] SUCCESS" | echohl None - else - echon "vim-go: " | echohl Function | echon "[test] PASS" | echohl None - endif + silent! exe 'make!' endif + redraw! + finally + execute cd . fnameescape(dir) + endtry + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + else + call go#util#EchoSuccess("installed to ". go#path#Default()) + endif + + let &makeprg = default_makeprg endfunction -" Testfunc runs a single test that surrounds the current cursor position. -" Arguments are passed to the `go test` command. -function! go#cmd#TestFunc(bang, ...) - " search flags legend (used only) - " 'b' search backward instead of forward - " 'c' accept a match at the cursor position - " 'n' do Not move the cursor - " 'W' don't wrap around the end of the file - " - " for the full list - " :help search - let test = search("func Test", "bcnW") - - if test == 0 - echo "vim-go: [test] no test found immediate to cursor" - return - end - - let line = getline(test) - let name = split(split(line, " ")[1], "(")[0] - let args = [a:bang, 0, "-run", name . "$"] - - if a:0 - call extend(args, a:000) +" Generate runs 'go generate' in similar fashion to go#cmd#Build() +function! go#cmd#Generate(bang, ...) abort + let default_makeprg = &makeprg + + " :make expands '%' and '#' wildcards, so they must also be escaped + let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) + if go#util#ShellError() != 0 + let &makeprg = "go generate " . goargs + else + let gofiles = go#util#Shelljoin(go#tool#Files(), 1) + let &makeprg = "go generate " . goargs . ' ' . gofiles + endif + + let l:listtype = go#list#Type("GoGenerate") + + echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None + if l:listtype == "locationlist" + silent! exe 'lmake!' + else + silent! exe 'make!' + endif + redraw! + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) + if !a:bang + call go#list#JumpToFirst(l:listtype) endif + else + redraws! | echon "vim-go: " | echohl Function | echon "[generate] SUCCESS"| echohl None + endif - call call('go#cmd#Test', args) + let &makeprg = default_makeprg endfunction -" Coverage creates a new cover profile with 'go test -coverprofile' and opens -" a new HTML coverage page from that profile. -function! go#cmd#Coverage(bang, ...) - let l:tmpname=tempname() +" --------------------- +" | Vim job callbacks | +" --------------------- - let command = "go test -coverprofile=" . l:tmpname . ' ' . go#util#Shelljoin(a:000) +function s:cmd_job(args) abort + let status_dir = expand('%:p:h') + let started_at = reltime() + call go#statusline#Update(status_dir, { + \ 'desc': "current status", + \ 'type': a:args.cmd[1], + \ 'state': "started", + \}) - let l:listtype = "quickfix" - call go#cmd#autowrite() - let out = go#tool#ExecuteInDir(command) - if v:shell_error - let errors = go#tool#ParseErrors(split(out, '\n')) - call go#list#Populate(l:listtype, errors) - call go#list#Window(l:listtype, len(errors)) - if !empty(errors) && !a:bang - call go#list#JumpToFirst(l:listtype) - endif - else - " clear previous location list - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) + " autowrite is not enabled for jobs + call go#cmd#autowrite() - let openHTML = 'go tool cover -html='.l:tmpname - call go#tool#ExecuteInDir(openHTML) - endif + function! s:error_info_cb(job, exit_status, data) closure abort + let status = { + \ 'desc': 'last status', + \ 'type': a:args.cmd[1], + \ 'state': "success", + \ } - call delete(l:tmpname) -endfunction + if a:exit_status + let status.state = "failed" + endif -" Generate runs 'go generate' in similar fashion to go#cmd#Build() -function! go#cmd#Generate(bang, ...) - let default_makeprg = &makeprg + let elapsed_time = reltimestr(reltime(started_at)) + " strip whitespace + let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') + let status.state .= printf(" (%ss)", elapsed_time) - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() + call go#statusline#Update(status_dir, status) + endfunction - " :make expands '%' and '#' wildcards, so they must also be escaped - let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) - if v:shell_error - let &makeprg = "go generate " . goargs - else - let gofiles = go#util#Shelljoin(go#tool#Files(), 1) - let &makeprg = "go generate " . goargs . ' ' . gofiles - endif + let a:args.error_info_cb = funcref('s:error_info_cb') + let callbacks = go#job#Spawn(a:args) - let l:listtype = go#list#Type("quickfix") + let start_options = { + \ 'callback': callbacks.callback, + \ 'exit_cb': callbacks.exit_cb, + \ } - echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None - if g:go_dispatch_enabled && exists(':Make') == 2 - silent! exe 'Make' - elseif l:listtype == "locationlist" - silent! exe 'lmake!' - else - silent! exe 'make!' - endif - redraw! + " pre start + let dir = getcwd() + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let jobdir = fnameescape(expand("%:p:h")) + execute cd . jobdir - let errors = go#list#Get(l:listtype) - call go#list#Window(l:listtype, len(errors)) - if !empty(errors) - if !a:bang - call go#list#JumpToFirst(l:listtype) - endif - else - redraws! | echon "vim-go: " | echohl Function | echon "[generate] SUCCESS"| echohl None - endif + call job_start(a:args.cmd, start_options) - let &makeprg = default_makeprg - let $GOPATH = old_gopath + " post start + execute cd . fnameescape(dir) endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/complete.vim b/vim/bundle/go/autoload/go/complete.vim index 9937e5d..32ac360 100644 --- a/vim/bundle/go/autoload/go/complete.vim +++ b/vim/bundle/go/autoload/go/complete.vim @@ -1,170 +1,172 @@ -if !exists("g:go_gocode_bin") - let g:go_gocode_bin = "gocode" -endif +let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix' +function! s:gocodeCurrentBuffer() abort + let file = tempname() + call writefile(go#util#GetLines(), file) + return file +endfunction -fu! s:gocodeCurrentBuffer() - let buf = getline(1, '$') +function! s:gocodeCommand(cmd, preargs, args) abort + for i in range(0, len(a:args) - 1) + let a:args[i] = go#util#Shellescape(a:args[i]) + endfor + for i in range(0, len(a:preargs) - 1) + let a:preargs[i] = go#util#Shellescape(a:preargs[i]) + endfor + + let bin_path = go#path#CheckBinPath("gocode") + if empty(bin_path) + return + endif + + " We might hit cache problems, as gocode doesn't handle different GOPATHs + " well. See: https://github.com/nsf/gocode/issues/239 + let old_goroot = $GOROOT + let $GOROOT = go#util#env("goroot") + + try + let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type) + let cmd = printf('%s -sock %s %s %s %s', + \ go#util#Shellescape(bin_path), + \ socket_type, + \ join(a:preargs), + \ go#util#Shellescape(a:cmd), + \ join(a:args) + \ ) + + let result = go#util#System(cmd) + finally + let $GOROOT = old_goroot + endtry + + if go#util#ShellError() != 0 + return "[\"0\", []]" + else 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"') + let result = iconv(result, 'utf-8', &encoding) endif - let file = tempname() - call writefile(buf, file) - - return file -endf - -if go#vimproc#has_vimproc() - let s:vim_system = get(g:, 'gocomplete#system_function', 'vimproc#system2') - let s:vim_shell_error = get(g:, 'gocomplete#shell_error_function', 'vimproc#get_last_status') -else - let s:vim_system = get(g:, 'gocomplete#system_function', 'system') - let s:vim_shell_error = '' -endif - -fu! s:shell_error() - if empty(s:vim_shell_error) - return v:shell_error - endif - return call(s:vim_shell_error, []) -endf - -fu! s:system(str, ...) - return call(s:vim_system, [a:str] + a:000) -endf - -fu! s:gocodeShellescape(arg) - if go#vimproc#has_vimproc() - return vimproc#shellescape(a:arg) - endif - try - let ssl_save = &shellslash - set noshellslash - return shellescape(a:arg) - finally - let &shellslash = ssl_save - endtry -endf - -fu! s:gocodeCommand(cmd, preargs, args) - for i in range(0, len(a:args) - 1) - let a:args[i] = s:gocodeShellescape(a:args[i]) - endfor - for i in range(0, len(a:preargs) - 1) - let a:preargs[i] = s:gocodeShellescape(a:preargs[i]) - endfor - - let bin_path = go#path#CheckBinPath(g:go_gocode_bin) - if empty(bin_path) - return - endif - - " we might hit cache problems, as gocode doesn't handle well different - " GOPATHS: https://github.com/nsf/gocode/issues/239 - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() + return result + endif +endfunction - let result = s:system(printf('%s %s %s %s', s:gocodeShellescape(bin_path), join(a:preargs), s:gocodeShellescape(a:cmd), join(a:args))) +function! s:gocodeCurrentBufferOpt(filename) abort + return '-in=' . a:filename +endfunction - let $GOPATH = old_gopath +let s:optionsEnabled = 0 +function! s:gocodeEnableOptions() abort + if s:optionsEnabled + return + endif - if s:shell_error() != 0 - return "[\"0\", []]" - else - if &encoding != 'utf-8' - let result = iconv(result, 'utf-8', &encoding) - endif - return result - endif -endf + let bin_path = go#path#CheckBinPath("gocode") + if empty(bin_path) + return + endif -fu! s:gocodeCurrentBufferOpt(filename) - return '-in=' . a:filename -endf + let s:optionsEnabled = 1 -fu! s:gocodeAutocomplete() - let filename = s:gocodeCurrentBuffer() - let result = s:gocodeCommand('autocomplete', - \ [s:gocodeCurrentBufferOpt(filename), '-f=vim'], - \ [expand('%:p'), go#util#OffsetCursor()]) - call delete(filename) - return result -endf + call go#util#System(printf('%s set propose-builtins %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_propose_builtins', 1)))) + call go#util#System(printf('%s set autobuild %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_autobuild', 1)))) + call go#util#System(printf('%s set unimported-packages %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_unimported_packages', 0)))) +endfunction -function! go#complete#GetInfo() - let offset = go#util#OffsetCursor()+1 - let filename = s:gocodeCurrentBuffer() - let result = s:gocodeCommand('autocomplete', - \ [s:gocodeCurrentBufferOpt(filename), '-f=godit'], - \ [expand('%:p'), offset]) - call delete(filename) - - " first line is: Charcount,,NumberOfCandidates, i.e: 8,,1 - " following lines are candiates, i.e: func foo(name string),,foo( - let out = split(result, '\n') - - " no candidates are found - if len(out) == 1 - return "" - endif +function! s:toBool(val) abort + if a:val | return 'true ' | else | return 'false' | endif +endfunction - " only one candiate is found - if len(out) == 2 - return split(out[1], ',,')[0] - endif +function! s:gocodeAutocomplete() abort + call s:gocodeEnableOptions() - " 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 filename = s:gocodeCurrentBuffer() + let result = s:gocodeCommand('autocomplete', + \ [s:gocodeCurrentBufferOpt(filename), '-f=vim'], + \ [expand('%:p'), go#util#OffsetCursor()]) + call delete(filename) + return result +endfunction - 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."'") +function! go#complete#GetInfo() abort + let offset = go#util#OffsetCursor()+1 + let filename = s:gocodeCurrentBuffer() + let result = s:gocodeCommand('autocomplete', + \ [s:gocodeCurrentBufferOpt(filename), '-f=godit'], + \ [expand('%:p'), offset]) + call delete(filename) - if len(filtered) == 1 - return filtered[0] - endif + " 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 candidate is found + if len(out) == 2 + return split(out[1], ',,')[0] + endif + + " to many candidates are available, pick one that maches the word under the + " cursor + let infos = [] + for info in out[1:] + call add(infos, split(info, ',,')[0]) + endfor + + let wordMatch = '\<' . expand("") . '\>' + " escape single quotes in wordMatch before passing it to filter + let wordMatch = substitute(wordMatch, "'", "''", "g") + let filtered = filter(infos, "v:val =~ '".wordMatch."'") + + if len(filtered) == 1 + return filtered[0] + endif + + return "" endfunction -function! go#complete#Info(auto) - " auto is true if we were called by g:go_auto_type_info's autocmd - let result = go#complete#GetInfo() - if !empty(result) - " if auto, and the result is a PANIC by gocode, hide it - if a:auto && result ==# 'PANIC PANIC PANIC' | return | endif - echo "vim-go: " | echohl Function | echon result | echohl None - endif +function! go#complete#Info(auto) abort + " auto is true if we were called by g:go_auto_type_info's autocmd + let result = go#complete#GetInfo() + if !empty(result) + " if auto, and the result is a PANIC by gocode, hide it + if a:auto && result ==# 'PANIC PANIC PANIC' | return | endif + echo "vim-go: " | echohl Function | echon result | echohl None + endif endfunction -function! s:trim_bracket(val) - let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '') - return a:val +function! s:trim_bracket(val) abort + let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '') + return a:val endfunction -fu! go#complete#Complete(findstart, base) - "findstart = 1 when we need to get the text length - if a:findstart == 1 - execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete() - return col('.') - g:gocomplete_completions[0] - 1 - "findstart = 0 when we need to return the list of completions - else - let s = getline(".")[col('.') - 1] - if s =~ '[(){}\{\}]' - return map(copy(g:gocomplete_completions[1]), 's:trim_bracket(v:val)') - endif - return g:gocomplete_completions[1] +function! go#complete#Complete(findstart, base) abort + "findstart = 1 when we need to get the text length + if a:findstart == 1 + execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete() + return col('.') - g:gocomplete_completions[0] - 1 + "findstart = 0 when we need to return the list of completions + else + let s = getline(".")[col('.') - 1] + if s =~ '[(){}\{\}]' + return map(copy(g:gocomplete_completions[1]), 's:trim_bracket(v:val)') endif + return g:gocomplete_completions[1] + endif endf -" vim:ts=4:sw=4:et +function! go#complete#ToggleAutoTypeInfo() abort + if get(g:, "go_auto_type_info", 0) + let g:go_auto_type_info = 0 + call go#util#EchoProgress("auto type info disabled") + return + end + + let g:go_auto_type_info = 1 + call go#util#EchoProgress("auto type info enabled") +endfunction + + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/coverage.vim b/vim/bundle/go/autoload/go/coverage.vim new file mode 100644 index 0000000..882f819 --- /dev/null +++ b/vim/bundle/go/autoload/go/coverage.vim @@ -0,0 +1,375 @@ +let s:toggle = 0 + +" Buffer creates a new cover profile with 'go test -coverprofile' and changes +" the current buffers highlighting to show covered and uncovered sections of +" the code. If run again it clears the annotation. +function! go#coverage#BufferToggle(bang, ...) abort + if s:toggle + call go#coverage#Clear() + return + endif + + if a:0 == 0 + return call(function('go#coverage#Buffer'), [a:bang]) + endif + + return call(function('go#coverage#Buffer'), [a:bang] + a:000) +endfunction + +" Buffer creates a new cover profile with 'go test -coverprofile' and changes +" the current buffers highlighting to show covered and uncovered sections of +" the code. Calling it again reruns the tests and shows the last updated +" coverage. +function! go#coverage#Buffer(bang, ...) abort + " we use matchaddpos() which was introduce with 7.4.330, be sure we have + " it: http://ftp.vim.org/vim/patches/7.4/7.4.330 + if !exists("*matchaddpos") + call go#util#EchoError("GoCoverage is supported with Vim version 7.4-330 or later") + return -1 + endif + + " check if there is any test file, if not we just return + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd . fnameescape(expand("%:p:h")) + if empty(glob("*_test.go")) + call go#util#EchoError("no test files available") + return + endif + finally + execute cd . fnameescape(dir) + endtry + + let s:toggle = 1 + let l:tmpname = tempname() + + if get(g:, 'go_echo_command_info', 1) + echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None + endif + + if go#util#has_job() + call s:coverage_job({ + \ 'cmd': ['go', 'test', '-coverprofile', l:tmpname] + a:000, + \ 'custom_cb': function('s:coverage_callback', [l:tmpname]), + \ 'bang': a:bang, + \ 'for': 'GoTest', + \ }) + return + endif + + let args = [a:bang, 0, "-coverprofile", l:tmpname] + if a:0 + call extend(args, a:000) + endif + + let disabled_term = 0 + if get(g:, 'go_term_enabled') + let disabled_term = 1 + let g:go_term_enabled = 0 + endif + + let id = call('go#test#Test', args) + + if disabled_term + let g:go_term_enabled = 1 + endif + + if has('nvim') + call go#jobcontrol#AddHandler(function('s:coverage_handler')) + let s:coverage_handler_jobs[id] = l:tmpname + return + endif + + if go#util#ShellError() == 0 + call go#coverage#overlay(l:tmpname) + endif + + call delete(l:tmpname) +endfunction + +" Clear clears and resets the buffer annotation matches +function! go#coverage#Clear() abort + call clearmatches() + + if exists("s:toggle") | let s:toggle = 0 | endif + + " remove the autocmd we defined + augroup vim-go-coverage + autocmd! + augroup end +endfunction + +" Browser creates a new cover profile with 'go test -coverprofile' and opens +" a new HTML coverage page from that profile in a new browser +function! go#coverage#Browser(bang, ...) abort + let l:tmpname = tempname() + if go#util#has_job() + call s:coverage_job({ + \ 'cmd': ['go', 'test', '-coverprofile', l:tmpname], + \ 'custom_cb': function('s:coverage_browser_callback', [l:tmpname]), + \ 'bang': a:bang, + \ 'for': 'GoTest', + \ }) + return + endif + + let args = [a:bang, 0, "-coverprofile", l:tmpname] + if a:0 + call extend(args, a:000) + endif + + let id = call('go#test#Test', args) + if has('nvim') + call go#jobcontrol#AddHandler(function('s:coverage_browser_handler')) + let s:coverage_browser_handler_jobs[id] = l:tmpname + return + endif + + + if go#util#ShellError() == 0 + let openHTML = 'go tool cover -html='.l:tmpname + call go#tool#ExecuteInDir(openHTML) + endif + + call delete(l:tmpname) +endfunction + +" Parses a single line from the cover file generated via go test -coverprofile +" and returns a single coverage profile block. +function! go#coverage#parsegocoverline(line) abort + " file:startline.col,endline.col numstmt count + let mx = '\([^:]\+\):\(\d\+\)\.\(\d\+\),\(\d\+\)\.\(\d\+\)\s\(\d\+\)\s\(\d\+\)' + let tokens = matchlist(a:line, mx) + let ret = {} + let ret.file = tokens[1] + let ret.startline = str2nr(tokens[2]) + let ret.startcol = str2nr(tokens[3]) + let ret.endline = str2nr(tokens[4]) + let ret.endcol = str2nr(tokens[5]) + let ret.numstmt = tokens[6] + let ret.cnt = tokens[7] + return ret +endfunction + +" Generates matches to be added to matchaddpos for the given coverage profile +" block +function! go#coverage#genmatch(cov) abort + let color = 'goCoverageCovered' + if a:cov.cnt == 0 + let color = 'goCoverageUncover' + endif + + let matches = [] + + " if start and end are the same, also specify the byte length + " example: foo.go:92.2,92.65 1 0 + if a:cov.startline == a:cov.endline + call add(matches, { + \ 'group': color, + \ 'pos': [[a:cov.startline, a:cov.startcol, a:cov.endcol - a:cov.startcol]], + \ 'priority': 2, + \ }) + return matches + endif + + " add start columns. Because we don't know the length of the of + " the line, we assume it is at maximum 200 bytes. I know this is hacky, + " but that's only way of fixing the issue + call add(matches, { + \ 'group': color, + \ 'pos': [[a:cov.startline, a:cov.startcol, 200]], + \ 'priority': 2, + \ }) + + " and then the remaining lines + let start_line = a:cov.startline + while start_line < a:cov.endline + let start_line += 1 + call add(matches, { + \ 'group': color, + \ 'pos': [[start_line]], + \ 'priority': 2, + \ }) + endwhile + + " finally end columns + call add(matches, { + \ 'group': color, + \ 'pos': [[a:cov.endline, a:cov.endcol-1]], + \ 'priority': 2, + \ }) + + return matches +endfunction + +" Reads the given coverprofile file and annotates the current buffer +function! go#coverage#overlay(file) abort + if !filereadable(a:file) + return + endif + let lines = readfile(a:file) + + " cover mode, by default it's 'set'. Just here for debugging purposes + let mode = lines[0] + + " contains matches for matchaddpos() + let matches = [] + + " first mark all lines as goCoverageNormalText. We use a custom group to not + " interfere with other buffers highlightings. Because the priority is + " lower than the cover and uncover matches, it'll be overridden. + let cnt = 1 + while cnt <= line('$') + call add(matches, {'group': 'goCoverageNormalText', 'pos': [cnt], 'priority': 1}) + let cnt += 1 + endwhile + + let fname = expand('%') + + " when called for a _test.go file, run the coverage for the actuall file + " file + if fname =~# '^\f\+_test\.go$' + let l:root = split(fname, '_test.go$')[0] + let fname = l:root . ".go" + + if !filereadable(fname) + call go#util#EchoError("couldn't find ".fname) + return + endif + + " open the alternate file to show the coverage + exe ":edit ". fnamemodify(fname, ":p") + endif + + " cov.file includes only the filename itself, without full path + let fname = fnamemodify(fname, ":t") + + for line in lines[1:] + let cov = go#coverage#parsegocoverline(line) + + " TODO(arslan): for now only include the coverage for the current + " buffer + if fname != fnamemodify(cov.file, ':t') + continue + endif + + call extend(matches, go#coverage#genmatch(cov)) + endfor + + " clear the matches if we leave the buffer + augroup vim-go-coverage + autocmd! + autocmd BufWinLeave call go#coverage#Clear() + augroup end + + for m in matches + call matchaddpos(m.group, m.pos) + endfor +endfunction + + +" --------------------- +" | Vim job callbacks | +" --------------------- +" +function s:coverage_job(args) + " autowrite is not enabled for jobs + call go#cmd#autowrite() + + let status_dir = expand('%:p:h') + function! s:error_info_cb(job, exit_status, data) closure + let status = { + \ 'desc': 'last status', + \ 'type': "coverage", + \ 'state': "finished", + \ } + + if a:exit_status + let status.state = "failed" + endif + + call go#statusline#Update(status_dir, status) + endfunction + + let a:args.error_info_cb = funcref('s:error_info_cb') + let callbacks = go#job#Spawn(a:args) + + let start_options = { + \ 'callback': callbacks.callback, + \ 'exit_cb': callbacks.exit_cb, + \ } + + " pre start + let dir = getcwd() + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let jobdir = fnameescape(expand("%:p:h")) + execute cd . jobdir + + call go#statusline#Update(status_dir, { + \ 'desc': "current status", + \ 'type': "coverage", + \ 'state': "started", + \}) + + call job_start(a:args.cmd, start_options) + + " post start + execute cd . fnameescape(dir) +endfunction + +" coverage_callback is called when the coverage execution is finished +function! s:coverage_callback(coverfile, job, exit_status, data) + if a:exit_status == 0 + call go#coverage#overlay(a:coverfile) + endif + + call delete(a:coverfile) +endfunction + +function! s:coverage_browser_callback(coverfile, job, exit_status, data) + if a:exit_status == 0 + let openHTML = 'go tool cover -html='.a:coverfile + call go#tool#ExecuteInDir(openHTML) + endif + + call delete(a:coverfile) +endfunction + +" ----------------------- +" | Neovim job handlers | +" ----------------------- + +let s:coverage_handler_jobs = {} +let s:coverage_browser_handler_jobs = {} + +function! s:coverage_handler(job, exit_status, data) abort + if !has_key(s:coverage_handler_jobs, a:job.id) + return + endif + let l:tmpname = s:coverage_handler_jobs[a:job.id] + if a:exit_status == 0 + call go#coverage#overlay(l:tmpname) + endif + + call delete(l:tmpname) + unlet s:coverage_handler_jobs[a:job.id] +endfunction + +function! s:coverage_browser_handler(job, exit_status, data) abort + if !has_key(s:coverage_browser_handler_jobs, a:job.id) + return + endif + + let l:tmpname = s:coverage_browser_handler_jobs[a:job.id] + if a:exit_status == 0 + let openHTML = 'go tool cover -html='.l:tmpname + call go#tool#ExecuteInDir(openHTML) + endif + + call delete(l:tmpname) + unlet s:coverage_browser_handler_jobs[a:job.id] +endfunction + + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/decls.vim b/vim/bundle/go/autoload/go/decls.vim new file mode 100644 index 0000000..493bbd3 --- /dev/null +++ b/vim/bundle/go/autoload/go/decls.vim @@ -0,0 +1,21 @@ +if !exists('g:go_decls_mode') + let g:go_decls_mode = '' +endif + +function! go#decls#Decls(mode, ...) abort + if g:go_decls_mode == 'ctrlp' + call ctrlp#init(call("ctrlp#decls#cmd", [a:mode] + a:000)) + elseif g:go_decls_mode == 'fzf' + call call("fzf#decls#cmd", [a:mode] + a:000) + else + if globpath(&rtp, 'plugin/ctrlp.vim') != "" + call ctrlp#init(call("ctrlp#decls#cmd", [a:mode] + a:000)) + elseif globpath(&rtp, 'plugin/fzf.vim') != "" + call call("fzf#decls#cmd", [a:mode] + a:000) + else + call go#util#EchoError("neither ctrlp.vim nor fzf.vim are installed. Please install either one") + end + end +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/def.vim b/vim/bundle/go/autoload/go/def.vim index 0928702..f2383c9 100644 --- a/vim/bundle/go/autoload/go/def.vim +++ b/vim/bundle/go/autoload/go/def.vim @@ -1,111 +1,317 @@ -if !exists("g:go_godef_bin") - let g:go_godef_bin = "godef" -endif - -if go#vimproc#has_vimproc() - let s:vim_system = get(g:, 'gocomplete#system_function', 'vimproc#system2') -else - let s:vim_system = get(g:, 'gocomplete#system_function', 'system') -endif - -fu! s:system(str, ...) - return call(s:vim_system, [a:str] + a:000) -endf - -" modified and improved version of vim-godef -function! go#def#Jump(...) - if !len(a:000) - let arg = "-o=" . go#util#OffsetCursor() - else - let arg = a:1 - endif - - let bin_path = go#path#CheckBinPath(g:go_godef_bin) - if empty(bin_path) - return - endif - - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() - - let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') - let command = bin_path . " -f=" . shellescape(fname) . " -i " . shellescape(arg) - - " get output of godef - let out = s:system(command, join(getbufline(bufnr('%'), 1, '$'), go#util#LineEnding())) - - " jump to it - call s:godefJump(out, "") - let $GOPATH = old_gopath +let s:go_stack = [] +let s:go_stack_level = 0 + +function! go#def#Jump(mode) abort + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + + " so guru right now is slow for some people. previously we were using + " godef which also has it's own quirks. But this issue come up so many + " times I've decided to support both. By default we still use guru as it + " covers all edge cases, but now anyone can switch to godef if they wish + let bin_name = get(g:, 'go_def_mode', 'guru') + if bin_name == 'godef' + if &modified + " Write current unsaved buffer to a temp file and use the modified content + let l:tmpname = tempname() + call writefile(go#util#GetLines(), l:tmpname) + let fname = l:tmpname + endif + + let bin_path = go#path#CheckBinPath("godef") + if empty(bin_path) + return + endif + let command = printf("%s -f=%s -o=%s -t", go#util#Shellescape(bin_path), + \ go#util#Shellescape(fname), go#util#OffsetCursor()) + let out = go#util#System(command) + if exists("l:tmpname") + call delete(l:tmpname) + endif + elseif bin_name == 'guru' + let bin_path = go#path#CheckBinPath("guru") + if empty(bin_path) + return + endif + + let cmd = [bin_path] + let stdin_content = "" + + if &modified + let content = join(go#util#GetLines(), "\n") + let stdin_content = fname . "\n" . strlen(content) . "\n" . content + call add(cmd, "-modified") + endif + + if exists('g:go_build_tags') + let tags = get(g:, 'go_build_tags') + call extend(cmd, ["-tags", tags]) + endif + + let fname = fname.':#'.go#util#OffsetCursor() + call extend(cmd, ["definition", fname]) + + if go#util#has_job() + let l:spawn_args = { + \ 'cmd': cmd, + \ 'custom_cb': function('s:jump_to_declaration_cb', [a:mode, bin_name]), + \ } + + if &modified + let l:spawn_args.input = stdin_content + endif + + call go#util#EchoProgress("searching declaration ...") + + call s:def_job(spawn_args) + return + endif + + let command = join(cmd, " ") + if &modified + let out = go#util#System(command, stdin_content) + else + let out = go#util#System(command) + endif + else + call go#util#EchoError('go_def_mode value: '. bin_name .' is not valid. Valid values are: [godef, guru]') + return + endif + + if go#util#ShellError() != 0 + call go#util#EchoError(out) + return + endif + + call go#def#jump_to_declaration(out, a:mode, bin_name) +endfunction + +function! s:jump_to_declaration_cb(mode, bin_name, job, exit_status, data) abort + if a:exit_status != 0 + return + endif + + call go#def#jump_to_declaration(a:data[0], a:mode, a:bin_name) + call go#util#EchoSuccess(fnamemodify(a:data[0], ":t")) +endfunction + +function! go#def#jump_to_declaration(out, mode, bin_name) abort + let final_out = a:out + if a:bin_name == "godef" + " append the type information to the same line so our we can parse it. + " This makes it compatible with guru output. + let final_out = join(split(a:out, '\n'), ':') + endif + + " strip line ending + let out = split(final_out, go#util#LineEnding())[0] + if go#util#IsWin() + let parts = split(out, '\(^[a-zA-Z]\)\@,:navigate :jump ,q:exit'] - let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') - let command = bin_path . " -f=" . shellescape(fname) . " -i " . shellescape(arg) + let i = 0 + while i < len(s:go_stack) + let entry = s:go_stack[i] + let prefix = "" - " get output of godef - let out = s:system(command, join(getbufline(bufnr('%'), 1, '$'), go#util#LineEnding())) + if i == s:go_stack_level + let prefix = ">" + else + let prefix = " " + endif - call s:godefJump(out, a:mode) - let $GOPATH = old_gopath + call add(stackOut, printf("%s %d %s|%d col %d|%s", + \ prefix, i+1, entry["file"], entry["line"], entry["col"], entry["ident"])) + let i += 1 + endwhile + + if s:go_stack_level == i + call add(stackOut, "> ") + endif + + call go#ui#OpenWindow("GoDef Stack", stackOut, "godefstack") + + noremap :call go#def#SelectStackEntry() + noremap :call go#ui#CloseWindow() + noremap q :call go#ui#CloseWindow() endfunction +function! go#def#StackClear(...) abort + let s:go_stack = [] + let s:go_stack_level = 0 +endfunction + +function! go#def#StackPop(...) abort + if len(s:go_stack) == 0 + call go#util#EchoError("godef stack empty") + return + endif + + if s:go_stack_level == 0 + call go#util#EchoError("at bottom of the godef stack") + return + endif -function! s:getOffset() - return "-o=" . go#util#OffsetCursor() + if !len(a:000) + let numPop = 1 + else + let numPop = a:1 + endif + + let newLevel = str2nr(s:go_stack_level) - str2nr(numPop) + call go#def#Stack(newLevel + 1) +endfunction + +function! go#def#Stack(...) abort + if len(s:go_stack) == 0 + call go#util#EchoError("godef stack empty") + return + endif + + if !len(a:000) + " Display interactive stack + call go#def#StackUI() + return + else + let jumpTarget = a:1 + endif + + if jumpTarget !~ '^\d\+$' + if jumpTarget !~ '^\s*$' + call go#util#EchoError("location must be a number") + endif + return + endif + + let jumpTarget = str2nr(jumpTarget) - 1 + + if jumpTarget >= 0 && jumpTarget < len(s:go_stack) + let s:go_stack_level = jumpTarget + let target = s:go_stack[s:go_stack_level] + + " jump + if expand('%:p') != target["file"] + if &modified + exec 'hide edit' target["file"] + else + exec 'edit' target["file"] + endif + endif + call cursor(target["line"], target["col"]) + normal! zz + else + call go#util#EchoError("invalid location. Try :GoDefStack to see the list of valid entries") + endif endfunction +function s:def_job(args) abort + function! s:error_info_cb(job, exit_status, data) closure + " do not print anything during async definition search&jump + endfunction + + let a:args.error_info_cb = funcref('s:error_info_cb') + let callbacks = go#job#Spawn(a:args) -function! s:godefJump(out, mode) - let old_errorformat = &errorformat - let &errorformat = "%f:%l:%c" - - if a:out =~ 'godef: ' - let out = substitute(a:out, go#util#LineEnding() . '$', '', '') - echom out - else - let parts = split(a:out, ':') - " parts[0] contains filename - let fileName = parts[0] - - " put the error format into location list so we can jump automatically to - " it - lgetexpr a:out - - " needed for restoring back user setting this is because there are two - " modes of switchbuf which we need based on the split mode - let old_switchbuf = &switchbuf - - if a:mode == "tab" - let &switchbuf = "usetab" - - if bufloaded(fileName) == 0 - tab split - endif - else - if a:mode == "split" - split - elseif a:mode == "vsplit" - vsplit - endif - endif - - " jump to file now - sil ll 1 - normal! zz - - let &switchbuf = old_switchbuf - end - let &errorformat = old_errorformat + let start_options = { + \ 'callback': callbacks.callback, + \ 'exit_cb': callbacks.exit_cb, + \ } + + if &modified + let l:tmpname = tempname() + call writefile(split(a:args.input, "\n"), l:tmpname, "b") + let l:start_options.in_io = "file" + let l:start_options.in_name = l:tmpname + endif + + call job_start(a:args.cmd, start_options) endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/def_test.vim b/vim/bundle/go/autoload/go/def_test.vim new file mode 100644 index 0000000..7e69b12 --- /dev/null +++ b/vim/bundle/go/autoload/go/def_test.vim @@ -0,0 +1,37 @@ +func! Test_jump_to_declaration_guru() abort + try + let l:filename = 'def/jump.go' + let lnum = 5 + let col = 6 + let l:tmp = gotest#load_fixture(l:filename) + + let guru_out = printf("%s:%d:%d: defined here as func main", filename, lnum, col) + call go#def#jump_to_declaration(guru_out, "", 'guru') + + call assert_equal(filename, bufname("%")) + call assert_equal(lnum, getcurpos()[1]) + call assert_equal(col, getcurpos()[2]) + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +func! Test_jump_to_declaration_godef() abort + try + let filename = 'def/jump.go' + let lnum = 5 + let col = 6 + let l:tmp = gotest#load_fixture(l:filename) + + let godef_out = printf("%s:%d:%d\ndefined here as func main", filename, lnum, col) + call go#def#jump_to_declaration(godef_out, "", 'godef') + + call assert_equal(filename, bufname("%")) + call assert_equal(lnum, getcurpos()[1]) + call assert_equal(col, getcurpos()[2]) + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/doc.vim b/vim/bundle/go/autoload/go/doc.vim index ab10725..7c2f53d 100644 --- a/vim/bundle/go/autoload/go/doc.vim +++ b/vim/bundle/go/autoload/go/doc.vim @@ -1,171 +1,229 @@ " 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" + 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 [] +function! go#doc#OpenBrowser(...) abort + " check if we have gogetdoc as it gives us more and accurate information. + " Only supported if we have json_decode as it's not worth to parse the plain + " non-json output of gogetdoc + let bin_path = go#path#CheckBinPath('gogetdoc') + if !empty(bin_path) && exists('*json_decode') + let json_out = s:gogetdoc(1) + if go#util#ShellError() != 0 + call go#util#EchoError(json_out) + 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 + let out = json_decode(json_out) + if type(out) != type({}) + call go#util#EchoError("gogetdoc output is malformed") endif - if !len(words) - return [] + let import = out["import"] + let name = out["name"] + let decl = out["decl"] + + let godoc_url = get(g:, 'go_doc_url', 'https://godoc.org') + if godoc_url isnot 'https://godoc.org' + " strip last '/' character if available + let last_char = strlen(godoc_url) - 1 + if godoc_url[last_char] == '/' + let godoc_url = strpart(godoc_url, 0, last_char) + endif + + " custom godoc installations expects it + let godoc_url .= "/pkg" endif - let pkg = words[0] - if len(words) == 1 - let exported_name = "" - else - let exported_name = words[1] + let godoc_url .= "/" . import + if decl !~ "^package" + let godoc_url .= "#" . name endif - let packages = go#tool#Imports() + echo godoc_url - if has_key(packages, pkg) - let pkg = packages[pkg] - endif + call go#tool#OpenBrowser(godoc_url) + return + endif - return [pkg, exported_name] -endfunction + let pkgs = s:godocWord(a:000) + if empty(pkgs) + return + endif -function! s:godocNotFound(content) - if len(a:content) == 0 - return 1 - endif + let pkg = pkgs[0] + let exported_name = pkgs[1] - return a:content =~# '^.*: no such file or directory\n$' + " 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#OpenBrowser(...) - let pkgs = s:godocWord(a:000) - if empty(pkgs) - return +function! go#doc#Open(newmode, mode, ...) abort + " With argument: run "godoc [arg]". + if len(a:000) + if empty(go#path#CheckBinPath(g:go_doc_command[0])) + return endif - let pkg = pkgs[0] - let exported_name = pkgs[1] + let command = printf("%s %s", go#util#Shelljoin(g:go_doc_command), join(a:000, ' ')) + let out = go#util#System(command) + " Without argument: run gogetdoc on cursor position. + else + let out = s:gogetdoc(0) + if out == -1 + return + endif + endif - " 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) + if go#util#ShellError() != 0 + call go#util#EchoError(out) + return + endif + + call s:GodocView(a:newmode, a:mode, out) endfunction -function! go#doc#Open(newmode, mode, ...) - let pkgs = s:godocWord(a:000) - if empty(pkgs) - return +function! s:GodocView(newposition, position, content) abort + " reuse existing buffer window if it exists otherwise create a new one + let is_visible = bufexists(s:buf_nr) && bufwinnr(s:buf_nr) != -1 + if !bufexists(s:buf_nr) + execute a:newposition + sil file `="[Godoc]"` + let s:buf_nr = bufnr('%') + elseif bufwinnr(s:buf_nr) == -1 + execute a:position + execute s:buf_nr . 'buffer' + elseif bufwinnr(s:buf_nr) != bufwinnr('%') + execute bufwinnr(s:buf_nr) . 'wincmd w' + endif + + " if window was not visible then resize it + if !is_visible + if a:position == "split" + " cap window height to 20, but resize it for smaller contents + let max_height = get(g:, "go_doc_max_height", 20) + let content_height = len(split(a:content, "\n")) + if content_height > max_height + exe 'resize ' . max_height + else + exe 'resize ' . content_height + endif + else + " set a sane maximum width for vertical splits. In this case the minimum + " that fits the godoc for package http without extra linebreaks and line + " numbers on + exe 'vertical resize 84' endif + 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 + sil normal! gg + + " close easily with or enter + noremap :close + noremap :close +endfunction - let pkg = pkgs[0] - let exported_name = pkgs[1] +function! s:gogetdoc(json) abort + " check if we have 'gogetdoc' and use it automatically + let bin_path = go#path#CheckBinPath('gogetdoc') + if empty(bin_path) + return -1 + endif - let command = g:go_doc_command . ' ' . g:go_doc_options . ' ' . pkg + let cmd = [go#util#Shellescape(bin_path)] - silent! let content = system(command) - if v:shell_error || s:godocNotFound(content) - echo 'No documentation found for "' . pkg . '".' - return -1 - endif + let offset = go#util#OffsetCursor() + let fname = expand("%:p:gs!\\!/!") + let pos = shellescape(fname.':#'.offset) - call s:GodocView(a:newmode, a:mode, content) + let cmd += ["-pos", pos] + if a:json + let cmd += ["-json"] + endif - 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 + let command = join(cmd, " ") - if search('^type ' . exported_name) - silent! normal! zt - return -1 - endif + if &modified + let command .= " -modified" + let out = go#util#System(command, go#util#archive()) + else + let out = go#util#System(command) + endif - if search('^\%(const\|var\|type\|\s\+\) ' . pkg . '\s\+=\s') - silent! normal! zt - return -1 - endif + return out +endfunction - " nothing found, jump to top - silent! normal! gg +" 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) abort + if !executable('godoc') + let msg = "godoc command not found." + let msg .= " install with: go get golang.org/x/tools/cmd/godoc" + call go#util#EchoWarning(msg) + return [] + endif + + if !len(a:args) + let oldiskeyword = &iskeyword + setlocal iskeyword+=. + let word = expand('') + let &iskeyword = oldiskeyword + let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g') + let words = split(word, '\.\ze[^./]\+$') + else + let words = a:args + endif + + if !len(words) + return [] + endif + + let pkg = words[0] + if len(words) == 1 + let exported_name = "" + else + let exported_name = words[1] + endif + + let packages = go#tool#Imports() + + if has_key(packages, pkg) + let pkg = packages[pkg] + endif + + return [pkg, exported_name] endfunction -function! s:GodocView(newposition, position, content) - " reuse existing buffer window if it exists otherwise create a new one - if !bufexists(s:buf_nr) - execute a:newposition - sil file `="[Godoc]"` - let s:buf_nr = bufnr('%') - elseif bufwinnr(s:buf_nr) == -1 - execute a:position - execute s:buf_nr . 'buffer' - elseif bufwinnr(s:buf_nr) != bufwinnr('%') - execute bufwinnr(s:buf_nr) . 'wincmd w' - endif +function! s:godocNotFound(content) abort + if len(a:content) == 0 + return 1 + 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 + return a:content =~# '^.*: no such file or directory\n$' endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/fillstruct.vim b/vim/bundle/go/autoload/go/fillstruct.vim new file mode 100644 index 0000000..ec88a76 --- /dev/null +++ b/vim/bundle/go/autoload/go/fillstruct.vim @@ -0,0 +1,62 @@ +function! go#fillstruct#FillStruct() abort + let l:cmd = ['fillstruct', + \ '-file', bufname(''), + \ '-offset', go#util#OffsetCursor(), + \ '-line', line('.')] + + " Read from stdin if modified. + if &modified + call add(l:cmd, '-modified') + let [l:out, l:err] = go#util#Exec(l:cmd, go#util#archive()) + else + let [l:out, l:err] = go#util#Exec(l:cmd) + endif + + if l:err + call go#util#EchoError(l:out) + return + endif + + try + let l:json = json_decode(l:out) + catch + call go#util#EchoError(l:out) + return + endtry + + " Output is array: + "[ + " {"start": 92, "end": 106, "code": "mail.Address{\n\tName: \"\",\n\tAddress: \"\",\n}"}, + " {...second struct...} + " ] + + let l:pos = getpos('.') + + try + for l:struct in l:json + let l:code = split(l:struct['code'], "\n") + + " Add any code before/after the struct. + exe l:struct['start'] . 'go' + let l:code[0] = getline('.')[:col('.')-1] . l:code[0] + exe l:struct['end'] . 'go' + let l:code[len(l:code)-1] .= getline('.')[col('.'):] + + " Indent every line except the first one; makes it look nice. + let l:indent = repeat("\t", indent('.') / &tabstop) + for l:i in range(1, len(l:code)-1) + let l:code[l:i] = l:indent . l:code[l:i] + endfor + + " Out with the old ... + exe 'normal! ' . l:struct['start'] . 'gov' . l:struct['end'] . 'gox' + " ... in with the new. + call setline('.', l:code[0]) + call append('.', l:code[1:]) + endfor + finally + call setpos('.', l:pos) + endtry +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/fillstruct_test.vim b/vim/bundle/go/autoload/go/fillstruct_test.vim new file mode 100644 index 0000000..d677ab7 --- /dev/null +++ b/vim/bundle/go/autoload/go/fillstruct_test.vim @@ -0,0 +1,90 @@ +func! Test_fillstruct() abort + try + let l:tmp = gotest#write_file('a/a.go', [ + \ 'package a', + \ 'import "net/mail"', + \ "var addr = mail.\x1fAddress{}"]) + + call go#fillstruct#FillStruct() + call gotest#assert_buffer(1, [ + \ 'var addr = mail.Address{', + \ '\tName: "",', + \ '\tAddress: "",', + \ '}']) + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +func! Test_fillstruct_line() abort + try + let l:tmp = gotest#write_file('a/a.go', [ + \ 'package a', + \ 'import "net/mail"', + \ "\x1f" . 'var addr = mail.Address{}']) + + call go#fillstruct#FillStruct() + call gotest#assert_buffer(1, [ + \ 'var addr = mail.Address{', + \ '\tName: "",', + \ '\tAddress: "",', + \ '}']) + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +func! Test_fillstruct_two_line() abort + try + let l:tmp = gotest#write_file('a/a.go', [ + \ 'package a', + \ 'import (', + \ '"fmt"', + \ '"net/mail"', + \ ')', + \ "\x1f" . 'func x() { fmt.Println(mail.Address{}, mail.Address{}) }']) + + call go#fillstruct#FillStruct() + call gotest#assert_buffer(1, [ + \ 'import (', + \ '"fmt"', + \ '"net/mail"', + \ ')', + \ 'func x() { fmt.Println(mail.Address{', + \ '\tName: "",', + \ '\tAddress: "",', + \ '}, mail.Address{', + \ '\tName: "",', + \ '\tAddress: "",', + \ '}) }']) + finally + "call delete(l:tmp, 'rf') + endtry +endfunc + +func! Test_fillstruct_two_cursor() abort + try + let l:tmp = gotest#write_file('a/a.go', [ + \ 'package a', + \ 'import (', + \ '"fmt"', + \ '"net/mail"', + \ ')', + \ "func x() { fmt.Println(mail.Address{}, mail.Ad\x1fdress{}) }"]) + + call go#fillstruct#FillStruct() + call gotest#assert_buffer(1, [ + \ 'import (', + \ '"fmt"', + \ '"net/mail"', + \ ')', + \ 'func x() { fmt.Println(mail.Address{}, mail.Address{', + \ '\tName: "",', + \ '\tAddress: "",', + \ '}) }']) + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/fmt.vim b/vim/bundle/go/autoload/go/fmt.vim index 56c6ee7..42d0419 100644 --- a/vim/bundle/go/autoload/go/fmt.vim +++ b/vim/bundle/go/autoload/go/fmt.vim @@ -2,48 +2,26 @@ " 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 -" +" fmt.vim: Vim command to format Go files with gofmt (and gofmt compatible +" toorls, such as goimports). if !exists("g:go_fmt_command") - let g:go_fmt_command = "gofmt" + let g:go_fmt_command = "gofmt" endif -if !exists("g:go_goimports_bin") - let g:go_goimports_bin = "goimports" +if !exists('g:go_fmt_options') + let g:go_fmt_options = '' 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 = '' + let g:go_fmt_fail_silently = 0 endif if !exists("g:go_fmt_experimental") - let g:go_fmt_experimental = 0 + let g:go_fmt_experimental = 0 endif -" we have those problems : +" 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 " @@ -51,160 +29,240 @@ endif " it doesn't undo changes and break undo history. If you are here reading " this and have VimL experience, please look at the function for " improvements, patches are welcome :) -function! go#fmt#Format(withGoimport) - if g:go_fmt_experimental == 1 - " Using winsaveview to save/restore cursor state has the problem of - " closing folds on save: - " https://github.com/fatih/vim-go/issues/502 - " One fix is to use mkview instead. Unfortunately, this sometimes causes - " other bad side effects: - " https://github.com/fatih/vim-go/issues/728 - " and still closes all folds if foldlevel>0: - " https://github.com/fatih/vim-go/issues/732 - let l:curw = {} - try - mkview! - catch - let l:curw=winsaveview() - endtry +function! go#fmt#Format(withGoimport) abort + if g:go_fmt_experimental == 1 + " Using winsaveview to save/restore cursor state has the problem of + " closing folds on save: + " https://github.com/fatih/vim-go/issues/502 + " One fix is to use mkview instead. Unfortunately, this sometimes causes + " other bad side effects: + " https://github.com/fatih/vim-go/issues/728 + " and still closes all folds if foldlevel>0: + " https://github.com/fatih/vim-go/issues/732 + let l:curw = {} + try + mkview! + catch + let l:curw = winsaveview() + endtry + + " 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 + else + " Save cursor position and many other things. + let l:curw = winsaveview() + endif + + " Write current unsaved buffer to a temp file + let l:tmpname = tempname() . '.go' + call writefile(go#util#GetLines(), l:tmpname) + if go#util#IsWin() + let l:tmpname = tr(l:tmpname, '\', '/') + endif + + let bin_name = g:go_fmt_command + if a:withGoimport == 1 + let bin_name = "goimports" + endif + + let current_col = col('.') + let out = go#fmt#run(bin_name, l:tmpname, expand('%')) + let diff_offset = len(readfile(l:tmpname)) - line('$') + + if go#util#ShellError() == 0 + call go#fmt#update_file(l:tmpname, expand('%')) + elseif g:go_fmt_fail_silently == 0 + let errors = s:parse_errors(expand('%'), out) + call s:show_errors(errors) + endif + + " We didn't use the temp file, so clean up + call delete(l:tmpname) + + if g:go_fmt_experimental == 1 + " restore our undo history + silent! exe 'rundo ' . tmpundofile + call delete(tmpundofile) + + " Restore our cursor/windows positions, folds, etc. + if empty(l:curw) + silent! loadview else - " Save cursor position and many other things. - let l:curw=winsaveview() + call winrestview(l:curw) endif + else + " Restore our cursor/windows positions. + call winrestview(l:curw) + endif - " Write current unsaved buffer to a temp file - let l:tmpname = tempname() - call writefile(getline(1, '$'), l:tmpname) - - if g:go_fmt_experimental == 1 - " save our undo file to be restored after we are done. This is needed to - " prevent an additional undo jump due to BufWritePre auto command and also - " restore 'redo' history because it's getting being destroyed every - " BufWritePre - let tmpundofile=tempname() - exe 'wundo! ' . tmpundofile - endif + " be smart and jump to the line the new statement was added/removed + call cursor(line('.') + diff_offset, current_col) - " get the command first so we can test it - let fmt_command = g:go_fmt_command - if a:withGoimport == 1 - let fmt_command = g:go_goimports_bin - endif + " Syntax highlighting breaks less often. + syntax sync fromstart +endfunction - " check if the user has installed command binary. - " For example if it's goimports, let us check if it's installed, - " if not the user get's a warning via go#path#CheckBinPath() - let bin_path = go#path#CheckBinPath(fmt_command) - if empty(bin_path) - return - endif +" update_file updates the target file with the given formatted source +function! go#fmt#update_file(source, target) + " remove undo point caused via BufWritePre + try | silent undojoin | catch | endtry - if fmt_command != "gofmt" - " change GOPATH too, so goimports can pick up the correct library - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() + let old_fileformat = &fileformat + if exists("*getfperm") + " save file permissions + let original_fperm = getfperm(a:target) + endif - let fmt_command = bin_path - endif + call rename(a:source, a:target) - " populate the final command with user based fmt options - let command = fmt_command . ' -w ' - if a:withGoimport != 1 - let command = command . g:go_fmt_options - endif + " restore file permissions + if exists("*setfperm") && original_fperm != '' + call setfperm(a:target , original_fperm) + endif - if fmt_command == "goimports" - if !exists('b:goimports_vendor_compatible') - let out = system("goimports --help") - if out !~ "-srcdir" - echohl WarningMsg - echomsg "vim-go: goimports does not support srcdir." - echomsg " update with: :GoUpdateBinaries" - echohl None - else - let b:goimports_vendor_compatible = 1 - endif - endif - - if exists('b:goimports_vendor_compatible') && b:goimports_vendor_compatible - let command = command . '-srcdir ' . fnameescape(expand("%:p:h")) - endif - endif + " reload buffer to reflect latest changes + silent edit! + + let &fileformat = old_fileformat + let &syntax = &syntax - " execute our command... - let out = system(command . " " . l:tmpname) + let l:listtype = go#list#Type("GoFmt") - if fmt_command != "gofmt" - let $GOPATH = old_gopath + " the title information was introduced with 7.4-2200 + " https://github.com/vim/vim/commit/d823fa910cca43fec3c31c030ee908a14c272640 + if has('patch-7.4.2200') + " clean up previous list + if l:listtype == "quickfix" + let l:list_title = getqflist({'title': 1}) + else + let l:list_title = getloclist(0, {'title': 1}) endif + else + " can't check the title, so assume that the list was for go fmt. + let l:list_title = {'title': 'Format'} + endif + + if has_key(l:list_title, "title") && l:list_title['title'] == "Format" + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + endif +endfunction + +" run runs the gofmt/goimport command for the given source file and returns +" the output of the executed command. Target is the real file to be formatted. +function! go#fmt#run(bin_name, source, target) + let cmd = s:fmt_cmd(a:bin_name, a:source, a:target) + if empty(cmd) + return + endif + + let command = join(cmd, " ") + + " execute our command... + let out = go#util#System(command) + + return out +endfunction + +" fmt_cmd returns a dict that contains the command to execute gofmt (or +" goimports). args is dict with +function! s:fmt_cmd(bin_name, source, target) + " check if the user has installed command binary. + " For example if it's goimports, let us check if it's installed, + " if not the user get's a warning via go#path#CheckBinPath() + let bin_path = go#path#CheckBinPath(a:bin_name) + if empty(bin_path) + return [] + endif - let l:listtype = "locationlist" - "if there is no error on the temp file replace the output with the current - "file (if this fails, we can always check the outputs first line with: - "splitted =~ 'package \w\+') - if v:shell_error == 0 - " remove undo point caused via BufWritePre - try | silent undojoin | catch | endtry - - " Replace current file with temp file, then reload buffer - let old_fileformat = &fileformat - call rename(l:tmpname, expand('%')) - silent edit! - let &fileformat = old_fileformat - let &syntax = &syntax - - " clean up previous location list, but only if it's due to fmt - if exists('b:got_fmt_error') && b:got_fmt_error - let b:got_fmt_error = 0 - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) - endif - elseif g:go_fmt_fail_silently == 0 - let splitted = split(out, '\n') - "otherwise get the errors and put them to location list - let errors = [] - for line in splitted - let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)') - if !empty(tokens) - call add(errors, {"filename": @%, - \"lnum": tokens[2], - \"col": tokens[3], - \"text": tokens[4]}) - endif - endfor - if empty(errors) - % | " Couldn't detect gofmt error format, output errors - endif - if !empty(errors) - call go#list#Populate(l:listtype, errors) - echohl Error | echomsg "Gofmt returned error" | echohl None - endif - - let b:got_fmt_error = 1 - call go#list#Window(l:listtype, len(errors)) - - " We didn't use the temp file, so clean up - call delete(l:tmpname) + " start constructing the command + let bin_path = go#util#Shellescape(bin_path) + let cmd = [bin_path] + call add(cmd, "-w") + + " add the options for binary (if any). go_fmt_options was by default of type + " string, however to allow customization it's now a dictionary of binary + " name mapping to options. + let opts = g:go_fmt_options + if type(g:go_fmt_options) == type({}) + let opts = has_key(g:go_fmt_options, a:bin_name) ? g:go_fmt_options[a:bin_name] : "" + endif + call extend(cmd, split(opts, " ")) + + if a:bin_name == "goimports" + " lazy check if goimports support `-srcdir`. We should eventually remove + " this in the future + if !exists('b:goimports_vendor_compatible') + let out = go#util#System(bin_path . " --help") + if out !~ "-srcdir" + call go#util#EchoWarning(printf("vim-go: goimports (%s) does not support srcdir. Update with: :GoUpdateBinaries", bin_path)) + else + let b:goimports_vendor_compatible = 1 + endif endif - if g:go_fmt_experimental == 1 - " restore our undo history - silent! exe 'rundo ' . tmpundofile - call delete(tmpundofile) + if exists('b:goimports_vendor_compatible') && b:goimports_vendor_compatible + let ssl_save = &shellslash + set noshellslash + " use the filename without the fully qualified name if the tree is + " symlinked into the GOPATH, goimports won't work properly. + call extend(cmd, ["-srcdir", shellescape(a:target)]) + let &shellslash = ssl_save endif + endif - if g:go_fmt_experimental == 1 - " Restore our cursor/windows positions, folds, etc. - if empty(l:curw) - silent! loadview - else - call winrestview(l:curw) - endif - else - " Restore our cursor/windows positions. - call winrestview(l:curw) + call add(cmd, a:source) + return cmd +endfunction + +" parse_errors parses the given errors and returns a list of parsed errors +function! s:parse_errors(filename, content) abort + let splitted = split(a:content, '\n') + + " list of errors to be put into location list + let errors = [] + for line in splitted + let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)') + if !empty(tokens) + call add(errors,{ + \"filename": a:filename, + \"lnum": tokens[2], + \"col": tokens[3], + \"text": tokens[4], + \ }) endif + endfor + + return errors +endfunction + +" show_errors opens a location list and shows the given errors. If the given +" errors is empty, it closes the the location list +function! s:show_errors(errors) abort + let l:listtype = go#list#Type("GoFmt") + if !empty(a:errors) + call go#list#Populate(l:listtype, a:errors, 'Format') + echohl Error | echomsg "Gofmt returned error" | echohl None + endif + + " this closes the window if there are no errors or it opens + " it if there is any + call go#list#Window(l:listtype, len(a:errors)) endfunction +function! go#fmt#ToggleFmtAutoSave() abort + if get(g:, "go_fmt_autosave", 1) + let g:go_fmt_autosave = 0 + call go#util#EchoProgress("auto fmt disabled") + return + end + + let g:go_fmt_autosave = 1 + call go#util#EchoProgress("auto fmt enabled") +endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/fmt_test.vim b/vim/bundle/go/autoload/go/fmt_test.vim new file mode 100644 index 0000000..2adbfba --- /dev/null +++ b/vim/bundle/go/autoload/go/fmt_test.vim @@ -0,0 +1,49 @@ +func! Test_run_fmt() abort + let actual_file = tempname() + call writefile(readfile("test-fixtures/fmt/hello.go"), actual_file) + + let expected = join(readfile("test-fixtures/fmt/hello_golden.go"), "\n") + + " run our code + call go#fmt#run("gofmt", actual_file, "test-fixtures/fmt/hello.go") + + " this should now contain the formatted code + let actual = join(readfile(actual_file), "\n") + + call assert_equal(expected, actual) +endfunc + +func! Test_update_file() abort + let expected = join(readfile("test-fixtures/fmt/hello_golden.go"), "\n") + let source_file = tempname() + call writefile(readfile("test-fixtures/fmt/hello_golden.go"), source_file) + + let target_file = tempname() + call writefile([""], target_file) + + " update_file now + call go#fmt#update_file(source_file, target_file) + + " this should now contain the formatted code + let actual = join(readfile(target_file), "\n") + + call assert_equal(expected, actual) +endfunc + +func! Test_goimports() abort + let $GOPATH = 'test-fixtures/fmt/' + let actual_file = tempname() + call writefile(readfile("test-fixtures/fmt/src/imports/goimports.go"), actual_file) + + let expected = join(readfile("test-fixtures/fmt/src/imports/goimports_golden.go"), "\n") + + " run our code + call go#fmt#run("goimports", actual_file, "test-fixtures/fmt/src/imports/goimports.go") + + " this should now contain the formatted code + let actual = join(readfile(actual_file), "\n") + + call assert_equal(expected, actual) +endfunc + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/guru.vim b/vim/bundle/go/autoload/go/guru.vim new file mode 100644 index 0000000..c252090 --- /dev/null +++ b/vim/bundle/go/autoload/go/guru.vim @@ -0,0 +1,628 @@ +" guru.vim -- Vim integration for the Go guru. + +" guru_cmd returns a dict that contains the command to execute guru. args +" is dict with following options: +" mode : guru mode, such as 'implements' +" format : output format, either 'plain' or 'json' +" needs_scope : if 1, adds the current package to the scope +" selected : if 1, means it's a range of selection, otherwise it picks up the +" offset under the cursor +" example output: +" {'cmd' : ['guru', '-json', 'implements', 'demo/demo.go:#66']} +function! s:guru_cmd(args) range abort + let mode = a:args.mode + let format = a:args.format + let needs_scope = a:args.needs_scope + let selected = a:args.selected + + let result = {} + let pkg = go#package#ImportPath() + + " this is important, check it! + if pkg == -1 && needs_scope + return {'err': "current directory is not inside of a valid GOPATH"} + endif + + "return with a warning if the binary doesn't exist + let bin_path = go#path#CheckBinPath("guru") + if empty(bin_path) + return {'err': "bin path not found"} + endif + + " start constructing the command + let cmd = [bin_path] + + let filename = fnamemodify(expand("%"), ':p:gs?\\?/?') + if &modified + let result.stdin_content = go#util#archive() + call add(cmd, "-modified") + endif + + " enable outputting in json format + if format == "json" + call add(cmd, "-json") + endif + + " check for any tags + if exists('g:go_build_tags') + let tags = get(g:, 'go_build_tags') + call extend(cmd, ["-tags", tags]) + let result.tags = tags + endif + + " some modes require scope to be defined (such as callers). For these we + " choose a sensible setting, which is using the current file's package + let scopes = [] + if needs_scope + let scopes = [pkg] + endif + + " check for any user defined scope setting. users can define the scope, + " in package pattern form. examples: + " golang.org/x/tools/cmd/guru # a single package + " golang.org/x/tools/... # all packages beneath dir + " ... # the entire workspace. + if exists('g:go_guru_scope') + " check that the setting is of type list + if type(get(g:, 'go_guru_scope')) != type([]) + return {'err' : "go_guru_scope should of type list"} + endif + + let scopes = get(g:, 'go_guru_scope') + endif + + " now add the scope to our command if there is any + if !empty(scopes) + " strip trailing slashes for each path in scoped. bug: + " https://github.com/golang/go/issues/14584 + let scopes = go#util#StripTrailingSlash(scopes) + + " create shell-safe entries of the list + if !go#util#has_job() | let scopes = go#util#Shelllist(scopes) | endif + + " guru expect a comma-separated list of patterns, construct it + let l:scope = join(scopes, ",") + let result.scope = l:scope + call extend(cmd, ["-scope", l:scope]) + endif + + let pos = printf("#%s", go#util#OffsetCursor()) + if selected != -1 + " means we have a range, get it + let pos1 = go#util#Offset(line("'<"), col("'<")) + let pos2 = go#util#Offset(line("'>"), col("'>")) + let pos = printf("#%s,#%s", pos1, pos2) + endif + + let filename .= ':'.pos + call extend(cmd, [mode, filename]) + + let result.cmd = cmd + return result +endfunction + +" sync_guru runs guru in sync mode with the given arguments +function! s:sync_guru(args) abort + let result = s:guru_cmd(a:args) + if has_key(result, 'err') + call go#util#EchoError(result.err) + return -1 + endif + + if !has_key(a:args, 'disable_progress') + if a:args.needs_scope + call go#util#EchoProgress("analysing with scope ". result.scope . + \ " (see ':help go-guru-scope' if this doesn't work)...") + elseif a:args.mode !=# 'what' + " the query might take time, let us give some feedback + call go#util#EchoProgress("analysing ...") + endif + endif + + + " run, forrest run!!! + let command = join(result.cmd, " ") + if has_key(result, 'stdin_content') + let out = go#util#System(command, result.stdin_content) + else + let out = go#util#System(command) + endif + + if has_key(a:args, 'custom_parse') + call a:args.custom_parse(go#util#ShellError(), out) + else + call s:parse_guru_output(go#util#ShellError(), out, a:args.mode) + endif + + return out +endfunc + +" async_guru runs guru in async mode with the given arguments +function! s:async_guru(args) abort + let result = s:guru_cmd(a:args) + if has_key(result, 'err') + call go#util#EchoError(result.err) + return + endif + + let status_dir = expand('%:p:h') + let statusline_type = printf("%s", a:args.mode) + + if !has_key(a:args, 'disable_progress') + if a:args.needs_scope + call go#util#EchoProgress("analysing with scope " . result.scope . + \ " (see ':help go-guru-scope' if this doesn't work)...") + endif + endif + + let messages = [] + function! s:callback(chan, msg) closure + call add(messages, a:msg) + endfunction + + let status = {} + let exitval = 0 + + function! s:exit_cb(job, exitval) closure + let status = { + \ 'desc': 'last status', + \ 'type': statusline_type, + \ 'state': "finished", + \ } + + if a:exitval + let exitval = a:exitval + let status.state = "failed" + endif + + call go#statusline#Update(status_dir, status) + endfunction + + function! s:close_cb(ch) closure + let out = join(messages, "\n") + + if has_key(a:args, 'custom_parse') + call a:args.custom_parse(exitval, out) + else + call s:parse_guru_output(exitval, out, a:args.mode) + endif + endfunction + + let start_options = { + \ 'callback': funcref("s:callback"), + \ 'exit_cb': funcref("s:exit_cb"), + \ 'close_cb': funcref("s:close_cb"), + \ } + + if has_key(result, 'stdin_content') + let l:tmpname = tempname() + call writefile(split(result.stdin_content, "\n"), l:tmpname, "b") + let l:start_options.in_io = "file" + let l:start_options.in_name = l:tmpname + endif + + call go#statusline#Update(status_dir, { + \ 'desc': "current status", + \ 'type': statusline_type, + \ 'state': "analysing", + \}) + + return job_start(result.cmd, start_options) +endfunc + +" run_guru runs the given guru argument +function! s:run_guru(args) abort + if go#util#has_job() + let res = s:async_guru(a:args) + else + let res = s:sync_guru(a:args) + endif + + return res +endfunction + +" Show 'implements' relation for selected package +function! go#guru#Implements(selected) abort + let args = { + \ 'mode': 'implements', + \ 'format': 'plain', + \ 'selected': a:selected, + \ 'needs_scope': 1, + \ } + + call s:run_guru(args) +endfunction + +" Report the possible constants, global variables, and concrete types that may +" appear in a value of type error +function! go#guru#Whicherrs(selected) abort + let args = { + \ 'mode': 'whicherrs', + \ 'format': 'plain', + \ 'selected': a:selected, + \ 'needs_scope': 1, + \ } + + + " TODO(arslan): handle empty case for both sync/async + " if empty(out.out) + " call go#util#EchoSuccess("no error variables found. Try to change the scope with :GoGuruScope") + " return + " endif + call s:run_guru(args) +endfunction + +" Describe selected syntax: definition, methods, etc +function! go#guru#Describe(selected) abort + let args = { + \ 'mode': 'describe', + \ 'format': 'plain', + \ 'selected': a:selected, + \ 'needs_scope': 1, + \ } + + call s:run_guru(args) +endfunction + +function! go#guru#DescribeInfo() abort + " json_encode() and friends are introduced with this patch (7.4.1304) + " vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ + " nvim: https://github.com/neovim/neovim/pull/4131 + if !exists("*json_decode") + call go#util#EchoError("requires 'json_decode'. Update your Vim/Neovim version.") + return + endif + + function! s:info(exit_val, output) + if a:exit_val != 0 + return + endif + + if a:output[0] !=# '{' + return + endif + + if empty(a:output) || type(a:output) != type("") + return + endif + + let result = json_decode(a:output) + if type(result) != type({}) + call go#util#EchoError(printf("malformed output from guru: %s", a:output)) + return + endif + + if !has_key(result, 'detail') + " if there is no detail check if there is a description and print it + if has_key(result, "desc") + call go#util#EchoInfo(result["desc"]) + return + endif + + call go#util#EchoError("detail key is missing. Please open a bug report on vim-go repo.") + return + endif + + let detail = result['detail'] + let info = "" + + " guru gives different information based on the detail mode. Let try to + " extract the most useful information + + if detail == "value" + if !has_key(result, 'value') + call go#util#EchoError("value key is missing. Please open a bug report on vim-go repo.") + return + endif + + let val = result["value"] + if !has_key(val, 'type') + call go#util#EchoError("type key is missing (value.type). Please open a bug report on vim-go repo.") + return + endif + + let info = val["type"] + elseif detail == "type" + if !has_key(result, 'type') + call go#util#EchoError("type key is missing. Please open a bug report on vim-go repo.") + return + endif + + let type = result["type"] + if !has_key(type, 'type') + call go#util#EchoError("type key is missing (type.type). Please open a bug report on vim-go repo.") + return + endif + + let info = type["type"] + elseif detail == "package" + if !has_key(result, 'package') + call go#util#EchoError("package key is missing. Please open a bug report on vim-go repo.") + return + endif + + let package = result["package"] + if !has_key(package, 'path') + call go#util#EchoError("path key is missing (package.path). Please open a bug report on vim-go repo.") + return + endif + + let info = printf("package %s", package["path"]) + elseif detail == "unknown" + let info = result["desc"] + else + call go#util#EchoError(printf("unknown detail mode found '%s'. Please open a bug report on vim-go repo", detail)) + return + endif + + call go#util#EchoInfo(info) + endfunction + + let args = { + \ 'mode': 'describe', + \ 'format': 'json', + \ 'selected': -1, + \ 'needs_scope': 0, + \ 'custom_parse': function('s:info'), + \ 'disable_progress': 1, + \ } + + call s:run_guru(args) +endfunction + +" Show possible targets of selected function call +function! go#guru#Callees(selected) abort + let args = { + \ 'mode': 'callees', + \ 'format': 'plain', + \ 'selected': a:selected, + \ 'needs_scope': 1, + \ } + + call s:run_guru(args) +endfunction + +" Show possible callers of selected function +function! go#guru#Callers(selected) abort + let args = { + \ 'mode': 'callers', + \ 'format': 'plain', + \ 'selected': a:selected, + \ 'needs_scope': 1, + \ } + + call s:run_guru(args) +endfunction + +" Show path from callgraph root to selected function +function! go#guru#Callstack(selected) abort + let args = { + \ 'mode': 'callstack', + \ 'format': 'plain', + \ 'selected': a:selected, + \ 'needs_scope': 1, + \ } + + call s:run_guru(args) +endfunction + +" Show free variables of selection +function! go#guru#Freevars(selected) abort + " Freevars requires a selection + if a:selected == -1 + call go#util#EchoError("GoFreevars requires a selection (range) of code") + return + endif + + let args = { + \ 'mode': 'freevars', + \ 'format': 'plain', + \ 'selected': 1, + \ 'needs_scope': 0, + \ } + + call s:run_guru(args) +endfunction + +" Show send/receive corresponding to selected channel op +function! go#guru#ChannelPeers(selected) abort + let args = { + \ 'mode': 'peers', + \ 'format': 'plain', + \ 'selected': a:selected, + \ 'needs_scope': 1, + \ } + + call s:run_guru(args) +endfunction + +" Show all refs to entity denoted by selected identifier +function! go#guru#Referrers(selected) abort + let args = { + \ 'mode': 'referrers', + \ 'format': 'plain', + \ 'selected': a:selected, + \ 'needs_scope': 0, + \ } + + call s:run_guru(args) +endfunction + +function! go#guru#SameIdsTimer() abort + call timer_start(200, function('go#guru#SameIds'), {'repeat': -1}) +endfunction + +function! go#guru#SameIds() abort + " we use matchaddpos() which was introduce with 7.4.330, be sure we have + " it: http://ftp.vim.org/vim/patches/7.4/7.4.330 + if !exists("*matchaddpos") + call go#util#EchoError("GoSameIds requires 'matchaddpos'. Update your Vim/Neovim version.") + return + endif + + " json_encode() and friends are introduced with this patch (7.4.1304) + " vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ + " nvim: https://github.com/neovim/neovim/pull/4131 + if !exists("*json_decode") + call go#util#EchoError("GoSameIds requires 'json_decode'. Update your Vim/Neovim version.") + return + endif + + let args = { + \ 'mode': 'what', + \ 'format': 'json', + \ 'selected': -1, + \ 'needs_scope': 0, + \ 'custom_parse': function('s:same_ids_highlight'), + \ } + + call s:run_guru(args) +endfunction + +function! s:same_ids_highlight(exit_val, output) abort + call go#guru#ClearSameIds() " run after calling guru to reduce flicker. + + if a:output[0] !=# '{' + if !get(g:, 'go_auto_sameids', 0) + call go#util#EchoError(a:output) + endif + return + endif + + let result = json_decode(a:output) + if type(result) != type({}) && !get(g:, 'go_auto_sameids', 0) + call go#util#EchoError("malformed output from guru") + return + endif + + if !has_key(result, 'sameids') + if !get(g:, 'go_auto_sameids', 0) + call go#util#EchoError("no same_ids founds for the given identifier") + endif + return + endif + + let poslen = 0 + for enclosing in result['enclosing'] + if enclosing['desc'] == 'identifier' + let poslen = enclosing['end'] - enclosing['start'] + break + endif + endfor + + " return when there's no identifier to highlight. + if poslen == 0 + return + endif + + let same_ids = result['sameids'] + " highlight the lines + for item in same_ids + let pos = split(item, ':') + call matchaddpos('goSameId', [[str2nr(pos[-2]), str2nr(pos[-1]), str2nr(poslen)]]) + endfor + + if get(g:, "go_auto_sameids", 0) + " re-apply SameIds at the current cursor position at the time the buffer + " is redisplayed: e.g. :edit, :GoRename, etc. + augroup vim-go-sameids + autocmd! + autocmd BufWinEnter nested call go#guru#SameIds() + augroup end + endif +endfunction + +" ClearSameIds returns 0 when it removes goSameId groups and non-zero if no +" goSameId groups are found. +function! go#guru#ClearSameIds() abort + let l:cleared = 0 + + let m = getmatches() + for item in m + if item['group'] == 'goSameId' + call matchdelete(item['id']) + let l:cleared = 1 + endif + endfor + + if !l:cleared + return 1 + endif + + " remove the autocmds we defined + augroup vim-go-sameids + autocmd! + augroup end + + return 0 +endfunction + +function! go#guru#ToggleSameIds() abort + if go#guru#ClearSameIds() != 0 + call go#guru#SameIds() + endif +endfunction + +function! go#guru#AutoToogleSameIds() abort + if get(g:, "go_auto_sameids", 0) + call go#util#EchoProgress("sameids auto highlighting disabled") + call go#guru#ClearSameIds() + let g:go_auto_sameids = 0 + return + endif + + call go#util#EchoSuccess("sameids auto highlighting enabled") + let g:go_auto_sameids = 1 +endfunction + + +"""""""""""""""""""""""""""""""""""""""" +"" HELPER FUNCTIONS +"""""""""""""""""""""""""""""""""""""""" + +" This uses Vim's errorformat to parse the output from Guru's 'plain output +" and put it into location list. I believe using errorformat is much more +" easier to use. If we need more power we can always switch back to parse it +" via regex. Match two possible styles of errorformats: +" +" 'file:line.col-line2.col2: message' +" 'file:line:col: message' +" +" We discard line2 and col2 for the first errorformat, because it's not +" useful and location only has the ability to show one line and column +" number +function! s:parse_guru_output(exit_val, output, title) abort + if a:exit_val + call go#util#EchoError(a:output) + return + endif + + let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m" + let l:listtype = go#list#Type("_guru") + call go#list#ParseFormat(l:listtype, errformat, a:output, a:title) + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) +endfun + +function! go#guru#Scope(...) abort + if a:0 + if a:0 == 1 && a:1 == '""' + unlet g:go_guru_scope + call go#util#EchoSuccess("guru scope is cleared") + else + let g:go_guru_scope = a:000 + call go#util#EchoSuccess("guru scope changed to: ". join(a:000, ",")) + endif + + return + endif + + if !exists('g:go_guru_scope') + call go#util#EchoError("guru scope is not set") + else + call go#util#EchoSuccess("current guru scope: ". join(g:go_guru_scope, ",")) + endif +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/impl.vim b/vim/bundle/go/autoload/go/impl.vim new file mode 100644 index 0000000..73707fa --- /dev/null +++ b/vim/bundle/go/autoload/go/impl.vim @@ -0,0 +1,140 @@ +function! go#impl#Impl(...) abort + let recv = "" + let iface = "" + let interactive = 0 + + let pos = getpos('.') + + if a:0 is 0 + " Interactive mode if user didn't pass any arguments. + let recv = s:getReceiver() + let iface = input("vim-go: generating method stubs for interface: ") + redraw! + if empty(iface) + call go#util#EchoError('usage: interface type is not provided') + return + endif + elseif a:0 is 1 + " we assume the user only passed the interface type, + " i.e: ':GoImpl io.Writer' + let recv = s:getReceiver() + let iface = a:1 + elseif a:0 > 2 + " user passed receiver and interface type both, + " i.e: 'GoImpl f *Foo io.Writer' + let recv = join(a:000[:-2], ' ') + let iface = a:000[-1] + else + call go#util#EchoError('usage: GoImpl {receiver} {interface}') + return + endif + + " Make sure we put the generated code *after* the struct. + if getline(".") =~ "struct " + normal! $% + endif + + try + let dirname = fnameescape(expand('%:p:h')) + let [result, err] = go#util#Exec(['impl', '-dir', dirname, recv, iface]) + let result = substitute(result, "\n*$", "", "") + if err + call go#util#EchoError(result) + return + endif + + if result is# '' + return + end + + put ='' + silent put =result + finally + call setpos('.', pos) + endtry +endfunction + +function! s:getReceiver() + let receiveType = expand("") + if receiveType == "type" + normal! w + let receiveType = expand("") + elseif receiveType == "struct" + normal! ge + let receiveType = expand("") + endif + return printf("%s *%s", tolower(receiveType)[0], receiveType) +endfunction + +if exists('*uniq') + function! s:uniq(list) + return uniq(a:list) + endfunction +else + " Note: Believe that the list is sorted + function! s:uniq(list) + let i = len(a:list) - 1 + while 0 < i + if a:list[i-1] ==# a:list[i] + call remove(a:list, i) + let i -= 2 + else + let i -= 1 + endif + endwhile + return a:list + endfunction +endif + +function! s:root_dirs() abort + let dirs = [] + let root = go#util#env("goroot") + if root !=# '' && isdirectory(root) + call add(dirs, root) + endif + + let paths = map(split(go#util#env("gopath"), go#util#PathListSep()), "substitute(v:val, '\\\\', '/', 'g')") + if !empty(filter(paths, 'isdirectory(v:val)')) + call extend(dirs, paths) + endif + + return dirs +endfunction + +function! s:go_packages(dirs) abort + let pkgs = [] + for d in a:dirs + let pkg_root = expand(d . '/pkg/' . go#util#osarch()) + call extend(pkgs, split(globpath(pkg_root, '**/*.a', 1), "\n")) + endfor + return map(pkgs, "fnamemodify(v:val, ':t:r')") +endfunction + +function! s:interface_list(pkg) abort + let [contents, err] = go#util#Exec(['go', 'doc', a:pkg]) + if err + return [] + endif + + let contents = split(contents, "\n") + call filter(contents, 'v:val =~# ''^type\s\+\h\w*\s\+interface''') + return map(contents, 'a:pkg . "." . matchstr(v:val, ''^type\s\+\zs\h\w*\ze\s\+interface'')') +endfunction + +" Complete package and interface for {interface} +function! go#impl#Complete(arglead, cmdline, cursorpos) abort + let words = split(a:cmdline, '\s\+', 1) + if words[-1] ==# '' + return s:uniq(sort(s:go_packages(s:root_dirs()))) + elseif words[-1] =~# '^\h\w*$' + return s:uniq(sort(filter(s:go_packages(s:root_dirs()), 'stridx(v:val, words[-1]) == 0'))) + elseif words[-1] =~# '^\h\w*\.\%(\h\w*\)\=$' + let [pkg, interface] = split(words[-1], '\.', 1) + echomsg pkg + return s:uniq(sort(filter(s:interface_list(pkg), 'v:val =~? words[-1]'))) + else + return [] + endif +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/impl_test.vim b/vim/bundle/go/autoload/go/impl_test.vim new file mode 100644 index 0000000..c417cd4 --- /dev/null +++ b/vim/bundle/go/autoload/go/impl_test.vim @@ -0,0 +1,37 @@ +func! Test_impl() abort + try + let l:tmp = gotest#write_file('a/a.go', [ + \ 'package a', + \ '', + \ '']) + + call go#impl#Impl('r', 'reader', 'io.Reader') + call gotest#assert_buffer(1, [ + \ 'func (r reader) Read(p []byte) (n int, err error) {', + \ ' panic("not implemented")', + \ '}']) + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +func! Test_impl_get() abort + try + let l:tmp = gotest#write_file('a/a.go', [ + \ 'package a', + \ '', + \ 'type reader struct {}']) + + call go#impl#Impl('io.Reader') + call gotest#assert_buffer(0, [ + \ 'package a', + \ '', + \ 'type reader struct {}', + \ '', + \ 'func (r *reader) Read(p []byte) (n int, err error) {', + \ ' panic("not implemented")', + \ '}']) + finally + call delete(l:tmp, 'rf') + endtry +endfunc diff --git a/vim/bundle/go/autoload/go/import.vim b/vim/bundle/go/autoload/go/import.vim index 2660099..8d9e8d4 100644 --- a/vim/bundle/go/autoload/go/import.vim +++ b/vim/bundle/go/autoload/go/import.vim @@ -4,210 +4,210 @@ " " Check out the docs for more information at /doc/vim-go.txt " -function! go#import#SwitchImport(enabled, localname, path, bang) - let view = winsaveview() - let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '') - - " Quotes are not necessary, so remove them if provided. - if path[0] == '"' - let path = strpart(path, 1) - endif - if path[len(path)-1] == '"' - let path = strpart(path, 0, len(path) - 1) +function! go#import#SwitchImport(enabled, localname, path, bang) abort + let view = winsaveview() + let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '') + + " Quotes are not necessary, so remove them if provided. + if path[0] == '"' + let path = strpart(path, 1) + endif + if path[len(path)-1] == '"' + let path = strpart(path, 0, len(path) - 1) + endif + + " if given a trailing slash, eg. `github.com/user/pkg/`, remove it + if path[len(path)-1] == '/' + let path = strpart(path, 0, len(path) - 1) + endif + + if path == '' + call s:Error('Import path not provided') + return + endif + + if a:bang == "!" + let out = go#util#System("go get -u -v ".shellescape(path)) + if go#util#ShellError() != 0 + call s:Error("Can't find import: " . path . ":" . out) endif - - " if given a trailing slash, eg. `github.com/user/pkg/`, remove it - if path[len(path)-1] == '/' - let path = strpart(path, 0, len(path) - 1) - endif - - if path == '' - call s:Error('Import path not provided') - return - endif - - if a:bang == "!" - let out = system("go get -u -v ".shellescape(path)) - if v:shell_error - call s:Error("Can't find import: " . path . ":" . out) + endif + 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 - endif - let exists = go#tool#Exists(path) - if exists == -1 - call s:Error("Can't find import: " . path) - return - 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 - " 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, "^[^/]*/") + elseif linestr =~# '^\(var\|const\|type\|func\)\>' + break - let qpath = '"' . path . '"' - if a:localname != '' - let qlocalpath = a:localname . ' ' . qpath + 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 - let qlocalpath = qpath + 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 - 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 - 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 + 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 + " 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) + " Put buffer back where it was. + call winrestview(view) endfunction -function! s:Error(s) - echohl Error | echo a:s | echohl None +function! s:Error(s) abort + echohl Error | echo a:s | echohl None endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/job.vim b/vim/bundle/go/autoload/go/job.vim new file mode 100644 index 0000000..965f20d --- /dev/null +++ b/vim/bundle/go/autoload/go/job.vim @@ -0,0 +1,102 @@ +" Spawn returns callbacks to be used with job_start. It's abstracted to be +" used with various go command, such as build, test, install, etc.. This avoid +" us to write the same callback over and over for some commands. It's fully +" customizable so each command can change it to it's own logic. +function go#job#Spawn(args) + let cbs = { + \ 'winnr': winnr(), + \ 'dir': getcwd(), + \ 'jobdir': fnameescape(expand("%:p:h")), + \ 'messages': [], + \ 'args': a:args.cmd, + \ 'bang': 0, + \ 'for': "_job", + \ } + + if has_key(a:args, 'bang') + let cbs.bang = a:args.bang + endif + + if has_key(a:args, 'for') + let cbs.for = a:args.for + endif + + " add final callback to be called if async job is finished + " The signature should be in form: func(job, exit_status, messages) + if has_key(a:args, 'custom_cb') + let cbs.custom_cb = a:args.custom_cb + endif + + if has_key(a:args, 'error_info_cb') + let cbs.error_info_cb = a:args.error_info_cb + endif + + function cbs.callback(chan, msg) dict + call add(self.messages, a:msg) + endfunction + + function cbs.exit_cb(job, exitval) dict + if has_key(self, 'error_info_cb') + call self.error_info_cb(a:job, a:exitval, self.messages) + endif + + if get(g:, 'go_echo_command_info', 1) + if a:exitval == 0 + call go#util#EchoSuccess("SUCCESS") + else + call go#util#EchoError("FAILED") + endif + endif + + if has_key(self, 'custom_cb') + call self.custom_cb(a:job, a:exitval, self.messages) + endif + + let l:listtype = go#list#Type(self.for) + if a:exitval == 0 + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + return + endif + + call self.show_errors(l:listtype) + endfunction + + function cbs.show_errors(listtype) dict + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + try + execute cd self.jobdir + let errors = go#tool#ParseErrors(self.messages) + let errors = go#tool#FilterValids(errors) + finally + execute cd . fnameescape(self.dir) + endtry + + if !len(errors) + " failed to parse errors, output the original content + call go#util#EchoError(self.messages + [self.dir]) + return + endif + + if self.winnr == winnr() + call go#list#Populate(a:listtype, errors, join(self.args)) + call go#list#Window(a:listtype, len(errors)) + if !empty(errors) && !self.bang + call go#list#JumpToFirst(a:listtype) + endif + endif + endfunction + + " override callback handler if user provided it + if has_key(a:args, 'callback') + let cbs.callback = a:args.callback + endif + + " override exit callback handler if user provided it + if has_key(a:args, 'exit_cb') + let cbs.exit_cb = a:args.exit_cb + endif + + return cbs +endfunction +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/jobcontrol.vim b/vim/bundle/go/autoload/go/jobcontrol.vim index 777b054..57eab32 100644 --- a/vim/bundle/go/autoload/go/jobcontrol.vim +++ b/vim/bundle/go/autoload/go/jobcontrol.vim @@ -2,62 +2,68 @@ " internal function s:spawn let s:jobs = {} +" s:handlers is a global event handlers for all jobs started with Spawn() or +" with the internal function s:spawn +let s:handlers = {} + " Spawn is a wrapper around s:spawn. It can be executed by other files and " scripts if needed. Desc defines the description for printing the status " during the job execution (useful for statusline integration). -function! go#jobcontrol#Spawn(bang, desc, args) +function! go#jobcontrol#Spawn(bang, desc, for, args) abort " autowrite is not enabled for jobs call go#cmd#autowrite() - let job = s:spawn(a:bang, a:desc, a:args) + let job = s:spawn(a:bang, a:desc, a:for, a:args) return job.id endfunction -" Statusline returns the current status of the job -function! go#jobcontrol#Statusline() abort - if empty(s:jobs) - return '' - endif - - let import_path = go#package#ImportPath(expand('%:p:h')) - - for job in values(s:jobs) - if job.importpath != import_path - continue - endif - - if job.state == "SUCCESS" - return '' - endif - - return printf("%s ... [%s]", job.desc, job.state) - endfor +" AddHandler adds a on_exit callback handler and returns the id. +function! go#jobcontrol#AddHandler(handler) abort + let i = len(s:handlers) + while has_key(s:handlers, string(i)) + let i += 1 + break + endwhile + let s:handlers[string(i)] = a:handler + return string(i) +endfunction - return '' +" RemoveHandler removes a callback handler by id. +function! go#jobcontrol#RemoveHandler(id) abort + unlet s:handlers[a:id] endfunction -" spawn spawns a go subcommand with the name and arguments with jobstart. Once -" a job is started a reference will be stored inside s:jobs. spawn changes the -" GOPATH when g:go_autodetect_gopath is enabled. The job is started inside the -" current files folder. -function! s:spawn(bang, desc, args) - let job = { - \ 'desc': a:desc, - \ 'bang': a:bang, +" spawn spawns a go subcommand with the name and arguments with jobstart. Once a +" job is started a reference will be stored inside s:jobs. The job is started +" inside the current files folder. +function! s:spawn(bang, desc, for, args) abort + let status_type = a:args[0] + let status_dir = expand('%:p:h') + let started_at = reltime() + + call go#statusline#Update(status_dir, { + \ 'desc': "current status", + \ 'type': status_type, + \ 'state': "started", + \}) + + let job = { + \ 'desc': a:desc, + \ 'bang': a:bang, \ 'winnr': winnr(), - \ 'importpath': go#package#ImportPath(expand('%:p:h')), + \ 'importpath': go#package#ImportPath(), \ 'state': "RUNNING", \ 'stderr' : [], \ 'stdout' : [], \ 'on_stdout': function('s:on_stdout'), \ 'on_stderr': function('s:on_stderr'), \ 'on_exit' : function('s:on_exit'), + \ 'status_type' : status_type, + \ 'status_dir' : status_dir, + \ 'started_at' : started_at, + \ 'for' : a:for, \ } - " modify GOPATH if needed - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() - " execute go build in the files directory let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' @@ -83,9 +89,6 @@ function! s:spawn(bang, desc, args) execute cd . fnameescape(dir) - " restore back GOPATH - let $GOPATH = old_gopath - return job endfunction @@ -93,29 +96,57 @@ endfunction " references and also displaying errors in the quickfix window collected by " on_stderr handler. If there are no errors and a quickfix window is open, " it'll be closed. -function! s:on_exit(job_id, exit_status) +function! s:on_exit(job_id, exit_status, event) dict abort + let status = { + \ 'desc': 'last status', + \ 'type': self.status_type, + \ 'state': "success", + \ } + + if a:exit_status + let status.state = "failed" + endif + + let elapsed_time = reltimestr(reltime(self.started_at)) + " strip whitespace + let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') + let status.state .= printf(" (%ss)", elapsed_time) + + call go#statusline#Update(self.status_dir, status) + let std_combined = self.stderr + self.stdout + + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + execute cd self.dir + + call s:callback_handlers_on_exit(s:jobs[a:job_id], a:exit_status, std_combined) + + let l:listtype = go#list#Type(self.for) if a:exit_status == 0 - call go#list#Clean(0) - call go#list#Window(0) + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) let self.state = "SUCCESS" - call go#util#EchoSuccess("SUCCESS") + + if get(g:, 'go_echo_command_info', 1) + call go#util#EchoSuccess("[" . self.status_type . "] SUCCESS") + endif + + execute cd . fnameescape(dir) return endif let self.state = "FAILED" - call go#util#EchoError("FAILED") - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - let dir = getcwd() - try - execute cd self.dir - let errors = go#tool#ParseErrors(std_combined) - let errors = go#tool#FilterValids(errors) - finally - execute cd . fnameescape(dir) - endtry + if get(g:, 'go_echo_command_info', 1) + call go#util#EchoError("[" . self.status_type . "] FAILED") + endif + + let errors = go#tool#ParseErrors(std_combined) + let errors = go#tool#FilterValids(errors) + + execute cd . fnameescape(dir) if !len(errors) " failed to parse errors, output the original content @@ -125,8 +156,7 @@ function! s:on_exit(job_id, exit_status) " if we are still in the same windows show the list if self.winnr == winnr() - let l:listtype = "locationlist" - call go#list#Populate(l:listtype, errors) + call go#list#Populate(l:listtype, errors, self.desc) call go#list#Window(l:listtype, len(errors)) if !empty(errors) && !self.bang call go#list#JumpToFirst(l:listtype) @@ -134,46 +164,27 @@ function! s:on_exit(job_id, exit_status) endif endfunction -" on_stdout is the stdout handler for jobstart(). It collects the output of -" stderr and stores them to the jobs internal stdout list. -function! s:on_stdout(job_id, data) - call extend(self.stdout, a:data) -endfunction - -" on_stderr is the stderr handler for jobstart(). It collects the output of -" stderr and stores them to the jobs internal stderr list. -function! s:on_stderr(job_id, data) - call extend(self.stderr, a:data) -endfunction - -" abort_all aborts all current jobs created with s:spawn() -function! s:abort_all() - if empty(s:jobs) +" callback_handlers_on_exit runs all handlers for job on exit event. +function! s:callback_handlers_on_exit(job, exit_status, data) abort + if empty(s:handlers) return endif - for id in keys(s:jobs) - if id > 0 - silent! call jobstop(id) - endif + for s:handler in values(s:handlers) + call s:handler(a:job, a:exit_status, a:data) endfor - - let s:jobs = {} endfunction -" abort aborts the job with the given name, where name is the first argument -" passed to s:spawn() -function! s:abort(path) - if empty(s:jobs) - return - endif +" on_stdout is the stdout handler for jobstart(). It collects the output of +" stderr and stores them to the jobs internal stdout list. +function! s:on_stdout(job_id, data, event) dict abort + call extend(self.stdout, a:data) +endfunction - for job in values(s:jobs) - if job.importpath == path && job.id > 0 - silent! call jobstop(job.id) - unlet s:jobs['job.id'] - endif - endfor +" on_stderr is the stderr handler for jobstart(). It collects the output of +" stderr and stores them to the jobs internal stderr list. +function! s:on_stderr(job_id, data, event) dict abort + call extend(self.stderr, a:data) endfunction -" vim:ts=2:sw=2:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/keyify.vim b/vim/bundle/go/autoload/go/keyify.vim new file mode 100644 index 0000000..23ca0f3 --- /dev/null +++ b/vim/bundle/go/autoload/go/keyify.vim @@ -0,0 +1,56 @@ +function! go#keyify#Keyify() + let bin_path = go#path#CheckBinPath("keyify") + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + + if empty(bin_path) || !exists('*json_decode') + return + endif + + " Get result of command as json, that contains `start`, `end` and `replacement` + let command = printf("%s -json %s:#%s", go#util#Shellescape(bin_path), + \ go#util#Shellescape(fname), go#util#OffsetCursor()) + let output = go#util#System(command) + silent! let result = json_decode(output) + + " We want to output the error message in case the result isn't a JSON + if type(result) != type({}) + call go#util#EchoError(s:chomp(output)) + return + endif + + " Because keyify returns the byte before the region we want, we goto the + " byte after that + execute "goto" result.start + 1 + let start = getpos('.') + execute "goto" result.end + let end = getpos('.') + + let vis_start = getpos("'<") + let vis_end = getpos("'>") + + " Replace contents between start and end with `replacement` + call setpos("'<", start) + call setpos("'>", end) + + let select = 'gv' + + " Make sure the visual mode is 'v', to avoid some bugs + normal! gv + if mode() !=# 'v' + let select .= 'v' + endif + + silent! execute "normal!" select."\"=result.replacement\p" + + " Replacement text isn't aligned, so it needs fix + normal! '= + + call setpos("'<", vis_start) + call setpos("'>", vis_end) +endfunction + +function! s:chomp(string) + return substitute(a:string, '\n\+$', '', '') +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/lint.vim b/vim/bundle/go/autoload/go/lint.vim index 91247ad..5bf4edb 100644 --- a/vim/bundle/go/autoload/go/lint.vim +++ b/vim/bundle/go/autoload/go/lint.vim @@ -1,199 +1,329 @@ if !exists("g:go_metalinter_command") - let g:go_metalinter_command = "" + let g:go_metalinter_command = "" endif if !exists("g:go_metalinter_autosave_enabled") - let g:go_metalinter_autosave_enabled = ['vet', 'golint'] + let g:go_metalinter_autosave_enabled = ['vet', 'golint'] endif if !exists("g:go_metalinter_enabled") - let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck'] + let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck'] endif -if !exists("g:go_metalinter_deadline") - let g:go_metalinter_deadline = "5s" +if !exists("g:go_metalinter_excludes") + let g:go_metalinter_excludes = [] endif if !exists("g:go_golint_bin") - let g:go_golint_bin = "golint" + let g:go_golint_bin = "golint" endif if !exists("g:go_errcheck_bin") - let g:go_errcheck_bin = "errcheck" + let g:go_errcheck_bin = "errcheck" endif function! go#lint#Gometa(autosave, ...) abort - if a:0 == 0 - let goargs = expand('%:p:h') - else - let goargs = go#util#Shelljoin(a:000) + if a:0 == 0 + let goargs = shellescape(expand('%:p:h')) + else + let goargs = go#util#Shelljoin(a:000) + endif + + let bin_path = go#path#CheckBinPath("gometalinter") + if empty(bin_path) + return + endif + + let cmd = [bin_path] + let cmd += ["--disable-all"] + + if a:autosave || empty(g:go_metalinter_command) + " linters + let linters = a:autosave ? g:go_metalinter_autosave_enabled : g:go_metalinter_enabled + for linter in linters + let cmd += ["--enable=".linter] + endfor + + for exclude in g:go_metalinter_excludes + let cmd += ["--exclude=".exclude] + endfor + + " gometalinter has a --tests flag to tell its linters whether to run + " against tests. While not all of its linters respect this flag, for those + " that do, it means if we don't pass --tests, the linter won't run against + " test files. One example of a linter that will not run against tests if + " we do not specify this flag is errcheck. + let cmd += ["--tests"] + + " path + let cmd += [expand('%:p:h')] + else + " the user wants something else, let us use it. + let cmd += split(g:go_metalinter_command, " ") + endif + + " gometalinter has a default deadline of 5 seconds. + " + " For async mode (s:lint_job), we want to override the default deadline only + " if we have a deadline configured. + " + " For sync mode (go#util#System), always explicitly pass the 5 seconds + " deadline if there is no other deadline configured. If a deadline is + " configured, then use it. + + " Call gometalinter asynchronously. + if go#util#has_job() && has('lambda') + let deadline = get(g:, 'go_metalinter_deadline', 0) + if deadline != 0 + let cmd += ["--deadline=" . deadline] endif - let meta_command = "gometalinter --disable-all" - if a:autosave || empty(g:go_metalinter_command) - let bin_path = go#path#CheckBinPath("gometalinter") - if empty(bin_path) - return - endif - - if a:autosave - " include only messages for the active buffer - let meta_command .= " --include='^" . expand('%:p') . ".*$'" - endif - - " linters - let linters = a:autosave ? g:go_metalinter_autosave_enabled : g:go_metalinter_enabled - for linter in linters - let meta_command .= " --enable=".linter - endfor - - " deadline - let meta_command .= " --deadline=" . g:go_metalinter_deadline - - " path - let meta_command .= " " . goargs - else - " the user wants something else, let us use it. - let meta_command = g:go_metalinter_command - endif + call s:lint_job({'cmd': cmd}) + return + endif + + " We're calling gometalinter synchronously. + + let cmd += ["--deadline=" . get(g:, 'go_metalinter_deadline', "5s")] + + if a:autosave + " include only messages for the active buffer + let cmd += ["--include='^" . expand('%:p') . ".*$'"] + endif + + + let meta_command = join(cmd, " ") + + let out = go#util#System(meta_command) - " comment out the following two lines for debugging - " echo meta_command - " return - - let out = go#tool#ExecuteInDir(meta_command) - - let l:listtype = "quickfix" - if v:shell_error == 0 - redraw | echo - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) - echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None - else - " GoMetaLinter can output one of the two, so we look for both: - " ::[]: () - " ::: () - " This can be defined by the following errorformat: - let errformat = "%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m" - - " Parse and populate our location list - call go#list#ParseFormat(l:listtype, errformat, split(out, "\n")) - - let errors = go#list#Get(l:listtype) - call go#list#Window(l:listtype, len(errors)) - - if !a:autosave - call go#list#JumpToFirst(l:listtype) - endif + let l:listtype = go#list#Type("GoMetaLinter") + if go#util#ShellError() == 0 + redraw | echo + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None + else + " GoMetaLinter can output one of the two, so we look for both: + " ::[]: () + " ::: () + " This can be defined by the following errorformat: + let errformat = "%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m" + + " Parse and populate our location list + call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter') + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + + if !a:autosave + call go#list#JumpToFirst(l:listtype) endif + endif endfunction " Golint calls 'golint' on the current directory. Any warnings are populated in " the location list function! go#lint#Golint(...) abort - let bin_path = go#path#CheckBinPath(g:go_golint_bin) - if empty(bin_path) - return - endif - - if a:0 == 0 - let goargs = shellescape(expand('%')) - else - let goargs = go#util#Shelljoin(a:000) - endif - - let out = system(bin_path . " " . goargs) - if empty(out) - echon "vim-go: " | echohl Function | echon "[lint] PASS" | echohl None - return - endif - - let l:listtype = "quickfix" - call go#list#Parse(l:listtype, out) - let errors = go#list#Get(l:listtype) - call go#list#Window(l:listtype, len(errors)) - call go#list#JumpToFirst(l:listtype) + let bin_path = go#path#CheckBinPath(g:go_golint_bin) + if empty(bin_path) + return + endif + let bin_path = go#util#Shellescape(bin_path) + + if a:0 == 0 + let out = go#util#System(bin_path . " " . go#util#Shellescape(go#package#ImportPath())) + else + let out = go#util#System(bin_path . " " . go#util#Shelljoin(a:000)) + endif + + if empty(out) + echon "vim-go: " | echohl Function | echon "[lint] PASS" | echohl None + return + endif + + let l:listtype = go#list#Type("GoLint") + call go#list#Parse(l:listtype, out) + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + call go#list#JumpToFirst(l:listtype) endfunction " Vet calls 'go vet' on the current directory. Any warnings are populated in " the location list -function! go#lint#Vet(bang, ...) - call go#cmd#autowrite() - echon "vim-go: " | echohl Identifier | echon "calling vet..." | echohl None - if a:0 == 0 - let out = go#tool#ExecuteInDir('go vet') - else - let out = go#tool#ExecuteInDir('go tool vet ' . go#util#Shelljoin(a:000)) - endif - - let l:listtype = "quickfix" - if v:shell_error - let errors = go#tool#ParseErrors(split(out, '\n')) - call go#list#Populate(l:listtype, errors) - call go#list#Window(l:listtype, len(errors)) - if !empty(errors) && !a:bang - call go#list#JumpToFirst(l:listtype) - endif - echon "vim-go: " | echohl ErrorMsg | echon "[vet] FAIL" | echohl None - else - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) - redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None +function! go#lint#Vet(bang, ...) abort + call go#cmd#autowrite() + echon "vim-go: " | echohl Identifier | echon "calling vet..." | echohl None + if a:0 == 0 + let out = go#util#System('go vet ' . go#util#Shellescape(go#package#ImportPath())) + else + let out = go#util#System('go tool vet ' . go#util#Shelljoin(a:000)) + endif + + let l:listtype = go#list#Type("GoVet") + if go#util#ShellError() != 0 + let errors = go#tool#ParseErrors(split(out, '\n')) + call go#list#Populate(l:listtype, errors, 'Vet') + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) endif + echon "vim-go: " | echohl ErrorMsg | echon "[vet] FAIL" | echohl None + else + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None + endif endfunction " ErrCheck calls 'errcheck' for the given packages. Any warnings are populated in " the location list function! go#lint#Errcheck(...) abort - if a:0 == 0 - let goargs = go#package#ImportPath(expand('%:p:h')) - if goargs == -1 - echohl Error | echomsg "vim-go: package is not inside GOPATH src" | echohl None - return - endif - else - let goargs = go#util#Shelljoin(a:000) + if a:0 == 0 + let import_path = go#package#ImportPath() + if import_path == -1 + echohl Error | echomsg "vim-go: package is not inside GOPATH src" | echohl None + return + endif + else + let import_path = go#util#Shelljoin(a:000) + endif + + let bin_path = go#path#CheckBinPath(g:go_errcheck_bin) + if empty(bin_path) + return + endif + + echon "vim-go: " | echohl Identifier | echon "errcheck analysing ..." | echohl None + redraw + + let command = go#util#Shellescape(bin_path) . ' -abspath ' . import_path + let out = go#tool#ExecuteInDir(command) + + let l:listtype = go#list#Type("GoErrCheck") + if go#util#ShellError() != 0 + let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m" + + " Parse and populate our location list + call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'Errcheck') + + let errors = go#list#Get(l:listtype) + if empty(errors) + echohl Error | echomsg "GoErrCheck returned error" | echohl None + echo out + return endif - let bin_path = go#path#CheckBinPath(g:go_errcheck_bin) - if empty(bin_path) - return + if !empty(errors) + echohl Error | echomsg "GoErrCheck found errors" | echohl None + call go#list#Populate(l:listtype, errors, 'Errcheck') + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) + call go#list#JumpToFirst(l:listtype) + endif endif + else + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None + endif + +endfunction + +function! go#lint#ToggleMetaLinterAutoSave() abort + if get(g:, "go_metalinter_autosave", 0) + let g:go_metalinter_autosave = 0 + call go#util#EchoProgress("auto metalinter disabled") + return + end + + let g:go_metalinter_autosave = 1 + call go#util#EchoProgress("auto metalinter enabled") +endfunction + +function s:lint_job(args) + let status_dir = expand('%:p:h') + let started_at = reltime() + + call go#statusline#Update(status_dir, { + \ 'desc': "current status", + \ 'type': "gometalinter", + \ 'state': "analysing", + \}) + + " autowrite is not enabled for jobs + call go#cmd#autowrite() + + let l:listtype = go#list#Type("GoMetaLinter") + let l:errformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m' + + function! s:callback(chan, msg) closure + let old_errorformat = &errorformat + let &errorformat = l:errformat + try + if l:listtype == "locationlist" + lad a:msg + elseif l:listtype == "quickfix" + caddexpr a:msg + endif + finally + let &errorformat = old_errorformat + endtry + + " TODO(jinleileiking): give a configure to jump or not + let l:winnr = winnr() + + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) - echon "vim-go: " | echohl Identifier | echon "errcheck analysing ..." | echohl None - redraw - - let command = bin_path . ' -abspath ' . goargs - let out = go#tool#ExecuteInDir(command) - - let l:listtype = "quickfix" - if v:shell_error - let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m" - - " Parse and populate our location list - call go#list#ParseFormat(l:listtype, errformat, split(out, "\n")) - - let errors = go#list#Get(l:listtype) - - if empty(errors) - echohl Error | echomsg "GoErrCheck returned error" | echohl None - echo out - return - endif - - if !empty(errors) - call go#list#Populate(l:listtype, errors) - call go#list#Window(l:listtype, len(errors)) - if !empty(errors) - call go#list#JumpToFirst(l:listtype) - endif - endif - else - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) - echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None + exe l:winnr . "wincmd w" + endfunction + + function! s:exit_cb(job, exitval) closure + let status = { + \ 'desc': 'last status', + \ 'type': "gometaliner", + \ 'state': "finished", + \ } + + if a:exitval + let status.state = "failed" + endif + + let elapsed_time = reltimestr(reltime(started_at)) + " strip whitespace + let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') + let status.state .= printf(" (%ss)", elapsed_time) + + call go#statusline#Update(status_dir, status) + + let errors = go#list#Get(l:listtype) + if empty(errors) + call go#list#Window(l:listtype, len(errors)) + elseif has("patch-7.4.2200") + if l:listtype == 'quickfix' + call setqflist([], 'a', {'title': 'GoMetaLinter'}) + else + call setloclist(0, [], 'a', {'title': 'GoMetaLinter'}) + endif + endif + + if get(g:, 'go_echo_command_info', 1) + call go#util#EchoSuccess("linting finished") endif + endfunction + + let start_options = { + \ 'callback': funcref("s:callback"), + \ 'exit_cb': funcref("s:exit_cb"), + \ } + + call job_start(a:args.cmd, start_options) + + call go#list#Clean(l:listtype) + if get(g:, 'go_echo_command_info', 1) + call go#util#EchoProgress("linting started ...") + endif endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/list.vim b/vim/bundle/go/autoload/go/list.vim index d91cfb1..472e0d3 100644 --- a/vim/bundle/go/autoload/go/list.vim +++ b/vim/bundle/go/autoload/go/list.vim @@ -1,126 +1,169 @@ if !exists("g:go_list_type") - let g:go_list_type = "" + let g:go_list_type = "" endif -" Window opens the list with the given height up to 10 lines maximum. -" Otherwise g:go_loclist_height is used. If no or zero height is given it -" closes the window -function! go#list#Window(listtype, ...) - let l:listtype = go#list#Type(a:listtype) - " we don't use lwindow to close the location list as we need also the - " ability to resize the window. So, we are going to use lopen and lclose - " for a better user experience. If the number of errors in a current - " location list increases/decreases, cwindow will not resize when a new - " updated height is passed. lopen in the other hand resizes the screen. - if !a:0 || a:1 == 0 - if l:listtype == "locationlist" - lclose - else - cclose - endif - return - endif +if !exists("g:go_list_type_commands") + let g:go_list_type_commands = {} +endif - let height = get(g:, "go_list_height", 0) - if height == 0 - " prevent creating a large location height for a large set of numbers - if a:1 > 10 - let height = 10 - else - let height = a:1 - endif +" Window opens the list with the given height up to 10 lines maximum. +" Otherwise g:go_loclist_height is used. +" +" If no or zero height is given it closes the window by default. +" To prevent this, set g:go_list_autoclose = 0 +function! go#list#Window(listtype, ...) abort + " we don't use lwindow to close the location list as we need also the + " ability to resize the window. So, we are going to use lopen and lclose + " for a better user experience. If the number of errors in a current + " location list increases/decreases, cwindow will not resize when a new + " updated height is passed. lopen in the other hand resizes the screen. + if !a:0 || a:1 == 0 + let autoclose_window = get(g:, 'go_list_autoclose', 1) + if autoclose_window + if a:listtype == "locationlist" + lclose + else + cclose + endif endif + return + endif - if l:listtype == "locationlist" - exe 'lopen ' . height + let height = get(g:, "go_list_height", 0) + if height == 0 + " prevent creating a large location height for a large set of numbers + if a:1 > 10 + let height = 10 else - exe 'copen ' . height + let height = a:1 endif + endif + + if a:listtype == "locationlist" + exe 'lopen ' . height + else + exe 'copen ' . height + endif endfunction -" Get returns the current list of items from the location list -function! go#list#Get(listtype) - let l:listtype = go#list#Type(a:listtype) - if l:listtype == "locationlist" - return getloclist(0) - else - return getqflist() - endif +" Get returns the current items from the list +function! go#list#Get(listtype) abort + if a:listtype == "locationlist" + return getloclist(0) + else + return getqflist() + endif endfunction -" Populate populate the location list with the given items -function! go#list#Populate(listtype, items) - let l:listtype = go#list#Type(a:listtype) - if l:listtype == "locationlist" - call setloclist(0, a:items, 'r') - else - call setqflist(a:items, 'r') - endif -endfunction +" Populate populate the list with the given items +function! go#list#Populate(listtype, items, title) abort + if a:listtype == "locationlist" + call setloclist(0, a:items, 'r') -function! go#list#PopulateWin(winnr, items) - call setloclist(a:winnr, a:items, 'r') + " The last argument ({what}) is introduced with 7.4.2200: + " https://github.com/vim/vim/commit/d823fa910cca43fec3c31c030ee908a14c272640 + if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif + else + call setqflist(a:items, 'r') + if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif + endif endfunction -" Parse parses the given items based on the specified errorformat nad -" populates the location list. -function! go#list#ParseFormat(listtype, errformat, items) - let l:listtype = go#list#Type(a:listtype) - " backup users errorformat, will be restored once we are finished - let old_errorformat = &errorformat - - " parse and populate the location list - let &errorformat = a:errformat - if l:listtype == "locationlist" - lgetexpr a:items +" Parse parses the given items based on the specified errorformat and +" populates the list. +function! go#list#ParseFormat(listtype, errformat, items, title) abort + " backup users errorformat, will be restored once we are finished + let old_errorformat = &errorformat + + " parse and populate the location list + let &errorformat = a:errformat + try + if a:listtype == "locationlist" + lgetexpr a:items + if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif else - cgetexpr a:items + cgetexpr a:items + if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif endif - + finally "restore back let &errorformat = old_errorformat + endtry endfunction " Parse parses the given items based on the global errorformat and -" populates the location list. -function! go#list#Parse(listtype, items) - let l:listtype = go#list#Type(a:listtype) - if l:listtype == "locationlist" - lgetexpr a:items - else - cgetexpr a:items - endif +" populates the list. +function! go#list#Parse(listtype, items) abort + if a:listtype == "locationlist" + lgetexpr a:items + else + cgetexpr a:items + endif endfunction " JumpToFirst jumps to the first item in the location list -function! go#list#JumpToFirst(listtype) - let l:listtype = go#list#Type(a:listtype) - if l:listtype == "locationlist" - ll 1 - else - cc 1 - endif +function! go#list#JumpToFirst(listtype) abort + if a:listtype == "locationlist" + ll 1 + else + cc 1 + endif endfunction " Clean cleans the location list -function! go#list#Clean(listtype) - let l:listtype = go#list#Type(a:listtype) - if l:listtype == "locationlist" - lex [] - else - cex [] - endif +function! go#list#Clean(listtype) abort + if a:listtype == "locationlist" + lex [] + else + cex [] + endif endfunction -function! go#list#Type(listtype) - if g:go_list_type == "locationlist" - return "locationlist" - elseif g:go_list_type == "quickfix" - return "quickfix" - else - return a:listtype - endif +function! s:listtype(listtype) abort + if g:go_list_type == "locationlist" + return "locationlist" + elseif g:go_list_type == "quickfix" + return "quickfix" + endif + + return a:listtype +endfunction + +" s:default_list_type_commands is the defaults that will be used for each of +" the supported commands (see documentation for g:go_list_type_commands). When +" defining a default, quickfix should be used if the command operates on +" multiple files, while locationlist should be used if the command operates on a +" single file or buffer. Keys that begin with an underscore are not supported +" in g:go_list_type_commands. +let s:default_list_type_commands = { + \ "GoBuild": "quickfix", + \ "GoErrCheck": "quickfix", + \ "GoFmt": "locationlist", + \ "GoGenerate": "quickfix", + \ "GoInstall": "quickfix", + \ "GoLint": "quickfix", + \ "GoMetaLinter": "quickfix", + \ "GoModifyTags": "locationlist", + \ "GoRename": "quickfix", + \ "GoRun": "quickfix", + \ "GoTest": "quickfix", + \ "GoVet": "quickfix", + \ "_guru": "locationlist", + \ "_term": "locationlist", + \ "_job": "locationlist", + \ } + +function! go#list#Type(for) abort + let l:listtype = s:listtype(get(s:default_list_type_commands, a:for)) + if l:listtype == "0" + call go#util#EchoError(printf( + \ "unknown list type command value found ('%s'). Please open a bug report in the vim-go repo.", + \ a:for)) + let l:listtype = "quickfix" + endif + + return get(g:go_list_type_commands, a:for, l:listtype) endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/oracle.vim b/vim/bundle/go/autoload/go/oracle.vim deleted file mode 100644 index 490b6b4..0000000 --- a/vim/bundle/go/autoload/go/oracle.vim +++ /dev/null @@ -1,233 +0,0 @@ -" oracle.vim -- Vim integration for the Go oracle. -" -" Part of this plugin was taken directly from the oracle repo, however it's -" massively changed for a better integration into vim-go. Thanks Alan Donovan -" for the first iteration based on quickfix! - Fatih Arslan -" - -if !exists("g:go_oracle_bin") - let g:go_oracle_bin = "oracle" -endif - -" Parses (via regex) Oracle's 'plain' format output and puts them into a -" location list -func! s:loclist(output) - let llist = [] - " Parse GNU-style 'file:line.col-line.col: message' format. - let mx = '^\(\a:[\\/][^:]\+\|[^:]\+\):\(\d\+\):\(\d\+\):\(.*\)$' - for line in split(a:output, "\n") - let ml = matchlist(line, mx) - - " Ignore non-match lines or warnings - if ml == [] || ml[4] =~ '^ warning:' - continue - endif - - let item = { - \ 'filename': ml[1], - \ 'text': ml[4], - \ 'lnum': ml[2], - \ 'col': ml[3], - \} - let bnr = bufnr(fnameescape(ml[1])) - if bnr != -1 - let item['bufnr'] = bnr - endif - call add(llist, item) - endfor - call go#list#Populate("locationlist", llist) - call go#list#Window("locationlist", len(llist)) -endfun - -" This uses Vim's errorformat to parse the output from Oracle's 'plain output -" and put it into location list. I believe using errorformat is much more -" easier to use. If we need more power we can always switch back to parse it -" via regex. -func! s:loclistSecond(output) - " backup users errorformat, will be restored once we are finished - let old_errorformat = &errorformat - - " match two possible styles of errorformats: - " - " 'file:line.col-line2.col2: message' - " 'file:line:col: message' - " - " We discard line2 and col2 for the first errorformat, because it's not - " useful and location only has the ability to show one line and column - " number - let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m" - call go#list#ParseFormat("locationlist", errformat, split(a:output, "\n")) - - let errors = go#list#Get("locationlist") - call go#list#Window("locationlist", len(errors)) -endfun - -func! s:RunOracle(mode, selected, needs_package) range abort - let fname = expand('%:p') - let dname = expand('%:p:h') - let pkg = go#package#ImportPath(dname) - - if exists('g:go_oracle_scope') - " let the user defines the scope, must be a space separated string, - " example: 'fmt math net/http' - let scopes = split(get(g:, 'go_oracle_scope')) - elseif a:needs_package || exists('g:go_oracle_include_tests') && pkg != -1 - " give import path so it includes all _test.go files too - let scopes = [pkg] - else - " best usable way, only pass the package itself, without the test - " files - let scopes = go#tool#Files() - endif - - "return with a warning if the bin doesn't exist - let bin_path = go#path#CheckBinPath(g:go_oracle_bin) - if empty(bin_path) - return - endif - - if exists('g:go_oracle_tags') - let tags = get(g:, 'go_oracle_tags') - else - let tags = "" - endif - - if a:selected != -1 - let pos1 = go#util#Offset(line("'<"), col("'<")) - let pos2 = go#util#Offset(line("'>"), col("'>")) - let cmd = printf('%s -format plain -pos=%s:#%d,#%d -tags=%s %s', - \ bin_path, - \ shellescape(fname), pos1, pos2, tags, a:mode) - else - let pos = go#util#OffsetCursor() - let cmd = printf('%s -format plain -pos=%s:#%d -tags=%s %s', - \ bin_path, - \ shellescape(fname), pos, tags, a:mode) - endif - - " strip trailing slashes for each path in scoped. bug: - " https://github.com/golang/go/issues/14584 - let scopes = go#util#StripTrailingSlash(scopes) - - " now append each scope to the end as Oracle's scope parameter. It can be - " a packages or go files, dependent on the User's own choice. For more - " info check Oracle's User Manual section about scopes: - " https://docs.google.com/document/d/1SLk36YRjjMgKqe490mSRzOPYEDe0Y_WQNRv-EiFYUyw/view#heading=h.nwso96pj07q8 - let cmd .= ' ' . go#util#Shelljoin(scopes) - - echon "vim-go: " | echohl Identifier | echon "analysing ..." | echohl None - - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() - - let out = system(cmd) - - let $GOPATH = old_gopath - - if v:shell_error - " unfortunaly oracle outputs a very long stack trace that is not - " parsable to show the real error. But the main issue is usually the - " package which doesn't build. - redraw | echon "vim-go: " | echohl Statement | echon out | echohl None - return "" - endif - - return out -endfunc - -function! go#oracle#Scope(...) - if a:0 - if a:0 == 1 && a:1 == '""' - unlet g:go_oracle_scope - echon "vim-go: " | echohl Function | echon "oracle scope is cleared"| echohl None - else - let g:go_oracle_scope = join(a:000, ' ') - echon "vim-go: " | echohl Function | echon "oracle scope changed to: '". g:go_oracle_scope ."'" | echohl None - endif - - return - endif - - if !exists('g:go_oracle_scope') - echon "vim-go: " | echohl Function | echon "oracle scope is not set"| echohl None - else - echon "vim-go: " | echohl Function | echon "current oracle scope: '". g:go_oracle_scope ."'" | echohl None - endif -endfunction - -function! go#oracle#Tags(...) - if a:0 - if a:0 == 1 && a:1 == '""' - unlet g:go_oracle_tags - echon "vim-go: " | echohl Function | echon "oracle tags is cleared"| echohl None - else - let g:go_oracle_tags = a:1 - echon "vim-go: " | echohl Function | echon "oracle tags changed to: '". g:go_oracle_tags ."'" | echohl None - endif - - return - endif - - if !exists('g:go_oracle_tags') - echon "vim-go: " | echohl Function | echon "oracle tags is not set"| echohl None - else - echon "vim-go: " | echohl Function | echon "current oracle tags: '". g:go_oracle_tags ."'" | echohl None - endif -endfunction - -" Show 'implements' relation for selected package -function! go#oracle#Implements(selected) - let out = s:RunOracle('implements', a:selected, 0) - call s:loclistSecond(out) -endfunction - -" Describe selected syntax: definition, methods, etc -function! go#oracle#Describe(selected) - let out = s:RunOracle('describe', a:selected, 0) - call s:loclistSecond(out) -endfunction - -" Show possible targets of selected function call -function! go#oracle#Callees(selected) - let out = s:RunOracle('callees', a:selected, 1) - call s:loclistSecond(out) -endfunction - -" Show possible callers of selected function -function! go#oracle#Callers(selected) - let out = s:RunOracle('callers', a:selected, 1) - call s:loclistSecond(out) -endfunction - -" Show path from callgraph root to selected function -function! go#oracle#Callstack(selected) - let out = s:RunOracle('callstack', a:selected, 1) - call s:loclistSecond(out) -endfunction - -" Show free variables of selection -function! go#oracle#Freevars(selected) - " Freevars requires a selection - if a:selected == -1 - echon "vim-go: " | echohl Statement | echon "GoFreevars requires a selection (range) of code "| echohl None - return - endif - - let out = s:RunOracle('freevars', a:selected, 0) - call s:loclistSecond(out) -endfunction - -" Show send/receive corresponding to selected channel op -function! go#oracle#ChannelPeers(selected) - let out = s:RunOracle('peers', a:selected, 1) - call s:loclistSecond(out) -endfunction - -" Show all refs to entity denoted by selected identifier -function! go#oracle#Referrers(selected) - let out = s:RunOracle('referrers', a:selected, 0) - call s:loclistSecond(out) -endfunction - -" vim:ts=4:sw=4:et -" diff --git a/vim/bundle/go/autoload/go/package.vim b/vim/bundle/go/autoload/go/package.vim index 32b38ae..940423f 100644 --- a/vim/bundle/go/autoload/go/package.vim +++ b/vim/bundle/go/autoload/go/package.vim @@ -9,120 +9,124 @@ 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 + 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 + if exists('g:golang_goarch') + let s:goarch = g:golang_goarch + else + let s:goarch = '*' + endif endif -function! go#package#Paths() - let dirs = [] - - if !exists("s:goroot") - if executable('go') - let s:goroot = substitute(system('go env GOROOT'), '\n', '', 'g') - if v:shell_error - echomsg '''go env GOROOT'' failed' - endif - else - let s:goroot = $GOROOT - endif - endif +function! go#package#Paths() abort + let dirs = [] - if len(s:goroot) != 0 && isdirectory(s:goroot) - let dirs += [s:goroot] + if !exists("s:goroot") + if executable('go') + let s:goroot = go#util#env("goroot") + if go#util#ShellError() != 0 + echomsg '''go env GOROOT'' failed' + endif + else + let s:goroot = $GOROOT endif + endif - let workspaces = split($GOPATH, go#util#PathListSep()) - if workspaces != [] - let dirs += workspaces - endif + if len(s:goroot) != 0 && isdirectory(s:goroot) + let dirs += [s:goroot] + endif + + let workspaces = split(go#path#Default(), go#util#PathListSep()) + if workspaces != [] + let dirs += workspaces + endif - return dirs + return dirs endfunction -function! go#package#ImportPath(arg) - let path = fnamemodify(resolve(a:arg), ':p') - let dirs = go#package#Paths() +" ImportPath returns the import path in the current directory it was executed +function! go#package#ImportPath() abort + let out = go#tool#ExecuteInDir("go list") + if go#util#ShellError() != 0 + return -1 + endif - for dir in dirs - if len(dir) && match(path, dir) == 0 - let workspace = dir - endif - endfor + let import_path = split(out, '\n')[0] - if !exists('workspace') - return -1 - endif + " go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH. + " Check it and retun an error if that is the case + if import_path[0] ==# '_' + return -1 + endif - let srcdir = substitute(workspace . '/src/', '//', '/', '') - return substitute(path, srcdir, '', '') + return import_path 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 +function! go#package#FromPath(arg) abort + let path = fnamemodify(resolve(a:arg), ':p') + let dirs = go#package#Paths() - if !exists('workspace') - return -1 - endif - - if isdirectory(path) - return substitute(path, workspace . 'src/', '', '') - else - return substitute(substitute(path, workspace . 'src/', '', ''), - \ '/' . fnamemodify(path, ':t'), '', '') + for dir in dirs + if len(dir) && match(path, dir) == 0 + let workspace = dir + break endif + endfor + + if !exists('workspace') + return -1 + endif + + let path = substitute(path, '/*$', '', '') + let workspace = substitute(workspace . '/src/', '/+', '', '') + if isdirectory(path) + return substitute(path, workspace, '', '') + else + return substitute(substitute(path, workspace, '', ''), + \ '/' . 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 +function! go#package#CompleteMembers(package, member) abort + silent! let content = go#util#System('godoc ' . a:package) + if go#util#ShellError() || !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) +function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort + let words = split(a:CmdLine, '\s\+', 1) - " do not complete package members for these commands - let neglect_commands = ["GoImportAs", "GoOracleScope"] + " do not complete package members for these commands + let neglect_commands = ["GoImportAs", "GoGuruScope"] - if len(words) > 2 && index(neglect_commands, words[0]) == -1 - " Complete package members - return go#package#CompleteMembers(words[1], words[2]) - endif + if len(words) > 2 && index(neglect_commands, words[0]) == -1 + " Complete package members + return go#package#CompleteMembers(words[1], words[2]) + endif let dirs = go#package#Paths() @@ -157,4 +161,4 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos) return sort(keys(ret)) endfunction -" vim:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/path.vim b/vim/bundle/go/autoload/go/path.vim index 295e103..8d91f26 100644 --- a/vim/bundle/go/autoload/go/path.vim +++ b/vim/bundle/go/autoload/go/path.vim @@ -4,171 +4,193 @@ " :GoPath is used let s:initial_go_path = "" -" GoPath sets or returns the current GOPATH. If no arguments are passed it +" GoPath sets or echos the current GOPATH. If no arguments are passed it " echoes the current GOPATH, if an argument is passed it replaces the current " GOPATH with it. If two double quotes are passed (the empty string in go), " it'll clear the GOPATH and will restore to the initial GOPATH. -function! go#path#GoPath(...) - " we have an argument, replace GOPATH - if len(a:000) - " clears the current manually set GOPATH and restores it to the - " initial GOPATH, which was set when Vim was started. - if len(a:000) == 1 && a:1 == '""' - if !empty(s:initial_go_path) - let $GOPATH = s:initial_go_path - let s:initial_go_path = "" - endif - - echon "vim-go: " | echohl Function | echon "GOPATH restored to ". $GOPATH | echohl None - return - endif - - echon "vim-go: " | echohl Function | echon "GOPATH changed to ". a:1 | echohl None - let s:initial_go_path = $GOPATH - let $GOPATH = a:1 - return +function! go#path#GoPath(...) abort + " no argument, show GOPATH + if len(a:000) == 0 + echo go#path#Default() + return + endif + + " we have an argument, replace GOPATH + " clears the current manually set GOPATH and restores it to the + " initial GOPATH, which was set when Vim was started. + if len(a:000) == 1 && a:1 == '""' + if !empty(s:initial_go_path) + let $GOPATH = s:initial_go_path + let s:initial_go_path = "" endif - echo go#path#Detect() -endfunction + echon "vim-go: " | echohl Function | echon "GOPATH restored to ". $GOPATH | echohl None + return + endif -" Default returns the default GOPATH. If there is a single GOPATH it returns -" it. For multiple GOPATHS separated with a the OS specific separator, only -" the first one is returned -function! go#path#Default() - let go_paths = split($GOPATH, go#util#PathListSep()) + echon "vim-go: " | echohl Function | echon "GOPATH changed to ". a:1 | echohl None + let s:initial_go_path = $GOPATH + let $GOPATH = a:1 +endfunction - if len(go_paths) == 1 - return $GOPATH - endif +" Default returns the default GOPATH. If GOPATH is not set, it uses the +" default GOPATH set starting with Go 1.8. This GOPATH can be retrieved via +" 'go env GOPATH' +function! go#path#Default() abort + if $GOPATH == "" + " use default GOPATH via go env + return go#util#env("gopath") + endif - return go_paths[0] + return $GOPATH endfunction " HasPath checks whether the given path exists in GOPATH environment variable " or not -function! go#path#HasPath(path) - let go_paths = split($GOPATH, go#util#PathListSep()) - let last_char = strlen(a:path) - 1 - - " check cases of '/foo/bar/' and '/foo/bar' - if a:path[last_char] == go#util#PathSep() - let withSep = a:path - let noSep = strpart(a:path, 0, last_char) - else - let withSep = a:path . go#util#PathSep() - let noSep = a:path - endif - - let hasA = index(go_paths, withSep) != -1 - let hasB = index(go_paths, noSep) != -1 - return hasA || hasB +function! go#path#HasPath(path) abort + let go_paths = split(go#path#Default(), go#util#PathListSep()) + let last_char = strlen(a:path) - 1 + + " check cases of '/foo/bar/' and '/foo/bar' + if a:path[last_char] == go#util#PathSep() + let withSep = a:path + let noSep = strpart(a:path, 0, last_char) + else + let withSep = a:path . go#util#PathSep() + let noSep = a:path + endif + + let hasA = index(go_paths, withSep) != -1 + let hasB = index(go_paths, noSep) != -1 + return hasA || hasB endfunction " Detect returns the current GOPATH. If a package manager is used, such as " Godeps, GB, it will modify the GOPATH so those directories take precedence " over the current GOPATH. It also detects diretories whose are outside " GOPATH. -function! go#path#Detect() - let gopath = $GOPATH +function! go#path#Detect() abort + let gopath = go#path#Default() - " don't lookup for godeps if autodetect is disabled. - if !get(g:, "go_autodetect_gopath", 1) - return gopath - endif + let current_dir = fnameescape(expand('%:p:h')) - let current_dir = fnameescape(expand('%:p:h')) + " TODO(arslan): this should be changed so folders or files should be + " fetched from a customizable list. The user should define any new package + " management tool by it's own. - " TODO(arslan): this should be changed so folders or files should be - " fetched from a customizable list. The user should define any new package - " management tool by it's own. + " src folders outside $GOPATH + let src_roots = finddir("src", current_dir .";", -1) - " src folder outside $GOPATH - let src_root = finddir("src", current_dir .";") - if !empty(src_root) - let src_path = fnamemodify(src_root, ':p:h:h') . go#util#PathSep() + " for cases like GOPATH/src/foo/src/bar, pick up GOPATH/src instead of + " GOPATH/src/foo/src + let src_root = "" + if len(src_roots) > 0 + let src_root = src_roots[-1] + endif - " gb vendor plugin - " (https://github.com/constabulary/gb/tree/master/cmd/gb-vendor) - let gb_vendor_root = src_path . "vendor" . go#util#PathSep() - if isdirectory(gb_vendor_root) && !go#path#HasPath(gb_vendor_root) - let gopath = gb_vendor_root . go#util#PathListSep() . gopath - endif + if !empty(src_root) + let src_path = fnamemodify(src_root, ':p:h:h') . go#util#PathSep() - if !go#path#HasPath(src_path) - let gopath = src_path . go#util#PathListSep() . gopath - endif + " gb vendor plugin + " (https://github.com/constabulary/gb/tree/master/cmd/gb-vendor) + let gb_vendor_root = src_path . "vendor" . go#util#PathSep() + if isdirectory(gb_vendor_root) && !go#path#HasPath(gb_vendor_root) + let gopath = gb_vendor_root . go#util#PathListSep() . gopath endif - " Godeps - let godeps_root = finddir("Godeps", current_dir .";") - if !empty(godeps_root) - let godeps_path = join([fnamemodify(godeps_root, ':p:h:h'), "Godeps", "_workspace" ], go#util#PathSep()) + if !go#path#HasPath(src_path) + let gopath = src_path . go#util#PathListSep() . gopath + endif + endif + + " Godeps + let godeps_root = finddir("Godeps", current_dir .";") + if !empty(godeps_root) + let godeps_path = join([fnamemodify(godeps_root, ':p:h:h'), "Godeps", "_workspace" ], go#util#PathSep()) - if !go#path#HasPath(godeps_path) - let gopath = godeps_path . go#util#PathListSep() . gopath - endif + if !go#path#HasPath(godeps_path) + let gopath = godeps_path . go#util#PathListSep() . gopath endif + endif - return gopath + " Fix up the case where initial $GOPATH is empty, + " and we end up with a trailing : + let gopath = substitute(gopath, ":$", "", "") + return gopath endfunction - " BinPath returns the binary path of installed go tools. -function! go#path#BinPath() - let bin_path = "" - - " check if our global custom path is set, if not check if $GOBIN is set so - " we can use it, otherwise use $GOPATH + '/bin' - if exists("g:go_bin_path") - let bin_path = g:go_bin_path - elseif $GOBIN != "" - let bin_path = $GOBIN - elseif $GOPATH != "" - let bin_path = expand(go#path#Default() . "/bin/") - else - " could not find anything +function! go#path#BinPath() abort + 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 default GOPATH + if exists("g:go_bin_path") + let bin_path = g:go_bin_path + elseif $GOBIN != "" + let bin_path = $GOBIN + else + let go_paths = split(go#path#Default(), go#util#PathListSep()) + if len(go_paths) == 0 + return "" "nothing found endif + let bin_path = expand(go_paths[0] . "/bin/") + endif - return bin_path + return bin_path endfunction " CheckBinPath checks whether the given binary exists or not and returns the " path of the binary. It returns an empty string doesn't exists. -function! go#path#CheckBinPath(binpath) - " remove whitespaces if user applied something like 'goimports ' - let binpath = substitute(a:binpath, '^\s*\(.\{-}\)\s*$', '\1', '') +function! go#path#CheckBinPath(binpath) abort + " remove whitespaces if user applied something like 'goimports ' + let binpath = substitute(a:binpath, '^\s*\(.\{-}\)\s*$', '\1', '') + " save off original path + let old_path = $PATH + + " check if we have an appropriate bin_path + let go_bin_path = go#path#BinPath() + if !empty(go_bin_path) + " append our GOBIN and GOPATH paths and be sure they can be found there... + " let us search in our GOBIN and GOPATH paths + let $PATH = go_bin_path . go#util#PathListSep() . $PATH + endif - " if it's in PATH just return it - if executable(binpath) - return binpath + " if it's in PATH just return it + if executable(binpath) + if exists('*exepath') + let binpath = exepath(binpath) endif + let $PATH = old_path - " just get the basename - let basename = fnamemodify(binpath, ":t") - - " check if we have an appropriate bin_path - let go_bin_path = go#path#BinPath() - if empty(go_bin_path) - echo "vim-go: could not find '" . basename . "'. Run :GoInstallBinaries to fix it." - return "" + if go#util#IsUsingCygwinShell() == 1 + return go#path#CygwinPath(binpath) endif - " append our GOBIN and GOPATH paths and be sure they can be found there... - " let us search in our GOBIN and GOPATH paths - let old_path = $PATH - let $PATH = $PATH . go#util#PathListSep() .go_bin_path - - if !executable(basename) - echo "vim-go: could not find '" . basename . "'. Run :GoInstallBinaries to fix it." - " restore back! - let $PATH = old_path - return "" - endif + return binpath + endif + " just get the basename + let basename = fnamemodify(binpath, ":t") + if !executable(basename) + call go#util#EchoError(printf("could not find '%s'. Run :GoInstallBinaries to fix it", basename)) + + " restore back! let $PATH = old_path + return "" + endif + + let $PATH = old_path + + if go#util#IsUsingCygwinShell() == 1 + return go#path#CygwinPath(a:binpath) + endif + + return go_bin_path . go#util#PathSep() . basename +endfunction - return go_bin_path . go#util#PathSep() . basename +function! go#path#CygwinPath(path) + return substitute(a:path, '\\', '/', "g") endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/play.vim b/vim/bundle/go/autoload/go/play.vim index 4b26fc1..2cf5009 100644 --- a/vim/bundle/go/autoload/go/play.vim +++ b/vim/bundle/go/autoload/go/play.vim @@ -1,94 +1,73 @@ if !exists("g:go_play_open_browser") - let g:go_play_open_browser = 1 + 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 +function! go#play#Share(count, line1, line2) abort + 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 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) + let command = "curl -s -X POST https://play.golang.org/share --data-binary '@".share_file."'" + let snippet_id = go#util#System(command) - " we can remove the temp file because it's now posted. - call delete(share_file) + " 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 + if go#util#ShellError() != 0 + echo 'A error has occurred. Run this command to see what the problem is:' + echo command + return + endif - let url = "http://play.golang.org/p/".snippet_id + 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 + " 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 + if g:go_play_open_browser != 0 + call go#tool#OpenBrowser(url) + endif - echo "vim-go: snippet uploaded: ".url + 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 +function! s:get_visual_content() abort + 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") +function! s:get_visual_selection() abort + 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 +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/rename.vim b/vim/bundle/go/autoload/go/rename.vim index 98c5fa3..3af9dcf 100644 --- a/vim/bundle/go/autoload/go/rename.vim +++ b/vim/bundle/go/autoload/go/rename.vim @@ -1,69 +1,160 @@ if !exists("g:go_gorename_bin") - let g:go_gorename_bin = "gorename" + let g:go_gorename_bin = "gorename" endif -if !exists("g:go_gorename_prefill") - let g:go_gorename_prefill = 1 -endif +" Set the default value. A value of "1" is a shortcut for this, for +" compatibility reasons. +function! s:default() abort + if !exists("g:go_gorename_prefill") || g:go_gorename_prefill == 1 + let g:go_gorename_prefill = 'expand("") =~# "^[A-Z]"' . + \ '? go#util#pascalcase(expand(""))' . + \ ': go#util#camelcase(expand(""))' + endif +endfunction +call s:default() -function! go#rename#Rename(bang, ...) - let to = "" - if a:0 == 0 - let from = expand("") - let ask = printf("vim-go: rename '%s' to: ", from) - if g:go_gorename_prefill - let to = input(ask, from) - else - let to = input(ask) - endif - redraw! - if empty(to) - return - endif +function! go#rename#Rename(bang, ...) abort + call s:default() + + let to_identifier = "" + if a:0 == 0 + let ask = printf("vim-go: rename '%s' to: ", expand("")) + if g:go_gorename_prefill != '' + let to_identifier = input(ask, eval(g:go_gorename_prefill)) else - let to = a:1 + let to_identifier = input(ask) + endif + redraw! + if empty(to_identifier) + return endif + else + let to_identifier = a:1 + endif + + " return with a warning if the bin doesn't exist + let bin_path = go#path#CheckBinPath(g:go_gorename_bin) + if empty(bin_path) + return + endif + + let fname = expand('%:p') + let pos = go#util#OffsetCursor() + let offset = printf('%s:#%d', fname, pos) + + " no need to escape for job call + let bin_path = go#util#has_job() ? bin_path : shellescape(bin_path) + let offset = go#util#has_job() ? offset : shellescape(offset) + let to_identifier = go#util#has_job() ? to_identifier : shellescape(to_identifier) + + let cmd = [bin_path, "-offset", offset, "-to", to_identifier] + + " check for any tags + if exists('g:go_build_tags') + let tags = get(g:, 'go_build_tags') + call extend(cmd, ["-tags", tags]) + endif + + if go#util#has_job() + call go#util#EchoProgress(printf("renaming to '%s' ...", to_identifier)) + call s:rename_job({ + \ 'cmd': cmd, + \ 'bang': a:bang, + \}) + return + endif + + let command = join(cmd, " ") + let out = go#tool#ExecuteInDir(command) + + let splitted = split(out, '\n') + call s:parse_errors(go#util#ShellError(), a:bang, splitted) +endfunction + +function s:rename_job(args) + let messages = [] + function! s:callback(chan, msg) closure + call add(messages, a:msg) + endfunction - "return with a warning if the bin doesn't exist - let bin_path = go#path#CheckBinPath(g:go_gorename_bin) - if empty(bin_path) - return + let status_dir = expand('%:p:h') + + function! s:exit_cb(job, exitval) closure + let status = { + \ 'desc': 'last status', + \ 'type': "gorename", + \ 'state': "finished", + \ } + + if a:exitval + let status.state = "failed" endif - let fname = expand('%:p') - let pos = go#util#OffsetCursor() - let cmd = printf('%s -offset %s -to %s', shellescape(bin_path), shellescape(printf('%s:#%d', fname, pos)), shellescape(to)) - - let out = go#tool#ExecuteInDir(cmd) - - " strip out newline on the end that gorename puts. If we don't remove, it - " will trigger the 'Hit ENTER to continue' prompt - let clean = split(out, '\n') - - let l:listtype = "quickfix" - if v:shell_error - let errors = go#tool#ParseErrors(split(out, '\n')) - call go#list#Populate(l:listtype, errors) - call go#list#Window(l:listtype, len(errors)) - if !empty(errors) && !a:bang - call go#list#JumpToFirst(l:listtype) - elseif empty(errors) - " failed to parse errors, output the original content - call go#util#EchoError(out) - endif - return - else - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) - redraw | echon "vim-go: " | echohl Function | echon clean[0] | echohl None + call go#statusline#Update(status_dir, status) + + call s:parse_errors(a:exitval, a:args.bang, messages) + endfunction + + let start_options = { + \ 'callback': funcref("s:callback"), + \ 'exit_cb': funcref("s:exit_cb"), + \ } + + call go#statusline#Update(status_dir, { + \ 'desc': "current status", + \ 'type': "gorename", + \ 'state': "started", + \}) + + call job_start(a:args.cmd, start_options) +endfunction + +function s:parse_errors(exit_val, bang, out) + " reload all files to reflect the new changes. We explicitly call + " checktime to trigger a reload of all files. See + " http://www.mail-archive.com/vim@vim.org/msg05900.html for more info + " about the autoread bug + let current_autoread = &autoread + set autoread + silent! checktime + let &autoread = current_autoread + + let l:listtype = go#list#Type("GoRename") + if a:exit_val != 0 + call go#util#EchoError("FAILED") + let errors = go#tool#ParseErrors(a:out) + call go#list#Populate(l:listtype, errors, 'Rename') + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + elseif empty(errors) + " failed to parse errors, output the original content + call go#util#EchoError(a:out) endif - " refresh the buffer so we can see the new content - " TODO(arslan): also find all other buffers and refresh them too. For this - " we need a way to get the list of changes from gorename upon an success - " change. - silent execute ":e" + return + endif + + " strip out newline on the end that gorename puts. If we don't remove, it + " will trigger the 'Hit ENTER to continue' prompt + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + call go#util#EchoSuccess(a:out[0]) + + " refresh the buffer so we can see the new content + " TODO(arslan): also find all other buffers and refresh them too. For this + " we need a way to get the list of changes from gorename upon an success + " change. + silent execute ":e" +endfunction + +" Commandline completion: original, unexported camelCase, and exported +" CamelCase. +function! go#rename#Complete(lead, cmdline, cursor) + let l:word = expand('') + return filter(uniq(sort( + \ [l:word, go#util#camelcase(l:word), go#util#pascalcase(l:word)])), + \ 'strpart(v:val, 0, len(a:lead)) == a:lead') endfunction -" vim:ts=4:sw=4:et -" +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/statusline.vim b/vim/bundle/go/autoload/go/statusline.vim new file mode 100644 index 0000000..6a4f0bf --- /dev/null +++ b/vim/bundle/go/autoload/go/statusline.vim @@ -0,0 +1,112 @@ +" Statusline +"""""""""""""""""""""""""""""""" + +" s:statuses is a global reference to all statuses. It stores the statuses per +" import paths (map[string]status), where each status is unique per its +" type. Current status dict is in form: +" { +" 'desc' : 'Job description', +" 'state' : 'Job state, such as success, failure, etc..', +" 'type' : 'Job type, such as build, test, etc..' +" 'created_at' : 'Time it was created as seconds since 1st Jan 1970' +" } +let s:statuses = {} + +" timer_id for cleaner +let s:timer_id = 0 + +" last_status stores the last generated text per status +let s:last_status = "" + +" Show returns the current status of the job for 20 seconds (configurable). It +" displays it in form of 'desc: [type|state]' if there is any state available, +" if not it returns an empty string. This function should be plugged directly +" into the statusline. +function! go#statusline#Show() abort + " lazy initialiation of the cleaner + if !s:timer_id + " clean every 60 seconds all statuses + let interval = get(g:, 'go_statusline_duration', 60000) + let s:timer_id = timer_start(interval, function('go#statusline#Clear'), {'repeat': -1}) + endif + + " nothing to show + if empty(s:statuses) + return '' + endif + + let status_dir = expand('%:p:h') + + if !has_key(s:statuses, status_dir) + return '' + endif + + let status = s:statuses[status_dir] + if !has_key(status, 'desc') || !has_key(status, 'state') || !has_key(status, 'type') + return '' + endif + + let status_text = printf("[%s|%s]", status.type, status.state) + if empty(status_text) + return '' + endif + + " only update highlight if status has changed. + if status_text != s:last_status + if status.state =~ "success" || status.state =~ "finished" || status.state =~ "pass" + hi goStatusLineColor cterm=bold ctermbg=76 ctermfg=22 + elseif status.state =~ "started" || status.state =~ "analysing" || status.state =~ "compiling" + hi goStatusLineColor cterm=bold ctermbg=208 ctermfg=88 + elseif status.state =~ "failed" + hi goStatusLineColor cterm=bold ctermbg=196 ctermfg=52 + endif + endif + + let s:last_status = status_text + return status_text +endfunction + +" Update updates (adds) the statusline for the given status_dir with the +" given status dict. It overrides any previously set status. +function! go#statusline#Update(status_dir, status) abort + let a:status.created_at = reltime() + let s:statuses[a:status_dir] = a:status + + " force to update the statusline, otherwise the user needs to move the + " cursor + exe 'let &ro = &ro' + + " before we stop the timer, check if we have any previous jobs to be cleaned + " up. Otherwise every job will reset the timer when this function is called + " and thus old jobs will never be cleaned + call go#statusline#Clear(0) + + " also reset the timer, so the user has time to see it in the statusline. + " Setting the timer_id to 0 will trigger a new cleaner routine. + call timer_stop(s:timer_id) + let s:timer_id = 0 +endfunction + +" Clear clears all currently stored statusline data. The timer_id argument is +" just a placeholder so we can pass it to a timer_start() function if needed. +function! go#statusline#Clear(timer_id) abort + for [status_dir, status] in items(s:statuses) + let elapsed_time = reltimestr(reltime(status.created_at)) + " strip whitespace + let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') + + if str2nr(elapsed_time) > 10 + call remove(s:statuses, status_dir) + endif + endfor + + if len(s:statuses) == 0 + let s:statuses = {} + endif + + " force to update the statusline, otherwise the user needs to move the + " cursor + exe 'let &ro = &ro' +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/tags.vim b/vim/bundle/go/autoload/go/tags.vim new file mode 100644 index 0000000..af030d2 --- /dev/null +++ b/vim/bundle/go/autoload/go/tags.vim @@ -0,0 +1,214 @@ +" mapped to :GoAddTags +function! go#tags#Add(start, end, count, ...) abort + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + let offset = 0 + if a:count == -1 + let offset = go#util#OffsetCursor() + endif + + let test_mode = 0 + call call("go#tags#run", [a:start, a:end, offset, "add", fname, test_mode] + a:000) +endfunction + +" mapped to :GoRemoveTags +function! go#tags#Remove(start, end, count, ...) abort + let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') + let offset = 0 + if a:count == -1 + let offset = go#util#OffsetCursor() + endif + + let test_mode = 0 + call call("go#tags#run", [a:start, a:end, offset, "remove", fname, test_mode] + a:000) +endfunction + +" run runs gomodifytag. This is an internal test so we can test it +function! go#tags#run(start, end, offset, mode, fname, test_mode, ...) abort + " do not split this into multiple lines, somehow tests fail in that case + let args = {'mode': a:mode,'start': a:start,'end': a:end,'offset': a:offset,'fname': a:fname,'cmd_args': a:000} + + if &modified + let args["modified"] = 1 + endif + + let result = s:create_cmd(args) + if has_key(result, 'err') + call go#util#EchoError(result.err) + return -1 + endif + + let command = join(result.cmd, " ") + + if &modified + let filename = expand("%:p:gs!\\!/!") + let content = join(go#util#GetLines(), "\n") + let in = filename . "\n" . strlen(content) . "\n" . content + let out = go#util#System(command, in) + else + let out = go#util#System(command) + endif + + if go#util#ShellError() != 0 + call go#util#EchoError(out) + return + endif + + if a:test_mode + exe 'edit ' . a:fname + endif + + call s:write_out(out) + + if a:test_mode + exe 'write! ' . a:fname + endif +endfunc + + +" write_out writes back the given output to the current buffer +func s:write_out(out) abort + " not a json output + if a:out[0] !=# '{' + return + endif + + " nothing to do + if empty(a:out) || type(a:out) != type("") + return + endif + + let result = json_decode(a:out) + if type(result) != type({}) + call go#util#EchoError(printf("malformed output from gomodifytags: %s", a:out)) + return + endif + + let lines = result['lines'] + let start_line = result['start'] + let end_line = result['end'] + + let index = 0 + for line in range(start_line, end_line) + call setline(line, lines[index]) + let index += 1 + endfor + + if has_key(result, 'errors') + let l:winnr = winnr() + let l:listtype = go#list#Type("GoModifyTags") + call go#list#ParseFormat(l:listtype, "%f:%l:%c:%m", result['errors'], "gomodifytags") + call go#list#Window(l:listtype, len(result['errors'])) + + "prevent jumping to quickfix list + exe l:winnr . "wincmd w" + endif +endfunc + + +" create_cmd returns a dict that contains the command to execute gomodifytags +func s:create_cmd(args) abort + if !exists("*json_decode") + return {'err': "requires 'json_decode'. Update your Vim/Neovim version."} + endif + + let bin_path = go#path#CheckBinPath('gomodifytags') + if empty(bin_path) + return {'err': "gomodifytags does not exist"} + endif + let bin_path = go#util#Shellescape(bin_path) + + let l:start = a:args.start + let l:end = a:args.end + let l:offset = a:args.offset + let l:mode = a:args.mode + let l:cmd_args = a:args.cmd_args + let l:modifytags_transform = get(g:, 'go_addtags_transform', "snakecase") + + " start constructing the command + let cmd = [bin_path] + call extend(cmd, ["-format", "json"]) + call extend(cmd, ["-file", go#util#Shellescape(a:args.fname)]) + call extend(cmd, ["-transform", l:modifytags_transform]) + + if has_key(a:args, "modified") + call add(cmd, "-modified") + endif + + if l:offset != 0 + call extend(cmd, ["-offset", l:offset]) + else + let range = printf("%d,%d", l:start, l:end) + call extend(cmd, ["-line", range]) + endif + + if l:mode == "add" + let l:tags = [] + let l:options = [] + + if !empty(l:cmd_args) + for item in l:cmd_args + let splitted = split(item, ",") + + " tag only + if len(splitted) == 1 + call add(l:tags, splitted[0]) + endif + + " options only + if len(splitted) == 2 + call add(l:tags, splitted[0]) + call add(l:options, printf("%s=%s", splitted[0], splitted[1])) + endif + endfor + endif + + " construct options + if !empty(l:options) + call extend(cmd, ["-add-options", join(l:options, ",")]) + else + " default value + if empty(l:tags) + let l:tags = ["json"] + endif + + " construct tags + call extend(cmd, ["-add-tags", join(l:tags, ",")]) + endif + elseif l:mode == "remove" + if empty(l:cmd_args) + call add(cmd, "-clear-tags") + else + let l:tags = [] + let l:options = [] + for item in l:cmd_args + let splitted = split(item, ",") + + " tag only + if len(splitted) == 1 + call add(l:tags, splitted[0]) + endif + + " options only + if len(splitted) == 2 + call add(l:options, printf("%s=%s", splitted[0], splitted[1])) + endif + endfor + + " construct tags + if !empty(l:tags) + call extend(cmd, ["-remove-tags", join(l:tags, ",")]) + endif + + " construct options + if !empty(l:options) + call extend(cmd, ["-remove-options", join(l:options, ",")]) + endif + endif + else + return {'err': printf("unknown mode: %s", l:mode)} + endif + + return {'cmd': cmd} +endfunc + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/tags_test.vim b/vim/bundle/go/autoload/go/tags_test.vim new file mode 100644 index 0000000..7d4d32b --- /dev/null +++ b/vim/bundle/go/autoload/go/tags_test.vim @@ -0,0 +1,22 @@ +func! Test_add_tags() abort + try + let l:tmp = gotest#load_fixture('tags/add_all_input.go') + silent call go#tags#run(0, 0, 40, "add", bufname(''), 1) + call gotest#assert_fixture('tags/add_all_golden.go') + finally + call delete(l:tmp, 'rf') + endtry +endfunc + + +func! Test_remove_tags() abort + try + let l:tmp = gotest#load_fixture('tags/remove_all_input.go') + silent call go#tags#run(0, 0, 40, "remove", bufname(''), 1) + call gotest#assert_fixture('tags/remove_all_golden.go') + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +" vim:ts=2:sts=2:sw=2:et diff --git a/vim/bundle/go/autoload/go/template.vim b/vim/bundle/go/autoload/go/template.vim new file mode 100644 index 0000000..11a91ae --- /dev/null +++ b/vim/bundle/go/autoload/go/template.vim @@ -0,0 +1,53 @@ +let s:current_file = expand("") + +function! go#template#create() abort + let l:go_template_use_pkg = get(g:, 'go_template_use_pkg', 0) + let l:root_dir = fnamemodify(s:current_file, ':h:h:h') + + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + let l:package_name = -1 + + if isdirectory(expand('%:p:h')) + execute cd . fnameescape(expand('%:p:h')) + let l:package_name = go#tool#PackageName() + endif + + " if we can't figure out any package name(no Go files or non Go package + " files) from the directory create the template or use the cwd + " as the name + if l:package_name == -1 && l:go_template_use_pkg != 1 + let l:filename = fnamemodify(expand("%"), ':t') + if l:filename =~ "_test.go$" + let l:template_file = get(g:, 'go_template_test_file', "hello_world_test.go") + else + let l:template_file = get(g:, 'go_template_file', "hello_world.go") + endif + let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file) + silent exe '0r ' . fnameescape(l:template_path) + elseif l:package_name == -1 && l:go_template_use_pkg == 1 + " cwd is now the dir of the package + let l:path = fnamemodify(getcwd(), ':t') + let l:content = printf("package %s", l:path) + call append(0, l:content) + else + let l:content = printf("package %s", l:package_name) + call append(0, l:content) + endif + $delete _ + + execute cd . fnameescape(dir) +endfunction + +function! go#template#ToggleAutoCreate() abort + if get(g:, "go_template_autocreate", 1) + let g:go_template_autocreate = 0 + call go#util#EchoProgress("auto template create disabled") + return + end + + let g:go_template_autocreate = 1 + call go#util#EchoProgress("auto template create enabled") +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/term.vim b/vim/bundle/go/autoload/go/term.vim index 693041e..23ee18c 100644 --- a/vim/bundle/go/autoload/go/term.vim +++ b/vim/bundle/go/autoload/go/term.vim @@ -1,5 +1,5 @@ if has('nvim') && !exists("g:go_term_mode") - let g:go_term_mode = 'vsplit' + let g:go_term_mode = 'vsplit' endif " s:jobs is a global reference to all jobs started with new() @@ -7,122 +7,134 @@ let s:jobs = {} " new creates a new terminal with the given command. Mode is set based on the " global variable g:go_term_mode, which is by default set to :vsplit -function! go#term#new(bang, cmd) - return go#term#newmode(a:bang, a:cmd, g:go_term_mode) +function! go#term#new(bang, cmd) abort + return go#term#newmode(a:bang, a:cmd, g:go_term_mode) endfunction " new creates a new terminal with the given command and window mode. -function! go#term#newmode(bang, cmd, mode) - let mode = a:mode - if empty(mode) - let mode = g:go_term_mode - endif +function! go#term#newmode(bang, cmd, mode) abort + let mode = a:mode + if empty(mode) + let mode = g:go_term_mode + endif - " modify GOPATH if needed - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() + " execute go build in the files directory + let l:winnr = winnr() + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() - " execute go build in the files directory - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - let dir = getcwd() + execute cd . fnameescape(expand("%:p:h")) - execute cd . fnameescape(expand("%:p:h")) + execute mode.' __go_term__' - execute mode.' __go_term__' + setlocal filetype=goterm + setlocal bufhidden=delete + setlocal winfixheight + setlocal noswapfile + setlocal nobuflisted - setlocal filetype=goterm - setlocal bufhidden=delete - setlocal winfixheight - setlocal noswapfile - setlocal nobuflisted + let job = { + \ 'stderr' : [], + \ 'stdout' : [], + \ 'bang' : a:bang, + \ 'on_stdout': function('s:on_stdout'), + \ 'on_stderr': function('s:on_stderr'), + \ 'on_exit' : function('s:on_exit'), + \ } - let job = { - \ 'stderr' : [], - \ 'stdout' : [], - \ 'bang' : a:bang, - \ 'on_stdout': function('s:on_stdout'), - \ 'on_stderr': function('s:on_stderr'), - \ 'on_exit' : function('s:on_exit'), - \ } + let id = termopen(a:cmd, job) - let id = termopen(a:cmd, job) + execute cd . fnameescape(dir) - execute cd . fnameescape(dir) + let job.id = id + let job.cmd = a:cmd + startinsert - " restore back GOPATH - let $GOPATH = old_gopath + " resize new term if needed. + let height = get(g:, 'go_term_height', winheight(0)) + let width = get(g:, 'go_term_width', winwidth(0)) - let job.id = id - startinsert + " we are careful how to resize. for example it's vsplit we don't change + " the height. The below command resizes the buffer - " resize new term if needed. - let height = get(g:, 'go_term_height', winheight(0)) - let width = get(g:, 'go_term_width', winwidth(0)) + if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew" + exe 'vertical resize ' . width + elseif mode =~ "split" || mode =~ "new" + exe 'resize ' . height + endif - " we are careful how to resize. for example it's vertical we don't change - " the height. The below command resizes the buffer - if a:mode == "split" - exe 'resize ' . height - elseif a:mode == "vertical" - exe 'vertical resize ' . width - endif + " we also need to resize the pty, so there you go... + call jobresize(id, width, height) - " we also need to resize the pty, so there you go... - call jobresize(id, width, height) + let s:jobs[id] = job + stopinsert - let s:jobs[id] = job - return id + if l:winnr !=# winnr() + exe l:winnr . "wincmd w" + endif + + return id endfunction -function! s:on_stdout(job_id, data) - if !has_key(s:jobs, a:job_id) - return - endif - let job = s:jobs[a:job_id] +function! s:on_stdout(job_id, data, event) dict abort + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] - call extend(job.stdout, a:data) + call extend(job.stdout, a:data) endfunction -function! s:on_stderr(job_id, data) - if !has_key(s:jobs, a:job_id) - return - endif - let job = s:jobs[a:job_id] +function! s:on_stderr(job_id, data, event) dict abort + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] - call extend(job.stderr, a:data) + call extend(job.stderr, a:data) endfunction -function! s:on_exit(job_id, data) - if !has_key(s:jobs, a:job_id) - return - endif - let job = s:jobs[a:job_id] - - let l:listtype = "locationlist" - " usually there is always output so never branch into this clause - if empty(job.stdout) - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) - else - let errors = go#tool#ParseErrors(job.stdout) - let errors = go#tool#FilterValids(errors) - if !empty(errors) - " close terminal we don't need it - close - - call go#list#Populate(l:listtype, errors) - call go#list#Window(l:listtype, len(errors)) - if !self.bang - call go#list#JumpToFirst(l:listtype) - endif - else - call go#list#Clean(l:listtype) - call go#list#Window(l:listtype) - endif +function! s:on_exit(job_id, exit_status, event) dict abort + if !has_key(s:jobs, a:job_id) + return + endif + let job = s:jobs[a:job_id] + + let l:listtype = go#list#Type("_term") + " usually there is always output so never branch into this clause + if empty(job.stdout) + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + unlet s:jobs[a:job_id] + return + endif + + let errors = go#tool#ParseErrors(job.stdout) + let errors = go#tool#FilterValids(errors) + + if !empty(errors) + " close terminal we don't need it anymore + close + + call go#list#Populate(l:listtype, errors, job.cmd) + call go#list#Window(l:listtype, len(errors)) + if !self.bang + call go#list#JumpToFirst(l:listtype) endif + unlet s:jobs[a:job_id] + return + endif + + " tests are passing clean the list and close the list. But we only can + " close them from a normal view, so jump back, close the list and then + " again jump back to the terminal + wincmd p + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + wincmd p unlet s:jobs[a:job_id] endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/test-fixtures/def/jump.go b/vim/bundle/go/autoload/go/test-fixtures/def/jump.go new file mode 100644 index 0000000..50e8d8d --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/def/jump.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("vim-go") +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/fmt/hello.go b/vim/bundle/go/autoload/go/test-fixtures/fmt/hello.go new file mode 100644 index 0000000..3be42f6 --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/fmt/hello.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + + func main() { +fmt.Println("vim-go") +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/fmt/hello_golden.go b/vim/bundle/go/autoload/go/test-fixtures/fmt/hello_golden.go new file mode 100644 index 0000000..50e8d8d --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/fmt/hello_golden.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("vim-go") +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/goimports.go b/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/goimports.go new file mode 100644 index 0000000..eec47f8 --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/goimports.go @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" +) + +func Foo(log *logging.TestLogger) { +log.Debug("vim-go") +} + +func main() { + fmt.Println("vim-go") +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/goimports_golden.go b/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/goimports_golden.go new file mode 100644 index 0000000..3719f6b --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/goimports_golden.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + + logging "gh.com/gi/foo-logging" +) + +func Foo(log *logging.TestLogger) { + log.Debug("vim-go") +} + +func main() { + fmt.Println("vim-go") +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/vendor/gh.com/gi/foo-logging/logger.go b/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/vendor/gh.com/gi/foo-logging/logger.go new file mode 100644 index 0000000..1396018 --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/fmt/imports/vendor/gh.com/gi/foo-logging/logger.go @@ -0,0 +1,12 @@ +package logging + +import "fmt" + +type TestLogger struct { + Value string +} + +func (l *TestLogger) Debug(msg string) { + fmt.Println(msg) + fmt.Println(l.Value) +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/fmt/src/imports b/vim/bundle/go/autoload/go/test-fixtures/fmt/src/imports new file mode 120000 index 0000000..60ee253 --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/fmt/src/imports @@ -0,0 +1 @@ +../imports/ \ No newline at end of file diff --git a/vim/bundle/go/autoload/go/test-fixtures/tags/add_all_golden.go b/vim/bundle/go/autoload/go/test-fixtures/tags/add_all_golden.go new file mode 100644 index 0000000..eaa3e7b --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/tags/add_all_golden.go @@ -0,0 +1,16 @@ +package main + +type Server struct { + Name string `json:"name"` + ID int `json:"id"` + MyHomeAddress string `json:"my_home_address"` + SubDomains []string `json:"sub_domains"` + Empty string `json:"empty"` + Example int64 `json:"example"` + Example2 string `json:"example_2"` + Bar struct { + Four string `json:"four"` + Five string `json:"five"` + } `json:"bar"` + Lala interface{} `json:"lala"` +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/tags/add_all_input.go b/vim/bundle/go/autoload/go/test-fixtures/tags/add_all_input.go new file mode 100644 index 0000000..bfc4d3e --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/tags/add_all_input.go @@ -0,0 +1,16 @@ +package main + +type Server struct { + Name string + ID int + MyHomeAddress string + SubDomains []string + Empty string + Example int64 + Example2 string + Bar struct { + Four string + Five string + } + Lala interface{} +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/tags/remove_all_golden.go b/vim/bundle/go/autoload/go/test-fixtures/tags/remove_all_golden.go new file mode 100644 index 0000000..de57085 --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/tags/remove_all_golden.go @@ -0,0 +1,16 @@ +package main + +type Server struct { + Name string + ID int + MyHomeAddress string + SubDomains []string + Empty string + Example int64 + Example2 string + Bar struct { + Four string + Five string + } + Lala interface{} +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/tags/remove_all_input.go b/vim/bundle/go/autoload/go/test-fixtures/tags/remove_all_input.go new file mode 100644 index 0000000..eaa3e7b --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/tags/remove_all_input.go @@ -0,0 +1,16 @@ +package main + +type Server struct { + Name string `json:"name"` + ID int `json:"id"` + MyHomeAddress string `json:"my_home_address"` + SubDomains []string `json:"sub_domains"` + Empty string `json:"empty"` + Example int64 `json:"example"` + Example2 string `json:"example_2"` + Bar struct { + Four string `json:"four"` + Five string `json:"five"` + } `json:"bar"` + Lala interface{} `json:"lala"` +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/test/.gitignore b/vim/bundle/go/autoload/go/test-fixtures/test/.gitignore new file mode 100644 index 0000000..fc5bd90 --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/test/.gitignore @@ -0,0 +1 @@ +/pkg diff --git a/vim/bundle/go/autoload/go/test-fixtures/test/src/compilerror/compilerror.go b/vim/bundle/go/autoload/go/test-fixtures/test/src/compilerror/compilerror.go new file mode 100644 index 0000000..3f3dc7c --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/test/src/compilerror/compilerror.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("vim-go" +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/test/src/play/mock/controller.go b/vim/bundle/go/autoload/go/test-fixtures/test/src/play/mock/controller.go new file mode 100644 index 0000000..893d6e9 --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/test/src/play/mock/controller.go @@ -0,0 +1,7 @@ +package mock + +import "testing" + +func Fail(t *testing.T) { + t.Fatal("another package badness") +} diff --git a/vim/bundle/go/autoload/go/test-fixtures/test/src/play/play_test.go b/vim/bundle/go/autoload/go/test-fixtures/test/src/play/play_test.go new file mode 100644 index 0000000..ed71ef3 --- /dev/null +++ b/vim/bundle/go/autoload/go/test-fixtures/test/src/play/play_test.go @@ -0,0 +1,58 @@ +package play + +import ( + "sync" + "testing" + + "play/mock" +) + +func TestTopSubHelper(t *testing.T) { + t.Run("sub", func(t *testing.T) { + t.Log("log message") + t.Error("sub badness") + }) + t.Error("badness") + helper(t) +} + +func TestMultiline(t *testing.T) { + t.Error("this is an error\nand a second line, too") +} + +func TestSub(t *testing.T) { + t.Run("indented", func(t *testing.T) { + t.Error("this is a sub-test error\nand a second line, too") + }) +} + +func TestOK(t *testing.T) { + t.Run("log", func(t *testing.T) { + t.Log("goodness") + }) +} + +// TestMocked tests behavior similar to what users may experience when using +// github.com/golang/mock/gomock. +func TestMocked(t *testing.T) { + mock.Fail(t) +} + +func TestPanic(t *testing.T) { + panic("worst ever") +} + +func TestConcurrentPanic(t *testing.T) { + var wg sync.WaitGroup + wg.Add(1) + go func() { + panic("concurrent fail") + wg.Done() + }() + wg.Wait() +} + +func helper(t *testing.T) { + t.Helper() + t.Fatal("helper badness") +} diff --git a/vim/bundle/go/autoload/go/test.vim b/vim/bundle/go/autoload/go/test.vim new file mode 100644 index 0000000..51e9a5d --- /dev/null +++ b/vim/bundle/go/autoload/go/test.vim @@ -0,0 +1,389 @@ +" Test runs `go test` in the current directory. If compile is true, it'll +" compile the tests instead of running them (useful to catch errors in the +" test files). Any other argument is appended to the final `go test` command. +function! go#test#Test(bang, compile, ...) abort + let args = ["test"] + + " don't run the test, only compile it. Useful to capture and fix errors. + if a:compile + let testfile = tempname() . ".vim-go.test" + call extend(args, ["-c", "-o", testfile]) + endif + + if exists('g:go_build_tags') + let tags = get(g:, 'go_build_tags') + call extend(args, ["-tags", tags]) + endif + + if a:0 + let goargs = a:000 + + " do not expand for coverage mode as we're passing the arg ourself + if a:1 != '-coverprofile' + " expand all wildcards(i.e: '%' to the current file name) + let goargs = map(copy(a:000), "expand(v:val)") + endif + + if !(has('nvim') || go#util#has_job()) + let goargs = go#util#Shelllist(goargs, 1) + endif + + call extend(args, goargs, 1) + else + " only add this if no custom flags are passed + let timeout = get(g:, 'go_test_timeout', '10s') + call add(args, printf("-timeout=%s", timeout)) + endif + + if get(g:, 'go_echo_command_info', 1) + if a:compile + call go#util#EchoProgress("compiling tests ...") + else + call go#util#EchoProgress("testing...") + endif + endif + + if go#util#has_job() + " use vim's job functionality to call it asynchronously + let job_args = { + \ 'cmd': ['go'] + args, + \ 'bang': a:bang, + \ 'winnr': winnr(), + \ 'dir': getcwd(), + \ 'compile_test': a:compile, + \ 'jobdir': fnameescape(expand("%:p:h")), + \ } + + call s:test_job(job_args) + return + elseif has('nvim') + " use nvims's job functionality + if get(g:, 'go_term_enabled', 0) + let id = go#term#new(a:bang, ["go"] + args) + else + let id = go#jobcontrol#Spawn(a:bang, "test", "GoTest", args) + endif + + return id + endif + + call go#cmd#autowrite() + redraw + + let command = "go " . join(args, ' ') + let out = go#tool#ExecuteInDir(command) + " TODO(bc): When the output is JSON, the JSON should be run through a + " filter to produce lines that are more easily described by errorformat. + + let l:listtype = go#list#Type("GoTest") + + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + execute cd fnameescape(expand("%:p:h")) + + if go#util#ShellError() != 0 + call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), command) + let errors = go#list#Get(l:listtype) + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:bang + call go#list#JumpToFirst(l:listtype) + elseif empty(errors) + " failed to parse errors, output the original content + call go#util#EchoError(out) + endif + call go#util#EchoError("[test] FAIL") + else + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + + if a:compile + call go#util#EchoSuccess("[test] SUCCESS") + else + call go#util#EchoSuccess("[test] PASS") + endif + endif + execute cd . fnameescape(dir) +endfunction + +" Testfunc runs a single test that surrounds the current cursor position. +" Arguments are passed to the `go test` command. +function! go#test#Func(bang, ...) abort + " search flags legend (used only) + " 'b' search backward instead of forward + " 'c' accept a match at the cursor position + " 'n' do Not move the cursor + " 'W' don't wrap around the end of the file + " + " for the full list + " :help search + let test = search('func \(Test\|Example\)', "bcnW") + + if test == 0 + echo "vim-go: [test] no test found immediate to cursor" + return + end + + let line = getline(test) + let name = split(split(line, " ")[1], "(")[0] + let args = [a:bang, 0, "-run", name . "$"] + + if a:0 + call extend(args, a:000) + endif + + call call('go#test#Test', args) +endfunction + +function! s:test_job(args) abort + let status_dir = expand('%:p:h') + let started_at = reltime() + + let status = { + \ 'desc': 'current status', + \ 'type': "test", + \ 'state': "started", + \ } + + if a:args.compile_test + let status.state = "compiling" + endif + + call go#statusline#Update(status_dir, status) + + " autowrite is not enabled for jobs + call go#cmd#autowrite() + + let messages = [] + function! s:callback(chan, msg) closure + call add(messages, a:msg) + endfunction + + function! s:exit_cb(job, exitval) closure + let status = { + \ 'desc': 'last status', + \ 'type': "test", + \ 'state': "pass", + \ } + + if a:args.compile_test + let status.state = "success" + endif + + if a:exitval + let status.state = "failed" + endif + + if get(g:, 'go_echo_command_info', 1) + if a:exitval == 0 + if a:args.compile_test + call go#util#EchoSuccess("[test] SUCCESS") + else + call go#util#EchoSuccess("[test] PASS") + endif + else + call go#util#EchoError("[test] FAIL") + endif + endif + + let elapsed_time = reltimestr(reltime(started_at)) + " strip whitespace + let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '') + let status.state .= printf(" (%ss)", elapsed_time) + + call go#statusline#Update(status_dir, status) + + let l:listtype = go#list#Type("GoTest") + if a:exitval == 0 + call go#list#Clean(l:listtype) + call go#list#Window(l:listtype) + return + endif + + " TODO(bc): When messages is JSON, the JSON should be run through a + " filter to produce lines that are more easily described by errorformat. + call s:show_errors(a:args, a:exitval, messages) + endfunction + + let start_options = { + \ 'callback': funcref("s:callback"), + \ 'exit_cb': funcref("s:exit_cb"), + \ } + + " pre start + let dir = getcwd() + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let jobdir = fnameescape(expand("%:p:h")) + execute cd . jobdir + + call job_start(a:args.cmd, start_options) + + " post start + execute cd . fnameescape(dir) +endfunction + +" show_errors parses the given list of lines of a 'go test' output and returns +" a quickfix compatible list of errors. It's intended to be used only for go +" test output. +function! s:show_errors(args, exit_val, messages) abort + let l:listtype = go#list#Type("GoTest") + + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + try + execute cd a:args.jobdir + call go#list#ParseFormat(l:listtype, s:errorformat(), a:messages, join(a:args.cmd)) + let errors = go#list#Get(l:listtype) + finally + execute cd . fnameescape(a:args.dir) + endtry + + if !len(errors) + " failed to parse errors, output the original content + call go#util#EchoError(a:messages) + call go#util#EchoError(a:args.dir) + return + endif + + if a:args.winnr == winnr() + call go#list#Window(l:listtype, len(errors)) + if !empty(errors) && !a:args.bang + call go#list#JumpToFirst(l:listtype) + endif + endif +endfunction + + +let s:efm= "" + +function! s:errorformat() abort + " NOTE(arslan): once we get JSON output everything will be easier :). + " TODO(bc): When the output is JSON, the JSON should be run through a + " filter to produce lines that are more easily described by errorformat. + " https://github.com/golang/go/issues/2981. + let goroot = go#util#goroot() + + if s:efm != "" + return s:efm + endif + + " each level of test indents the test output 4 spaces. + " TODO(bc): figure out how to use 0 or more groups of four spaces for the + " indentation. '%\\( %\\)%#' should work, but doesn't. + let indent = " %#" + + " match compiler errors + let format = "%f:%l:%c: %m" + + " ignore `go test -v` output for starting tests + let format .= ",%-G=== RUN %.%#" + " ignore `go test -v` output for passing tests + let format .= ",%-G" . indent . "--- PASS: %.%#" + + " Match failure lines. + " + " Test failures start with '--- FAIL: ', followed by the test name followed + " by a space the duration of the test in parentheses + " + " e.g.: + " '--- FAIL: TestSomething (0.00s)' + if get(g:, 'go_test_show_name', 0) + let format .= ",%+G" . indent . "--- FAIL: %.%#" + else + let format .= ",%-G" . indent . "--- FAIL: %.%#" + endif + + " Matches test output lines. + " + " All test output lines start with the test indentation and a tab, followed + " by the filename, a colon, the line number, another colon, a space, and the + " message. e.g.: + " '\ttime_test.go:30: Likely problem: the time zone files have not been installed.' + let format .= ",%A" . indent . "%\\t%\\+%f:%l: %m" + + " Match the 2nd and later lines of multi-line output. These lines are + " indented the number of spaces for the level of nesting of the test, + " followed by two tabs, followed by the message. + " + " Treat these lines as if they are stand-alone lines of output by using %G. + " It would also be valid to treat these lines as if they were the + " continuation of a multi-line error by using %C instead of %G, but that + " would also require that all test errors using a %A or %E modifier to + " indicate that they're multiple lines of output, but in that case the lines + " get concatenated in the quickfix list, which is not what users typically + " want when writing a newline into their test output. + let format .= ",%G" . indent . "%\\t%\\{2}%m" + + " set the format for panics. + + " In addition to 'panic', check for 'fatal error' to support older versions + " of Go that used 'fatal error'. + " + " Panics come in two flavors. When the goroutine running the tests panics, + " `go test` recovers and tries to exit more cleanly. In that case, the panic + " message is suffixed with ' [recovered]'. If the panic occurs in a + " different goroutine, it will not be suffixed with ' [recovered]'. + let format .= ",%+Afatal error: %.%# [recovered]" + let format .= ",%+Apanic: %.%# [recovered]" + let format .= ",%+Afatal error: %.%#" + let format .= ",%+Apanic: %.%#" + + " Match address lines in stacktraces produced by panic. + " + " Address lines in the stack trace have leading tabs, followed by the path + " to the file. The file path is followed by a colon and then the line number + " within the file where the panic occurred. After that there's a space and + " hexadecimal number. + " + " e.g.: + " '\t/usr/local/go/src/time.go:1313 +0x5d' + + " panicaddress, and readyaddress are identical except for + " panicaddress sets the filename and line number. + let panicaddress = "%\\t%f:%l +0x%[0-9A-Fa-f]%\\+" + let readyaddress = "%\\t%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+" + " stdlib address is identical to readyaddress, except it matches files + " inside GOROOT. + let stdlibaddress = "%\\t" . goroot . "%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+" + + " Match and ignore the running goroutine line. + let format .= ",%-Cgoroutine %\\d%\\+ [running]:" + " Match address lines that refer to stdlib, but consider them informational + " only. This is to catch the lines after the first address line in the + " running goroutine of a panic stack trace. Ideally, this wouldn't be + " necessary, but when a panic happens in the goroutine running a test, it's + " recovered and another panic is created, so the stack trace actually has + " the line that caused the original panic a couple of addresses down the + " stack. + let format .= ",%-C" . stdlibaddress + " Match address lines in the first matching goroutine. This means the panic + " message will only be shown as the error message in the first address of + " the running goroutine's stack. + let format .= ",%Z" . panicaddress + + " Match and ignore panic address without being part of a multi-line message. + " This is to catch those lines that come after the top most non-standard + " library line in stack traces. + let format .= ",%-G" . readyaddress + + " Match and ignore exit status lines (produced when go test panics) whether + " part of a multi-line message or not, because these lines sometimes come + " before and sometimes after panic stacktraces. + let format .= ",%-Cexit status %[0-9]%\\+" + "let format .= ",exit status %[0-9]%\\+" + + " Match and ignore exit failure lines whether part of a multi-line message + " or not, because these lines sometimes come before and sometimes after + " panic stacktraces. + let format .= ",%-CFAIL%\\t%.%#" + "let format .= ",FAIL%\\t%.%#" + + " Match and ignore everything else in multi-line messages. + let format .= ",%-C%.%#" + " Match and ignore everything else not in a multi-line message: + let format .= ",%-G%.%#" + + let s:efm = format + + return s:efm +endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/test_test.vim b/vim/bundle/go/autoload/go/test_test.vim new file mode 100644 index 0000000..e9ab162 --- /dev/null +++ b/vim/bundle/go/autoload/go/test_test.vim @@ -0,0 +1,123 @@ +func! Test_GoTest() abort + let expected = [ + \ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'}, + \ {'lnum': 13, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'sub badness'}, + \ {'lnum': 15, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'badness'}, + \ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'}, + \ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'}, + \ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'}, + \ {'lnum': 25, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'}, + \ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'}, + \ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'}, + \ {'lnum': 42, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'} + \ ] + call s:test('play/play_test.go', expected) + +endfunc + +func! Test_GoTestConcurrentPanic() + let expected = [ + \ {'lnum': 49, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: concurrent fail'} + \ ] + call s:test('play/play_test.go', expected, "-run", "TestConcurrentPanic") +endfunc + +func! Test_GoTestVerbose() abort + let expected = [ + \ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'}, + \ {'lnum': 13, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'sub badness'}, + \ {'lnum': 15, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'badness'}, + \ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'}, + \ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'}, + \ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'}, + \ {'lnum': 25, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'}, + \ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'}, + \ {'lnum': 31, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'goodness'}, + \ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'}, + \ {'lnum': 42, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'} + \ ] + call s:test('play/play_test.go', expected, "-v") +endfunc + +func! Test_GoTestCompilerError() abort + let expected = [ + \ {'lnum': 6, 'bufnr': 6, 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'syntax error: unexpected newline, expecting comma or )'} + \ ] + + call s:test('compilerror/compilerror_test.go', expected) +endfunc + +func! s:test(file, expected, ...) abort + if has('nvim') + " nvim mostly shows test errors correctly, but the the expected errors are + " slightly different; buffer numbers are not the same and stderr doesn't + " seem to be redirected to the job, so the lines from the panic aren't in + " the output to be parsed, and hence are not in the quickfix lists. Once + " those two issues are resolved, this early return should be removed so + " the tests will run for Neovim, too. + return + endif + let $GOPATH = fnameescape(expand("%:p:h")) . '/test-fixtures/test' + silent exe 'e ' . $GOPATH . '/src/' . a:file + + " clear the quickfix lists + call setqflist([], 'r') + + let args = [1,0] + if a:0 + let args += a:000 + endif + + " run the tests + call call(function('go#test#Test'), args) + + let actual = getqflist() + let start = reltime() + while len(actual) == 0 && reltimefloat(reltime(start)) < 10 + sleep 100m + let actual = getqflist() + endwhile + + " for some reason, when run headless, the quickfix lists includes a line + " that should have been filtered out; remove it manually. The line is not + " present when run manually. + let i = 0 + while i < len(actual) + if actual[i].text =~# '^=== RUN .*' + call remove(actual, i) + endif + let i += 1 + endwhile + + call assert_equal(len(a:expected), len(actual), "number of errors") + if len(a:expected) != len(actual) + return + endif + + let i = 0 + while i < len(a:expected) + let expected_item = a:expected[i] + let actual_item = actual[i] + let i += 1 + + call assert_equal(expected_item.bufnr, actual_item.bufnr, "bufnr") + call assert_equal(expected_item.lnum, actual_item.lnum, "lnum") + call assert_equal(expected_item.col, actual_item.col, "col") + call assert_equal(expected_item.vcol, actual_item.vcol, "vcol") + call assert_equal(expected_item.nr, actual_item.nr, "nr") + call assert_equal(expected_item.pattern, actual_item.pattern, "pattern") + + let expected_text = s:normalize_durations(expected_item.text) + let actual_text = s:normalize_durations(actual_item.text) + + call assert_equal(expected_text, actual_text, "text") + call assert_equal(expected_item.type, actual_item.type, "type") + call assert_equal(expected_item.valid, actual_item.valid, "valid") + endwhile +endfunc + +func! s:normalize_durations(str) abort + return substitute(a:str, '[0-9]\+\(\.[0-9]\+\)\?s', '0.000s', 'g') +endfunc + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/textobj.vim b/vim/bundle/go/autoload/go/textobj.vim index a8e91a2..62a7042 100644 --- a/vim/bundle/go/autoload/go/textobj.vim +++ b/vim/bundle/go/autoload/go/textobj.vim @@ -6,6 +6,10 @@ if !exists("g:go_textobj_include_function_doc") let g:go_textobj_include_function_doc = 1 endif +if !exists("g:go_textobj_include_variable") + let g:go_textobj_include_variable = 1 +endif + " ( ) motions " { } motions " s for sentence @@ -13,14 +17,14 @@ endif " < > " t for tag -function! go#textobj#Function(mode) +function! go#textobj#Function(mode) abort let offset = go#util#OffsetCursor() - let fname = expand("%:p") + let fname = shellescape(expand("%:p")) if &modified " Write current unsaved buffer to a temp file and use the modified content let l:tmpname = tempname() - call writefile(getline(1, '$'), l:tmpname) + call writefile(go#util#GetLines(), l:tmpname) let fname = l:tmpname endif @@ -36,8 +40,8 @@ function! go#textobj#Function(mode) let command .= " -parse-comments" endif - let out = system(command) - if v:shell_error != 0 + let out = go#util#System(command) + if go#util#ShellError() != 0 call go#util#EchoError(out) return endif @@ -60,6 +64,16 @@ function! go#textobj#Function(mode) " want's to include doc comments for function declarations if has_key(info, 'doc') && g:go_textobj_include_function_doc call cursor(info.doc.line, info.doc.col) + elseif info['sig']['name'] == '' && g:go_textobj_include_variable + " one liner anonymous functions + if info.lbrace.line == info.rbrace.line + " jump to first nonblack char, to get the correct column + call cursor(info.lbrace.line, 0 ) + normal! ^ + call cursor(info.func.line, col(".")) + else + call cursor(info.func.line, info.rbrace.col) + endif else call cursor(info.func.line, info.func.col) endif @@ -67,7 +81,7 @@ function! go#textobj#Function(mode) normal! v call cursor(info.rbrace.line, info.rbrace.col) return - endif + endif " rest is inner mode, a:mode == 'i' @@ -84,7 +98,7 @@ function! go#textobj#Function(mode) call cursor(info.rbrace.line-1, 1) endfunction -function! go#textobj#FunctionJump(mode, direction) +function! go#textobj#FunctionJump(mode, direction) abort " get count of the motion. This should be done before all the normal " expressions below as those reset this value(because they have zero " count!). We abstract -1 because the index starts from 0 in motion. @@ -103,11 +117,11 @@ function! go#textobj#FunctionJump(mode, direction) let offset = go#util#OffsetCursor() - let fname = expand("%:p") + let fname = shellescape(expand("%:p")) if &modified " Write current unsaved buffer to a temp file and use the modified content let l:tmpname = tempname() - call writefile(getline(1, '$'), l:tmpname) + call writefile(go#util#GetLines(), l:tmpname) let fname = l:tmpname endif @@ -129,8 +143,8 @@ function! go#textobj#FunctionJump(mode, direction) let command .= " -parse-comments" endif - let out = system(command) - if v:shell_error != 0 + let out = go#util#System(command) + if go#util#ShellError() != 0 call go#util#EchoError(out) return endif @@ -177,4 +191,4 @@ function! go#textobj#FunctionJump(mode, direction) keepjumps call cursor(info.func.line, 1) endfunction -" vim:ts=2:sw=2:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/tool.vim b/vim/bundle/go/autoload/go/tool.vim index d378b5a..d163600 100644 --- a/vim/bundle/go/autoload/go/tool.vim +++ b/vim/bundle/go/autoload/go/tool.vim @@ -1,155 +1,218 @@ -function! go#tool#Files() - if go#util#IsWin() - let command = 'go list -f "{{range $f := .GoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}"' - else - let command = "go list -f '{{range $f := .GoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}'" - endif - let out = go#tool#ExecuteInDir(command) - return split(out, '\n') +" From "go list -h". +function! go#tool#ValidFiles(...) + let l:list = ["GoFiles", "CgoFiles", "IgnoredGoFiles", "CFiles", "CXXFiles", + \ "MFiles", "HFiles", "FFiles", "SFiles", "SwigFiles", "SwigCXXFiles", + \ "SysoFiles", "TestGoFiles", "XTestGoFiles"] + + " Used as completion + if len(a:000) > 0 + let l:list = filter(l:list, 'strpart(v:val, 0, len(a:1)) == a:1') + endif + + return l:list endfunction -function! go#tool#Deps() - if go#util#IsWin() - let command = 'go list -f "{{range $f := .Deps}}{{$f}}{{printf \"\n\"}}{{end}}"' - else - let command = "go list -f $'{{range $f := .Deps}}{{$f}}\n{{end}}'" +function! go#tool#Files(...) abort + if len(a:000) > 0 + let source_files = a:000 + else + let source_files = ['GoFiles'] + endif + + let combined = '' + for sf in source_files + " Strip dot in case people used ":GoFiles .GoFiles". + let sf = substitute(sf, '^\.', '', '') + + " Make sure the passed options are valid. + if index(go#tool#ValidFiles(), sf) == -1 + echoerr "unknown source file variable: " . sf endif - let out = go#tool#ExecuteInDir(command) - return split(out, '\n') -endfunction -function! go#tool#Imports() - let imports = {} if go#util#IsWin() - let command = 'go list -f "{{range $f := .Imports}}{{$f}}{{printf \"\n\"}}{{end}}"' + let combined .= '{{range $f := .' . sf . '}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}\{{$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 + let combined .= "{{range $f := ." . sf . "}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}" endif + endfor + + let out = go#tool#ExecuteInDir('go list -f ' . shellescape(combined)) + return split(out, '\n') +endfunction - for package_path in split(out, '\n') - let cmd = "go list -f {{.Name}} " . package_path - let package_name = substitute(go#tool#ExecuteInDir(cmd), '\n$', '', '') - let imports[package_name] = package_path - endfor +function! go#tool#Deps() abort + if go#util#IsWin() + let format = '{{range $f := .Deps}}{{$f}}{{printf \"\n\"}}{{end}}' + else + let format = "{{range $f := .Deps}}{{$f}}\n{{end}}" + endif + let command = 'go list -f '.shellescape(format) + let out = go#tool#ExecuteInDir(command) + return split(out, '\n') +endfunction +function! go#tool#Imports() abort + let imports = {} + if go#util#IsWin() + let format = '{{range $f := .Imports}}{{$f}}{{printf \"\n\"}}{{end}}' + else + let format = "{{range $f := .Imports}}{{$f}}{{printf \"\\n\"}}{{end}}" + endif + let command = 'go list -f '.shellescape(format) + let out = go#tool#ExecuteInDir(command) + if go#util#ShellError() != 0 + echo out return imports + endif + + for package_path in split(out, '\n') + let cmd = "go list -f '{{.Name}}' " . shellescape(package_path) + let package_name = substitute(go#tool#ExecuteInDir(cmd), '\n$', '', '') + let imports[package_name] = package_path + endfor + + return imports endfunction -function! go#tool#ParseErrors(lines) - let errors = [] - - for line in a:lines - let fatalerrors = matchlist(line, '^\(fatal error:.*\)$') - let tokens = matchlist(line, '^\s*\(.\{-}\):\(\d\+\):\s*\(.*\)') - - if !empty(fatalerrors) - call add(errors, {"text": fatalerrors[1]}) - elseif !empty(tokens) - " strip endlines of form ^M - let out=substitute(tokens[3], '\r$', '', '') - - call add(errors, { - \ "filename" : fnamemodify(tokens[1], ':p'), - \ "lnum" : tokens[2], - \ "text" : out, - \ }) - elseif !empty(errors) - " Preserve indented lines. - " This comes up especially with multi-line test output. - if match(line, '^\s') >= 0 - call add(errors, {"text": line}) - endif - endif - endfor +function! go#tool#Info(auto) abort + let l:mode = get(g:, 'go_info_mode', 'gocode') + if l:mode == 'gocode' + call go#complete#Info(a:auto) + elseif l:mode == 'guru' + call go#guru#DescribeInfo() + else + call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [gocode, guru]') + endif +endfunction + +function! go#tool#PackageName() abort + let command = "go list -f \"{{.Name}}\"" + let out = go#tool#ExecuteInDir(command) + if go#util#ShellError() != 0 + return -1 + endif + + return split(out, '\n')[0] +endfunction - return errors +function! go#tool#ParseErrors(lines) abort + let errors = [] + + for line in a:lines + let fatalerrors = matchlist(line, '^\(fatal error:.*\)$') + let tokens = matchlist(line, '^\s*\(.\{-}\):\(\d\+\):\s*\(.*\)') + + if !empty(fatalerrors) + call add(errors, {"text": fatalerrors[1]}) + elseif !empty(tokens) + " strip endlines of form ^M + let out = substitute(tokens[3], '\r$', '', '') + + call add(errors, { + \ "filename" : fnamemodify(tokens[1], ':p'), + \ "lnum" : tokens[2], + \ "text" : out, + \ }) + elseif !empty(errors) + " Preserve indented lines. + " This comes up especially with multi-line test output. + if match(line, '^\s') >= 0 + call add(errors, {"text": line}) + endif + endif + endfor + + return errors endfunction "FilterValids filters the given items with only items that have a valid "filename. Any non valid filename is filtered out. -function! go#tool#FilterValids(items) - " Remove any nonvalid filename from the location list to avoid opening an - " empty buffer. See https://github.com/fatih/vim-go/issues/287 for - " details. - let filtered = [] - let is_readable = {} - - for item in a:items - if has_key(item, 'bufnr') - let filename = bufname(item.bufnr) - elseif has_key(item, 'filename') - let filename = item.filename - else - " nothing to do, add item back to the list - call add(filtered, item) - continue - endif +function! go#tool#FilterValids(items) abort + " Remove any nonvalid filename from the location list to avoid opening an + " empty buffer. See https://github.com/fatih/vim-go/issues/287 for + " details. + let filtered = [] + let is_readable = {} + + for item in a:items + if has_key(item, 'bufnr') + let filename = bufname(item.bufnr) + elseif has_key(item, 'filename') + let filename = item.filename + else + " nothing to do, add item back to the list + call add(filtered, item) + continue + endif - if !has_key(is_readable, filename) - let is_readable[filename] = filereadable(filename) - endif - if is_readable[filename] - call add(filtered, item) - endif - endfor + if !has_key(is_readable, filename) + let is_readable[filename] = filereadable(filename) + endif + if is_readable[filename] + call add(filtered, item) + endif + endfor - for k in keys(filter(is_readable, '!v:val')) - echo "vim-go: " | echohl Identifier | echon "[run] Dropped " | echohl Constant | echon '"' . k . '"' - echohl Identifier | echon " from location list (nonvalid filename)" | echohl None - endfor + for k in keys(filter(is_readable, '!v:val')) + echo "vim-go: " | echohl Identifier | echon "[run] Dropped " | echohl Constant | echon '"' . k . '"' + echohl Identifier | echon " from location list (nonvalid filename)" | echohl None + endfor - return filtered + return filtered endfunction function! go#tool#ExecuteInDir(cmd) abort - let old_gopath = $GOPATH - let $GOPATH = go#path#Detect() - - let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' - let dir = getcwd() - try - execute cd . fnameescape(expand("%:p:h")) - let out = system(a:cmd) - finally - execute cd . fnameescape(dir) - endtry - - let $GOPATH = old_gopath - return out + " Verify that the directory actually exists. If the directory does not + " exist, then assume that the a:cmd should not be executed. Callers expect + " to check v:shell_error (via go#util#ShellError()), so execute a command + " that will return an error as if a:cmd was run and exited with an error. + " This helps avoid errors when working with plugins that use virtual files + " that don't actually exist on the file system (e.g. vim-fugitive's + " GitDiff). + if !isdirectory(expand("%:p:h")) + let [out, err] = go#util#Exec(["false"]) + return '' + endif + + let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' + let dir = getcwd() + try + execute cd . fnameescape(expand("%:p:h")) + let out = go#util#System(a:cmd) + finally + execute cd . fnameescape(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) +function! go#tool#Exists(importpath) abort let command = "go list ". a:importpath let out = go#tool#ExecuteInDir(command) - if v:shell_error + if go#util#ShellError() != 0 return -1 endif return 0 endfunction - -" following two functions are from: https://github.com/mattn/gist-vim +" following two functions are from: https://github.com/mattn/gist-vim " thanks @mattn -function! s:get_browser_command() +function! s:get_browser_command() abort let go_play_browser_command = get(g:, 'go_play_browser_command', '') if go_play_browser_command == '' if go#util#IsWin() let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%' - elseif has('mac') || has('macunix') || has('gui_macvim') || system('uname') =~? '^darwin' + elseif go#util#IsMac() 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% &' + elseif executable('chromium') + let go_play_browser_command = 'chromium %URL% &' else let go_play_browser_command = '' endif @@ -157,7 +220,7 @@ function! s:get_browser_command() return go_play_browser_command endfunction -function! go#tool#OpenBrowser(url) +function! go#tool#OpenBrowser(url) abort let cmd = s:get_browser_command() if len(cmd) == 0 redraw @@ -168,15 +231,15 @@ function! go#tool#OpenBrowser(url) return endif if cmd =~ '^!' - let cmd = substitute(cmd, '%URL%', '\=shellescape(a:url)', 'g') + let cmd = substitute(cmd, '%URL%', '\=escape(shellescape(a:url),"#")', 'g') silent! exec cmd elseif cmd =~ '^:[A-Z]' - let cmd = substitute(cmd, '%URL%', '\=a:url', 'g') + let cmd = substitute(cmd, '%URL%', '\=escape(a:url,"#")', 'g') exec cmd else let cmd = substitute(cmd, '%URL%', '\=shellescape(a:url)', 'g') - call system(cmd) + call go#util#System(cmd) endif endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/tool_test.vim b/vim/bundle/go/autoload/go/tool_test.vim new file mode 100644 index 0000000..1af3b7b --- /dev/null +++ b/vim/bundle/go/autoload/go/tool_test.vim @@ -0,0 +1,23 @@ +func! Test_ExecuteInDir() abort + let l:tmp = gotest#write_file('a/a.go', ['package a']) + try + let l:out = go#tool#ExecuteInDir("pwd") + call assert_equal(l:tmp . "/src/a\n", l:out) + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +func! Test_ExecuteInDir_nodir() abort + let l:tmp = go#util#tempdir("executeindir") + exe ':e ' . l:tmp . '/new-dir/a' + + try + let l:out = go#tool#ExecuteInDir("pwd") + call assert_equal('', l:out) + finally + call delete(l:tmp, 'rf') + endtry +endfunc + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/ui.vim b/vim/bundle/go/autoload/go/ui.vim index c658141..3f61257 100644 --- a/vim/bundle/go/autoload/go/ui.vim +++ b/vim/bundle/go/autoload/go/ui.vim @@ -1,89 +1,114 @@ let s:buf_nr = -1 "OpenWindow opens a new scratch window and put's the content into the window -function! go#ui#OpenWindow(title, content) - " reuse existing buffer window if it exists otherwise create a new one - if !bufexists(s:buf_nr) - execute 'botright new' - file `="[" . a:title . "]"` - let s:buf_nr = bufnr('%') - elseif bufwinnr(s:buf_nr) == -1 - execute 'botright new' - execute s:buf_nr . 'buffer' - elseif bufwinnr(s:buf_nr) != bufwinnr('%') - execute bufwinnr(s:buf_nr) . 'wincmd w' - endif - +function! go#ui#OpenWindow(title, content, filetype) abort + " Ensure there's only one return window in this session/tabpage + call go#util#Windo("unlet! w:vim_go_return_window") + " Mark the window we're leaving as such + let w:vim_go_return_window = 1 + + " reuse existing buffer window if it exists otherwise create a new one + if !bufexists(s:buf_nr) + execute 'botright new' + file `="[" . a:title . "]"` + let s:buf_nr = bufnr('%') + elseif bufwinnr(s:buf_nr) == -1 + execute 'botright new' + execute s:buf_nr . 'buffer' + elseif bufwinnr(s:buf_nr) != bufwinnr('%') + execute bufwinnr(s:buf_nr) . 'wincmd w' + endif + + " Resize window to content length + exe 'resize' . len(a:content) + + execute "setlocal filetype=".a:filetype + + " some sane default values for a readonly buffer + setlocal bufhidden=delete + setlocal buftype=nofile + setlocal noswapfile + setlocal nobuflisted + setlocal winfixheight + setlocal cursorline " make it easy to distinguish + setlocal nonumber + setlocal norelativenumber + setlocal showbreak="" + + " 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 + + " Remove the '... [New File]' message line from the command line + echon +endfunction - " Keep minimum height to 10, if there is more just increase it that it - " occupies all results - let buffer_height = 10 - if len(a:content) < buffer_height - exe 'resize ' . buffer_height - else - exe 'resize ' . len(a:content) +function! go#ui#GetReturnWindow() abort + for l:wn in range(1, winnr("$")) + if !empty(getwinvar(l:wn, "vim_go_return_window")) + return l:wn 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 + endfor endfunction - " CloseWindow closes the current window -function! go#ui#CloseWindow() - close - echo "" +function! go#ui#CloseWindow() abort + " Close any window associated with the ui buffer, if it's there + if bufexists(s:buf_nr) + let ui_window_number = bufwinnr(s:buf_nr) + if ui_window_number != -1 + execute ui_window_number . 'close' + endif + endif + + "return to original window, if it's there + let l:rw = go#ui#GetReturnWindow() + if !empty(l:rw) + execute l:rw . 'wincmd w' + unlet! w:vim_go_return_window + endif endfunction " OpenDefinition parses the current line and jumps to it by openening a new " tab -function! go#ui#OpenDefinition(filter) - let curline = getline('.') - - " don't touch our first line or any blank line - if curline =~ a:filter || curline =~ "^$" - " supress information about calling this function - echo "" - return - endif +function! go#ui#OpenDefinition(filter) abort + let curline = getline('.') + + " don't touch our first line or any blank line + if curline =~ a:filter || curline =~ "^$" + " suppress information about calling this function + echo "" + return + endif - " format: 'interface file:lnum:coln' - let mx = '^\(^\S*\)\s*\(.\{-}\):\(\d\+\):\(\d\+\)' + " format: 'interface file:lnum:coln' + let mx = '^\(^\S*\)\s*\(.\{-}\):\(\d\+\):\(\d\+\)' - " parse it now into the list - let tokens = matchlist(curline, mx) + " parse it now into the list + let tokens = matchlist(curline, mx) - " convert to: 'file:lnum:coln' - let expr = tokens[2] . ":" . tokens[3] . ":" . tokens[4] + " 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 + " 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 + " center the word + norm! zz endfunction +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/util.vim b/vim/bundle/go/autoload/go/util.vim index 24aa254..bf72dad 100644 --- a/vim/bundle/go/autoload/go/util.vim +++ b/vim/bundle/go/autoload/go/util.vim @@ -1,123 +1,398 @@ " PathSep returns the appropriate OS specific path separator. -function! go#util#PathSep() - if go#util#IsWin() - return '\' - endif - return '/' +function! go#util#PathSep() abort + if go#util#IsWin() + return '\' + endif + return '/' endfunction " PathListSep returns the appropriate OS specific path list separator. -function! go#util#PathListSep() - if go#util#IsWin() - return ";" - endif - return ":" +function! go#util#PathListSep() abort + if go#util#IsWin() + return ";" + endif + return ":" endfunction " LineEnding returns the correct line ending, based on the current fileformat -function! go#util#LineEnding() - if &fileformat == 'dos' - return "\r\n" - elseif &fileformat == 'mac' - return "\r" - endif +function! go#util#LineEnding() abort + if &fileformat == 'dos' + return "\r\n" + elseif &fileformat == 'mac' + return "\r" + endif + + return "\n" +endfunction - return "\n" +" Join joins any number of path elements into a single path, adding a +" Separator if necessary and returns the result +function! go#util#Join(...) abort + return join(a:000, go#util#PathSep()) endfunction " IsWin returns 1 if current OS is Windows or 0 otherwise -function! go#util#IsWin() - let win = ['win16', 'win32', 'win64', 'win95'] - for w in win - if (has(w)) - return 1 - endif - endfor +function! go#util#IsWin() abort + let win = ['win16', 'win32', 'win64', 'win95'] + for w in win + if (has(w)) + return 1 + endif + endfor + + return 0 +endfunction + +" IsMac returns 1 if current OS is macOS or 0 otherwise. +function! go#util#IsMac() abort + return has('mac') || + \ has('macunix') || + \ has('gui_macvim') || + \ go#util#System('uname') =~? '^darwin' +endfunction + + " Checks if using: + " 1) Windows system, + " 2) And has cygpath executable, + " 3) And uses *sh* as 'shell' +function! go#util#IsUsingCygwinShell() + return go#util#IsWin() && executable('cygpath') && &shell =~ '.*sh.*' +endfunction + +function! go#util#has_job() abort + " job was introduced in 7.4.xxx however there are multiple bug fixes and one + " of the latest is 8.0.0087 which is required for a stable async API. + return has('job') && has("patch-8.0.0087") +endfunction + +let s:env_cache = {} + +" env returns the go environment variable for the given key. Where key can be +" GOARCH, GOOS, GOROOT, etc... It caches the result and returns the cached +" version. +function! go#util#env(key) abort + let l:key = tolower(a:key) + if has_key(s:env_cache, l:key) + return s:env_cache[l:key] + endif + + if executable('go') + let l:var = call('go#util#'.l:key, []) + if go#util#ShellError() != 0 + call go#util#EchoError(printf("'go env %s' failed", toupper(l:key))) + return '' + endif + else + let l:var = eval("$".toupper(a:key)) + endif + + let s:env_cache[l:key] = l:var + return l:var +endfunction + +" goarch returns 'go env GOARCH'. This is an internal function and shouldn't +" be used. Instead use 'go#util#env("goarch")' +function! go#util#goarch() abort + return substitute(go#util#System('go env GOARCH'), '\n', '', 'g') +endfunction + +" goos returns 'go env GOOS'. This is an internal function and shouldn't +" be used. Instead use 'go#util#env("goos")' +function! go#util#goos() abort + return substitute(go#util#System('go env GOOS'), '\n', '', 'g') +endfunction + +" goroot returns 'go env GOROOT'. This is an internal function and shouldn't +" be used. Instead use 'go#util#env("goroot")' +function! go#util#goroot() abort + return substitute(go#util#System('go env GOROOT'), '\n', '', 'g') +endfunction + +" gopath returns 'go env GOPATH'. This is an internal function and shouldn't +" be used. Instead use 'go#util#env("gopath")' +function! go#util#gopath() abort + return substitute(go#util#System('go env GOPATH'), '\n', '', 'g') +endfunction + +function! go#util#osarch() abort + return go#util#env("goos") . '_' . go#util#env("goarch") +endfunction + +" Run a shell command. +" +" It will temporary set the shell to /bin/sh for Unix-like systems if possible, +" so that we always use a standard POSIX-compatible Bourne shell (and not e.g. +" csh, fish, etc.) See #988 and #1276. +function! s:system(cmd, ...) abort + " Preserve original shell and shellredir values + let l:shell = &shell + let l:shellredir = &shellredir + + if !go#util#IsWin() && executable('/bin/sh') + set shell=/bin/sh shellredir=>%s\ 2>&1 + endif + + try + return call('system', [a:cmd] + a:000) + finally + " Restore original values + let &shell = l:shell + let &shellredir = l:shellredir + endtry +endfunction + +" System runs a shell command "str". Every arguments after "str" is passed to +" stdin. +function! go#util#System(str, ...) abort + return call('s:system', [a:str] + a:000) +endfunction + +" Exec runs a shell command "cmd", which must be a list, one argument per item. +" Every list entry will be automatically shell-escaped +" Every other argument is passed to stdin. +function! go#util#Exec(cmd, ...) abort + if len(a:cmd) == 0 + call go#util#EchoError("go#util#Exec() called with empty a:cmd") + return + endif + + " CheckBinPath will show a warning for us. + let l:bin = go#path#CheckBinPath(a:cmd[0]) + if empty(l:bin) + return ["", 1] + endif - return 0 + let l:out = call('s:system', [go#util#Shelljoin([l:bin] + a:cmd[1:])] + a:000) + return [l:out, go#util#ShellError()] +endfunction + +function! go#util#ShellError() abort + return v:shell_error endfunction " StripPath strips the path's last character if it's a path separator. " example: '/foo/bar/' -> '/foo/bar' -function! go#util#StripPathSep(path) - let last_char = strlen(a:path) - 1 - if a:path[last_char] == go#util#PathSep() - return strpart(a:path, 0, last_char) - endif +function! go#util#StripPathSep(path) abort + let last_char = strlen(a:path) - 1 + if a:path[last_char] == go#util#PathSep() + return strpart(a:path, 0, last_char) + endif - return a:path + return a:path endfunction " StripTrailingSlash strips the trailing slash from the given path list. " example: ['/foo/bar/'] -> ['/foo/bar'] -function! go#util#StripTrailingSlash(paths) +function! go#util#StripTrailingSlash(paths) abort return map(copy(a:paths), 'go#util#StripPathSep(v:val)') endfunction " Shelljoin returns a shell-safe string representation of arglist. The " {special} argument of shellescape() may optionally be passed. -function! go#util#Shelljoin(arglist, ...) - try - let ssl_save = &shellslash - set noshellslash - if a:0 - return join(map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')'), ' ') - endif +function! go#util#Shelljoin(arglist, ...) abort + try + let ssl_save = &shellslash + set noshellslash + if a:0 + return join(map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')'), ' ') + endif - return join(map(copy(a:arglist), 'shellescape(v:val)'), ' ') - finally - let &shellslash = ssl_save - endtry + return join(map(copy(a:arglist), 'shellescape(v:val)'), ' ') + finally + let &shellslash = ssl_save + endtry endfunction +fu! go#util#Shellescape(arg) + try + let ssl_save = &shellslash + set noshellslash + return shellescape(a:arg) + finally + let &shellslash = ssl_save + endtry +endf + " Shelllist returns a shell-safe representation of the items in the given " arglist. The {special} argument of shellescape() may optionally be passed. -function! go#util#Shelllist(arglist, ...) - try - let ssl_save = &shellslash - set noshellslash - if a:0 - return map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')') - endif - return map(copy(a:arglist), 'shellescape(v:val)') - finally - let &shellslash = ssl_save - endtry +function! go#util#Shelllist(arglist, ...) abort + try + let ssl_save = &shellslash + set noshellslash + if a:0 + return map(copy(a:arglist), 'shellescape(v:val, ' . a:1 . ')') + endif + return map(copy(a:arglist), 'shellescape(v:val)') + finally + let &shellslash = ssl_save + endtry endfunction " Returns the byte offset for line and column -function! go#util#Offset(line, col) - if &encoding != 'utf-8' - let sep = go#util#LineEnding() - let buf = a:line == 1 ? '' : (join(getline(1, a:line-1), sep) . sep) - let buf .= a:col == 1 ? '' : getline('.')[:a:col-2] - return len(iconv(buf, &encoding, 'utf-8')) - endif - return line2byte(a:line) + (a:col-2) +function! go#util#Offset(line, col) abort + if &encoding != 'utf-8' + let sep = go#util#LineEnding() + let buf = a:line == 1 ? '' : (join(getline(1, a:line-1), sep) . sep) + let buf .= a:col == 1 ? '' : getline('.')[:a:col-2] + return len(iconv(buf, &encoding, 'utf-8')) + endif + return line2byte(a:line) + (a:col-2) endfunction " " Returns the byte offset for the cursor -function! go#util#OffsetCursor() - return go#util#Offset(line('.'), col('.')) +function! go#util#OffsetCursor() abort + return go#util#Offset(line('.'), col('.')) endfunction -" TODO(arslan): I couldn't parameterize the highlight types. Check if we can -" simplify the following functions +" Windo is like the built-in :windo, only it returns to the window the command +" was issued from +function! go#util#Windo(command) abort + let s:currentWindow = winnr() + try + execute "windo " . a:command + finally + execute s:currentWindow. "wincmd w" + unlet s:currentWindow + endtry +endfunction -function! go#util#EchoSuccess(msg) - redraws! | echon "vim-go: " | echohl Function | echon a:msg | echohl None +" snippetcase converts the given word to given preferred snippet setting type +" case. +function! go#util#snippetcase(word) abort + let l:snippet_case = get(g:, 'go_addtags_transform', "snakecase") + if l:snippet_case == "snakecase" + return go#util#snakecase(a:word) + elseif l:snippet_case == "camelcase" + return go#util#camelcase(a:word) + else + return a:word " do nothing + endif endfunction -function! go#util#EchoError(msg) - redraws! | echon "vim-go: " | echohl ErrorMsg | echon a:msg | echohl None +" snakecase converts a string to snake case. i.e: FooBar -> foo_bar +" Copied from tpope/vim-abolish +function! go#util#snakecase(word) abort + let word = substitute(a:word, '::', '/', 'g') + let word = substitute(word, '\(\u\+\)\(\u\l\)', '\1_\2', 'g') + let word = substitute(word, '\(\l\|\d\)\(\u\)', '\1_\2', 'g') + let word = substitute(word, '[.-]', '_', 'g') + let word = tolower(word) + return word endfunction -function! go#util#EchoWarning(msg) - redraws! | echon "vim-go: " | echohl WarningMsg | echon a:msg | echohl None +" camelcase converts a string to camel case. e.g. FooBar or foo_bar will become +" fooBar. +" Copied from tpope/vim-abolish. +function! go#util#camelcase(word) abort + let word = substitute(a:word, '-', '_', 'g') + if word !~# '_' && word =~# '\l' + return substitute(word, '^.', '\l&', '') + else + return substitute(word, '\C\(_\)\=\(.\)', '\=submatch(1)==""?tolower(submatch(2)) : toupper(submatch(2))','g') + endif +endfunction + +" pascalcase converts a string to 'PascalCase'. e.g. fooBar or foo_bar will +" become FooBar. +function! go#util#pascalcase(word) abort + let word = go#util#camelcase(a:word) + return toupper(word[0]) . word[1:] endfunction +" Echo a message to the screen and highlight it with the group in a:hi. +" +" The message can be a list or string; every line with be :echomsg'd separately. +function! s:echo(msg, hi) + let l:msg = [] + if type(a:msg) != type([]) + let l:msg = split(a:msg, "\n") + else + let l:msg = a:msg + endif + + " Tabs display as ^I or <09>, so manually expand them. + let l:msg = map(l:msg, 'substitute(v:val, "\t", " ", "")') + + exe 'echohl ' . a:hi + for line in l:msg + echom "vim-go: " . line + endfor + echohl None +endfunction + +function! go#util#EchoSuccess(msg) + call s:echo(a:msg, 'Function') +endfunction +function! go#util#EchoError(msg) + call s:echo(a:msg, 'ErrorMsg') +endfunction +function! go#util#EchoWarning(msg) + call s:echo(a:msg, 'WarningMsg') +endfunction function! go#util#EchoProgress(msg) - redraws! | echon "vim-go: " | echohl Identifier | echon a:msg | echohl None + call s:echo(a:msg, 'Identifier') +endfunction +function! go#util#EchoInfo(msg) + call s:echo(a:msg, 'Debug') +endfunction + +" Get all lines in the buffer as a a list. +function! go#util#GetLines() + 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 + return buf +endfunction + +" Convert the current buffer to the "archive" format of +" golang.org/x/tools/go/buildutil: +" https://godoc.org/golang.org/x/tools/go/buildutil#ParseOverlayArchive +" +" > The archive consists of a series of files. Each file consists of a name, a +" > decimal file size and the file contents, separated by newlinews. No newline +" > follows after the file contents. +function! go#util#archive() + let l:buffer = join(go#util#GetLines(), "\n") + return expand("%:p:gs!\\!/!") . "\n" . strlen(l:buffer) . "\n" . l:buffer +endfunction + + +" Make a named temporary directory which starts with "prefix". +" +" Unfortunately Vim's tempname() is not portable enough across various systems; +" see: https://github.com/mattn/vim-go/pull/3#discussion_r138084911 +function! go#util#tempdir(prefix) abort + " See :help tempfile + if go#util#IsWin() + let l:dirs = [$TMP, $TEMP, 'c:\tmp', 'c:\temp'] + else + let l:dirs = [$TMPDIR, '/tmp', './', $HOME] + endif + + let l:dir = '' + for l:d in dirs + if !empty(l:d) && filewritable(l:d) == 2 + let l:dir = l:d + break + endif + endfor + + if l:dir == '' + echoerr 'Unable to find directory to store temporary directory in' + return + endif + + " Not great randomness, but "good enough" for our purpose here. + let l:rnd = sha256(printf('%s%s', localtime(), fnamemodify(bufname(''), ":p"))) + let l:tmp = printf("%s/%s%s", l:dir, a:prefix, l:rnd) + call mkdir(l:tmp, 'p', 0700) + return l:tmp endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/go/vimproc.vim b/vim/bundle/go/autoload/go/vimproc.vim deleted file mode 100644 index 42d1392..0000000 --- a/vim/bundle/go/autoload/go/vimproc.vim +++ /dev/null @@ -1,21 +0,0 @@ -"Check if has vimproc -function! go#vimproc#has_vimproc() - if !exists('g:go#use_vimproc') - if go#util#IsWin() - try - call vimproc#version() - let exists_vimproc = 1 - catch - let exists_vimproc = 0 - endtry - else - let exists_vimproc = 0 - endif - - let g:go#use_vimproc = exists_vimproc - endif - - return g:go#use_vimproc -endfunction - -" vim:ts=4:sw=4:et diff --git a/vim/bundle/go/autoload/gotest.vim b/vim/bundle/go/autoload/gotest.vim new file mode 100644 index 0000000..199df1e --- /dev/null +++ b/vim/bundle/go/autoload/gotest.vim @@ -0,0 +1,105 @@ +" Write a Go file to a temporary directory and append this directory to $GOPATH. +" +" The file will written to a:path, which is relative to the temporary directory, +" and this file will be loaded as the current buffer. +" +" The cursor will be placed on the character before any 0x1f byte. +" +" The full path to the created directory is returned, it is the caller's +" responsibility to clean that up! +fun! gotest#write_file(path, contents) abort + let l:dir = go#util#tempdir("vim-go-test/testrun/") + let $GOPATH .= ':' . l:dir + let l:full_path = l:dir . '/src/' . a:path + + call mkdir(fnamemodify(l:full_path, ':h'), 'p') + call writefile(a:contents, l:full_path) + exe 'cd ' . l:dir . '/src' + silent exe 'e! ' . a:path + + " Set cursor. + let l:lnum = 1 + for l:line in a:contents + let l:m = match(l:line, "\x1f") + if l:m > -1 + call setpos('.', [0, l:lnum, l:m, 0]) + call setline('.', substitute(getline('.'), "\x1f", '', '')) + break + endif + + let l:lnum += 1 + endfor + + return l:dir +endfun + +" Load a fixture file from test-fixtures. +" +" The file will be copied to a new GOPATH-compliant temporary directory and +" loaded as the current buffer. +fun! gotest#load_fixture(path) abort + let l:dir = go#util#tempdir("vim-go-test/testrun/") + let $GOPATH .= ':' . l:dir + let l:full_path = l:dir . '/src/' . a:path + + call mkdir(fnamemodify(l:full_path, ':h'), 'p') + exe 'cd ' . l:dir . '/src' + silent exe 'noautocmd e ' . a:path + silent exe printf('read %s/test-fixtures/%s', g:vim_go_root, a:path) + silent noautocmd w! + + return l:dir +endfun + +" Diff the contents of the current buffer to a:want, which should be a list. +" If a:skipHeader is true we won't bother with the package and import +" declarations; so e.g.: +" +" let l:diff = s:diff_buffer(1, ['_ = mail.Address{}']) +" +" will pass, whereas otherwise you'd have to: +" +" let l:diff = s:diff_buffer(0, ['package main', 'import "net/mail", '_ = mail.Address{}']) +fun! gotest#assert_buffer(skipHeader, want) abort + let l:buffer = go#util#GetLines() + + if a:skipHeader + for l:lnum in range(0, len(l:buffer) - 1) + " Bit rudimentary, but works reasonably well. + if match(l:buffer[l:lnum], '^\v(func|var|const|import \(|\))') > -1 + " vint bug: https://github.com/Kuniwak/vint/issues/179 + " vint: -ProhibitUsingUndeclaredVariable + let l:buffer = l:buffer[l:lnum:len(l:buffer)] + break + endif + endfor + endif + + " Using ' is often easier so we don't have to escape ". + let l:want = map(a:want, 'substitute(v:val, "\\\\t", "\t", "")') + + let l:tmp = go#util#tempdir('assert_buffer') + try + call writefile(l:buffer, l:tmp . '/have') + call writefile(l:want, l:tmp . '/want') + call go#fmt#run('gofmt', l:tmp . '/have', l:tmp . '/have') + call go#fmt#run('gofmt', l:tmp . '/want', l:tmp . '/want') + let [l:out, l:err] = go#util#Exec(["diff", "-u", l:tmp . '/have', l:tmp . '/want']) + finally + call delete(l:tmp . '/have') + call delete(l:tmp . '/want') + call delete(l:tmp, 'd') + endtry + + if l:err || l:out != '' + let v:errors = extend(v:errors, split(l:out, "\n")) + endif +endfun + +" Diff the contents of the current buffer to the fixture file in a:path. +fun! gotest#assert_fixture(path) abort + let l:want = readfile(printf('%s/test-fixtures/%s', g:vim_go_root, a:path)) + call gotest#assert_buffer(0, l:want) +endfun + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/autoload/unite/sources/decls.vim b/vim/bundle/go/autoload/unite/sources/decls.vim new file mode 100644 index 0000000..b4d5da8 --- /dev/null +++ b/vim/bundle/go/autoload/unite/sources/decls.vim @@ -0,0 +1,70 @@ +let s:save_cpo = &cpoptions +set cpoptions&vim + +let s:source = { + \ 'name': 'decls', + \ 'description': 'GoDecls implementation for unite', + \ 'syntax': 'uniteSource__Decls', + \ 'action_table': {}, + \ 'hooks': {}, + \ } + +function! unite#sources#decls#define() + return s:source +endfunction + +function! s:source.gather_candidates(args, context) abort + let l:bin_path = go#path#CheckBinPath('motion') + if empty(l:bin_path) + return [] + endif + + let l:path = expand(get(a:args, 0, '%:p:h')) + if isdirectory(l:path) + let l:mode = 'dir' + elseif filereadable(l:path) + let l:mode = 'file' + else + return [] + endif + + let l:include = get(g:, 'go_decls_includes', 'func,type') + let l:command = printf('%s -format vim -mode decls -include %s -%s %s', l:bin_path, l:include, l:mode, shellescape(l:path)) + let l:candidates = [] + try + let l:result = eval(unite#util#system(l:command)) + let l:candidates = get(l:result, 'decls', []) + catch + call unite#print_source_error(['command returned invalid response.', v:exception], s:source.name) + endtry + + return map(l:candidates, "{ + \ 'word': printf('%s :%d :%s', fnamemodify(v:val.filename, ':~:.'), v:val.line, v:val.full), + \ 'kind': 'jump_list', + \ 'action__path': v:val.filename, + \ 'action__line': v:val.line, + \ 'action__col': v:val.col, + \ }") +endfunction + +function! s:source.hooks.on_syntax(args, context) abort + syntax match uniteSource__Decls_Filepath /[^:]*\ze:/ contained containedin=uniteSource__Decls + syntax match uniteSource__Decls_Line /\d\+\ze :/ contained containedin=uniteSource__Decls + syntax match uniteSource__Decls_WholeFunction /\vfunc %(\([^)]+\) )?[^(]+/ contained containedin=uniteSource__Decls + syntax match uniteSource__Decls_Function /\S\+\ze(/ contained containedin=uniteSource__Decls_WholeFunction + syntax match uniteSource__Decls_WholeType /type \S\+/ contained containedin=uniteSource__Decls + syntax match uniteSource__Decls_Type /\v( )@<=\S+/ contained containedin=uniteSource__Decls_WholeType + highlight default link uniteSource__Decls_Filepath Comment + highlight default link uniteSource__Decls_Line LineNr + highlight default link uniteSource__Decls_Function Function + highlight default link uniteSource__Decls_Type Type + + syntax match uniteSource__Decls_Separator /:/ contained containedin=uniteSource__Decls conceal + syntax match uniteSource__Decls_SeparatorFunction /func / contained containedin=uniteSource__Decls_WholeFunction conceal + syntax match uniteSource__Decls_SeparatorType /type / contained containedin=uniteSource__Decls_WholeType conceal +endfunction + +let &cpoptions = s:save_cpo +unlet s:save_cpo + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/compiler/go.vim b/vim/bundle/go/compiler/go.vim index 7b5a025..523cb28 100644 --- a/vim/bundle/go/compiler/go.vim +++ b/vim/bundle/go/compiler/go.vim @@ -4,10 +4,10 @@ " " compiler/go.vim: Vim compiler file for Go. -if exists("current_compiler") +if exists("g:current_compiler") finish endif -let current_compiler = "go" +let g:current_compiler = "go" if exists(":CompilerSet") != 2 command -nargs=* CompilerSet setlocal @@ -16,9 +16,9 @@ endif let s:save_cpo = &cpo set cpo-=C if filereadable("makefile") || filereadable("Makefile") - CompilerSet makeprg=make + CompilerSet makeprg=make else - CompilerSet makeprg=go\ build + CompilerSet makeprg=go\ build endif " Define the patterns that will be recognized by QuickFix when parsing the @@ -38,4 +38,4 @@ CompilerSet errorformat+=%-G%.%# " All lines not matching a let &cpo = s:save_cpo unlet s:save_cpo -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/doc/vim-go.txt b/vim/bundle/go/doc/vim-go.txt index 08ad5bc..9adb896 100644 --- a/vim/bundle/go/doc/vim-go.txt +++ b/vim/bundle/go/doc/vim-go.txt @@ -1,18 +1,18 @@ *vim-go.txt* Go development plugin *vim-go* -=============================================================================== -# # -# ## ## #### ## ## ###### ####### # -# ## ## ## ### ### ## ## ## ## # -# ## ## ## #### #### ## ## ## # -# ## ## ## ## ### ## ####### ## #### ## ## # -# ## ## ## ## ## ## ## ## ## # -# ## ## ## ## ## ## ## ## ## # -# ### #### ## ## ###### ####### # -# # -=============================================================================== -CONTENTS *go-contents* +============================================================================== +# # +# ## ## #### ## ## ###### ####### # +# ## ## ## ### ### ## ## ## ## # +# ## ## ## #### #### ## ## ## # +# ## ## ## ## ### ## ####### ## #### ## ## # +# ## ## ## ## ## ## ## ## ## # +# ## ## ## ## ## ## ## ## ## # +# ### #### ## ## ###### ####### # +# # +============================================================================== +CONTENTS *go-contents* 1. Intro........................................|go-intro| 2. Install......................................|go-install| @@ -21,120 +21,149 @@ CONTENTS *go-contents* 5. Text Objects.................................|go-text-objects| 6. Functions....................................|go-functions| 7. Settings.....................................|go-settings| - 8. Troubleshooting..............................|go-troubleshooting| - 9. Credits......................................|go-credits| - -=============================================================================== -INTRO *go-intro* - -Go (golang) support for Vim. vim-go installs automatically all necessary -binaries for providing seamless Vim integration. It comes with pre-defined -sensible settings (like auto gofmt on save), has autocomplete, snippet -support, improved syntax highlighting, go toolchain commands, etc... It's -highly customizable and each individual feature can be disabled/enabled -easily. - - * Improved Syntax highlighting with items such as Functions, Operators, Methods. - * Auto completion support via `gocode` - * Better `gofmt` on save, which keeps cursor position and doesn't break your undo - history - * Go to symbol/declaration with `:GoDef` - * Look up documentation with `:GoDoc` inside Vim or open it in browser - * Automatically import packages via `:GoImport` or plug it into autosave - * Compile your package with `:GoBuild`, install it with `:GoInstall` or test - them with `:GoTest` (also supports running single tests via `:GoTestFunc`) - * Quickly execute your current file/files with `:GoRun` - * Automatic `GOPATH` detection based on the directory structure (i.e. `gb` - projects, `godep` vendored projects) - * Change or display `GOPATH` with `:GoPath` - * Create a coverage profile and display annotated source code in browser to see - which functions are covered with `:GoCoverage` - * Call `gometalinter` with `:GoMetaLinter`, which invokes all possible linters - (golint, vet, errcheck, deadcode, etc..) and shows the warnings/errors - * Lint your code with `:GoLint` - * Run your code through `:GoVet` to catch static errors - * Advanced source analysis tools utilizing oracle, such as `:GoImplements`, - `:GoCallees`, and `:GoReferrers` - * Precise type-safe renaming of identifiers with `:GoRename` - * List all source files and dependencies - * Unchecked error checking with `:GoErrCheck` - * Integrated and improved snippets, supporting `ultisnips` or `neosnippet` - * Share your current code to [play.golang.org](http://play.golang.org) with `:GoPlay` - * On-the-fly type information about the word under the cursor. Plug it into - your custom vim function. - * Go asm formatting on save - * Tagbar support to show tags of the source code in a sidebar with `gotags` - * Custom vim text objects such as `a function` or `inner function` - * A async launcher for the go command is implemented for neovim, fully async + 8. Syntax highlighting..........................|go-syntax| + 9. FAQ/Troubleshooting..........................|go-troubleshooting| + 10. Development..................................|go-development| + 11. Donation.....................................|go-donation| + 12. Credits......................................|go-credits| + +============================================================================== +INTRO *go-intro* + +Go (golang) support for Vim. vim-go comes with sensible predefined settings +(e.g. automatic `gofmt` on save), has autocomplete, snippet support, improved +syntax highlighting, go toolchain commands, etc. It is highly customizable, +and individual features can be toggled easily. vim-go leverages a number of +tools developed by the Go community to provide a seamless Vim experience. + + * Compile your package with |:GoBuild|, install it with |:GoInstall| or + test it with |:GoTest|. Run a single tests with |:GoTestFunc|). + * Quickly execute your current file(s) with |:GoRun|. + * Improved syntax highlighting and folding. + * Completion support via `gocode`. + * `gofmt` or `goimports` on save keeps the cursor position and undo history. + * Go to symbol/declaration with |:GoDef|. + * Look up documentation with |:GoDoc| or |:GoDocBrowser|. + * Easily import packages via |:GoImport|, remove them via |:GoDrop|. + * Automatic `GOPATH` detection which works with `gb` and `godep`. Change or + display `GOPATH` with |:GoPath|. + * See which code is covered by tests with |:GoCoverage|. + * Add or remove tags on struct fields with |:GoAddTags| and |:GoRemoveTags|. + * Call `gometalinter` with |:GoMetaLinter| to invoke all possible linters + (`golint`, `vet`, `errcheck`, `deadcode`, etc.) and put the result in the + quickfix or location list. + * Lint your code with |:GoLint|, run your code through |:GoVet| to catch + static errors, or make sure errors are checked with |:GoErrCheck|. + * Advanced source analysis tools utilizing `guru`, such as |:GoImplements|, + |:GoCallees|, and |:GoReferrers|. + * Precise type-safe renaming of identifiers with |:GoRename|. + * Integrated and improved snippets, supporting `ultisnips`, `neosnippet`, + and `vim-minisnip`. + * Share your current code to play.golang.org with |:GoPlay|. + * On-the-fly information about the word under the cursor. Plug it into your + custom Vim function. + * Text objects such as "a function" (|go-af|) or "inner function" (|go-if|). + * Most commands are run asynchronous in Neovim and Vim 8. Fully async building and testing. - * Integrated with the neovim terminal, launch `:GoRun` and other go commands - in their own new terminal. - * Alternate between implementation and test code with `:GoAlternate` + * Integrated with the Neovim terminal, launch |:GoRun| and other Go commands + in a terminal buffer. + * Switch between `file.go` and `file_test.go` code with |:GoAlternate|. + * Supports integration with the Tagbar and ctrlp.vim plugins. + * ...and more... -=============================================================================== +============================================================================== INSTALL *go-install* -Vim-go follows the standard runtime path structure, so I highly recommend to use -a common and well known plugin manager to install vim-go. Do not use vim-go with -other Go plugins. For Pathogen just clone the repo, for other plugin managers -add the appropriate lines and execute the plugin's install command. +The latest stable release, https://github.com/fatih/vim-go/releases/latest, is +the recommended version to use. If you choose to use the master branch +instead, please do so with caution; it is a _development_ branch. + +vim-go follows the standard runtime path structure and should work with any of +the major plugin managers. + +For Pathogen or Vim |packages|, just clone the repo. For other plugin managers +you may also need to add the lines to your vimrc to execute the plugin +manager's install command. + +* Vim 8 |packages| +> + git clone https://github.com/fatih/vim-go.git \ + ~/.vim/pack/plugins/start/vim-go * https://github.com/tpope/vim-pathogen > git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go < - * https://github.com/junegunn/vim-plug > Plug 'fatih/vim-go' -< * https://github.com/Shougo/neobundle.vim > NeoBundle 'fatih/vim-go' < - * https://github.com/gmarik/vundle > Plugin 'fatih/vim-go' - < -* Manual > +* Manual (not recommended) > Copy all of the files into your `~/.vim` directory < +You will also need to install all the necessary binaries. vim-go makes it easy +to install all of them by providing a command, |:GoInstallBinaries|, to +`go get` all the required binaries. The binaries will be installed to $GOBIN +or $GOPATH/bin (default: $HOME/go/bin). It requires `git`. + +Depending on your installation method, you may have to generate the plugin's +|:helptags| manually (e.g. `:helptags ALL`). + +Autocompletion is enabled by default via 'omnifunc', which you can trigger +with |i_CTRL-X_CTRL-O| (``). + +Supported Go plugins~ *vim-go-plugins* + +The following plugins are supported for use with vim-go: + +* Real-time completion (Vim): + https://github.com/Shougo/neocomplete.vim + +* Real-time completion (Neovim and Vim 8): + https://github.com/Shougo/deoplete.nvim and + https://github.com/zchee/deoplete-go -Please be sure all necessary binaries are installed (such as `gocode`, `godef`, -`goimports`, etc..). You can easily install them with the included -|GoInstallBinaries| command. If you invoke it, all necessary binaries will be -automatically downloaded and installed to your `$GOBIN` environment (if not set -it will use `$GOPATH/bin`). It requires `git` for fetching the individual Go -packages. - -* Autocompletion is enabled by default via ``, to get real-time -completion (completion by type) install: -https://github.com/Shougo/neocomplete.vim for Vim or -https://github.com/Shougo/deoplete.nvim and -https://github.com/zchee/deoplete-go for Neovim -* To get displayed source code tag informations on a sidebar install -https://github.com/majutsushi/tagbar. -* For snippet feature install: -https://github.com/Shougo/neosnippet.vim or -https://github.com/SirVer/ultisnips. -* For a better documentation viewer checkout: https://github.com/garyburd/go-explorer - -=============================================================================== -COMMANDS *go-commands* - - *:GoPath* +* Display source code navigation in a sidebar: + https://github.com/majutsushi/tagbar + +* Snippets: + https://github.com/Shougo/neosnippet.vim or + https://github.com/SirVer/ultisnips or + https://github.com/joereynolds/vim-minisnip + +* For a better documentation viewer check out: + https://github.com/garyburd/go-explorer + +* Integration with `delve` (Neovim only): + https://github.com/jodosha/vim-godebug + +* Interactive |:GoDecls| and |:GoDeclsDir|: + https://github.com/ctrlpvim/ctrlp.vim or + https://github.com/junegunn/fzf.vim or + https://github.com/Shougo/unite.vim or + https://github.com/Shougo/denite.nvim + +============================================================================== +COMMANDS *go-commands* + + *:GoPath* :GoPath [path] GoPath sets and overrides GOPATH with the given {path}. If no {path} is given it shows the current GOPATH. If `""` is given as path, it clears - current `GOPATH` which was set with |GoPath| and restores `GOPATH` back to - the initial value which was sourced when Vim was started. + current `GOPATH` which was set with |:GoPath| and restores `GOPATH` back + to the initial value which was sourced when Vim was started. - *:GoImport* + *:GoImport* :GoImport[!] [path] Import ensures that the provided package {path} is imported in the current @@ -143,36 +172,38 @@ COMMANDS *go-commands* If [!] is given it will download the package with `go get` - *:GoImportAs* + *:GoImportAs* :GoImportAs [localname] [path] Same as Import, but uses a custom local name for the package. - *:GoDrop* + *: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* :GoLint [packages] - Run golint for the current Go file, or for given packages. + Run golint for the directory under your current file, or for the given + packages. - *:GoDoc* + *: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* :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. + command or by default, the word under the cursor. By default it opens the + documentation in 'https://godoc.org'. To change it see |'g:go_doc_url'|. - *:GoFmt* + *:GoFmt* :GoFmt Filter the current Go buffer through gofmt. It tries to preserve cursor @@ -183,7 +214,7 @@ COMMANDS *go-commands* 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 + |:GoFmt|, It tries to preserve cursor position and avoids replacing the buffer with stderr output. *:GoPlay* @@ -194,14 +225,14 @@ COMMANDS *go-commands* is copied to system clipboard if Vim is compiled with 'clipboard' or 'xterm-clipboard' otherwise it's get yanked into the `""` register. - *:GoVet* + *:GoVet* :GoVet[!] [options] Run `go vet` for the directory under your current file. Vet examines Go source code and reports suspicious constructs, such as Printf calls whose - arguments do not align with the format string. Vet uses heuristics that do not - guarantee all reports are genuine problems, but it can find errors not caught - by the compilers. + arguments do not align with the format string. Vet uses heuristics that do + not guarantee all reports are genuine problems, but it can find errors not + caught by the compilers. You may optionally pass any valid go tool vet flags/options. In this case, `go tool vet` is run in place of `go vet`. For a full list please see @@ -209,19 +240,78 @@ COMMANDS *go-commands* If [!] is not given the first error is jumped to. - *:GoDef* -:GoDef [identifier] + *:GoDef* +:GoDef +gd +CTRL-] +g + + + Goto declaration/definition for the declaration under the cursor. By + default the CTRL-] shortcut, the mapping `gd` and , + g are enabled to invoke :GoDef for the identifier under the + cursor. See |'g:go_def_mapping_enabled'| to disable them. No explicit + arguments are supported. + + vim-go also keeps a per-window location stack, roughly analogous to how + Vim's internal |tags| functionality works. This is pushed to every time a + jump is made using the GoDef functionality. In essence, this is a LIFO + list of file locations you have visited with :GoDef that is retained to + help you navigate software. + + *:GoDefStack* +:GoDefStack [number] + + This command Jumps to a given location in the jumpstack, retaining all + other entries. Jumps to non-existent entries will print an informative + message, but are otherwise a noop. + + If no argument is given, it will print out an interactive list of all + items in the stack. Its output looks like this: + + 1 /path/first/file.go|1187 col 16|AddThing func(t *Thing) + > 2 /path/thing/thing.go|624 col 19|String() string + 3 /path/thing/thing.go|744 col 6|func Sprintln(a ...interface{}) string + + This list shows the identifiers that you jumped to and the file and cursor + position before that jump. The older jumps are at the top, the newer at + the bottom. - 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. + The '>' points to the active entry. This entry and any newer entries + below it will be replaced if |:GoDef| is done from this location. The + CTRL-t and |:GoDefPop| command will jump to the position above the active + entry. - *:GoRun* + Jumps to non-existent entries will print an informative message, but are + otherwise a noop. + + *:GoDefStackClear* +:GoDefStackClear + + Clears the current stack list and resets it. + + *:GoDefPop* +:GoDefPop [count] +CTRL-t + + Navigate to the [count] earlier entry in the jump stack, retaining the + newer entries. If no argument is given, it will jump to the next most + recent entry (`:GoDefPop 1`). If [count] is greater than the number of + prior entries, an error will be printed and no jump will be performed. + + If you have used :GoDefPop to jump to an earlier location, and you issue + another :GoDef command, the current entry will be replaced, and all newer + entries will be removed, effectively resuming the stack at that location. + + By default [count]CTRL-t is enabled to invoke :GoDefPop. Similarly, + hitting CTRL-t without a prior count is equivalent to `:GoDefPop 1`. See + |'g:go_def_mapping_enabled'| to disable this. + + *: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 + current file is used. If an argument is passed, [expand] is used as file selector. For example use `:GoRun %` to select the current file only. You may optionally pass any valid go run flags/options. For a full list @@ -230,73 +320,75 @@ COMMANDS *go-commands* If [!] is not given the first error is jumped to. If using neovim then `:GoRun` will run in a new terminal according to - |g:go_term_mode|. + |'g:go_term_mode'|. - *:GoBuild* + *:GoBuild* :GoBuild[!] [expand] Build your package with `go build`. Errors are populated in the quickfix window. It automatically builds only the files that depends on the current file. `:GoBuild` doesn't produce a result file. - Use 'make' to create a result file. + Use |:make| to create a result file. You may optionally pass any valid go build flags/options. For a full list - please see `go help build`. Options are expanded with 'expand'. + please see `go help build`. Options are expanded with [expand]. If [!] is not given the first error is jumped to. If using neovim then this command is fully async, it does not block the UI. - *:GoGenerate* + *:GoGenerate* :GoGenerate[!] [expand] Creates or updates your auto-generated source files by running `go generate`. - You may optionally pass any valid go generate flags/options. For a full list - please see `go help generate`. Options are expanded with 'expand'. + You may optionally pass any valid go generate flags/options. For a full + list please see `go help generate`. Options are expanded with [expand]. If [!] is not given the first error is jumped to. - *:GoInfo* + *:GoInfo* :GoInfo Show type information about the identifier under the cursor. For example putting it above a function call is going to show the full function - signature. It uses gocode to get the type informations. + signature. By default it uses `gocode` to get the type informations. To + change the underlying tool from `gocode` to another tool, see + |'g:go_info_mode'|. - *:GoInstall* + *:GoInstall* :GoInstall[!] [options] Install your package with `go install`. - You may optionally pass any valid go install flags/options. For a full list - please see `go help install`. + You may optionally pass any valid go install flags/options. For a full + list please see `go help install`. If [!] is not given the first error is jumped to. - *:GoTest* + *:GoTest* :GoTest[!] [expand] Run the tests on your _test.go files via in your current directory. Errors - are populated in the quickfix window. If an argument is passed, 'expand' + are populated in the quickfix window. If an argument is passed, [expand] is used as file selector (useful for cases like `:GoTest ./...`). You may optionally pass any valid go test flags/options. For a full list please see `go help test`. GoTest timesout automatically after 10 seconds. To customize the timeout - use |g:go_test_timeout|. This feature is disabled if any arguments are + use |'g:go_test_timeout'|. This feature is disabled if any arguments are passed to the `:GoTest` command. If [!] is not given the first error is jumped to. If using neovim `:GoTest` will run in a new terminal or run asynchronously - in the background according to |g:go_term_enabled|. You can set the mode of - the new terminal with |g:go_term_mode|. + in the background according to |'g:go_term_enabled'|. You can set the mode + of the new terminal with |'g:go_term_mode'|. - *:GoTestFunc* + *:GoTestFunc* :GoTestFunc[!] [expand] Runs :GoTest, but only on the single test function immediate to your @@ -308,28 +400,50 @@ COMMANDS *go-commands* If [!] is not given the first error is jumped to. - If using neovim `:GoTestFunc` will run in a new terminal or run asynchronously - in the background according to |g:go_term_enabled|. You can set the mode of - the new terminal with |g:go_term_mode|. + If using neovim `:GoTestFunc` will run in a new terminal or run + asynchronously in the background according to |'g:go_term_enabled'|. You + can set the mode of the new terminal with |'g:go_term_mode'|. *:GoTestCompile* :GoTestCompile[!] [expand] Compile your _test.go files via in your current directory. Errors are - populated in the quickfix window. If an argument is passed, 'expand' is + populated in the quickfix window. If an argument is passed, [expand] is used as file selector (useful for cases like `:GoTest ./...`). Useful to not run the tests and capture/fix errors before running the tests or to create test binary. If [!] is not given the first error is jumped to. - If using neovim `:GoTestCompile` will run in a new terminal or run asynchronously - in the background according to |g:go_term_enabled|. You can set the mode of - the new terminal with |g:go_term_mode|. + If using neovim `:GoTestCompile` will run in a new terminal or run + asynchronously in the background according to |'g:go_term_enabled'|. You + can set the mode of the new terminal with |'g:go_term_mode'|. - *:GoCoverage* + *:GoCoverage* :GoCoverage[!] [options] + Create a coverage profile and annotates the current file's source code. If + called again it rerurns the tests. + + If [!] is not given the first error is jumped to. + + *:GoCoverageToggle* +:GoCoverageToggle[!] [options] + + Create a coverage profile and annotates the current file's source code. If + called again clears the annotation (works as a toggle). + + If [!] is not given the first error is jumped to. + + *:GoCoverageClear* +:GoCoverageClear [options] + + Clears the coverage annotation. + + + *:GoCoverageBrowser* +:GoCoverageBrowser[!] [options] + Create a coverage profile and open a browser to display the annotated source code of the current package. @@ -338,45 +452,56 @@ COMMANDS *go-commands* If [!] is not given the first error is jumped to. - *:GoErrCheck* + *:GoErrCheck* :GoErrCheck [options] Check for unchecked errors in you current package. Errors are populated in the quickfix window. - You may optionally pass any valid errcheck flags/options. For a full list - please see `errcheck -h`. + You may optionally pass any valid errcheck flags/options. See + `errcheck -h` for a full list. - *:GoFiles* -:GoFiles + *:GoFiles* +:GoFiles [source_files] - Show source files that depends for the current package + Show source files for the current package. The [source_files] specifies + which file types to list. See the "// Source files" section of + `go list -h` for possible values; multiple values are accepted. + Command-line completion also works for this command. + The default is to use `GoFiles` if no arguments are given. - *:GoDeps* + *:GoDeps* :GoDeps - Show dependencies for the current package + Show dependencies for the current package. *:GoInstallBinaries* -:GoInstallBinaries +:GoInstallBinaries [binaries] - Download and Install all necessary Go tool binaries such as `godef`, - `goimports`, `gocode`, etc.. under `g:go_bin_path` + Download and install all necessary Go tool binaries such as `godef`, + `goimports`, `gocode`, etc. under |'g:go_bin_path'|. If [binaries] is + supplied, then only the specified binaries will be installed. The default + is to install everything. - *:GoUpdateBinaries* -:GoUpdateBinaries + Set |'g:go_get_update'| to disable updating dependencies. - 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. + *:GoUpdateBinaries* +:GoUpdateBinaries [binaries] - *:GoImplements* + Download and update previously installed Go tool binaries such as `godef`, + `goimports`, `gocode`, etc. under |'g:go_bin_path'|. If [binaries] is + supplied, then only the specified binaries will be updated. The default is + to update everything. + + Set |'g:go_get_update'| to disable updating dependencies. + + *:GoImplements* :GoImplements - Show 'implements' relation for a selected package. A list of interfaces + Show "implements" relation for a selected package. A list of interfaces for the type that implements an interface under the cursor (or selected package) is shown in a location list. - *:GoRename* + *:GoRename* :GoRename[!] [to] Rename the identifier under the cursor to the desired new name. If no @@ -385,152 +510,339 @@ COMMANDS *go-commands* If [!] is not given the first error is jumped to. - *:GoOracleScope* -:GoOracleScope [path1] [path2] ... + *:GoGuruScope* +:GoGuruScope [pattern] [pattern2] ... [patternN] - Changes the custom |g:go_oracle_scope| setting and overrides it with the - given import paths. The custom scope is cleared (unset) if `""` is given - as the only path. If no arguments is given it prints the current custom - scope. + Changes the custom |'g:go_guru_scope'| setting and overrides it with the + given package patterns. The custom scope is cleared (unset) if `""` is + given as the only path. If no arguments is given it prints the current + custom scope. Example patterns are: +> + golang.org/x/tools/cmd/guru # a single package + golang.org/x/tools/... # all packages beneath dir + ... # the entire workspace. +< + Example usage, the following sets the scope to a `github.com/fatih/color` + and to all packages under `golang.org/x/tools/`: +> + :GoGuruScope github.com/fatih/color golang.org/x/tools/... +< + The following sets it to the entire workspace: +> + :GoGuruScope ... +< + Under the hood, the patterns are all joined to a comma-separated list and + passed to `guru`'s `-scope` flag. - *:GoCallees* + Also see |go-guru-scope|. + + *:GoCallees* :GoCallees - Show 'callees' relation for a selected package. A list of possible call + Show "callees" relation for a selected package. A list of possible call targets for the type under the cursor (or selected package) is shown in a location list. - *:GoCallers* + *:GoCallers* :GoCallers - Show 'callers' relation for a selected function. A list of possible + Show "callers" relation for a selected function. A list of possible callers for the selected function under the cursor is shown in a location list. - *:GoDescribe* + *:GoDescribe* :GoDescribe Shows various properties of the selected syntax: its syntactic kind, its type (for an expression), its value (for a constant expression), its size, alignment, method set and interfaces (for a type), its declaration (for an identifier), etc. Almost any piece of syntax may be described, and the - oracle will try to print all the useful information it can. + guru will try to print all the useful information it can. - *:GoCallstack* + *:GoCallstack* :GoCallstack - Shows 'callstack' relation for the selected function. An arbitrary path + Shows "callstack" relation for the selected function. An arbitrary path from the root of the callgraph to the selected function is shown in a location list. This may be useful to understand how the function is reached in a given program. - *:GoFreevars* + *:GoFreevars* :GoFreevars - Enumerates the free variables of the selection. “Free variables” is a + Enumerates the free variables of the selection. "Free variables" is a technical term meaning the set of variables that are referenced but not defined within the selection, or loosely speaking, its inputs. - This information is useful if you’re considering whether to refactor the + This information is useful when considering whether to refactor the selection into a function of its own, as the free variables would be the - necessary parameters of that function. It’s also useful when you want to + necessary parameters of that function. It's also useful when you want to understand what the inputs are to a complex block of code even if you don’t plan to change it. - *:GoChannelPeers* + *:GoChannelPeers* :GoChannelPeers Shows the set of possible sends/receives on the channel operand of the - selected send or receive operation; the selection must be a <- token. + selected send or receive operation; the selection must be a `<-` token. For example, visually select a channel operand in the form of: - - "done <- true" - - and call |GoChannelPeers| on it. It will show where it was allocated, and +> + done <- true +< + And call |:GoChannelPeers| on it. It will show where it was allocated, and the sending and receiving endings. - *:GoReferrers* + *:GoReferrers* :GoReferrers The referrers query shows the set of identifiers that refer to the same object as does the selected identifier, within any package in the analysis scope. - *:GoMetaLinter* + *:GoSameIds* +:GoSameIds + + Highlights all identifiers that are equivalent to the identifier under the + cursor. + + *:GoSameIdsClear* +:GoSameIdsClear + + Clears all SameIds highlights from a |:GoSameIds| call. + + *:GoSameIdsToggle* +:GoSameIdsToggle + + Toggle between |:GoSameIds| and |:GoSameIdsClear|. + + *:GoSameIdsAutoToggle* +:GoSameIdsAutoToggle + + Enables or disables automatic highlighting of |:GoSameIds| while moving + the cursor. This basically toggles the option |'g:go_auto_sameids'| + on/off. + If enabled it starts highlighting whenever your cursor is staying at the + same position for a configurable period of time (see 'updatetime'). If + disabled it clears and stops automatic highlighting. + + *:GoMetaLinter* :GoMetaLinter [path] Calls the underlying `gometalinter` tool and displays all warnings and - errors in the quickfix window. By default the following linters are - enabled: "'vet', 'golint', 'errcheck'". This can be changed with the - |g:go_metalinter_enabled| variable. To override the command completely use - the variable |g:go_metalinter_command|. To override the maximum linters - execution time use |g:go_metalinter_deadline| variable. - - *:GoOracleTags* -:GoOracleTags [tags] - - Changes the custom |g:go_oracle_tags| setting and overrides it with the - given build tags. This command cooperate with GoReferrers command when - there exist mulitiple build tags in your project, then you can set one - of the build tags for GoReferrers to find more accurate. - The custom build tags is cleared (unset) if `""` is given. If no arguments - is given it prints the current custom build tags. + errors in the |quickfix| window. By default the following linters are + enabled: `vet`, `golint`, and `errcheck`. This can be changed with the + |'g:go_metalinter_enabled'| variable. To override the command completely + use the variable |'g:go_metalinter_command'|. To override the maximum + linters execution time use |'g:go_metalinter_deadline'| variable. + + *:GoBuildTags* +:GoBuildTags [tags] + + Changes the build tags for various commands. If you have any file that + uses a custom build tag, such as `//+build integration` , this command can + be used to pass it to all tools that accepts tags, such as guru, gorename, + etc.. + + The build tags is cleared (unset) if `""` is given. If no arguments is + given it prints the current custom build tags. *:AsmFmt* :AsmFmt - Filter the current Go asm buffer through asmfmt. It tries to preserve cursor - position and avoids replacing the buffer with stderr output. - + Filter the current Go asm buffer through asmfmt. It tries to preserve + cursor position and avoids replacing the buffer with stderr output. - *:GoAlternate* + *:GoAlternate* :GoAlternate[!] - Alternates between the implementation and test code. For example if in main.go, - switch to main_test.go. Uses the |g:go_alternate_mode| setting as the command - to open the file. + Alternates between the implementation and test code. For example if in + main.go, switch to main_test.go. Uses the |'g:go_alternate_mode'| setting + as the command to open the file. - If [!] is given then it switches to the new file even if it does not exist. + If [!] is given then it switches to the new file even if it does not + exist. - If you would like to override the traditional commands for alternating, add - the following to your .vimrc: + If you would like to override the traditional commands for alternating, + add the following to your .vimrc: > augroup go autocmd! - autocmd Filetype go command! -bang A call go#alternate#Switch(0, 'edit') - autocmd Filetype go command! -bang AV call go#alternate#Switch(0, 'vsplit') - autocmd Filetype go command! -bang AS call go#alternate#Switch(0, 'split') + autocmd Filetype go + \ command! -bang A call go#alternate#Switch(0, 'edit') + \| command! -bang AV call go#alternate#Switch(0, 'vsplit') + \| command! -bang AS call go#alternate#Switch(0, 'split') augroup END < - *:GoDecls* + *:GoWhicherrs* +:GoWhicherrs + + Show the list of possible constants, global variables, and concrete types + for the error type under the cursor in a location list. + + *:GoDecls* :GoDecls [file] - - Only enabled if `ctrlp.vim` is installed. If run shows all function and - type declarations for the current file. If [file] is non empty it parses - the given file. - By default `type` and `func` declarations are being showed. This can be - changed via |g:go_decls_includes|, which accepts a comma delimited list of - definitions. By default set to: `"func,type"`. Possible options are: - `{func,type}` - - *:GoDeclsDir* + + Show all function and type declarations for the current file. If + [file] is non empty it parses the given file. + Requires `ctrlp.vim` or `fzf`; it will autodetect the plugin if installed, + but you can use |'g:go_decls_mode'| to force using one or the other. + By default `type` and `func` declarations are shown. This can be changed + via |'g:go_decls_includes'|. Also see |unite-decls|, |denite-decls|. + + *:GoDeclsDir* :GoDeclsDir [dir] - - Only enabled if `ctrlp.vim` is installed. If run shows all function and - type declarations for the current directory. If [dir] is given it parses - the given directory. - By default `type` and `func` declarations are being showed. This can be - changed via |g:go_decls_includes|, which accepts a comma delimited list of - definitions. By default set to: `"func,type"`. Possible options are: - `{func,type}` - -=============================================================================== + + Show all function and type declarations for the current directory. If + [dir] is given it parses the given directory. + + *unite-decls* + *denite-decls* +:Unite decls[:path] +:Denite decls[:path] + + Only enabled if `unite.vim` or `denite.nvim` is installed. Show + declarations for all functions and types on the current file or directory + or for [path] if given. + + Note: `denite.nvim` requires NeoVim or Vim 8 with |:python3| enabled. +> + " show declarations on the parent directory of the current file + :Unite decls + :Denite decls + + " show declarations in the file. + :Unite decls:foo/bar.go + :Denite decls:foo/bar.go + + " show declarations in the directory "foo". + :Unite decls:foo + :Denite decls:foo +< + *:GoImpl* +:GoImpl [receiver] [interface] + + Generates method stubs for implementing an interface. If no arguments is + passed it takes the identifier under the cursor to be the receiver and + asks for the interface type to be generated. If used with arguments, the + receiver and the interface needs to be specified. Example usages: +> + :GoImpl f *Foo io.Writer + :GoImpl t Type io.ReadWriteCloser +< + *:GoAddTags* +:[range]GoAddTags [key],[option] [key1],[option] ... + + Adds field tags for the fields of a struct. If called inside a struct it + automatically add field tags with the `json` key and the value + automatically generated based on the field name. An error message is given + if it's called outside a struct definition or if the file is not correctly + formatted. + + If [range] is given, only the selected fields will be changed. + + The default `json` can be changed by providing one or more [key] + arguments. An example of adding `xml` and `db` would be: +> + :GoAddTags xml db +< + If [option] is passed it'll either add a new tag with an option or will + modify existing tags. An example of adding `omitempty` to all `json` + fields would be: +> + :GoAddTags json,omitempty +< + You can define a constant value instead of the default field based value. + For example the following command will add ``valid:"1"`` to all fields. +> + :GoAddTags valid=1 +< + *:GoRemoveTags* +:[range]GoRemoveTags [key],[option] [key1],[option1] ... + + Rmove field tags for the fields of a struct. If called inside a struct it + automatically remove all field tags. An error message is given if it's + called outside a struct definition or if the file is not correctly + formatted + + If [range] is given, only the selected fields will be changed. + + If [key] is given, it will only remove those keys. Example: +> + :GoRemoveTags json +< + If [option] is passed with a [key], it will only remove the options. + Example, this will only remove `omitempty` options from fields containing + `json`: +> + :GoRemoveTags json,omitempty +< + *:GoAutoTypeInfoToggle* +:GoAutoTypeInfoToggle + + Toggles |'g:go_auto_type_info'|. + + *:GoFmtAutoSaveToggle* +:GoFmtAutoSaveToggle + + Toggles |'g:go_fmt_autosave'|. + + *:GoAsmFmtAutoSaveToggle* +:GoAsmFmtAutoSaveToggle + + Toggles |'g:go_asmfmt_autosave'|. + + *:GoMetaLinterAutoSaveToggle* +:GoMetaLinterAutoSaveToggle + + Toggles |'g:go_metalinter_autosave'|. + + *:GoTemplateAutoCreateToggle* +:GoTemplateAutoCreateToggle + + Toggles |'g:go_template_autocreate'|. + + *:GoKeyify* +:GoKeyify + + Uses `keyify` to turn unkeyed struct literals into keyed ones. + + For example: +> + Person{"John", "Smith"} +< + Becomes: +> + Person{ + Name: "John", + Surname: "Smith", + } +< + *:GoFillStruct* +:GoFillStruct + + Use `fillstruct` to fill a struct literal with default values. Existing + values (if any) are preserved. The cursor must be on the struct you wish + to fill. + + For example: +> + addr := net.Address{Name: "Ford Prefect"} +< + Becomes: +> + addr := net.Address{ + Name: "Ford Prefect", + Email: "", + } +< + +============================================================================== 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)`: > +For example, to create a mapping that calls `go run` for the current package, +create a mapping for the `(go-run)`: > au FileType go nmap r (go-run) @@ -538,11 +850,11 @@ As always one is free to create more advanced mappings or functions based with |go-commands|. For more information please check out the mappings command documentation in the |go-commands| section. Available keys are: - *(go-run)* + *(go-run)* -Calls `go run` for the current file +Calls `go run` for the current main package - *(go-run-tab)* + *(go-run-tab)* Calls `go run` for the current file in a new terminal tab This option is neovim only. @@ -552,169 +864,201 @@ This option is neovim only. Calls `go run` for the current file in a new terminal horizontal split This option is neovim only. - *(go-run-vertical)* + *(go-run-vertical)* Calls `go run` for the current file in a new terminal vertical split This option is neovim only. - - *(go-build)* + *(go-build)* Calls `go build` for the current package - *(go-generate)* + *(go-generate)* Calls `go generate` for the current package - - *(go-info)* + *(go-info)* Shows type information for the word under the cursor - - *(go-install)* + *(go-install)* Calls `go install` for the current package - - *(go-test)* + *(go-test)* Calls `go test` for the current package - *(go-test-func)* + *(go-test-func)* Calls `go test -run '...'` for the test function immediate to cursor - *(go-test-compile)* + *(go-test-compile)* Calls `go test -c` for the current package - *(go-coverage)* + *(go-coverage)* + +Calls `go test -coverprofile-temp.out` for the current package and shows the +coverage annotation. + + *(go-coverage-clear)* + +Clears the coverage annotation + + *(go-coverage-toggle)* -Calls `go test -coverprofile-temp.out` for the current package +Calls `go test -coverprofile-temp.out` for the current package and shows the +coverage annotation. If run again it acts as a toggle and clears the +annotation. - *(go-vet)* + *(go-imports)* + +Calls `goimports` for the current package + + *(go-lint)* + +Calls `golint` for the current package + + *(go-vet)* Calls `go vet` for the current package - *(go-files)* + *(go-files)* Show source files that depends for the current package - *(go-deps)* + *(go-deps)* Show dependencies for the current package - *(go-doc)* + *(go-doc)* Show the relevant GoDoc for the word under the cursor in a split window leftabove (default mode). - *(go-doc-split)* + *(go-doc-split)* Show the relevant GoDoc for the word under the cursor in a split window. - *(go-doc-vertical)* + *(go-doc-vertical)* Show the relevant GoDoc for the word under the cursor in a vertical split window. - - *(go-doc-tab)* + *(go-doc-tab)* Show the relevant GoDoc for the word under the cursor in a tab window. - *(go-doc-browser)* + *(go-doc-browser)* Show the relevant GoDoc for the word under in browser - *(go-def)* + *(go-def)* Goto declaration/definition. Results are shown in the current buffer. - - *(go-def-split)* + *(go-def-split)* Goto declaration/definition. Results are shown in a split window. +Jumps to an existing buffer if |'g:go_def_reuse_buffer'| is enabled. - - *(go-def-vertical)* + *(go-def-vertical)* Goto declaration/definition. Results are shown in a vertical split window. +Jumps to an existing buffer if |'g:go_def_reuse_buffer'| is enabled. - - *(go-def-tab)* + *(go-def-tab)* Goto declaration/definition. Results are shown in a tab window. +Jumps to an existing buffer if |'g:go_def_reuse_buffer'| is enabled. + + *(go-def-stack)* + +Shows the godef tag stack + + *(go-def-stack-clear)* + +Resets and clears the tag stack + + *(go-def-pop)* + +Jump to previous entry in the tag stack - *(go-implements)* + *(go-implements)* Show the interfaces that the type under the cursor implements. - *(go-rename)* + *(go-rename)* Rename the identifier under the cursor to the desired new name - *(go-callees)* + *(go-callees)* Show the call targets for the type under the cursor - *(go-callers)* + *(go-callers)* Show possible callers of selected function - *(go-describe)* + *(go-describe)* Describe selected syntax: definition, methods, etc - - *(go-callstack)* + *(go-callstack)* Show path from callgraph root to selected function - *(go-freevars)* + *(go-freevars)* Show free variables of selection - *(go-channelpeers)* + *(go-channelpeers)* Show send/receive corresponding to selected channel op - *(go-referrers)* + *(go-referrers)* Show all refs to entity denoted by selected identifier - *(go-metalinter)* + *(go-metalinter)* Calls `go-metalinter` for the current directory - *(go-alternate-edit)* + *(go-alternate-edit)* Alternates between the implementation and test code in the current window - *(go-alternate-split)* + *(go-alternate-split)* Alternates between the implementation and test code in a new horizontal split - *(go-alternate-vertical)* + *(go-alternate-vertical)* Alternates between the implementation and test code in a new vertical split -=============================================================================== + *(go-import)* + +Calls `:GoImport` for the current package + + +============================================================================== TEXT OBJECTS *go-text-objects* vim-go comes with several custom |text-objects| that can be used to operate upon regions of text. vim-go currently defines the following text objects: *go-v_af* *go-af* -af "a function", select contents from a function definition to the - closing bracket. If |g:go_textobj_include_function_doc| is +af "a function", select contents from a function definition to the + closing bracket. If |'g:go_textobj_include_function_doc'| is enabled it also includes the comment doc for a function declaration. This text-object also supports literal functions. + If |'g:go_textobj_include_variable'| is enabled it also + includes the variable of an function assignment *go-v_if* *go-if* if "inside a function", select contents of a function, @@ -725,9 +1069,9 @@ if "inside a function", select contents of a function, vim-go also defines the following text motion objects: *go-v_]]* *go-]]* -]] [count] forward to next function declaration. If - |g:go_textobj_include_function_doc| is enabled and if your - on a comment, it skips the function which the comment +]] [count] forward to next function declaration. If + |'g:go_textobj_include_function_doc'| is enabled and if your + on a comment, it skips the function which the comment belongs and forwards to the next function declaration. *go-v_[[* *go-[[* @@ -735,59 +1079,99 @@ vim-go also defines the following text motion objects: -=============================================================================== +============================================================================== FUNCTIONS *go-functions* - *go#jobcontrol#Statusline()* + *go#statusline#Show()* Shows the status of a job running asynchronously. Can be used to plug into the statusline. It works to show the status per package instead of per -file. Assume you have three files open, all belonging to the same package, -if the package build (`:GoBuild`) is successful, all statusline's will be empty -(means SUCCESS), if you it fails all file's statusline will show FAILED. +file. Assume you have three files open, all belonging to the same package, if +the package build (`:GoBuild`) is successful, all statusline's will show +`success`, if you it fails all file's statusline will show `failed`. - *go#complete#GetInfo()* +To avoid always showing old status information, the status information is +cleaned for each package after `60` seconds. This can be changed with the +|'g:go_statusline_duration'| setting. + + *go#complete#GetInfo()* Returns the description of the identifer under the cursor. Can be used to plug -into the statusline. This function is also used for |g:go_auto_type_info|. +into the statusline. This function is also used for |'g:go_auto_type_info'|. -=============================================================================== +============================================================================== SETTINGS *go-settings* - *'g:go_test_timeout'* + *'g:go_test_show_name'* + +Show the name of each failed test before the errors and logs output by the +test. By default it is disabled. +> + let g:go_test_show_name = 0 +< + + *'g:go_test_timeout'* Use this option to change the test timeout of |:GoTest|. By default it is set to 10 seconds . > let g:go_test_timeout= '10s' < + *'g:go_play_browser_command'* - *'g:go_play_browser_command'* +Browser to use for |:GoPlay| or |:GoDocBrowser|. The url must be added with +`%URL%`, and it's advisable to include `&` to make sure the shell returns. For +example: +> + let g:go_play_browser_command = 'firefox-developer %URL% &' +< -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. > +By default it tries to find it automatically for the current OS. > let g:go_play_browser_command = '' < - *'g:go_play_open_browser'* + *'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 > + *'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. The delay can be configured with the +'g:go_updatetime' setting. +> let g:go_auto_type_info = 0 < - *'g:go_jump_to_error'* + *'g:go_info_mode'* + +Use this option to define the command to be used for |:GoInfo|. By default +`gocode` is being used as it's the fastest option. But one might also use +`guru` as it's covers more cases and is more accurate. Current valid options +are: `[gocode, guru]` > + + let g:go_info_mode = 'gocode' +< + *'g:go_auto_sameids'* + +Use this option to highlight all uses of the identifier under the cursor +(:GoSameIds) automatically. By default it's disabled. The delay can be +configured with the 'g:go_updatetime' setting. +> + let g:go_auto_sameids = 0 +< + *'g:go_updatetime'* + +Use this option to configure the a custom 'updatetime' for Go source files. If +set to 0, no custom time will be configured. By default it's set to 800ms. +> + let g:go_updatetime = 800 +< + *'g:go_jump_to_error'* Use this option to enable/disable passing the bang attribute to the mappings |(go-build)|, |(go-run)|, etc.. When enabled it will jump to the first error @@ -798,36 +1182,47 @@ changing the behaviour of our custom static mappings. By default it's enabled. > let g:go_jump_to_error = 1 < - *'g:go_fmt_autosave'* + *'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'* + *'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. > + *'g:go_fmt_options'* +Use this option to add additional options to the |'g:go_fmt_command'|. It's +value type can be either a string or a dictionary. This is due backwards +compatibility. The string version will be removed in the future so please use +the dictionary version. Default is empty. +> let g:go_fmt_options = '' -< - *'g:go_fmt_fail_silently'* + or -Use this option to disable showing a location list when |g:go_fmt_command| + let g:go_fmt_options = {} +< +The dictionary version allows you to define options for multiple binaries: +> + let g:go_fmt_options = { + \ 'gofmt': '-s', + \ 'goimports': '-local mycompany.com', + \ } +< + *'g:go_fmt_fail_silently'* + +Use this option to disable showing a location list when |'g:go_fmt_command'| fails. By default the location list is shown. > let g:go_fmt_fail_silently = 0 < - - *'g:go_fmt_experimental'* + *'g:go_fmt_experimental'* Use this option to enable fmt's experimental mode. This experimental mode is superior to the current mode as it fully saves the undo history, so undo/redo @@ -836,192 +1231,174 @@ it's causing problems on some Vim versions. By default it's disabled. > let g:go_fmt_experimental = 0 < - *'g:go_doc_keywordprg_enabled'* + *'g:go_doc_keywordprg_enabled'* -Use this option to run `godoc` on words under the cursor with the default -K , keywordprg shortcut. Usually this shortcut is set to use the program `man`. -In Go, using `godoc` is more idiomatic. Default is enabled. > +Use this option to run `godoc` on words under the cursor with |K|; this will +normally run the `man` program, but for Go using `godoc` is more idiomatic. It +will not override the |'keywordprg'| setting, but will run |:GoDoc|. Default +is enabled. > let g:go_doc_keywordprg_enabled = 1 < - *'g:go_def_mapping_enabled'* + *'g:go_doc_height'* -Use this option to enable/disable the default mapping of (`gd`) for GoDef. -Disabling it allows you to map something else to `gd`. Default is enabled. > +Maximum height for the GoDoc window created with |:GoDoc|. Default is 20. > - let g:go_def_mapping_enabled = 1 + let g:go_doc_max_height = 20 < - *'g:go_dispatch_enabled'* -Use this option to enable/disable the use of Dispatch to execute the -`:GoRun`, `:GoBuild` and `:GoGenerate` commands. More information about Dispatch -is available at https://github.com/tpope/vim-dispatch. Default is disabled. > + *'g:go_doc_url'* - let g:go_dispatch_enabled = 0 -< - *'g:go_doc_command'* - -Use this option to define which tool is used to godoc. By default `godoc` is -used > - - let g:go_doc_command = "godoc" -< - *'g:go_doc_options'* - -Use this option to add additional options to the |g:go_doc_command|. Default -is empty. > - - let g:go_doc_options = '' - -< *'g:go_bin_path'* - -Use this option to change default path for vim-go tools when using -|GoInstallBinaries| and |GoUpdateBinaries|. If not set `$GOBIN` or -`$GOPATH/bin` is used. > - - let g:go_bin_path = "" -< - *'g:go_snippet_engine'* - -Use this option to define the default snippet engine. By default "ultisnips" -is used. Use "neosnippet" for neosnippet.vim: > - - let g:go_snippet_engine = "ultisnips" -< - - *'g:go_oracle_scope'* - -Use this option to define the scope of the analysis to be passed for oracle -related commands, such as |GoImplements|, |GoCallers|, etc. By default it's -not set, so only the current package's go files are passed as scope. You can -change it on-the-fly with |GoOracleScope|. For more info, please have a look -at oracle's user manual: -https://golang.org/s/oracle-user-manual#heading=h.nwso96pj07q8 > - - let g:go_oracle_scope = '' +godoc server URL used when |:GoDocBrowser| is used. Change if you want to use +a private internal service. Default is 'https://godoc.org'. +> + let g:go_doc_url = 'https://godoc.org' < - *'g:go_highlight_array_whitespace_error'* + *'g:go_def_mode'* -Highlights white space after "[]". > +Use this option to define the command to be used for |:GoDef|. By default +`guru` is being used as it covers all edge cases. But one might also use +`godef` as it's faster. Current valid options are: `[guru, godef]` > - let g:go_highlight_array_whitespace_error = 1 + let g:go_def_mode = 'guru' < + *'g:go_def_mapping_enabled'* - *'g:go_highlight_chan_whitespace_error'* - -Highlights white space around the communications operator (`<-`) that doesn't -follow the standard style. > +Use this option to enable/disable the default mapping of CTRL-], +, g and (`gd`) for GoDef and CTRL-t for :GoDefPop. +Disabling it allows you to map something else to these keys or mappings. +Default is enabled. > - let g:go_highlight_chan_whitespace_error = 1 + let g:go_def_mapping_enabled = 1 < + *'g:go_def_reuse_buffer'* - *'g:go_highlight_extra_types'* +Use this option to jump to an existing buffer for the split, vsplit and tab +mappings of |:GoDef|. By default it's disabled. > -Highlights commonly used library types (io.Reader, etc.). > - - let g:go_highlight_extra_types = 1 + let g:go_def_reuse_buffer = 0 < + *'g:go_doc_command'* - *'g:go_highlight_space_tab_error'* +Command to use for |:GoDoc|; only used when invoked with a package name. The +`gogetdoc` command is always used when |:GoDoc| is used on the identifier +under the cursor (i.e. without argument or from |K|). > -Highlights instances of tabs following spaces. > + let g:go_doc_command = ["godoc"] - let g:go_highlight_space_tab_error = 1 -< - *'g:go_highlight_trailing_whitespace_error'* - -Highlights trailing white space. > +< *'g:go_bin_path'* - let g:go_highlight_trailing_whitespace_error = 1 +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_highlight_operators'* + *'g:go_snippet_engine'* -Highlights operators such as `:=` , `==`, `-=`, etc. By default it's -disabled. > +Define the snippet engine to use. The default is to auto-detect one. Valid +values are: - let g:go_highlight_operators = 0 + automatic Automatically detect a snippet engine. + ultisnips https://github.com/SirVer/ultisnips + neosnippet https://github.com/Shougo/neosnippet.vim + minisnip https://github.com/joereynolds/vim-minisnip + Note: the original at KeyboardFire/vim-minisnip won't work. +> + let g:go_snippet_engine = "automatic" < - *'g:go_highlight_functions'* + *'g:go_get_update'* -Highlights function names. By default it's disabled. > - - let g:go_highlight_functions = 0 +Use this option to disable updating dependencies with |:GoInstallBinaries|. By +default this is enabled. +> + let g:go_get_update = 1 < - *'g:go_highlight_methods'* + *'g:go_guru_scope'* -Highlights method names. By default it's disabled. > +Use this option to define the scope of the analysis to be passed for guru +related commands, such as |:GoImplements|, |:GoCallers|, etc. You can change +it on-the-fly with |:GoGuruScope|. The input should be a a list of package +pattern. An example input might be: +`["github.com/fatih/color","github.com/fatih/structs"]` - let g:go_highlight_methods = 0 -< - *'g:go_highlight_structs'* - -Highlights struct names. By default it's disabled. > +Also see |go-guru-scope|. - let g:go_highlight_structs = 0 +By default it's not set, so the relevant commands defaults are being used. +> + let g:go_guru_scope = [] < - *'g:go_highlight_interfaces'* + *'g:go_build_tags'* -Highlights interface names. By default it's disabled. > - - let g:go_highlight_interfaces = 0 +These options that will be automatically passed to the `-tags` option of +various tools, such as `guru`, `gorename`, etc... This is a permanent +setting. A more useful way is to use |:GoBuildTags| to dynamically change or +remove build tags. By default it's not set. +> + let g:go_build_tags = '' < - *'g:go_highlight_build_constraints'* + *'g:go_autodetect_gopath'* -Highlights build constraints. By default it's disabled. > - - let g:go_highlight_build_constraints = 0 +Automatically modify GOPATH for certain directory structures, such as for +the `godep` tool which stores dependencies in the `Godeps` folder. What this +means is that all tools are now working with the newly modified GOPATH. So +|:GoDef| for example jumps to the source inside the `Godeps` (vendored) +source. Currently `godep` and `gb` are supported. By default it's disabled. +> + let g:go_autodetect_gopath = 0 < - *'g:go_highlight_string_spellcheck* + *'g:go_textobj_enabled'* -Use this option to highlight spelling errors in strings when |spell| is -also enabled. By default it's enabled. > +Adds custom text objects. By default it's enabled. > - let g:go_highlight_string_spellcheck = 1 + let g:go_textobj_enabled = 1 < + *'g:go_textobj_include_function_doc'* - *'g:go_autodetect_gopath'* - -Automatically modifies GOPATH for certain directory structures, such as for -the tool `godep` which has his own dependencies via the `Godeps` folder. What -this means is that all tools are now working with the newly modified GOPATH. -So |GoDef| for example jumps to the source inside the `Godeps` (vendored) -source. Currently `godep` and `gb` is supported, in the near future more tool -supports will be added. By default it's enabled. > +Consider the comment above a function to be part of the function when using +the `af` text object and `[[` motion. By default it's enabled. > - let g:go_autodetect_gopath = 1 + let g:go_textobj_include_function_doc = 1 < - *'g:go_textobj_enabled'* + *'g:go_textobj_include_variable'* -Adds custom text objects. By default it's enabled. > +Consider the variable of an function assignment to be part of the anonymous +function when using the `af` text object. By default it's enabled. > - let g:go_textobj_enabled = 1 + let g:go_textobj_include_variable = 1 < - *'g:go_metalinter_autosave'* + *'g:go_metalinter_autosave'* Use this option to auto |:GoMetaLinter| on save. Only linter messages for the active buffer will be shown. By default it's disabled > let g:go_metalinter_autosave = 0 < - *'g:go_metalinter_autosave_enabled'* + *'g:go_metalinter_autosave_enabled'* -Specifies the enabled linters for auto |GoMetaLinter| on save. By +Specifies the enabled linters for auto |:GoMetaLinter| on save. By default it's using `vet` and `golint`. > let g:go_metalinter_autosave_enabled = ['vet', 'golint'] < - *'g:go_metalinter_enabled'* + *'g:go_metalinter_enabled'* -Specifies the currently enabled linters for the |GoMetaLinter| command. By +Specifies the currently enabled linters for the |:GoMetaLinter| command. By default it's using `vet`, `golint` and `errcheck`. > let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck'] < - *'g:go_metalinter_command'* + *'g:go_metalinter_excludes'* -Overrides the command to be executed when |GoMetaLinter| is called. This is +Specifies the linters to be excluded from the |:GoMetaLinter| command. By +default it's empty +> + let g:go_metalinter_excludes = [] +< + *'g:go_metalinter_command'* + +Overrides the command to be executed when |:GoMetaLinter| is called. This is an advanced settings and is for users who want to have a complete control over how `gometalinter` should be executed. By default it's empty. > @@ -1034,7 +1411,7 @@ seconds. > let g:go_metalinter_deadline = "5s" < - *'g:go_list_height'* + *'g:go_list_height'* Specifies the window height for the quickfix and location list windows. The default value (empty) automatically sets the height to the number of items @@ -1043,21 +1420,52 @@ explicitly overrides this behavior. For standard Vim behavior, set it to 10. > let g:go_list_height = 0 < - *'g:go_list_type'* - -Specifies the type of list to use. The default value (empty) will use the -appropriate kind of list for the command that was called. Supported values are -"", "quickfix", and "locationlist". > + *'g:go_list_type'* +Specifies the type of list to use for command outputs (such as errors from +builds, results from static analysis commands, etc...). The list type for +specific commands can be overridden with |'g:go_list_type_commands'|. The +default value (empty) will use the appropriate kind of list for the command +that was called. Supported values are "", "quickfix", and "locationlist". +> let g:go_list_type = "" < - *'g:go_asmfmt_autosave'* -Use this option to auto |:AsmFmt| on save. By default it's enabled. > + *'g:go_list_type_commands'* + +Specifies the type of list to use for command outputs (such as errors from +builds, results from static analysis commands, etc...). When an expected key +is not present in the dictionary, |'g:go_list_type'| will be used instead. +Supported keys are "GoBuild", "GoErrCheck", "GoFmt", "GoInstall", "GoLint", +"GoMetaLinter", "GoModifyTags" (used for both :GoAddTags and :GoRemoveTags), +"GoRename", "GoRun", and "GoTest". Supported values for each command are +"quickfix" and "locationlist". +> + let g:go_list_type_commands = {} +< +As an example, the following settings will change all list types to +`locationlist` except for `:GoBuild` where `quickfix` is used: +> + let g:go_list_type = "locationlist" + let g:go_list_type_commands = {"GoBuild": "quickfix"} +< + + *'g:go_list_autoclose'* + +Specifies whether the quickfix/location list should be closed automatically +in the absence of errors. The default value is 1. +If you prefer to keep a long running error window open, you can disable +this by setting the value to 0. +> + let g:go_list_autoclose = 1 +< + *'g:go_asmfmt_autosave'* + +Use this option to auto |:AsmFmt| on save. By default it's disabled. > - let g:go_asmfmt_autosave = 1 + let g:go_asmfmt_autosave = 0 < - *g:go_term_mode* + *'g:go_term_mode'* This option is Neovim only. Use it to change the default command used to open a new terminal for go commands such as |:GoRun|. @@ -1065,8 +1473,8 @@ The default is vsplit. > let g:go_term_mode = "vsplit" < - *g:go_term_height* - *g:go_term_width* + *'g:go_term_height'* + *'g:go_term_width'* These options are Neovim only. Use them to control the height and width of a terminal split. By default these are not set, meaning that the height and @@ -1078,82 +1486,527 @@ For example here is how to set each to 30. let g:go_term_height = 30 let g:go_term_width = 30 < - *g:go_term_enabled* + *'g:go_term_enabled'* This option is Neovim only. Use it to change the behavior of the test commands. If set to 1 it opens the test commands inside a new terminal -according to |g:go_term_mode|, otherwise it will run them in the background -just like `:GoBuild` and then display the status with |go#jobcontrol#Statusline()|. -By default it is disabled. +according to |'g:go_term_mode'|, otherwise it will run them in the background +just like `:GoBuild`. By default it is disabled. > let g:go_term_enabled = 0 < - *g:go_alternate_mode* + *'g:go_alternate_mode'* Specifies the command that |:GoAlternate| uses to open the alternate file. By default it is set to edit. > let g:go_alternate_mode = "edit" < - *g:go_gorename_prefill* + *'g:go_gorename_prefill'* -Specifies whether |:GoRename| prefills the new identifier name with the -word under the cursor. By default is is enabled. +Expression to prefill the new identifier when using |:GoRename| without any +arguments. Use an empty string if you don't want to prefill anything. By +default it converts the identifier to camel case but preserves the +capitalisation of the first letter to ensure that the exported state stays the +same. > - let g:go_gorename_prefill = 1 + let g:go_gorename_prefill = 'expand("") =~# "^[A-Z]"' . + \ '? go#util#pascalcase(expand(""))' . + \ ': go#util#camelcase(expand(""))' < + *'g:go_gocode_autobuild'* +Specifies whether `gocode` should automatically build out-of-date packages +when their source fields are modified, in order to obtain the freshest +autocomplete results for them. By default it is enabled. +> + let g:go_gocode_autobuild = 1 +< + *'g:go_gocode_propose_builtins'* + +Specifies whether `gocode` should add built-in types, functions and constants +to an autocompletion proposals. By default it is enabled. +> + let g:go_gocode_propose_builtins = 1 +< + *'g:go_gocode_unimported_packages'* -=============================================================================== -TROUBLESHOOTING *go-troubleshooting* +Specifies whether `gocode` should include suggestions from unimported +packages. By default it is disabled. +> + let g:go_gocode_unimported_packages = 0 +< -I'm using Fish shell but have some problems using vim-go~ + *'g:go_gocode_socket_type'* -First environment variables in Fish are applied differently, it should be like: +Specifies whether `gocode` should use a different socket type. By default +`unix` is enabled. Possible values: `unix`, `tcp` > - set -x GOPATH /your/own/gopath + let g:go_gocode_socket_type = 'unix' < -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): + *'g:go_template_autocreate'* + +When a new Go file is created, vim-go automatically fills the buffer content +with a Go code template. By default, the templates under the `templates` +folder are used. This can be changed with the |'g:go_template_file'| and +|'g:go_template_test_file'| settings. + +If the new file is created in an already prepopulated package (with other Go +files), in this case a Go code template with only the Go package declaration +(which is automatically determined according to the current package) is added. + +To always use the package name instead of the template, enable the +|'g:go_template_use_pkg'| setting. + +By default it is enabled. > - if $SHELL =~ 'fish' - set shell='/bin/sh' - endif + let g:go_template_autocreate = 1 < -or + *'g:go_template_file'* + +Specifies the file under the `templates` folder that is used if a new Go file +is created. Checkout |'g:go_template_autocreate'| for more info. By default +the `hello_world.go` file is used. > - set shell='/bin/sh' + let g:go_template_file = "hello_world.go" +< + *'g:go_template_test_file'* + +Specifies the file under the `templates` folder that is used if a new Go test +file is created. Checkout |'g:go_template_autocreate'| for more info. By +default the `hello_world_test.go` file is used. > + let g:go_template_test_file = "hello_world_test.go" +< + *'g:go_template_use_pkg'* -I'm seeing weird errors during installation of binaries with -GoInstallBinaries: +Specifies that, rather than using a template, the package name is used if a +new Go file is created. Checkout |'g:go_template_autocreate'| for more info. +By default the template file specified by |'g:go_template_file'| is used. -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: + let g:go_template_use_pkg = 0 +< + *'g:go_decls_includes'* + +Only useful if `ctrlp.vim`, `unite.vim`, `denite.nvim` or `fzf` are installed. +This sets which declarations to show for |:GoDecls| (`ctrp.vim`), +|unite-decls| (`unite.vim`) and |denite-decls| (`denite.nvim`). It is a Comma +delimited list. Possible options are: {func,type}. The default is: > + + let g:go_decls_includes = 'func,type' +< + *'g:go_decls_mode'* + +Define the tool to be used for |:GoDecls|. Valid options are `ctrlp.vim`, +`fzf`, or an empty string; in which case it will try to autodetect either +`ctrlp.vim` or `fzf`. +> + let g:go_decls_mode = '' +< + *'g:go_echo_command_info'* + +Echoes information about various Go commands, such as `:GoBuild`, `:GoTest`, +`:GoCoverage`, etc... Useful to disable if you use the statusline integration, +i.e: |go#statusline#Show()|. By default it's enabled +> + let g:go_echo_command_info = 1 +< + *'g:go_echo_go_info'* + +Use this option to show the identifier information when completion is done. By +default it's enabled > + + let g:go_echo_go_info = 1 +< +Please note that 'noshowmode' must be set for this feature to work correctly. + + *'g:go_statusline_duration'* + +Specifies the duration of statusline information being showed per package. By +default it's 60 seconds. Must be in milliseconds. +> + let g:go_statusline_duration = 60000 +< + *'g:go_addtags_transform'* + +Sets the `transform` option for `gomodifytags` when using |:GoAddTags| or if +it's being used for snippet expansion of single fields. Possible options are: +`snakecase`, `camelcase`. For the following case, if `snakecase` is used the +field will be transformed to: +> + type T struct { + FooBarQuz string `json:"foo_bar_quz" + } +< + +If "camelcase" is used: +> + type T struct { + FooBarQuz string `json:"fooBarQuz" + } +< +By default "snakecase" is used. Current values are: ["snakecase", +"camelcase"]. +> + let g:go_addtags_transform = 'snakecase' +< + +============================================================================== +SYNTAX HIGHLIGHTING *ft-go-syntax* *go-syntax* + +vim-go comes with an enhanced version of Vim's Go syntax highlighting. It +comes with a number of features, most of which are disabled by default. + +The recommended settings are the default values. If you're experiencing +slowdowns in Go files and you enabled some of these options then try disabling +them; some can be resource intensive. + + *'g:go_fold_enable'* + +Control syntax-based folding which takes effect when 'foldmethod' is set to +`syntax`. +You can enable specific fold regions by setting an array. Possible values are: + + block `{` .. `}` blocks. + import `import` block. + varconst `var` and `const` blocks. + package_comment The package comment. + comment Any comment that is not the package comment. + +By default all except "comment" are enabled: +> + let g:go_fold_enable = ['block', 'import', 'varconst', 'package_comment'] +< +Enable folding of only imports: +> + let g:go_fold_enable = ['import'] +< +Disable everything (same as not setting 'foldmethod' to `syntax`): +> + let g:go_fold_enable = [] +< + *'g:go_highlight_array_whitespace_error'* + +Highlight white space after `[]`. > + + let g:go_highlight_array_whitespace_error = 0 +< + *'g:go_highlight_chan_whitespace_error'* + +Highlight white space around the receive operator (`<-`) that doesn't follow +the standard style. > + + let g:go_highlight_chan_whitespace_error = 0 +< + *'g:go_highlight_extra_types'* + +Highlight commonly used library types (`io.Reader`, etc.). > + + let g:go_highlight_extra_types = 0 +< + *'g:go_highlight_space_tab_error'* + +Highlight instances of tabs following spaces. > + + let g:go_highlight_space_tab_error = 0 +< + *'g:go_highlight_trailing_whitespace_error'* + +Highlight trailing white space. > + + let g:go_highlight_trailing_whitespace_error = 0 +< + *'g:go_highlight_operators'* + +Highlight operators such as `:=` , `==`, `-=`, etc. +> + let g:go_highlight_operators = 0 +< + *'g:go_highlight_functions'* + +Highlight function names. +> + let g:go_highlight_functions = 0 +< + *'g:go_highlight_function_arguments'* + +Highlight the variable names in arguments and return values in function +declarations. Setting this implies the functionality from +|'g:go_highlight_functions'|. +> + let g:go_highlight_function_arguments = 0 +< + *'g:go_highlight_methods'* + +Highlight method names. +> + let g:go_highlight_methods = 0 +< + *'g:go_highlight_types'* + +Highlight struct and interface names. +> + let g:go_highlight_types = 0 +< + *'g:go_highlight_fields'* + +Highlight struct field names. +> + let g:go_highlight_fields = 0 +< + *'g:go_highlight_build_constraints'* + +Highlights build constraints. +> + let g:go_highlight_build_constraints = 0 +< + *'g:go_highlight_generate_tags'* + +Highlight go:generate directives. +> + let g:go_highlight_generate_tags = 0 +< + *'g:go_highlight_string_spellcheck'* + +Highlight spelling errors in strings when |spell| is enabled. +> + let g:go_highlight_string_spellcheck = 1 +< + *'g:go_highlight_format_strings'* + +Highlight printf-style formatting verbs inside string literals. +> + let g:go_highlight_format_strings = 1 +< + *'g:go_highlight_variable_declarations'* + +Highlight variable names in variable declarations (`x` in ` x :=`). +> + let g:go_highlight_variable_declarations = 0 +< + *'g:go_highlight_variable_assignments'* + +Highlight variable names in variable assignments (`x` in `x =`). +> + let g:go_highlight_variable_assignments = 0 < -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: +============================================================================== + *gohtmltmpl* *ft-gohtmltmpl-syntax* + *gotexttmpl* *ft-gotexttmpl-syntax* +Go template syntax~ + +The `gotexttmpl` 'filetype' provides syntax highlighting and indentation for +Go's `text/template` package. + +The `gohtmltmpl` filetype is for use with the `html/template` package and is +identical to `gotexttmpl` except that it will also load the standard `html` +filetype. + +The `gohtmltmpl` filetype is automatically set for `*.tmpl` files; the +`gotexttmpl` is never automatically set and needs to be set manually. + + +============================================================================== +FAQ TROUBLESHOOTING *go-troubleshooting* + +I get "not an editor command" error when I invoke :GoXXX~ + +This happens if vim-go is not installed properly. Be sure you have added this +line into your vimrc: > - go get code.google.com/p/go.tools/cmd/goimports + filetype plugin indent on +< + +I get a "command not found" error when I invoke :GoXXX~ + +If you try to call |:GoDef|, |:GoInfo| and get a command not found, check that +you have the binaries installed by using |:GoInstallBinaries|. + +Before opening vim, check your current $PATH: +> + echo $PATH +< +After opening vim, run `:echo $PATH`, the output must be your current `$PATH` +plus `$GOPATH/bin` (the location where |:GoInstallBinaries| installed the +binaries). + + *go-guru-scope* +What is the guru scope and how do I set it?~ + +Many vim-go commands use the `guru` commandline tool to get information. Some +`guru` commands require an expensive analysis of the source code. To still get +a reasonable amount of performance `guru` limits this analysis to a selected +list of packages. This is known as the "guru scope". + +The default is to use the package the curent buffer belongs to, but this may +not always be correct. For example for the file `guthub.com/user/pkg/a/a.go` +the scope will be set to `github.com/user/pkg/a`, but you probably want +`github.com/user/pkg` + +Guessing what package(s) you do want is not easy so you may need to set this +manually, usually from an |autocommand|: +> + autocmd BufRead /home/martin/go/src/github.com/user/pkg/*.go + \ :GoGuruScope github.com/user/pkg +< + +If you have a lot of packages with the same prefix (`github.com/user`) you can +use a single autocommand: +> + autocmd BufRead /home/martin/go/src/*.go + \ let s:tmp = matchlist(expand('%:p'), + \ '/home/martin/go/src/\(github.com/user/[^/]\+\)') + \| if len(s:tmp) > 1 | exe 'silent :GoGuruScope ' . s:tmp[1] | endif + \| unlet s:tmp +< +Also see |:GoGuruScope| and |'g:go_guru_scope'|. + + +Vim becomes slow while editing Go files~ + +This is usually caused by `g:go_highlight_*` options. Try disabling them if +you've enabled some of them. + +Other common culprits are |'g:go_auto_sameids'| and |go#statusline#Show()|. + + +I get errors when using GoInstallBinaries~ + +If you see errors like this: +> + Error installing golang.org/x/tools/cmd/goimports +< +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 golang.org/x/tools/cmd/goimports < You'll see a more detailed error. If this works, vim-go will work too. -=============================================================================== + +I want to use a different binary name than "go", can I do this?~ + +There is no way to directly configure the binary name; but you can use a +wrapper script; for example if you would like to run `goapp` instead of `go`: + +1. In `~/gobin/go` (remember to make it executable): +> + #!/bin/sh + # Remove gobin from PATH and run goapp. + PATH=${PATH#$HOME/gobin} goapp "$@" +< +2. Start Vim with `~/gobin` as the first `PATH` entry so it will use the + wrapper script: +> + PATH="$HOME/gobin/:$PATH" vim +< + Alternatively you you could set `$PATH` in your vimrc with an |:autocmd|. + + +How do I use vim-go with syntastic?~ + +Sometimes when using both `vim-go` and `syntastic` Vim will start lagging +while saving and opening files. The following fixes this: +> + let g:syntastic_go_checkers = ['golint', 'govet'] + let g:syntastic_mode_map = { 'mode': 'active', 'passive_filetypes': ['go'] } +< +If you want to add errcheck you can use gometalinter as a wrapper +> + let g:syntastic_go_checkers = ['golint', 'govet', 'gometalinter'] + let g:syntastic_go_gometalinter_args = ['--disable-all', '--enable=errcheck'] + let g:syntastic_mode_map = { 'mode': 'active', 'passive_filetypes': ['go'] } +< +Another issue with `vim-go` and `syntastic` is that the location list window +that contains the output of commands such as `:GoBuild` and `:GoTest` might +not appear. To resolve this: +> + let g:go_list_type = "quickfix" +< + +How do I run focused ginkgo tests?~ + +You must set this environment variable in your `.vimrc`: +> + let $GINKGO_EDITOR_INTEGRATION = "true" +< + +Using with NeoVim~ + +Note: Neovim currently is not a first class citizen for vim-go. You are free +to open bug, however I'm not using Neovim so it's hard for me to test it. +vim-go might not work well as good as in Vim. I'm happy to accept pull +requests or very detailed bug reports. If you're interested to improve the +state of Neovim in vim-go you're always welcome! + +Run `:GoRun` in a new tab, horizontal split or vertical split terminal +> + au FileType go nmap rt (go-run-tab) + au FileType go nmap rs (go-run-split) + au FileType go nmap rv (go-run-vertical) +< +By default new terminals are opened in a vertical split. To change it +> + let g:go_term_mode = "split" +> + +============================================================================== +DEVELOPMENT *go-development* + +vim-go supports test files written in VimScript; the way they're run is +roughly similar to Go tests: + +- A `*.vim` file has a corresponding `*_test.vim`. +- All functions starting with `Test_` are run as test. +- A test is considered to be "failed" if |v:errors| has any entries. You can + use one of the |test-functions| to set this, or append to it directly. + +A simple example: +> + function Test_run_fmt() + call assert_equal(expected, actual) + ... + endfunction +< +To run tests vim-go comes with three small helper scripts: + + `scripts/install-vim` Install a pristine Vim to `/tmp/vim-go-test/`. + `scripts/run-vim` Run a Vim version from `/tmp/vim-go-test/`. + `scripts/test` Run all tests with a Vim from `/tmp/vim-go-test/`. + +All scripts accept a Vim version as the first argument, which can be +`vim-7.4`, `vim-8.0`, or `nvim`. You will need to install a Vim version with +`install-vim` before you can use `run-vim` or `test`. + +You can install and test all Vim versions by running `make`. + + +============================================================================== +DONATION *go-donation* + +People have asked for this for a long time, now you can be a fully supporter +by being a patreon at: https://www.patreon.com/fatih + +By being a patron, you are enabling vim-go to grow and mature, helping me to +invest in bug fixes, new documentation, and improving both current and future +features. It's completely optional and is just a direct way to support +vim-go's ongoing development. Thanks! + +Check it out: https://www.patreon.com/fatih + + +============================================================================== CREDITS *go-credits* -* Go Authors for official vim plugins -* Gocode, Godef, Golint, Oracle, Goimports, Errcheck projects and authors of +* Go Authors for official Vim plugins. +* Gocode, Godef, Golint, Guru, 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-godef). +* vim-go contributors: https://github.com/fatih/vim-go/graphs/contributors. -vim:ft=help:et:ts=2:sw=2:sts=2:norl + vim: ft=help tw=78 et ts=2 sw=2 sts=2 norl diff --git a/vim/bundle/go/ftdetect/gofiletype.vim b/vim/bundle/go/ftdetect/gofiletype.vim index b5578a6..bc47776 100644 --- a/vim/bundle/go/ftdetect/gofiletype.vim +++ b/vim/bundle/go/ftdetect/gofiletype.vim @@ -6,26 +6,29 @@ let s:current_fileencodings = '' " define fileencodings to open as utf-8 encoding even if it's ascii. function! s:gofiletype_pre(type) - let s:current_fileformats = &g:fileformats - let s:current_fileencodings = &g:fileencodings - set fileencodings=utf-8 fileformats=unix - let &l:filetype = a:type + let s:current_fileformats = &g:fileformats + let s:current_fileencodings = &g:fileencodings + set fileencodings=utf-8 fileformats=unix + let &l:filetype = a:type endfunction " restore fileencodings as others function! s:gofiletype_post() - let &g:fileformats = s:current_fileformats - let &g:fileencodings = s:current_fileencodings + let &g:fileformats = s:current_fileformats + let &g:fileencodings = s:current_fileencodings endfunction -au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix -au BufRead *.go call s:gofiletype_pre("go") -au BufReadPost *.go call s:gofiletype_post() +augroup vim-go-filetype + autocmd! + au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix + au BufRead *.go call s:gofiletype_pre("go") + au BufReadPost *.go call s:gofiletype_post() -au BufNewFile *.s setfiletype asm | setlocal fileencoding=utf-8 fileformat=unix -au BufRead *.s call s:gofiletype_pre("asm") -au BufReadPost *.s call s:gofiletype_post() + au BufNewFile *.s setfiletype asm | setlocal fileencoding=utf-8 fileformat=unix + au BufRead *.s call s:gofiletype_pre("asm") + au BufReadPost *.s call s:gofiletype_post() -au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl + au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl +augroup end -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/ftplugin/asm.vim b/vim/bundle/go/ftplugin/asm.vim index 0ac3594..99996aa 100644 --- a/vim/bundle/go/ftplugin/asm.vim +++ b/vim/bundle/go/ftplugin/asm.vim @@ -1,7 +1,7 @@ " asm.vim: Vim filetype plugin for Go assembler. if exists("b:did_ftplugin") - finish + finish endif let b:did_ftplugin = 1 @@ -15,3 +15,5 @@ setlocal commentstring=//\ %s setlocal noexpandtab command! -nargs=0 AsmFmt call go#asmfmt#Format() + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/ftplugin/go.vim b/vim/bundle/go/ftplugin/go.vim index 3b317d2..a0ecd32 100644 --- a/vim/bundle/go/ftplugin/go.vim +++ b/vim/bundle/go/ftplugin/go.vim @@ -5,7 +5,7 @@ " go.vim: Vim filetype plugin for Go. if exists("b:did_ftplugin") - finish + finish endif let b:did_ftplugin = 1 @@ -24,34 +24,110 @@ compiler go 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 + " 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 + " these are default Vim mappings, we're overriding them to make them + " useful again for Go source code + nnoremap gd :GoDef + nnoremap :GoDef + nnoremap :GoDef + nnoremap g :GoDef + nnoremap :call go#def#Jump("split") + nnoremap ] :call go#def#Jump("split") + nnoremap :call go#def#StackPop(v:count1) endif if get(g:, "go_textobj_enabled", 1) - onoremap af :call go#textobj#Function('a') - onoremap if :call go#textobj#Function('i') + onoremap af :call go#textobj#Function('a') + onoremap if :call go#textobj#Function('i') - xnoremap af :call go#textobj#Function('a') - xnoremap if :call go#textobj#Function('i') + xnoremap af :call go#textobj#Function('a') + xnoremap if :call go#textobj#Function('i') - " Remap ]] and [[ to jump betweeen functions as they are useless in Go - nnoremap ]] :call go#textobj#FunctionJump('n', 'next') - nnoremap [[ :call go#textobj#FunctionJump('n', 'prev') + " Remap ]] and [[ to jump betweeen functions as they are useless in Go + nnoremap ]] :call go#textobj#FunctionJump('n', 'next') + nnoremap [[ :call go#textobj#FunctionJump('n', 'prev') - onoremap ]] :call go#textobj#FunctionJump('o', 'next') - onoremap [[ :call go#textobj#FunctionJump('o', 'prev') + onoremap ]] :call go#textobj#FunctionJump('o', 'next') + onoremap [[ :call go#textobj#FunctionJump('o', 'prev') - xnoremap ]] :call go#textobj#FunctionJump('v', 'next') - xnoremap [[ :call go#textobj#FunctionJump('v', 'prev') + xnoremap ]] :call go#textobj#FunctionJump('v', 'next') + xnoremap [[ :call go#textobj#FunctionJump('v', 'prev') endif -if get(g:, "go_auto_type_info", 0) - setlocal updatetime=800 +if get(g:, "go_auto_type_info", 0) || get(g:, "go_auto_sameids", 0) + let &l:updatetime= get(g:, "go_updatetime", 800) endif -" vim:ts=4:sw=4:et +" NOTE(arslan): experimental, disabled by default, doesn't work well. No +" documentation as well. If anyone feels adventurous, enable the following and +" try to search for Go identifiers ;) +" +" if get(g:, "go_sameid_search_enabled", 0) +" autocmd FileType go nnoremap * :call Sameids_search(0) +" autocmd FileType go nnoremap # :call Sameids_search(1) +" autocmd FileType go nnoremap n :call Sameids_repeat(0) +" autocmd FileType go nnoremap N :call Sameids_repeat(1) +" autocmd FileType go cabbrev nohlsearch =Sameids_nohlsearch() +" endif + +" " mode 0: next 1: prev +" function! Sameids_repeat(mode) +" let matches = getmatches() +" if empty(matches) +" return +" endif +" let cur_offset = go#util#OffsetCursor() + +" " reverse list to make it easy to find the prev occurrence +" if a:mode +" call reverse(matches) +" endif + +" for m in matches +" if !has_key(m, "group") +" return +" endif + +" if m.group != "goSameId" +" return +" endif + +" let offset = go#util#Offset(m.pos1[0], m.pos1[1]) + +" if a:mode && cur_offset > offset +" call cursor(m.pos1[0], m.pos1[1]) +" return +" elseif !a:mode && cur_offset < offset +" call cursor(m.pos1[0], m.pos1[1]) +" return +" endif +" endfor + +" " reached start/end, jump to the end/start +" let initial_match = matches[0] +" if !has_key(initial_match, "group") +" return +" endif + +" if initial_match.group != "goSameId" +" return +" endif + +" call cursor(initial_match.pos1[0], initial_match.pos1[1]) +" endfunction + +" function! Sameids_search(mode) +" call go#guru#SameIds() +" call Sameids_repeat(a:mode) +" endfunction + +" function! Sameids_nohlsearch() +" call go#guru#ClearSameIds() +" return "nohlsearch" +" endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/ftplugin/go/commands.vim b/vim/bundle/go/ftplugin/go/commands.vim index 4c4f070..9b983ef 100644 --- a/vim/bundle/go/ftplugin/go/commands.vim +++ b/vim/bundle/go/ftplugin/go/commands.vim @@ -1,38 +1,59 @@ -" gorename -command! -nargs=? GoRename call go#rename#Rename(0,) - -" oracle -command! -nargs=* -complete=customlist,go#package#Complete GoOracleScope call go#oracle#Scope() -command! -range=% GoImplements call go#oracle#Implements() -command! -range=% GoCallees call go#oracle#Callees() -command! -range=% GoDescribe call go#oracle#Describe() -command! -range=% GoCallers call go#oracle#Callers() -command! -range=% GoCallstack call go#oracle#Callstack() -command! -range=% GoFreevars call go#oracle#Freevars() -command! -range=% GoChannelPeers call go#oracle#ChannelPeers() -command! -range=% GoReferrers call go#oracle#Referrers() -command! -nargs=? GoOracleTags call go#oracle#Tags() - -" tool -command! -nargs=0 GoFiles echo go#tool#Files() +" -- gorename +command! -nargs=? -complete=customlist,go#rename#Complete GoRename call go#rename#Rename(0, ) + +" -- guru +command! -nargs=* -complete=customlist,go#package#Complete GoGuruScope call go#guru#Scope() +command! -range=% GoImplements call go#guru#Implements() +command! -range=% GoWhicherrs call go#guru#Whicherrs() +command! -range=% GoCallees call go#guru#Callees() +command! -range=% GoDescribe call go#guru#Describe() +command! -range=% GoCallers call go#guru#Callers() +command! -range=% GoCallstack call go#guru#Callstack() +command! -range=% GoFreevars call go#guru#Freevars() +command! -range=% GoChannelPeers call go#guru#ChannelPeers() +command! -range=% GoReferrers call go#guru#Referrers() + +command! -range=0 GoSameIds call go#guru#SameIds() +command! -range=0 GoSameIdsClear call go#guru#ClearSameIds() +command! -range=0 GoSameIdsToggle call go#guru#ToggleSameIds() +command! -range=0 GoSameIdsAutoToggle call go#guru#AutoToogleSameIds() + +" -- tags +command! -nargs=* -range GoAddTags call go#tags#Add(, , , ) +command! -nargs=* -range GoRemoveTags call go#tags#Remove(, , , ) + +" -- tool +command! -nargs=* -complete=customlist,go#tool#ValidFiles GoFiles echo go#tool#Files() command! -nargs=0 GoDeps echo go#tool#Deps() -command! -nargs=* GoInfo call go#complete#Info(0) +command! -nargs=* GoInfo call go#tool#Info(0) +command! -nargs=0 GoAutoTypeInfoToggle call go#complete#ToggleAutoTypeInfo() -" cmd +" -- cmd command! -nargs=* -bang GoBuild call go#cmd#Build(0,) +command! -nargs=? -bang GoBuildTags call go#cmd#BuildTags(0, ) command! -nargs=* -bang GoGenerate call go#cmd#Generate(0,) command! -nargs=* -bang -complete=file GoRun call go#cmd#Run(0,) command! -nargs=* -bang GoInstall call go#cmd#Install(0, ) -command! -nargs=* -bang GoTest call go#cmd#Test(0, 0, ) -command! -nargs=* -bang GoTestFunc call go#cmd#TestFunc(0, ) -command! -nargs=* -bang GoTestCompile call go#cmd#Test(0, 1, ) -command! -nargs=* -bang GoCoverage call go#cmd#Coverage(0, ) + +" -- test +command! -nargs=* -bang GoTest call go#test#Test(0, 0, ) +command! -nargs=* -bang GoTestFunc call go#test#Func(0, ) +command! -nargs=* -bang GoTestCompile call go#test#Test(0, 1, ) + +" -- cover +command! -nargs=* -bang GoCoverage call go#coverage#Buffer(0, ) +command! -nargs=* -bang GoCoverageClear call go#coverage#Clear() +command! -nargs=* -bang GoCoverageToggle call go#coverage#BufferToggle(0, ) +command! -nargs=* -bang GoCoverageBrowser call go#coverage#Browser(0, ) " -- play command! -nargs=0 -range=% GoPlay call go#play#Share(, , ) " -- def -command! -nargs=* -range GoDef :call go#def#Jump() +command! -nargs=* -range GoDef :call go#def#Jump('') +command! -nargs=? GoDefPop :call go#def#StackPop() +command! -nargs=? GoDefStack :call go#def#Stack() +command! -nargs=? GoDefStackClear :call go#def#StackClear() " -- doc command! -nargs=* -range -complete=customlist,go#package#Complete GoDoc call go#doc#Open('new', 'split', ) @@ -40,8 +61,12 @@ command! -nargs=* -range -complete=customlist,go#package#Complete GoDocBrowser c " -- fmt command! -nargs=0 GoFmt call go#fmt#Format(-1) +command! -nargs=0 GoFmtAutoSaveToggle call go#fmt#ToggleFmtAutoSave() command! -nargs=0 GoImports call go#fmt#Format(1) +" -- asmfmt +command! -nargs=0 GoAsmFmtAutoSaveToggle call go#asmfmt#ToggleAsmFmtAutoSave() + " -- import command! -nargs=? -complete=customlist,go#package#Complete GoDrop call go#import#SwitchImport(0, '', , '') command! -nargs=1 -bang -complete=customlist,go#package#Complete GoImport call go#import#SwitchImport(1, '', , '') @@ -49,6 +74,7 @@ command! -nargs=* -bang -complete=customlist,go#package#Complete GoImportAs call " -- linters command! -nargs=* GoMetaLinter call go#lint#Gometa(0, ) +command! -nargs=0 GoMetaLinterAutoSaveToggle call go#lint#ToggleMetaLinterAutoSave() command! -nargs=* GoLint call go#lint#Golint() command! -nargs=* -bang GoVet call go#lint#Vet(0, ) command! -nargs=* -complete=customlist,go#package#Complete GoErrCheck call go#lint#Errcheck() @@ -56,10 +82,20 @@ command! -nargs=* -complete=customlist,go#package#Complete GoErrCheck call go#li " -- alternate command! -bang GoAlternate call go#alternate#Switch(0, '') -" -- ctrlp -if globpath(&rtp, 'plugin/ctrlp.vim') != "" - command! -nargs=? -complete=file GoDecls call ctrlp#init(ctrlp#decls#cmd(0, )) - command! -nargs=? -complete=dir GoDeclsDir call ctrlp#init(ctrlp#decls#cmd(1, )) -endif +" -- decls +command! -nargs=? -complete=file GoDecls call go#decls#Decls(0, ) +command! -nargs=? -complete=dir GoDeclsDir call go#decls#Decls(1, ) + +" -- impl +command! -nargs=* -complete=customlist,go#impl#Complete GoImpl call go#impl#Impl() + +" -- template +command! -nargs=0 GoTemplateAutoCreateToggle call go#template#ToggleAutoCreate() + +" -- keyify +command! -nargs=0 GoKeyify call go#keyify#Keyify() + +" -- fillstruct +command! -nargs=0 GoFillStruct call go#fillstruct#FillStruct() -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/ftplugin/go/mappings.vim b/vim/bundle/go/ftplugin/go/mappings.vim index 2529a84..4cb8278 100644 --- a/vim/bundle/go/ftplugin/go/mappings.vim +++ b/vim/bundle/go/ftplugin/go/mappings.vim @@ -5,46 +5,58 @@ " which by default is enabled. For commands the user has the ability to pass " the '!', such as :GoBuild or :GoBuild! if !exists("g:go_jump_to_error") - let g:go_jump_to_error = 1 + let g:go_jump_to_error = 1 endif " Some handy plug mappings nnoremap (go-run) :call go#cmd#Run(!g:go_jump_to_error) if has("nvim") - nnoremap (go-run-vertical) :call go#cmd#RunTerm(!g:go_jump_to_error, 'vsplit', []) - nnoremap (go-run-split) :call go#cmd#RunTerm(!g:go_jump_to_error, 'split', []) - nnoremap (go-run-tab) :call go#cmd#RunTerm(!g:go_jump_to_error, 'tabe', []) + nnoremap (go-run-vertical) :call go#cmd#RunTerm(!g:go_jump_to_error, 'vsplit', []) + nnoremap (go-run-split) :call go#cmd#RunTerm(!g:go_jump_to_error, 'split', []) + nnoremap (go-run-tab) :call go#cmd#RunTerm(!g:go_jump_to_error, 'tabe', []) endif nnoremap (go-build) :call go#cmd#Build(!g:go_jump_to_error) nnoremap (go-generate) :call go#cmd#Generate(!g:go_jump_to_error) nnoremap (go-install) :call go#cmd#Install(!g:go_jump_to_error) -nnoremap (go-test) :call go#cmd#Test(!g:go_jump_to_error, 0) -nnoremap (go-test-func) :call go#cmd#TestFunc(!g:go_jump_to_error) -nnoremap (go-test-compile) :call go#cmd#Test(!g:go_jump_to_error, 1) -nnoremap (go-coverage) :call go#cmd#Coverage(!g:go_jump_to_error) +nnoremap (go-test) :call go#test#Test(!g:go_jump_to_error, 0) +nnoremap (go-test-func) :call go#test#Func(!g:go_jump_to_error) +nnoremap (go-test-compile) :call go#test#Test(!g:go_jump_to_error, 1) + +nnoremap (go-coverage) :call go#coverage#Buffer(!g:go_jump_to_error) +nnoremap (go-coverage-clear) :call go#coverage#Clear() +nnoremap (go-coverage-toggle) :call go#coverage#BufferToggle(!g:go_jump_to_error) +nnoremap (go-coverage-browser) :call go#coverage#Browser(!g:go_jump_to_error) nnoremap (go-files) :call go#tool#Files() nnoremap (go-deps) :call go#tool#Deps() -nnoremap (go-info) :call go#complete#Info(0) +nnoremap (go-info) :call go#tool#Info(0) nnoremap (go-import) :call go#import#SwitchImport(1, '', expand(''), '') +nnoremap (go-imports) :call go#fmt#Format(1) -nnoremap (go-implements) :call go#oracle#Implements(-1) -nnoremap (go-callees) :call go#oracle#Callees(-1) -nnoremap (go-callers) :call go#oracle#Callers(-1) -nnoremap (go-describe) :call go#oracle#Describe(-1) -nnoremap (go-callstack) :call go#oracle#Callstack(-1) -nnoremap (go-freevars) :call go#oracle#Freevars(-1) -nnoremap (go-channelpeers) :call go#oracle#ChannelPeers(-1) -nnoremap (go-referrers) :call go#oracle#Referrers(-1) +nnoremap (go-implements) :call go#guru#Implements(-1) +nnoremap (go-callees) :call go#guru#Callees(-1) +nnoremap (go-callers) :call go#guru#Callers(-1) +nnoremap (go-describe) :call go#guru#Describe(-1) +nnoremap (go-callstack) :call go#guru#Callstack(-1) +xnoremap (go-freevars) :call go#guru#Freevars(0) +nnoremap (go-channelpeers) :call go#guru#ChannelPeers(-1) +nnoremap (go-referrers) :call go#guru#Referrers(-1) +nnoremap (go-sameids) :call go#guru#SameIds() +nnoremap (go-whicherrs) :call go#guru#Whicherrs(-1) +nnoremap (go-sameids-toggle) :call go#guru#ToggleSameIds() nnoremap (go-rename) :call go#rename#Rename(!g:go_jump_to_error) -nnoremap (go-def) :call go#def#Jump() -nnoremap (go-def-vertical) :call go#def#JumpMode("vsplit") -nnoremap (go-def-split) :call go#def#JumpMode("split") -nnoremap (go-def-tab) :call go#def#JumpMode("tab") +nnoremap (go-def) :call go#def#Jump('') +nnoremap (go-def-vertical) :call go#def#Jump("vsplit") +nnoremap (go-def-split) :call go#def#Jump("split") +nnoremap (go-def-tab) :call go#def#Jump("tab") + +nnoremap (go-def-pop) :call go#def#StackPop() +nnoremap (go-def-stack) :call go#def#Stack() +nnoremap (go-def-stack-clear) :call go#def#StackClear() nnoremap (go-doc) :call go#doc#Open("new", "split") nnoremap (go-doc-tab) :call go#doc#Open("tabnew", "tabe") @@ -53,8 +65,11 @@ nnoremap (go-doc-split) :call go#doc#Open("new", "split") (go-doc-browser) :call go#doc#OpenBrowser() nnoremap (go-metalinter) :call go#lint#Gometa(0) +nnoremap (go-lint) :call go#lint#Golint() nnoremap (go-vet) :call go#lint#Vet(!g:go_jump_to_error) nnoremap (go-alternate-edit) :call go#alternate#Switch(0, "edit") nnoremap (go-alternate-vertical) :call go#alternate#Switch(0, "vsplit") nnoremap (go-alternate-split) :call go#alternate#Switch(0, "split") + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/ftplugin/go/snippets.vim b/vim/bundle/go/ftplugin/go/snippets.vim index 3bf13d2..33a3cd5 100644 --- a/vim/bundle/go/ftplugin/go/snippets.vim +++ b/vim/bundle/go/ftplugin/go/snippets.vim @@ -3,44 +3,65 @@ if exists("g:go_loaded_gosnippets") 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() abort + if get(g:, 'did_plugin_ultisnips') isnot 1 + return + endif + + if !exists("g:UltiSnipsSnippetDirectories") + let g:UltiSnipsSnippetDirectories = ["gosnippets/UltiSnips"] + else + let g:UltiSnipsSnippetDirectories += ["gosnippets/UltiSnips"] + endif +endfunction + +function! s:GoNeosnippet() abort + if get(g:, 'loaded_neosnippet') isnot 1 + return + endif -function! s:GoUltiSnips() - if globpath(&rtp, 'plugin/UltiSnips.vim') == "" - return - endif + let g:neosnippet#enable_snipmate_compatibility = 1 - if !exists("g:UltiSnipsSnippetDirectories") - let g:UltiSnipsSnippetDirectories = ["gosnippets/UltiSnips"] - else - let g:UltiSnipsSnippetDirectories += ["gosnippets/UltiSnips"] - endif + let l:gosnippets_dir = globpath(&rtp, 'gosnippets/snippets') + if type(g:neosnippet#snippets_directory) == type([]) + let g:neosnippet#snippets_directory += [l:gosnippets_dir] + elseif type(g:neosnippet#snippets_directory) == type("") + if strlen(g:neosnippet#snippets_directory) > 0 + let g:neosnippet#snippets_directory = g:neosnippet#snippets_directory . "," . l:gosnippets_dir + else + let g:neosnippet#snippets_directory = l:gosnippets_dir + endif + endif endfunction -function! s:GoNeosnippet() - if globpath(&rtp, 'plugin/neosnippet.vim') == "" - return - endif - - let g:neosnippet#enable_snipmate_compatibility = 1 - - let gosnippets_dir = globpath(&rtp, 'gosnippets/snippets') - if type(g:neosnippet#snippets_directory) == type([]) - let g:neosnippet#snippets_directory += [gosnippets_dir] - elseif type(g:neosnippet#snippets_directory) == type("") - if strlen(g:neosnippet#snippets_directory) > 0 - let g:neosnippet#snippets_directory = g:neosnippet#snippets_directory . "," . gosnippets_dir - else - let g:neosnippet#snippets_directory = gosnippets_dir - endif - endif +function! s:GoMinisnip() abort + if get(g:, 'loaded_minisnip') isnot 1 + return + endif + + if exists('g:minisnip_dir') + let g:minisnip_dir .= ':' . globpath(&rtp, 'gosnippets/minisnip') + else + let g:minisnip_dir = globpath(&rtp, 'gosnippets/minisnip') + endif endfunction -if g:go_snippet_engine == "ultisnips" - call s:GoUltiSnips() -elseif g:go_snippet_engine == "neosnippet" - call s:GoNeosnippet() + +let s:engine = get(g:, 'go_snippet_engine', 'automatic') +if s:engine is? "automatic" + if get(g:, 'did_plugin_ultisnips') is 1 + call s:GoUltiSnips() + elseif get(g:, 'loaded_neosnippet') is 1 + call s:GoNeosnippet() + elseif get(g:, 'loaded_minisnip') is 1 + call s:GoMinisnip() + endif +elseif s:engine is? "ultisnips" + call s:GoUltiSnips() +elseif s:engine is? "neosnippet" + call s:GoNeosnippet() +elseif s:engine is? "minisnip" + call s:GoMinisnip() endif + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/ftplugin/go/tagbar.vim b/vim/bundle/go/ftplugin/go/tagbar.vim index ca5b184..2d22fd7 100644 --- a/vim/bundle/go/ftplugin/go/tagbar.vim +++ b/vim/bundle/go/ftplugin/go/tagbar.vim @@ -4,52 +4,54 @@ " Also make sure the ctags command exists " if !executable('ctags') - finish + finish elseif globpath(&rtp, 'plugin/tagbar.vim') == "" - finish + finish endif if !exists("g:go_gotags_bin") - let g:go_gotags_bin = "gotags" + let g:go_gotags_bin = "gotags" endif function! s:SetTagbar() - let bin_path = go#path#CheckBinPath(g:go_gotags_bin) - if empty(bin_path) - return - endif + let bin_path = go#path#CheckBinPath(g:go_gotags_bin) + if empty(bin_path) + return + endif - if !exists("g:tagbar_type_go") - let g:tagbar_type_go = { - \ 'ctagstype' : 'go', - \ 'kinds' : [ - \ 'p:package', - \ 'i:imports', - \ 'c:constants', - \ 'v:variables', - \ 't:types', - \ 'n:interfaces', - \ 'w:fields', - \ 'e:embedded', - \ 'm:methods', - \ 'r:constructor', - \ 'f:functions' - \ ], - \ 'sro' : '.', - \ 'kind2scope' : { - \ 't' : 'ctype', - \ 'n' : 'ntype' - \ }, - \ 'scope2kind' : { - \ 'ctype' : 't', - \ 'ntype' : 'n' - \ }, - \ 'ctagsbin' : expand(bin_path), - \ 'ctagsargs' : '-sort -silent' - \ } - endif + if !exists("g:tagbar_type_go") + let g:tagbar_type_go = { + \ 'ctagstype' : 'go', + \ 'kinds' : [ + \ 'p:package', + \ 'i:imports', + \ 'c:constants', + \ 'v:variables', + \ 't:types', + \ 'n:interfaces', + \ 'w:fields', + \ 'e:embedded', + \ 'm:methods', + \ 'r:constructor', + \ 'f:functions' + \ ], + \ 'sro' : '.', + \ 'kind2scope' : { + \ 't' : 'ctype', + \ 'n' : 'ntype' + \ }, + \ 'scope2kind' : { + \ 'ctype' : 't', + \ 'ntype' : 'n' + \ }, + \ 'ctagsbin' : bin_path, + \ 'ctagsargs' : '-sort -silent' + \ } + endif endfunction call s:SetTagbar() + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/ftplugin/gohtmltmpl.vim b/vim/bundle/go/ftplugin/gohtmltmpl.vim new file mode 100644 index 0000000..ea0aacf --- /dev/null +++ b/vim/bundle/go/ftplugin/gohtmltmpl.vim @@ -0,0 +1,7 @@ +if exists("b:did_ftplugin") + finish +endif + +runtime! ftplugin/html.vim + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/gosnippets/UltiSnips/go.snippets b/vim/bundle/go/gosnippets/UltiSnips/go.snippets index d08d72e..1a78a04 100644 --- a/vim/bundle/go/gosnippets/UltiSnips/go.snippets +++ b/vim/bundle/go/gosnippets/UltiSnips/go.snippets @@ -110,7 +110,7 @@ snippet gpl * 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")` +* Copyright (C) ${1:Author}, `!v strftime("%Y")` */ ${0} endsnippet @@ -143,6 +143,13 @@ else { } endsnippet +# if inline error +snippet ife "If with inline erro" +if err := ${1:condition}; err != nil { + ${0:${VISUAL}} +} +endsnippet + # error snippet snippet errn "Error return " !b if err != nil { @@ -151,14 +158,15 @@ if err != nil { ${0} endsnippet -# error snippet -snippet errt "Error test fatal " !b +# error log snippet +snippet errl "Error with log.Fatal(err)" !b if err != nil { - t.Fatal(err) + log.Fatal(err) } ${0} endsnippet +# error multiple return snippet errn, "Error return with two return values" !b if err != nil { return ${1:nil}, err @@ -166,6 +174,23 @@ if err != nil { ${0} endsnippet +# error panic +snippet errp "Error panic" !b +if err != nil { + panic(${1}) +} +${0} +endsnippet + +# error test +snippet errt "Error test fatal " !b +if err != nil { + t.Fatal(err) +} +${0} +endsnippet + +# error handle snippet errh "Error handle and return" !b if err != nil { ${1} @@ -174,8 +199,14 @@ if err != nil { ${0} endsnippet +# json field tag snippet json "\`json:key\`" -\`json:"${1:keyName}"\` +\`json:"${1:`!v go#util#snippetcase(matchstr(getline("."), '\w\+'))`}"\` +endsnippet + +# yaml field tag +snippet yaml "\`yaml:key\`" +\`yaml:"${1:`!v go#util#snippetcase(matchstr(getline("."), '\w\+'))`}"\` endsnippet # fallthrough @@ -327,6 +358,18 @@ func Test${1:Function}(t *testing.T) { } endsnippet +snippet hf "http.HandlerFunc" !b +func ${1:handler}(w http.ResponseWriter, r *http.Request) { + ${0:fmt.Fprintf(w, "hello world")} +} +endsnippet + +snippet hhf "mux.HandleFunc" !b +${1:http}.HandleFunc("${2:/}", func(w http.ResponseWriter, r *http.Request) { + ${0:fmt.Fprintf(w, "hello world")} +}) +endsnippet + # quick test server snippet tsrv "httptest.NewServer" ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -351,6 +394,21 @@ if err != nil { } endsnippet +snippet example "func ExampleXYZ() { ... }" +func Example${1:Method}() { + ${0:${VISUAL}} + // Output: +} +endsnippet + +snippet benchmark "func BenchmarkXYZ(b *testing.B) { ... }" +func Benchmark${1:Method}(b *testing.B) { + for i := 0; i < b.N; i++ { + ${0:${VISUAL}} + } +} +endsnippet + # variable declaration snippet var "var x Type [= ...]" var ${1:x} ${2:Type}${3: = ${0:value}} @@ -372,7 +430,6 @@ if !reflect.DeepEqual(${1:expected}, ${2:actual}) { } endsnippet - global !p import re diff --git a/vim/bundle/go/gosnippets/minisnip/_go_eq b/vim/bundle/go/gosnippets/minisnip/_go_eq new file mode 100644 index 0000000..9e8fad0 --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_eq @@ -0,0 +1,3 @@ +if !reflect.DeepEqual({{+got+}}, {{+want+}}) { + t.Errorf("\ngot: %#v\nwant: %#v\n", {{+~\~2+}}, {{+~\~2+}}) +} diff --git a/vim/bundle/go/gosnippets/minisnip/_go_err b/vim/bundle/go/gosnippets/minisnip/_go_err new file mode 100644 index 0000000..3a5a493 --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_err @@ -0,0 +1,3 @@ +if err != nil { + return {{+err+}} +} diff --git a/vim/bundle/go/gosnippets/minisnip/_go_errt b/vim/bundle/go/gosnippets/minisnip/_go_errt new file mode 100644 index 0000000..c5cb6fc --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_errt @@ -0,0 +1,4 @@ +if err != nil { + t.Fatal(err) +} +{{++}} diff --git a/vim/bundle/go/gosnippets/minisnip/_go_errw b/vim/bundle/go/gosnippets/minisnip/_go_errw new file mode 100644 index 0000000..d1958d5 --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_errw @@ -0,0 +1,3 @@ +if err != nil { + return errors.Wrap(err, "{{++}}") +} diff --git a/vim/bundle/go/gosnippets/minisnip/_go_f b/vim/bundle/go/gosnippets/minisnip/_go_f new file mode 100644 index 0000000..6e35d24 --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_f @@ -0,0 +1,3 @@ +// {{++}} +func {{+~\~1+}}() { +} diff --git a/vim/bundle/go/gosnippets/minisnip/_go_ff b/vim/bundle/go/gosnippets/minisnip/_go_ff new file mode 100644 index 0000000..ed942c2 --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_ff @@ -0,0 +1 @@ +fmt.Printf("%#v\n", {{++}}) diff --git a/vim/bundle/go/gosnippets/minisnip/_go_fori b/vim/bundle/go/gosnippets/minisnip/_go_fori new file mode 100644 index 0000000..8a52ec9 --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_fori @@ -0,0 +1,3 @@ +for i := 0; i < {{++}}; i++ { + {{++}} +} diff --git a/vim/bundle/go/gosnippets/minisnip/_go_pkg b/vim/bundle/go/gosnippets/minisnip/_go_pkg new file mode 100644 index 0000000..2491d9a --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_pkg @@ -0,0 +1,2 @@ +// Package {{+~expand('%:p:h:t')+}} {{++}} +package {{+~\~2+}} diff --git a/vim/bundle/go/gosnippets/minisnip/_go_sp b/vim/bundle/go/gosnippets/minisnip/_go_sp new file mode 100644 index 0000000..b392681 --- /dev/null +++ b/vim/bundle/go/gosnippets/minisnip/_go_sp @@ -0,0 +1,2 @@ +fmt.Sprintf("{{++}}", {{++}}) + diff --git a/vim/bundle/go/gosnippets/snippets/go.snip b/vim/bundle/go/gosnippets/snippets/go.snip index f60aeb5..9a480b0 100644 --- a/vim/bundle/go/gosnippets/snippets/go.snip +++ b/vim/bundle/go/gosnippets/snippets/go.snip @@ -118,6 +118,14 @@ snippet else else { ${0} } + +# if inline error +snippet ife +abbr if err := ...; err != nil { ... } + if err := ${1:condition}; err != nil { + ${0} + } + # error snippet snippet errn abbr if err != nil { ... } @@ -132,6 +140,13 @@ abbr if err != nil { ... } t.Fatal(err) } +# error snippet in log.Fatal +snippet errl +abbr if err != nil { ... } + if err != nil { + log.Fatal(err) + } + # error snippet with two return values snippet errn, abbr if err != nil { return [...], err } @@ -149,11 +164,24 @@ abbr if err != nil { return } } ${0} +# error snippet with panic +snippet errp +abbr if err != nil { ... } + if err != nil { + panic(${1}) + } + ${0} + # json snippet snippet json abbr \`json:key\` \`json:"${1:keyName}"\` +# yaml snippet +snippet yaml +abbr \`yaml:key\` + \`yaml:"${1:keyName}"\` + # fallthrough snippet ft abbr fallthrough @@ -305,6 +333,19 @@ abbr if err != nil { t.Fatalf(...) } if err != nil { t.Fatalf("${1}") } +# test example +snippet example + func Example${1:Method}() { + ${0} + // Output: + } +# test benchmark +snippet benchmark + func Benchmark${1:Method}(b *testing.B) { + for i := 0; i < b.N; i++ { + ${0} + } + } # variable declaration snippet var abbr var x Type [= ...] @@ -323,3 +364,15 @@ abbr equals: test two identifiers with DeepEqual fmt.Printf("%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\n\n", filepath.Base(file), line, $1, $2) t.FailNow() } + +snippet hf +abbr http.HandlerFunc + func ${1:handler}(w http.ResponseWriter, r *http.Request) { + ${0:fmt.Fprintf(w, "hello world")} + } + +snippet hhf +abbr mux.HandleFunc(...) + ${1:http}.HandleFunc("${2:/}", func(w http.ResponseWriter, r *http.Request) { + ${0:fmt.Fprintf(w, "hello world")} + }) diff --git a/vim/bundle/go/indent/go.vim b/vim/bundle/go/indent/go.vim index a3fa2b7..ba99d54 100644 --- a/vim/bundle/go/indent/go.vim +++ b/vim/bundle/go/indent/go.vim @@ -9,7 +9,7 @@ " - general line splits (line ends in an operator) if exists("b:did_indent") - finish + finish endif let b:did_indent = 1 @@ -21,58 +21,58 @@ setlocal indentexpr=GoIndent(v:lnum) setlocal indentkeys+=<:>,0=},0=) if exists("*GoIndent") - finish + finish endif " use shiftwidth function only if it's available if exists('*shiftwidth') - func s:sw() - return shiftwidth() - endfunc + func s:sw() + return shiftwidth() + endfunc else - func s:sw() - return &sw - endfunc + func s:sw() + return &sw + endfunc endif function! GoIndent(lnum) - let prevlnum = prevnonblank(a:lnum-1) - if prevlnum == 0 - " top of file - return 0 - endif + 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) + " 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 + let ind = previ - if prevl =~ '[({]\s*$' - " previous line opened a block - let ind += s:sw() - endif - if prevl =~# '^\s*\(case .*\|default\):$' - " previous line is part of a switch statement - let ind += s:sw() - endif - " TODO: handle if the previous line is a label. + if prevl =~ '[({]\s*$' + " previous line opened a block + let ind += s:sw() + endif + if prevl =~# '^\s*\(case .*\|default\):$' + " previous line is part of a switch statement + let ind += s:sw() + endif + " TODO: handle if the previous line is a label. - if thisl =~ '^\s*[)}]' - " this line closed a block - let ind -= s:sw() - endif + if thisl =~ '^\s*[)}]' + " this line closed a block + let ind -= s:sw() + endif - " Colons are tricky. - " We want to outdent if it's part of a switch ("case foo:" or "default:"). - " We ignore trying to deal with jump labels because (a) they're rare, and - " (b) they're hard to disambiguate from a composite literal key. - if thisl =~# '^\s*\(case .*\|default\):$' - let ind -= s:sw() - endif + " Colons are tricky. + " We want to outdent if it's part of a switch ("case foo:" or "default:"). + " We ignore trying to deal with jump labels because (a) they're rare, and + " (b) they're hard to disambiguate from a composite literal key. + if thisl =~# '^\s*\(case .*\|default\):$' + let ind -= s:sw() + endif - return ind + return ind endfunction -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/indent/gohtmltmpl.vim b/vim/bundle/go/indent/gohtmltmpl.vim index 94ea135..864913d 100644 --- a/vim/bundle/go/indent/gohtmltmpl.vim +++ b/vim/bundle/go/indent/gohtmltmpl.vim @@ -30,15 +30,17 @@ function! GetGoHTMLTmplIndent(lnum) " If need to indent based on last line let last_line = getline(a:lnum-1) - if last_line =~ '^\s*{{\s*\%(if\|else\|range\|with\|define\|block\).*}}' + if last_line =~ '^\s*{{-\=\s*\%(if\|else\|range\|with\|define\|block\).*}}' let ind += sw endif " End of FuncMap block let current_line = getline(a:lnum) - if current_line =~ '^\s*{{\s*\%(else\|end\).*}}' + if current_line =~ '^\s*{{-\=\s*\%(else\|end\).*}}' let ind -= sw endif return ind endfunction + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/plugin/go.vim b/vim/bundle/go/plugin/go.vim index e03565c..bb88608 100644 --- a/vim/bundle/go/plugin/go.vim +++ b/vim/bundle/go/plugin/go.vim @@ -1,173 +1,278 @@ " install necessary Go tools if exists("g:go_loaded_install") - finish + finish endif let g:go_loaded_install = 1 +" Not using the has('patch-7.4.1689') syntax because that wasn't added until +" 7.4.237, and we want to be sure this works for everyone (this is also why +" we're not using utils#EchoError()). +" +" Version 7.4.1689 was chosen because that's what the most recent Ubuntu LTS +" release (16.04) uses. +if + \ get(g:, 'go_version_warning', 1) != 0 && + \ (v:version < 704 || (v:version == 704 && !has('patch1689'))) + \ && !has('nvim') + echohl Error + echom "vim-go requires Vim 7.4.1689 or Neovim, but you're using an older version." + echom "Please update your Vim for the best vim-go experience." + echom "If you really want to continue you can set this to make the error go away:" + echom " let g:go_version_warning = 0" + echom "Note that some features may error out or behave incorrectly." + echom "Please do not report bugs unless you're using Vim 7.4.1689 or newer." + echohl None + + " Make sure people see this. + sleep 2 +endif " these packages are used by vim-go and can be automatically installed if " needed by the user with GoInstallBinaries -let s:packages = [ - \ "github.com/nsf/gocode", - \ "github.com/alecthomas/gometalinter", - \ "golang.org/x/tools/cmd/goimports", - \ "github.com/rogpeppe/godef", - \ "golang.org/x/tools/cmd/oracle", - \ "golang.org/x/tools/cmd/gorename", - \ "github.com/golang/lint/golint", - \ "github.com/kisielk/errcheck", - \ "github.com/jstemmer/gotags", - \ "github.com/klauspost/asmfmt/cmd/asmfmt", - \ "github.com/fatih/motion", - \ ] +let s:packages = { + \ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt'], + \ 'errcheck': ['github.com/kisielk/errcheck'], + \ 'fillstruct': ['github.com/davidrjenni/reftools/cmd/fillstruct'], + \ 'gocode': ['github.com/nsf/gocode', {'windows': '-ldflags -H=windowsgui'}], + \ 'godef': ['github.com/rogpeppe/godef'], + \ 'gogetdoc': ['github.com/zmb3/gogetdoc'], + \ 'goimports': ['golang.org/x/tools/cmd/goimports'], + \ 'golint': ['github.com/golang/lint/golint'], + \ 'gometalinter': ['github.com/alecthomas/gometalinter'], + \ 'gomodifytags': ['github.com/fatih/gomodifytags'], + \ 'gorename': ['golang.org/x/tools/cmd/gorename'], + \ 'gotags': ['github.com/jstemmer/gotags'], + \ 'guru': ['golang.org/x/tools/cmd/guru'], + \ 'impl': ['github.com/josharian/impl'], + \ 'keyify': ['github.com/dominikh/go-tools/cmd/keyify'], + \ 'motion': ['github.com/fatih/motion'], +\ } " These commands are available on any filetypes -command! GoInstallBinaries call s:GoInstallBinaries(-1) -command! GoUpdateBinaries call s:GoInstallBinaries(1) +command! -nargs=* -complete=customlist,s:complete GoInstallBinaries call s:GoInstallBinaries(-1, ) +command! -nargs=* -complete=customlist,s:complete GoUpdateBinaries call s:GoInstallBinaries(1, ) command! -nargs=? -complete=dir GoPath call go#path#GoPath() - -" GoInstallBinaries downloads and install all necessary binaries stated in the -" packages variable. It uses by default $GOBIN or $GOPATH/bin as the binary -" target install directory. GoInstallBinaries doesn't install binaries if they -" exist, to update current binaries pass 1 to the argument. -function! s:GoInstallBinaries(updateBinaries) - if $GOPATH == "" - echohl Error - echomsg "vim.go: $GOPATH is not set" - echohl None - return - endif - - let err = s:CheckBinaries() - if err != 0 +fun! s:complete(lead, cmdline, cursor) + return filter(keys(s:packages), 'strpart(v:val, 0, len(a:lead)) == a:lead') +endfun + +" GoInstallBinaries downloads and installs binaries defined in s:packages to +" $GOBIN or $GOPATH/bin. GoInstallBinaries will update already installed +" binaries only if updateBinaries = 1. By default, all packages in s:packages +" will be installed, but the set can be limited by passing the desired +" packages in the unnamed arguments. +function! s:GoInstallBinaries(updateBinaries, ...) + let err = s:CheckBinaries() + if err != 0 + return + endif + + if go#path#Default() == "" + echohl Error + echomsg "vim.go: $GOPATH is not set and 'go env GOPATH' returns empty" + echohl None + return + endif + + let go_bin_path = go#path#BinPath() + + " change $GOBIN so go get can automatically install to it + let $GOBIN = go_bin_path + + " old_path is used to restore users own path + let old_path = $PATH + + " vim's executable path is looking in PATH so add our go_bin path to it + let $PATH = go_bin_path . go#util#PathListSep() . $PATH + + " when shellslash is set on MS-* systems, shellescape puts single quotes + " around the output string. cmd on Windows does not handle single quotes + " correctly. Unsetting shellslash forces shellescape to use double quotes + " instead. + let resetshellslash = 0 + if has('win32') && &shellslash + let resetshellslash = 1 + set noshellslash + endif + + let cmd = "go get -v " + if get(g:, "go_get_update", 1) != 0 + let cmd .= "-u " + endif + + let s:go_version = matchstr(go#util#System("go version"), '\d.\d.\d') + + " https://github.com/golang/go/issues/10791 + if s:go_version > "1.4.0" && s:go_version < "1.5.0" + let cmd .= "-f " + endif + + " Filter packages from arguments (if any). + let l:packages = {} + if a:0 > 0 + for l:bin in a:000 + let l:pkg = get(s:packages, l:bin, []) + if len(l:pkg) == 0 + call go#util#EchoError('unknown binary: ' . l:bin) return - endif - - let go_bin_path = go#path#BinPath() + endif + let l:packages[l:bin] = l:pkg + endfor + else + let l:packages = s:packages + endif - " change $GOBIN so go get can automatically install to it - let $GOBIN = go_bin_path + let l:platform = '' + if go#util#IsWin() + let l:platform = 'windows' + endif - " old_path is used to restore users own path - let old_path = $PATH + for [binary, pkg] in items(l:packages) + let l:importPath = pkg[0] + let l:goGetFlags = len(pkg) > 1 ? get(pkg[1], l:platform, '') : '' - " vim's executable path is looking in PATH so add our go_bin path to it - let $PATH = $PATH . go#util#PathListSep() .go_bin_path + let binname = "go_" . binary . "_bin" - " when shellslash is set on MS-* systems, shellescape puts single quotes - " around the output string. cmd on Windows does not handle single quotes - " correctly. Unsetting shellslash forces shellescape to use double quotes - " instead. - let resetshellslash = 0 - if has('win32') && &shellslash - let resetshellslash = 1 - set noshellslash + let bin = binary + if exists("g:{binname}") + let bin = g:{binname} endif - let cmd = "go get -u -v " - - let s:go_version = matchstr(system("go version"), '\d.\d.\d') - - " https://github.com/golang/go/issues/10791 - if s:go_version > "1.4.0" && s:go_version < "1.5.0" - let cmd .= "-f " + if !executable(bin) || a:updateBinaries == 1 + if a:updateBinaries == 1 + echo "vim-go: Updating " . binary . ". Reinstalling ". importPath . " to folder " . go_bin_path + else + echo "vim-go: ". binary ." not found. Installing ". importPath . " to folder " . go_bin_path + endif + + let out = go#util#System(printf('%s %s %s', cmd, l:goGetFlags, shellescape(importPath))) + if go#util#ShellError() != 0 + echom "Error installing " . importPath . ": " . out + endif endif + endfor - for pkg in s:packages - let basename = fnamemodify(pkg, ":t") - let binname = "go_" . basename . "_bin" - - let bin = basename - if exists("g:{binname}") - let bin = g:{binname} - endif - - if !executable(bin) || a:updateBinaries == 1 - if a:updateBinaries == 1 - echo "vim-go: Updating ". basename .". Reinstalling ". pkg . " to folder " . go_bin_path - else - echo "vim-go: ". basename ." not found. Installing ". pkg . " to folder " . go_bin_path - endif - - - let out = system(cmd . shellescape(pkg)) - if v:shell_error - echo "Error installing ". pkg . ": " . out - endif - endif - endfor - - " restore back! - let $PATH = old_path - if resetshellslash - set shellslash - endif + " restore back! + let $PATH = old_path + if resetshellslash + set shellslash + endif endfunction " CheckBinaries checks if the necessary binaries to install the Go tool " commands are available. function! s:CheckBinaries() - if !executable('go') - echohl Error | echomsg "vim-go: go executable not found." | echohl None - return -1 - endif - - if !executable('git') - echohl Error | echomsg "vim-go: git executable not found." | echohl None - return -1 - endif + if !executable('go') + echohl Error | echomsg "vim-go: go executable not found." | echohl None + return -1 + endif + + if !executable('git') + echohl Error | echomsg "vim-go: git executable not found." | echohl None + return -1 + endif endfunction " Autocommands " ============================================================================ " function! s:echo_go_info() - if !exists('v:completed_item') || empty(v:completed_item) - return - endif - let item = v:completed_item + if !get(g:, "go_echo_go_info", 1) + return + endif - if !has_key(item, "info") - return - endif + if !exists('v:completed_item') || empty(v:completed_item) + return + endif + let item = v:completed_item - if empty(item.info) - return - endif + if !has_key(item, "info") + return + endif - redraws! | echo "vim-go: " | echohl Function | echon item.info | echohl None -endfunction + if empty(item.info) + return + endif -augroup vim-go - autocmd! + redraws! | echo "vim-go: " | echohl Function | echon item.info | echohl None +endfunction - " GoInfo automatic update - if get(g:, "go_auto_type_info", 0) - autocmd CursorHold *.go nested call go#complete#Info(1) - endif +function! s:auto_type_info() + " GoInfo automatic update + if get(g:, "go_auto_type_info", 0) + call go#tool#Info(1) + endif +endfunction - " Echo the identifier information when completion is done. Useful to see - " the signature of a function, etc... - if exists('##CompleteDone') - autocmd CompleteDone *.go nested call s:echo_go_info() - endif +function! s:auto_sameids() + " GoSameId automatic update + if get(g:, "go_auto_sameids", 0) + call go#guru#SameIds() + endif +endfunction - " Go code formatting on save - if get(g:, "go_fmt_autosave", 1) - autocmd BufWritePre *.go call go#fmt#Format(-1) - endif +function! s:fmt_autosave() + " Go code formatting on save + if get(g:, "go_fmt_autosave", 1) + call go#fmt#Format(-1) + endif +endfunction - " Go asm formatting on save - if get(g:, "go_asmfmt_autosave", 1) - autocmd BufWritePre *.s call go#asmfmt#Format() - endif +function! s:asmfmt_autosave() + " Go asm formatting on save + if get(g:, "go_asmfmt_autosave", 0) + call go#asmfmt#Format() + endif +endfunction - " run gometalinter on save - if get(g:, "go_metalinter_autosave", 0) - autocmd BufWritePost *.go call go#lint#Gometa(1) - endif -augroup END +function! s:metalinter_autosave() + " run gometalinter on save + if get(g:, "go_metalinter_autosave", 0) + call go#lint#Gometa(1) + endif +endfunction +function! s:template_autocreate() + " create new template from scratch + if get(g:, "go_template_autocreate", 1) + call go#template#create() + endif +endfunction -" vim:ts=4:sw=4:et +augroup vim-go + autocmd! + + autocmd CursorHold *.go call s:auto_type_info() + autocmd CursorHold *.go call s:auto_sameids() + + " Echo the identifier information when completion is done. Useful to see + " the signature of a function, etc... + if exists('##CompleteDone') + autocmd CompleteDone *.go call s:echo_go_info() + endif + + autocmd BufWritePre *.go call s:fmt_autosave() + autocmd BufWritePre *.s call s:asmfmt_autosave() + autocmd BufWritePost *.go call s:metalinter_autosave() + autocmd BufNewFile *.go call s:template_autocreate() + " clear SameIds when the buffer is unloaded so that loading another buffer + " in the same window doesn't highlight the most recently matched + " identifier's positions. + autocmd BufWinEnter *.go call go#guru#ClearSameIds() + + autocmd BufEnter *.go + \ if get(g:, 'go_autodetect_gopath', 0) && !exists('b:old_gopath') + \| let b:old_gopath = exists('$GOPATH') ? $GOPATH : -1 + \| let $GOPATH = go#path#Detect() + \| endif + autocmd BufLeave *.go + \ if exists('b:old_gopath') + \| if b:old_gopath isnot -1 + \| let $GOPATH = b:old_gopath + \| endif + \| unlet b:old_gopath + \| endif +augroup end + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/rplugin/python3/denite/source/decls.py b/vim/bundle/go/rplugin/python3/denite/source/decls.py new file mode 100644 index 0000000..68651a8 --- /dev/null +++ b/vim/bundle/go/rplugin/python3/denite/source/decls.py @@ -0,0 +1,93 @@ +# ============================================================================ +# FILE: decls.py +# AUTHOR: delphinus +# License: MIT license +# ============================================================================ + +import os +import subprocess +import json +import denite.util +from .base import Base + +DECLS_SYNTAX_HIGHLIGHT = [ + {'name': 'FilePath', 're': r'[^:]*\ze:', 'link': 'Comment'}, + {'name': 'Line', 're': r'\d\+\ze :', 'link': 'LineNr'}, + {'name': 'WholeFunction', 're': r'\vfunc %(\([^)]+\) )?[^(]+'}, + {'name': 'Function', 'parent': 'WholeFunction', + 're': r'\S\+\ze(', 'link': 'Function'}, + {'name': 'WholeType', 're': r'type \S\+'}, + {'name': 'Type', 'parent': 'WholeType', + 're': r'\v( )@<=\S+', 'link': 'Type'}, + {'name': 'Separator', 're': r':', 'conceal': True}, + {'name': 'SeparatorFunction', 'parent': 'WholeFunction', + 're': r'func ', 'conceal': True}, + {'name': 'SeparatorType', 'parent': 'WholeType', + 're': r'type ', 'conceal': True}, + ] + +class Source(Base): + + def __init__(self, vim): + super().__init__(vim) + + self.name = 'decls' + self.kind = 'file' + + def gather_candidates(self, context): + bin_path = self.vim.call('go#path#CheckBinPath', 'motion') + if bin_path == '': + return [] + + expand = context['args'][0] if context['args'] else '%:p:h' + target = self.vim.funcs.expand(expand) + + if os.path.isdir(target): + mode = 'dir' + elif os.path.isfile(target): + mode = 'file' + else: + return [] + + if self.vim.funcs.exists('g:go_decls_includes'): + include = self.vim.eval('g:go_decls_includes') + else: + include = 'func,type' + + command = [bin_path, '-mode', 'decls', '-include', include, + '-' + mode, target] + + try: + cmd = subprocess.run(command, stdout=subprocess.PIPE, check=True) + except subprocess.CalledProcessError as err: + denite.util.error(self.vim, + 'command returned invalid response: ' + str(err)) + return [] + + txt = cmd.stdout.decode('utf-8') + output = json.loads(txt, encoding='utf-8') + + def make_candidates(row): + name = self.vim.funcs.fnamemodify(row['filename'], ':~:.') + return { + 'word': '{0} :{1} :{2}'.format(name, row['line'], row['full']), + 'action__path': row['filename'], + 'action__line': row['line'], + 'action__col': row['col'], + } + return list(map(make_candidates, output['decls'])) + + def highlight(self): + for syn in DECLS_SYNTAX_HIGHLIGHT: + containedin = self.syntax_name + containedin += '_' + syn['parent'] if 'parent' in syn else '' + conceal = ' conceal' if 'conceal' in syn else '' + + self.vim.command( + 'syntax match {0}_{1} /{2}/ contained containedin={3}{4}' + .format(self.syntax_name, syn['name'], syn['re'], + containedin, conceal)) + + if 'link' in syn: + self.vim.command('highlight default link {0}_{1} {2}'.format( + self.syntax_name, syn['name'], syn['link'])) diff --git a/vim/bundle/go/scripts/docker-test b/vim/bundle/go/scripts/docker-test new file mode 100755 index 0000000..2b56ad8 --- /dev/null +++ b/vim/bundle/go/scripts/docker-test @@ -0,0 +1,13 @@ +#!/bin/sh +# +# Run all tests inside a Docker container +# + +set -euC +vimgodir=$(cd -P "$(dirname "$0")/.." > /dev/null && pwd) +cd "$vimgodir" + +docker build --tag vim-go-test . +docker run --rm vim-go-test + +# vim:ts=2:sts=2:sw=2:et diff --git a/vim/bundle/go/scripts/install-vim b/vim/bundle/go/scripts/install-vim new file mode 100755 index 0000000..7b4e7a4 --- /dev/null +++ b/vim/bundle/go/scripts/install-vim @@ -0,0 +1,112 @@ +#!/bin/sh +# +# Install and setup a Vim or Neovim for running tests. +# This should work on both Travis and people's desktop computers, and be 100% +# independent from any system installed Vim. +# +# It will echo the full path to a Vim binary, e.g.: +# /some/path/src/vim + +set -euC + +vimgodir=$(cd -P "$(dirname "$0")/.." > /dev/null && pwd) +cd "$vimgodir" + +vim=${1:-} + +case "$vim" in + "vim-7.4") + # This is what the most recent Ubuntu LTS (16.04) ships with. + tag="v7.4.1689" + giturl="https://github.com/vim/vim" + ;; + + "vim-8.0") + # This follows the version in Arch Linux. Vim's master branch isn't always + # stable, and we don't want to have the build fail because Vim introduced a + # bug. + tag="v8.0.1176" + giturl="https://github.com/vim/vim" + ;; + + "nvim") + # Use latest stable version. + tag="v0.2.0" + giturl="https://github.com/neovim/neovim" + ;; + + *) + echo "unknown version: '${1:-}'" + echo "First argument must be 'vim-7.4', 'vim-8.0', or 'nvim'." + exit 1 + ;; +esac + +srcdir="/tmp/vim-go-test/$1-src" +installdir="/tmp/vim-go-test/$1-install" + +# Use cached installdir. +if [ -d "$installdir" ]; then + echo "$installdir exists; skipping build." + + # The ./scripts/test script relies on this. + echo "installed to: $installdir" + exit 0 +fi + +mkdir -p "$srcdir" +cd "$srcdir" + +# Neovim build requires more deps than Vim and is annoying, so we use the +# binary. +# 0.2.0 doesn't have a binary build for Linux, so we use 0.2.1-dev for now. +if [ "$1" = "nvim" ]; then + + # TODO: Use macOS binaries on macOS + curl -Ls https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz | + tar xzf - -C /tmp/vim-go-test/ + mv /tmp/vim-go-test/nvim-linux64 /tmp/vim-go-test/nvim-install + mkdir -p "$installdir/share/nvim/runtime/pack/vim-go/start" + ln -s "$vimgodir" "$installdir/share/nvim/runtime/pack/vim-go/start/vim-go" + + # Consistent paths makes calling things easier. + mv "$installdir/bin/nvim" "$installdir/bin/vim" + mkdir -p "$installdir/share/vim/vimgo/pack" + ln -s "$installdir/share/nvim/runtime/pack/vim-go" "$installdir/share/vim/vimgo/pack/vim-go" + +# Build Vim from source. +else + if [ -d "$srcdir/.git" ]; then + echo "Skipping clone as $srcdir/.git exists" + else + echo "Cloning $tag from $giturl" + git clone --branch "$tag" --depth 1 "$giturl" "$srcdir" + fi + + ./configure --prefix="$installdir" --with-features=huge --disable-gui + make install + mkdir -p "$installdir/share/vim/vimgo/pack/vim-go/start" + ln -s "$vimgodir" "$installdir/share/vim/vimgo/pack/vim-go/start/vim-go" +fi + +# Make sure all Go tools and other dependencies are installed. +echo "Installing Go binaries" +export GOPATH=$installdir +export PATH=${GOPATH}/bin:$PATH +"$vimgodir/scripts/run-vim" $vim +':silent :GoUpdateBinaries' +':qa' + +echo "Installing lint tools" +( + mkdir -p "$installdir/share/vim/vimgo/pack/vim-go/start/" + cd "$installdir/share/vim/vimgo/pack/vim-go/start/" + [ -d "vim-vimhelplint" ] || git clone --depth 1 --quiet https://github.com/machakann/vim-vimhelplint + [ -d "vim-vimlparser" ] || git clone --depth 1 --quiet https://github.com/ynkdir/vim-vimlparser + [ -d "vim-vimlint" ] || git clone --depth 1 --quiet https://github.com/syngan/vim-vimlint +) + +# Don't really need source after successful install. +rm -rf "$srcdir" + +echo "installed to: $installdir" + +# vim:ts=2:sts=2:sw=2:et diff --git a/vim/bundle/go/scripts/lint b/vim/bundle/go/scripts/lint new file mode 100755 index 0000000..553413a --- /dev/null +++ b/vim/bundle/go/scripts/lint @@ -0,0 +1,88 @@ +#!/bin/sh +# +# Run all linting tools. +# + +set -euC +vimgodir=$(cd -P "$(dirname "$0")/.." > /dev/null && pwd) +cd "$vimgodir" + +### Setup Vim and other dependencies. +##################################### +if [ -z "${1:-}" ]; then + echo "unknown version: '${1:-}'" + echo "First argument must be 'vim-7.4', 'vim-8.0', or 'nvim'." + exit 1 +fi + +vim=$1 +vimdir="/tmp/vim-go-test/$vim-install" +export GOPATH=$vimdir +export PATH=${GOPATH}/bin:$PATH + +if [ ! -f "$vimdir/bin/vim" ]; then + echo "$vimdir/bin/vim doesn't exist; did you install it with the install-vim script?" + exit 1 +fi + +### Run vint +############ +failed=0 +printf "Running vint ... " +if [ -x "$(command -v vint)" ]; then + lint=$(vint "$vimgodir" 2>&1 ||:) + if [ -n "$lint" ]; then + echo "FAILED" + echo "$lint" + echo + failed=6 + else + echo "PASSED" + fi +else + echo "SKIPPED" + echo "'vint' binary not found; use 'pip install vim-vint' to install it." +fi + +### Run vim-vimlint +################### +printf "Running vim-vimlint ... " +lint=$(sh "$vimdir/share/vim/vimgo/pack/vim-go/start/vim-vimlint/bin/vimlint.sh" \ + -p "$vimdir/share/vim/vimgo/pack/vim-go/start/vim-vimlparser" \ + -l "$vimdir/share/vim/vimgo/pack/vim-go/start/vim-vimlint" \ + -u \ + -c func_abort=1 \ + -e EVL110=1 -e EVL103=1 -e EVL104=1 -e EVL102=1 \ + "$vimgodir" \ + 2>&1 ||:) +if [ -n "$lint" ]; then + echo "FAILED" + echo "$lint" + echo + failed=6 +else + echo "PASSED" +fi + +### Run vimhelplint. +#################### +printf "Running vimhelplint ... " + +# set modeline explicitly so that the modeline will be respected when run as root. +lint=$($vimdir/bin/vim -esNR \ + --cmd "set rtp+=$vimdir/share/vim/vimgo/pack/vim-go/start/vim-vimhelplint/" \ + --cmd 'set modeline' \ + +'filetype plugin on' \ + +"e $vimgodir/doc/vim-go.txt" \ + +'verbose VimhelpLintEcho' \ + +q \ + 2>&1 ||:) +if [ "$lint" ]; then + echo "FAILED" + echo "$lint" + failed=6 +else + echo "PASSED" +fi + +exit "$failed" diff --git a/vim/bundle/go/scripts/run-vim b/vim/bundle/go/scripts/run-vim new file mode 100755 index 0000000..f020ea9 --- /dev/null +++ b/vim/bundle/go/scripts/run-vim @@ -0,0 +1,50 @@ +#!/bin/sh +# +# Run a "bare" Vim with just vim-go and ignoring ~/.vim +# + +set -euC +vimgodir=$(cd -P "$(dirname "$0")/.." > /dev/null && pwd) +cd "$vimgodir" + +coverage=0 +while getopts "c" option; do + case "$option" in + c) coverage=1; ;; + esac +done +shift $((OPTIND - 1)) + +if [ -z "${1:-}" ]; then + echo "unknown version: '${1:-}'" + echo "First argument must be 'vim-7.4', 'vim-8.0', or 'nvim'." + exit 1 +fi + +dir="/tmp/vim-go-test/$1-install" +export GOPATH=$dir +export PATH=${GOPATH}/bin:$PATH +shift + +if [ ! -f "$dir/bin/vim" ]; then + echo "$dir/bin/vim doesn't exist; did you install it with the install-vim script?" + exit 1 +fi + +if [ $coverage -eq 1 ]; then + covimerage -q run --report-file /tmp/vim-go-test/cov-profile.txt --append \ + $dir/bin/vim --noplugin -u NONE -N \ + +"set shm+=WAFI rtp=$dir/share/vim/vimgo packpath=$dir/share/vim/vimgo,$vimgodir" \ + +'filetype plugin indent on' \ + +'packloadall!' \ + "$@" +else + $dir/bin/vim --noplugin -u NONE -N \ + +"set shm+=WAFI rtp=$dir/share/vim/vimgo packpath=$dir/share/vim/vimgo,$vimgodir" \ + +'filetype plugin indent on' \ + +'packloadall!' \ + "$@" +fi + + +# vim:ts=2:sts=2:sw=2:et diff --git a/vim/bundle/go/scripts/runtest.vim b/vim/bundle/go/scripts/runtest.vim new file mode 100644 index 0000000..23d9186 --- /dev/null +++ b/vim/bundle/go/scripts/runtest.vim @@ -0,0 +1,103 @@ +" Make sure some options are set to sane defaults and output all messages in +" English. + +" vint: -ProhibitSetNoCompatible +set nocompatible nomore shellslash encoding=utf-8 shortmess+=WIF +lang mess C + +" Initialize variables. +let s:total_started = reltime() +let s:fail = 0 +let s:done = 0 +let s:logs = [] +let s:gopath = $GOPATH +if !exists('g:test_verbose') + let g:test_verbose = 0 +endif + +" Source the passed test file. +source % + +" cd into the folder of the test file. +let s:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' +let s:testfile = expand('%:t') +execute s:cd . expand('%:p:h') + +" Export root path to vim-go dir. +let g:vim_go_root = fnamemodify(getcwd(), ':p') + +" Get a list of all Test_ functions for the given file. +redir @q + silent function /^Test_ +redir END +let s:tests = split(substitute(@q, 'function \(\k*()\)', '\1', 'g')) + +" Iterate over all tests and execute them. +for s:test in sort(s:tests) + " Since we extract the tests from a regexp the "abort" keyword is also in the + " list, which is not a test name :-) + if s:test == 'abort' + continue + endif + + let s:started = reltime() + if g:test_verbose is 1 + call add(s:logs, printf("=== RUN %s", s:test[:-3])) + endif + try + exe 'call ' . s:test + catch + let v:errors += [v:exception] + endtry + + " Restore GOPATH after each test. + let $GOPATH = s:gopath + + let s:elapsed_time = substitute(reltimestr(reltime(s:started)), '^\s*\(.\{-}\)\s*$', '\1', '') + let s:done += 1 + + if len(v:errors) > 0 + let s:fail += 1 + call add(s:logs, printf("--- FAIL %s (%ss)", s:test[:-3], s:elapsed_time)) + call extend(s:logs, map(v:errors, '" ". v:val')) + + " Reset so we can capture failures of the next test. + let v:errors = [] + else + if g:test_verbose is 1 + call add(s:logs, printf("--- PASS %s (%ss)", s:test[:-3], s:elapsed_time)) + endif + endif +endfor + +" Create an empty fail to indicate that at least one test failed. +if s:fail > 0 + split /tmp/vim-go-test/FAILED + silent write +endif + +let s:total_elapsed_time = substitute(reltimestr(reltime(s:total_started)), '^\s*\(.\{-}\)\s*$', '\1', '') + +" Add all messages (usually errors). +redir => s:mess + silent messages +redir END +let s:logs = s:logs + filter(split(s:mess, "\n"), 'v:val !~ "^Messages maintainer"') + +" Also store all internal messages from s:logs as well. +silent! split /tmp/vim-go-test/test.tmp +call append(line('$'), s:logs) +call append(line('$'), printf("%s %s %s %ss / %s tests", + \ (s:fail > 0 ? 'FAIL' : 'ok '), + \ s:testfile, + \ repeat(' ', 25 - len(s:testfile)), + \ s:total_elapsed_time, s:done)) +if g:test_verbose is 0 + silent :g/^$/d +endif +silent! write + +" Our work here is done. +qall! + +" vim:ts=2:sts=2:sw=2:et diff --git a/vim/bundle/go/scripts/test b/vim/bundle/go/scripts/test new file mode 100755 index 0000000..04e5169 --- /dev/null +++ b/vim/bundle/go/scripts/test @@ -0,0 +1,101 @@ +#!/bin/sh +# +# Run all tests. +# + +set -euC +vimgodir=$(cd -P "$(dirname "$0")/.." > /dev/null && pwd) +cd "$vimgodir" + +_usage() { + echo "Usage: ${0##*/} [-hvc] [-r file] vim_version" + echo + echo "Options:" + echo " -h Show this help" + echo " -v Enable verbose output" + echo " -r Run only the tests from this file" + echo " -c Generate and submit code coverage reports" + echo +} + +verbose=0 +run="" +coverage="" +while getopts "hvcr:" option; do + case "$option" in + h) _usage; exit 0 ;; + v) verbose=1; ;; + r) run=$OPTARG ;; + c) coverage="-c" ;; + *) + echo "error: unknown option '$option'" + _usage + exit 1 + ;; + esac +done +shift $((OPTIND - 1)) + +### Setup Vim and other dependencies. +##################################### +if [ -z "${1:-}" ]; then + echo "unknown version: '${1:-}'" + echo "First argument must be 'vim-7.4', 'vim-8.0', or 'nvim'." + exit 1 +fi + +vim=$1 +vimdir="/tmp/vim-go-test/$vim-install" +export GOPATH=$vimdir +export PATH=${GOPATH}/bin:$PATH + +if [ ! -f "$vimdir/bin/vim" ]; then + echo "$vimdir/bin/vim doesn't exist; did you install it with the install-vim script?" + exit 1 +fi + +### Run tests. +############## +# Clean stale log file. +[ -f '/tmp/vim-go-test/test.log' ] && rm '/tmp/vim-go-test/test.log' +[ -f '/tmp/vim-go-test/FAILED' ] && rm '/tmp/vim-go-test/FAILED' +[ -f '/tmp/vim-go-test/cov-profile.txt' ] && rm '/tmp/vim-go-test/cov-profile.txt' +[ -f '/tmp/vim-go-test/cov-report.txt' ] && rm '/tmp/vim-go-test/cov-report.txt' + +# Run the actual tests. +find "$vimgodir" -name '*_test.vim' | while read test_file; do + [ -n "$run" -a "$(basename "$test_file")" != "$run" ] && continue + + "$vimgodir/scripts/run-vim" $coverage $vim -e \ + +"silent e $test_file" \ + +"let g:test_verbose=$verbose" \ + -S ./scripts/runtest.vim < /dev/null || ( + # If Vim exits with non-0 it's almost certainly a bug in the test runner; + # should very rarely happen in normal usage. + echo 'test runner failure' + cat '/tmp/vim-go-test/test.tmp' + rm '/tmp/vim-go-test/test.tmp' + exit 5 + ) + + # Append logs + cat '/tmp/vim-go-test/test.tmp' | tee '/tmp/vim-go-test/test.log' + rm '/tmp/vim-go-test/test.tmp' +done + +echo +if [ -f "/tmp/vim-go-test/FAILED" ]; then + echo 2>&1 "Some ($vim) tests FAILED" + exit 1 +fi +echo 2>&1 "All ($vim) tests PASSED" + +# Submit coverage reports +if [ -n "$coverage" ]; then + coverage xml --omit '*_test.vim' + codecov -X search gcov pycov -f coverage.xml --required \ + --flags "$(echo "$vim" | sed -s 's/[-.]//g')" + rm coverage.xml +fi + +# vim:ts=2:sts=2:sw=2:et diff --git a/vim/bundle/go/scripts/test.sh b/vim/bundle/go/scripts/test.sh deleted file mode 100755 index d8a5b89..0000000 --- a/vim/bundle/go/scripts/test.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/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/vim/bundle/go/syntax/go.vim b/vim/bundle/go/syntax/go.vim index 484b400..8e7df12 100644 --- a/vim/bundle/go/syntax/go.vim +++ b/vim/bundle/go/syntax/go.vim @@ -3,54 +3,31 @@ " license that can be found in the LICENSE file. " " go.vim: Vim syntax file for Go. -" -" Options: -" There are some options for customizing the highlighting; the recommended -" settings are the default values, but you can write: -" let OPTION_NAME = 0 -" in your ~/.vimrc file to disable particular options. You can also write: -" let OPTION_NAME = 1 -" to enable particular options. -" At present, all options default to on, except highlight of: -" functions, methods, structs, operators, build constraints and interfaces. -" -" - go_highlight_array_whitespace_error -" Highlights white space after "[]". -" - go_highlight_chan_whitespace_error -" Highlights white space around the communications operator that don't follow -" the standard style. -" - go_highlight_extra_types -" Highlights commonly used library types (io.Reader, etc.). -" - go_highlight_space_tab_error -" Highlights instances of tabs following spaces. -" - go_highlight_trailing_whitespace_error -" Highlights trailing white space. -" - go_highlight_string_spellcheck -" Specifies that strings should be spell checked " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") finish endif +" Set settings to default values. if !exists("g:go_highlight_array_whitespace_error") - let g:go_highlight_array_whitespace_error = 1 + let g:go_highlight_array_whitespace_error = 0 endif if !exists("g:go_highlight_chan_whitespace_error") - let g:go_highlight_chan_whitespace_error = 1 + let g:go_highlight_chan_whitespace_error = 0 endif if !exists("g:go_highlight_extra_types") - let g:go_highlight_extra_types = 1 + let g:go_highlight_extra_types = 0 endif if !exists("g:go_highlight_space_tab_error") - let g:go_highlight_space_tab_error = 1 + let g:go_highlight_space_tab_error = 0 endif if !exists("g:go_highlight_trailing_whitespace_error") - let g:go_highlight_trailing_whitespace_error = 1 + let g:go_highlight_trailing_whitespace_error = 0 endif if !exists("g:go_highlight_operators") @@ -61,16 +38,20 @@ if !exists("g:go_highlight_functions") let g:go_highlight_functions = 0 endif +if !exists("g:go_highlight_function_arguments") + let g:go_highlight_function_arguments = 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 +if !exists("g:go_highlight_fields") + let g:go_highlight_fields = 0 endif -if !exists("g:go_highlight_interfaces") - let g:go_highlight_interfaces = 0 +if !exists("g:go_highlight_types") + let g:go_highlight_types = 0 endif if !exists("g:go_highlight_build_constraints") @@ -81,19 +62,61 @@ if !exists("g:go_highlight_string_spellcheck") let g:go_highlight_string_spellcheck = 1 endif +if !exists("g:go_highlight_format_strings") + let g:go_highlight_format_strings = 1 +endif + if !exists("g:go_highlight_generate_tags") let g:go_highlight_generate_tags = 0 endif +if !exists("g:go_highlight_variable_assignments") + let g:go_highlight_variable_assignments = 0 +endif + +if !exists("g:go_highlight_variable_declarations") + let g:go_highlight_variable_declarations = 0 +endif + +let s:fold_block = 1 +let s:fold_import = 1 +let s:fold_varconst = 1 +let s:fold_package_comment = 1 +let s:fold_comment = 0 + +if exists("g:go_fold_enable") + " Enabled by default. + if index(g:go_fold_enable, 'block') == -1 + let s:fold_block = 0 + endif + if index(g:go_fold_enable, 'import') == -1 + let s:fold_import = 0 + endif + if index(g:go_fold_enable, 'varconst') == -1 + let s:fold_varconst = 0 + endif + if index(g:go_fold_enable, 'package_comment') == -1 + let s:fold_package_comment = 0 + endif + + " Disabled by default. + if index(g:go_fold_enable, 'comment') > -1 + let s:fold_comment = 1 + endif +endif + syn case match -syn keyword goDirective package import -syn keyword goDeclaration var const type -syn keyword goDeclType struct interface +syn keyword goPackage package +syn keyword goImport import contained +syn keyword goVar var contained +syn keyword goConst const contained -hi def link goDirective Statement +hi def link goPackage Statement +hi def link goImport Statement +hi def link goVar Keyword +hi def link goConst Keyword hi def link goDeclaration Keyword -hi def link goDeclType Keyword " Keywords within functions syn keyword goStatement defer go goto return break continue fallthrough @@ -119,24 +142,28 @@ hi def link goUnsignedInts Type hi def link goFloats Type hi def link goComplexes Type -" Treat func specially: it's a declaration at the start of a line, but a type -" elsewhere. Order matters here. -syn match goDeclaration /\/ - " Predefined functions and values -syn match goBuiltins /\<\v(append|cap|close|complex|copy|delete|imag|len)\ze\(/ -syn match goBuiltins /\<\v(make|new|panic|print|println|real|recover)\ze\(/ -syn keyword goBoolean iota true false nil +syn match goBuiltins /\<\v(append|cap|close|complex|copy|delete|imag|len)\ze\(/ +syn match goBuiltins /\<\v(make|new|panic|print|println|real|recover)\ze\(/ +syn keyword goBoolean true false +syn keyword goPredefinedIdentifiers nil iota -hi def link goBuiltins Keyword -hi def link goBoolean Boolean +hi def link goBuiltins Keyword +hi def link goBoolean Boolean +hi def link goPredefinedIdentifiers goBoolean " Comments; their contents syn keyword goTodo contained TODO FIXME XXX BUG syn cluster goCommentGroup contains=goTodo -syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell + syn region goComment start="//" end="$" contains=goGenerate,@goCommentGroup,@Spell +if s:fold_comment + syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell fold + syn match goComment "\v(^\s*//.*\n)+" contains=goGenerate,@goCommentGroup,@Spell fold +else + syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell +endif hi def link goComment Comment hi def link goTodo Todo @@ -173,11 +200,14 @@ else syn region goString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup syn region goRawString start=+`+ end=+`+ endif -syn match goFormatSpecifier /%[-#0 +]*\%(\*\|\d\+\)\=\%(\.\%(\*\|\d\+\)\)*[vTtbcdoqxXUeEfgGsp]/ contained containedin=goString + +if g:go_highlight_format_strings != 0 + syn match goFormatSpecifier /\([^%]\(%%\)*\)\@<=%[-#0 +]*\%(\*\|\d\+\)\=\%(\.\%(\*\|\d\+\)\)*[vTtbcdoqxXUeEfgGsp]/ contained containedin=goString + hi def link goFormatSpecifier goSpecialString +endif 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 @@ -186,8 +216,35 @@ syn region goCharacter start=+'+ skip=+\\\\\|\\'+ end=+'+ contains= hi def link goCharacter Character " Regions -syn region goBlock start="{" end="}" transparent fold syn region goParen start='(' end=')' transparent +if s:fold_block + syn region goBlock start="{" end="}" transparent fold +else + syn region goBlock start="{" end="}" transparent +endif + +" import +if s:fold_import + syn region goImport start='import (' end=')' transparent fold contains=goImport,goString,goComment +else + syn region goImport start='import (' end=')' transparent contains=goImport,goString,goComment +endif + +" var, const +if s:fold_varconst + syn region goVar start='var (' end='^\s*)$' transparent fold + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goArgumentName,goArgumentType,goSimpleArguments + syn region goConst start='const (' end='^\s*)$' transparent fold + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goArgumentName,goArgumentType,goSimpleArguments +else + syn region goVar start='var (' end='^\s*)$' transparent + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goArgumentName,goArgumentType,goSimpleArguments + syn region goConst start='const (' end='^\s*)$' transparent + \ contains=ALLBUT,goParen,goBlock,goFunction,goTypeName,goReceiverType,goReceiverVar,goArgumentName,goArgumentType,goSimpleArguments +endif + +" Single-line var, const, and import. +syn match goSingleDecl /\(import\|var\|const\) [^(]\@=/ contains=goImport,goVar,goConst " Integers syn match goDecimalInt "\<-\=\d\+\%([Ee][-+]\=\d\+\)\=\>" @@ -268,6 +325,7 @@ hi def link goSpaceError Error syn keyword goTodo contained NOTE hi def link goTodo Todo +syn match goVarArgs /\.\.\./ " Operators; if g:go_highlight_operators != 0 @@ -282,72 +340,131 @@ if g:go_highlight_operators != 0 " match remaining two-char operators: := && || <- ++ -- syn match goOperator /:=\|||\|<-\|++\|--/ " match ... - syn match goOperator /\.\.\./ + + hi def link goPointerOperator goOperator + hi def link goVarArgs goOperator endif hi def link goOperator Operator " Functions; -if g:go_highlight_functions != 0 - syn match goFunction /\(func\s\+\)\@<=\w\+\((\)\@=/ - syn match goFunction /\()\s\+\)\@<=\w\+\((\)\@=/ +if g:go_highlight_functions isnot 0 || g:go_highlight_function_arguments isnot 0 + syn match goFunctionCall /\w\+\ze(/ contains=goBuiltins,goDeclaration + syn match goDeclaration /\/ nextgroup=goReceiver,goFunction,goSimpleArguments skipwhite skipnl + syn match goReceiverVar /\w\+\ze\s\+\(\w\|\*\)/ nextgroup=goPointerOperator,goReceiverType skipwhite skipnl contained + syn match goPointerOperator /\*/ nextgroup=goReceiverType contained skipwhite skipnl + syn match goFunction /\w\+/ nextgroup=goSimpleArguments contained skipwhite skipnl + syn match goReceiverType /\w\+/ contained +if g:go_highlight_function_arguments isnot 0 + syn match goSimpleArguments /(\(\w\|\_s\|[*\.\[\],\{\}<>-]\)*)/ contained contains=goArgumentName nextgroup=goSimpleArguments skipwhite skipnl + syn match goArgumentName /\w\+\(\s*,\s*\w\+\)*\ze\s\+\(\w\|\.\|\*\|\[\)/ contained nextgroup=goArgumentType skipwhite skipnl + syn match goArgumentType /\([^,)]\|\_s\)\+,\?/ contained nextgroup=goArgumentName skipwhite skipnl + \ contains=goVarArgs,goType,goSignedInts,goUnsignedInts,goFloats,goComplexes,goDeclType,goBlock + hi def link goReceiverVar goArgumentName + hi def link goArgumentName Identifier +endif + syn match goReceiver /(\s*\w\+\(\s\+\*\?\s*\w\+\)\?\s*)\ze\s*\w/ contained nextgroup=goFunction contains=goReceiverVar skipwhite skipnl +else + syn keyword goDeclaration func endif hi def link goFunction Function +hi def link goFunctionCall Type " Methods; if g:go_highlight_methods != 0 - syn match goMethod /\(\.\)\@<=\w\+\((\)\@=/ + syn match goMethodCall /\.\w\+\ze(/hs=s+1 endif -hi def link goMethod Type +hi def link goMethodCall Type -" Structs; -if g:go_highlight_structs != 0 - syn match goStruct /\(.\)\@<=\w\+\({\)\@=/ - syn match goStructDef /\(type\s\+\)\@<=\w\+\(\s\+struct\s\+{\)\@=/ +" Fields; +if g:go_highlight_fields != 0 + syn match goField /\.\w\+\([.\ \n\r\:\)\[,]\)\@=/hs=s+1 endif -hi def link goStruct Function -hi def link goStructDef Function +hi def link goField Identifier + +" Structs & Interfaces; +if g:go_highlight_types != 0 + syn match goTypeConstructor /\<\w\+{\@=/ + syn match goTypeDecl /\/ nextgroup=goTypeName skipwhite skipnl + syn match goTypeName /\w\+/ contained nextgroup=goDeclType skipwhite skipnl + syn match goDeclType /\<\(interface\|struct\)\>/ skipwhite skipnl + hi def link goReceiverType Type +else + syn keyword goDeclType struct interface + syn keyword goDeclaration type +endif +hi def link goTypeConstructor Type +hi def link goTypeName Type +hi def link goTypeDecl Keyword +hi def link goDeclType Keyword -" Interfaces; -if g:go_highlight_interfaces != 0 - syn match goInterface /\(.\)\@<=\w\+\({\)\@=/ - syn match goInterfaceDef /\(type\s\+\)\@<=\w\+\(\s\+interface\s\+{\)\@=/ +" Variable Assignments +if g:go_highlight_variable_assignments != 0 + syn match goVarAssign /\v[_.[:alnum:]]+(,\s*[_.[:alnum:]]+)*\ze(\s*([-^+|^\/%&]|\*|\<\<|\>\>|\&\^)?\=[^=])/ + hi def link goVarAssign Special +endif + +" Variable Declarations +if g:go_highlight_variable_declarations != 0 + syn match goVarDefs /\v\w+(,\s*\w+)*\ze(\s*:\=)/ + hi def link goVarDefs Special endif -hi def link goInterface Function -hi def link goInterfaceDef Function " Build Constraints if g:go_highlight_build_constraints != 0 - syn match goBuildKeyword display contained "+build" - " Highlight the known values of GOOS, GOARCH, and other +build options. - syn keyword goBuildDirectives contained - \ android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 - \ solaris windows 386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 - \ ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc - \ s390 s390x sparc sparc64 cgo ignore race - - " Other words in the build directive are build tags not listed above, so - " avoid highlighting them as comments by using a matchgroup just for the - " start of the comment. - " The rs=s+2 option lets the \s*+build portion be part of the inner region - " instead of the matchgroup so it will be highlighted as a goBuildKeyword. - syn region goBuildComment matchgroup=goBuildCommentStart - \ start="//\s*+build\s"rs=s+2 end="$" - \ contains=goBuildKeyword,goBuildDirectives - hi def link goBuildCommentStart Comment - hi def link goBuildDirectives Type - hi def link goBuildKeyword PreProc - - " One or more line comments that are followed immediately by a "package" - " declaration are treated like package documentation, so these must be - " matched as comments to avoid looking like working build constraints. - " The he, me, and re options let the "package" itself be highlighted by - " the usual rules. - syn region goPackageComment start=/\v(\/\/.*\n)+\s*package/ - \ end=/\v\n\s*package/he=e-7,me=e-7,re=e-7 - \ contains=@goCommentGroup,@Spell - hi def link goPackageComment Comment + syn match goBuildKeyword display contained "+build" + " Highlight the known values of GOOS, GOARCH, and other +build options. + syn keyword goBuildDirectives contained + \ android darwin dragonfly freebsd linux nacl netbsd openbsd plan9 + \ solaris windows 386 amd64 amd64p32 arm armbe arm64 arm64be ppc64 + \ ppc64le mips mipsle mips64 mips64le mips64p32 mips64p32le ppc + \ s390 s390x sparc sparc64 cgo ignore race + + " Other words in the build directive are build tags not listed above, so + " avoid highlighting them as comments by using a matchgroup just for the + " start of the comment. + " The rs=s+2 option lets the \s*+build portion be part of the inner region + " instead of the matchgroup so it will be highlighted as a goBuildKeyword. + syn region goBuildComment matchgroup=goBuildCommentStart + \ start="//\s*+build\s"rs=s+2 end="$" + \ contains=goBuildKeyword,goBuildDirectives + hi def link goBuildCommentStart Comment + hi def link goBuildDirectives Type + hi def link goBuildKeyword PreProc +endif + +if g:go_highlight_build_constraints != 0 || s:fold_package_comment + " One or more line comments that are followed immediately by a "package" + " declaration are treated like package documentation, so these must be + " matched as comments to avoid looking like working build constraints. + " The he, me, and re options let the "package" itself be highlighted by + " the usual rules. + exe 'syn region goPackageComment start=/\v(\/\/.*\n)+\s*package/' + \ . ' end=/\v\n\s*package/he=e-7,me=e-7,re=e-7' + \ . ' contains=@goCommentGroup,@Spell' + \ . (s:fold_package_comment ? ' fold' : '') + exe 'syn region goPackageComment start=/\v\/\*.*\n(.*\n)*\s*\*\/\npackage/' + \ . ' end=/\v\n\s*package/he=e-7,me=e-7,re=e-7' + \ . ' contains=@goCommentGroup,@Spell' + \ . (s:fold_package_comment ? ' fold' : '') + hi def link goPackageComment Comment endif +" :GoCoverage commands +hi def link goCoverageNormalText Comment + +function! s:hi() + hi def link goSameId Search + + " :GoCoverage commands + hi def goCoverageCovered ctermfg=green guifg=#A6E22E + hi def goCoverageUncover ctermfg=red guifg=#F92672 +endfunction + +augroup vim-go-hi + autocmd! + autocmd ColorScheme * call s:hi() +augroup end +call s:hi() " Search backwards for a global declaration to start processing the syntax. "syn sync match goSync grouphere NONE /^\(const\|var\|type\|func\)\>/ @@ -357,3 +474,5 @@ endif syn sync minlines=500 let b:current_syntax = "go" + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/syntax/godefstack.vim b/vim/bundle/go/syntax/godefstack.vim new file mode 100644 index 0000000..e4eefff --- /dev/null +++ b/vim/bundle/go/syntax/godefstack.vim @@ -0,0 +1,20 @@ +if exists("b:current_syntax") + finish +endif + +syn match godefStackComment '^".*' +syn match godefLinePrefix '^[>\s]\s' nextgroup=godefStackEntryNumber contains=godefStackCurrentPosition +syn match godefStackEntryNumber '\d\+' nextgroup=godefStackFilename skipwhite +syn match godefStackCurrentPosition '>' contained +syn match godefStackFilename '[^|]\+' contained nextgroup=godefStackEntryLocation +syn region godefStackEntryLocation oneline start='|' end='|' contained contains=godefStackEntryLocationNumber +syn match godefStackEntryLocationNumber '\d\+' contained display + +let b:current_syntax = "godefstack" + +hi def link godefStackComment Comment +hi def link godefStackCurrentPosition Special +hi def link godefStackFilename Directory +hi def link godefStackEntryLocationNumber LineNr + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/syntax/godoc.vim b/vim/bundle/go/syntax/godoc.vim deleted file mode 100644 index 0fba208..0000000 --- a/vim/bundle/go/syntax/godoc.vim +++ /dev/null @@ -1,47 +0,0 @@ -" 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/vim/bundle/go/syntax/gohtmltmpl.vim b/vim/bundle/go/syntax/gohtmltmpl.vim index 5aa6c98..fd2612a 100644 --- a/vim/bundle/go/syntax/gohtmltmpl.vim +++ b/vim/bundle/go/syntax/gohtmltmpl.vim @@ -1,9 +1,9 @@ if exists("b:current_syntax") - finish + finish endif -if !exists("main_syntax") - let main_syntax = 'html' +if !exists("g:main_syntax") + let g:main_syntax = 'html' endif runtime! syntax/gotexttmpl.vim @@ -12,4 +12,4 @@ unlet b:current_syntax let b:current_syntax = "gohtmltmpl" -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/syntax/gotexttmpl.vim b/vim/bundle/go/syntax/gotexttmpl.vim index f8f4e68..ab8b1b6 100644 --- a/vim/bundle/go/syntax/gotexttmpl.vim +++ b/vim/bundle/go/syntax/gotexttmpl.vim @@ -6,7 +6,7 @@ " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") - finish + finish endif syn case match @@ -74,12 +74,12 @@ hi def link goTplVariable Special syn region gotplAction start="{{" end="}}" contains=@gotplLiteral,gotplControl,gotplFunctions,gotplVariable,goTplIdentifier display syn region gotplAction start="\[\[" end="\]\]" contains=@gotplLiteral,gotplControl,gotplFunctions,gotplVariable display -syn region goTplComment start="{{/\*" end="\*/}}" display -syn region goTplComment start="\[\[/\*" end="\*/\]\]" display +syn region goTplComment start="{{\(- \)\?/\*" end="\*/\( -\)\?}}" display +syn region goTplComment start="\[\[\(- \)\?/\*" end="\*/\( -\)\?\]\]" display hi def link gotplAction PreProc hi def link goTplComment Comment let b:current_syntax = "gotexttmpl" -" vim:ts=4:sw=4:et +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/syntax/vimgo.vim b/vim/bundle/go/syntax/vimgo.vim index d62791d..3a2204c 100644 --- a/vim/bundle/go/syntax/vimgo.vim +++ b/vim/bundle/go/syntax/vimgo.vim @@ -1,5 +1,5 @@ if exists("b:current_syntax") - finish + finish endif let b:current_syntax = "vimgo" @@ -9,3 +9,5 @@ syn region goTitle start="\%1l" end=":" hi def link goInterface Type hi def link goTitle Label + +" vim: sw=2 ts=2 et diff --git a/vim/bundle/go/templates/hello_world.go b/vim/bundle/go/templates/hello_world.go new file mode 100644 index 0000000..50e8d8d --- /dev/null +++ b/vim/bundle/go/templates/hello_world.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("vim-go") +} diff --git a/vim/bundle/go/templates/hello_world_test.go b/vim/bundle/go/templates/hello_world_test.go new file mode 100644 index 0000000..3ef4f52 --- /dev/null +++ b/vim/bundle/go/templates/hello_world_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func TestHelloWorld(t *testing.T) { + // t.Fatal("not implemented") +} diff --git a/vim/bundle/go/test/gopath_test.vim b/vim/bundle/go/test/gopath_test.vim new file mode 100644 index 0000000..fef9d50 --- /dev/null +++ b/vim/bundle/go/test/gopath_test.vim @@ -0,0 +1,55 @@ +fun! Test_Detect_Gopath() abort + let l:gopath = $GOPATH + try + let g:go_autodetect_gopath = 1 + let l:tmp = go#util#tempdir("pathdetect") + let l:tmp2 = go#util#tempdir("pathdetect-nogodep") + + call mkdir(l:tmp . '/subdir', 'p') + call mkdir(l:tmp . '/Godeps/_workspace', 'p') + + exe ':e ' . l:tmp . '/a.go' + call assert_equal(l:tmp . '/Godeps/_workspace:' . l:gopath, $GOPATH) + + exe ':e ' . l:tmp . '/not-a-go-file' + call assert_equal(l:gopath, $GOPATH) + + exe ':e ' . l:tmp . '/subdir/a.go' + call assert_equal(l:tmp . '/Godeps/_workspace:' . l:gopath, $GOPATH) + + exec ':e ' . l:tmp2 . '/a.go' + call assert_equal(l:gopath, $GOPATH) + finally + let g:go_autodetect_gopath = 0 + call delete(l:tmp, 'rf') + call delete(l:tmp2, 'rf') + endtry +endfun + +fun! Test_Detect_Gopath_disabled() abort + let l:gopath = $GOPATH + try + let g:go_autodetect_gopath = 0 + let l:tmp = go#util#tempdir("pathdetect") + let l:tmp2 = go#util#tempdir("pathdetect-nogodep") + + call mkdir(l:tmp . '/subdir', 'p') + call mkdir(l:tmp . '/Godeps/_workspace', 'p') + + exe ':e ' . l:tmp . '/a.go' + call assert_equal(l:gopath, $GOPATH) + + exe ':e ' . l:tmp . '/not-a-go-file' + call assert_equal(l:gopath, $GOPATH) + + exe ':e ' . l:tmp . '/subdir/a.go' + call assert_equal(l:gopath, $GOPATH) + + exec ':e ' . l:tmp2 . '/a.go' + call assert_equal(l:gopath, $GOPATH) + finally + let g:go_autodetect_gopath = 0 + call delete(l:tmp, 'rf') + call delete(l:tmp2, 'rf') + endtry +endfun