Update vim-go to v1.20
This commit is contained in:
parent
b815d29703
commit
566926ad64
114 changed files with 8345 additions and 2826 deletions
|
@ -6,6 +6,7 @@ coverage:
|
||||||
target: auto
|
target: auto
|
||||||
threshold: 1
|
threshold: 1
|
||||||
base: auto
|
base: auto
|
||||||
|
patch: off
|
||||||
comment: false
|
comment: false
|
||||||
ignore:
|
ignore:
|
||||||
- "!autoload/go/*.vim$"
|
- "!autoload/go/*.vim$"
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
[run]
|
[run]
|
||||||
plugins = covimerage
|
plugins = covimerage
|
||||||
data_file = .coverage.covimerage
|
data_file = .coverage_covimerage
|
||||||
|
|
|
@ -1,2 +1,6 @@
|
||||||
.local/
|
.local/
|
||||||
|
.config/
|
||||||
|
.cache/
|
||||||
|
.dlv/
|
||||||
.git/
|
.git/
|
||||||
|
.viminfo
|
||||||
|
|
11
pack/acp/start/vim-go/.github/ISSUE_TEMPLATE.md
vendored
11
pack/acp/start/vim-go/.github/ISSUE_TEMPLATE.md
vendored
|
@ -9,14 +9,13 @@
|
||||||
|
|
||||||
### Configuration (**MUST** fill this out):
|
### Configuration (**MUST** fill this out):
|
||||||
|
|
||||||
* Vim version (first two lines from `:version`):
|
* 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):
|
||||||
|
|
||||||
|
* Vim version (first three lines from `:version`):
|
||||||
|
|
||||||
* Go version (`go version`):
|
* Go version (`go version`):
|
||||||
|
|
||||||
* Go environment (`go env`):
|
* 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):
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
language: go
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.12.1
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
matrix:
|
matrix:
|
||||||
|
@ -6,9 +8,16 @@ matrix:
|
||||||
- env: SCRIPT="test -c" VIM_VERSION=vim-7.4
|
- env: SCRIPT="test -c" VIM_VERSION=vim-7.4
|
||||||
- env: SCRIPT="test -c" VIM_VERSION=vim-8.0
|
- env: SCRIPT="test -c" VIM_VERSION=vim-8.0
|
||||||
- env: SCRIPT="test -c" VIM_VERSION=nvim
|
- env: SCRIPT="test -c" VIM_VERSION=nvim
|
||||||
- env: SCRIPT=lint VIM_VERSION=vim-8.0
|
- env: ENV=vimlint SCRIPT=lint VIM_VERSION=vim-8.0
|
||||||
|
language: python
|
||||||
|
python: 3.6
|
||||||
install:
|
install:
|
||||||
- ./scripts/install-vim $VIM_VERSION
|
- ./scripts/install-vim $VIM_VERSION
|
||||||
- pip install --user vim-vint covimerage codecov
|
- |
|
||||||
|
if [ "$ENV" = "vimlint" ]; then
|
||||||
|
pip install vim-vint covimerage codecov pathlib
|
||||||
|
else
|
||||||
|
pip install --user vim-vint covimerage codecov pathlib
|
||||||
|
fi
|
||||||
script:
|
script:
|
||||||
- ./scripts/$SCRIPT $VIM_VERSION
|
- ./scripts/$SCRIPT $VIM_VERSION
|
||||||
|
|
|
@ -1,11 +1,314 @@
|
||||||
## unplanned
|
## unplanned
|
||||||
|
|
||||||
|
## 1.20 - (April 22, 2019)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
* ***gopls support!***
|
||||||
|
* use gopls for autocompletion by default in Vim8 and Neovim.
|
||||||
|
* use gopls for `:GoDef` by setting `g:go_def_mode='gopls'`.
|
||||||
|
* use gopls for `:GoInfo` by setting `g:go_info_mode='gopls'`.
|
||||||
|
* Add support for golangci-lint.
|
||||||
|
* set `g:go_metalinter_command='golangci-lint'` to use golangci-lint instead
|
||||||
|
of gometalinter.
|
||||||
|
* New `:GoDefType` command to jump to a type definition from an instance of the
|
||||||
|
type.
|
||||||
|
|
||||||
|
BACKWARDS INCOMPATABILITIES:
|
||||||
|
* `g:go_highlight_function_arguments` is renamed to `g:go_highlight_function_parameters`
|
||||||
|
[[GH-2117]](https://github.com/fatih/vim-go/pull/2117)
|
||||||
|
|
||||||
|
IMPROVEMENTS:
|
||||||
|
* Disable `g:go_gocode_propose_source` by default.
|
||||||
|
[[GH-2050]](https://github.com/fatih/vim-go/pull/2050)
|
||||||
|
* Don't spam users when Vim is run with vi compatibility.
|
||||||
|
[[GH-2055]](https://github.com/fatih/vim-go/pull/2055)
|
||||||
|
* Add bang support to lint commands to allow them to be run without jumping to
|
||||||
|
errors.
|
||||||
|
[[GH-2056]](https://github.com/fatih/vim-go/pull/2056)
|
||||||
|
* Use `go doc` for `:GoDoc` instead of `godoc`.
|
||||||
|
[[GH-2070]](https://github.com/fatih/vim-go/pull/2070)
|
||||||
|
* Detach from and shutdown dlv correctly.
|
||||||
|
[[GH-2075]](https://github.com/fatih/vim-go/pull/2075)
|
||||||
|
* Do not require `'autowrite'` or `'autowriteall'` to be set when using
|
||||||
|
autocompletion in module mode.
|
||||||
|
[[GH-2091]](https://github.com/fatih/vim-go/pull/2091)
|
||||||
|
* Fix use of `g:go_metalinter_command` _and_ apply it even when autosaving.
|
||||||
|
[[GH-2101]](https://github.com/fatih/vim-go/pull/2101)
|
||||||
|
* Report errors in quickfix when Delve fails to start (e.g. compiler errors).
|
||||||
|
[[GH-2111]](https://github.com/fatih/vim-go/pull/2111)
|
||||||
|
* Support `'undo_ftplugin'`, make most autocmds buffer-local, and only do the
|
||||||
|
bare minimum based on file names alone.
|
||||||
|
[[GH-2108]](https://github.com/fatih/vim-go/pull/2108)
|
||||||
|
* Write a message when `:GoInfo` can't display any results when `g:go_info_mode='gocode'`.
|
||||||
|
[[GH-2122]](https://github.com/fatih/vim-go/pull/2122)
|
||||||
|
* Highlight fields followed by an operator when `g:go_highlight_fields` is set.
|
||||||
|
[[GH-1907]](https://github.com/fatih/vim-go/pull/1907)
|
||||||
|
* Skip autosave actions when the buffer is not a readable file.
|
||||||
|
[[GH-2143]](https://github.com/fatih/vim-go/pull/2143)
|
||||||
|
* Run `godef` from the current buffer's directory to make sure it works with modules.
|
||||||
|
[[GH-2150]](https://github.com/fatih/vim-go/pull/2150)
|
||||||
|
* Add a function, `go#tool#DescribeBalloon`, to show information in a balloon
|
||||||
|
with `'balloonexpr'`. (Vim8 only).
|
||||||
|
[[GH-1975]](https://github.com/fatih/vim-go/pull/1975)
|
||||||
|
* Add initial support for `gopls`.
|
||||||
|
[[GH-2163]](https://github.com/fatih/vim-go/pull/2163).
|
||||||
|
* Add `:GoDefType` to jump to the type definition of the identifier under the
|
||||||
|
cursor.
|
||||||
|
[[GH-2165]](https://github.com/fatih/vim-go/pull/2165)
|
||||||
|
* Notify gopls about changes.
|
||||||
|
[[GH-2171]](https://github.com/fatih/vim-go/pull/2171)
|
||||||
|
* Respect `g:go_jump_to_error` when running `gometalinter` automatically on
|
||||||
|
save. [[GH-2176]](https://github.com/fatih/vim-go/pull/2176)
|
||||||
|
* Use gopls for code completion by default in Vim8 and Neovim.
|
||||||
|
[[GH-2172]](https://github.com/fatih/vim-go/pull/2172)
|
||||||
|
* Add support for golangci-lint.
|
||||||
|
[[GH-2182]](https://github.com/fatih/vim-go/pull/2182)
|
||||||
|
* Show hover balloon using gopls instead of gocode.
|
||||||
|
[[GH-2202]](https://github.com/fatih/vim-go/pull/2202)
|
||||||
|
* Add a new option, `g:go_debug_log_output`, to control logging with the
|
||||||
|
debugger.
|
||||||
|
[[GH-2203]](https://github.com/fatih/vim-go/pull/2203)
|
||||||
|
* Do not jump to quickfix or location list window when bang is used for async
|
||||||
|
jobs or linting.
|
||||||
|
[[GH-2205]](https://github.com/fatih/vim-go/pull/2205)
|
||||||
|
* Tab complete package names for commands from vendor directories and in
|
||||||
|
modules.
|
||||||
|
[[GH-2213]](https://github.com/fatih/vim-go/pull/2213)
|
||||||
|
* Add support for `gopls` to `g:go_info_mode`.
|
||||||
|
[[GH-2224]](https://github.com/fatih/vim-go/pull/2224)
|
||||||
|
|
||||||
|
BUG FIXES:
|
||||||
|
* Fix opening of non-existent file from `:GoDeclsDir` when the current
|
||||||
|
directory is not the directory containing the current buffer.
|
||||||
|
[[GH-2048]](https://github.com/fatih/vim-go/pull/2048)
|
||||||
|
* Fix jumping to an identifier with godef from a modified buffer.
|
||||||
|
[[GH-2054]](https://github.com/fatih/vim-go/pull/2054)
|
||||||
|
* Fix errors when `g:go_debug` contains `debugger-commands`.
|
||||||
|
[[GH-2075]](https://github.com/fatih/vim-go/pull/2075)
|
||||||
|
* Fix errors from `:GoDebugStop` in Neovim.
|
||||||
|
[[GH-2075]](https://github.com/fatih/vim-go/pull/2075)
|
||||||
|
* Fix `:GoSameIdsToggle`.
|
||||||
|
[[GH-2086]](https://github.com/fatih/vim-go/pull/2086)
|
||||||
|
* Do not set fileencoding or fileformat options or populate from template when
|
||||||
|
the buffer is not modifiable.
|
||||||
|
[[GH-2097]](https://github.com/fatih/vim-go/pull/2097)
|
||||||
|
* Do not clear buffer-local autocmds of other buffers.
|
||||||
|
[[GH-2109]](https://github.com/fatih/vim-go/pull/2109)
|
||||||
|
* Highlight return parameter types when g:go_highlight_function_arguments is
|
||||||
|
set. [[GH-2116]](https://github.com/fatih/vim-go/pull/2116)
|
||||||
|
* Fix lockup in Neovim when trying to run `:GoDebugTest` when there are no
|
||||||
|
tests. [[GH-2125]](https://github.com/fatih/vim-go/pull/2125)
|
||||||
|
* Keep track of breakpoints correctly when buffer is edited after breakpoints
|
||||||
|
are set.
|
||||||
|
[[GH-2126]](https://github.com/fatih/vim-go/pull/2126)
|
||||||
|
* Fix race conditions in `:GoDebugStop`.
|
||||||
|
[[GH-2127]](https://github.com/fatih/vim-go/pull/2127)
|
||||||
|
* Fix jumping to module or package using godef.
|
||||||
|
[[GH-2141]](https://github.com/fatih/vim-go/pull/2141)
|
||||||
|
* Fix errors caused by redefining functions within functions.
|
||||||
|
[[GH-2189]](https://github.com/fatih/vim-go/pull/2189)
|
||||||
|
* Highlight pre-release and metadata in versions in go.mod.
|
||||||
|
[[GH-2192]](https://github.com/fatih/vim-go/pull/2192)
|
||||||
|
* Handle runtime panics from `:GoRun` when using Neovim's terminal.
|
||||||
|
[[GH-2209]](https://github.com/fatih/vim-go/pull/2209)
|
||||||
|
* Fix adding tag option when a tag is added.
|
||||||
|
[[GH-2227]](https://github.com/fatih/vim-go/pull/2227)
|
||||||
|
|
||||||
|
## 1.19 - (November 4, 2018)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
|
||||||
|
* **go.mod file support!** This is the first feature for upcoming Go modules
|
||||||
|
support. The followings are added:
|
||||||
|
* Syntax highlighting for the `go.mod` file.
|
||||||
|
* A new `gomod` filetype is set if a `go.mod` file has been opened and starts
|
||||||
|
with the line `module `
|
||||||
|
* New **:GoModFmt** command that formats the `go.mod` file
|
||||||
|
* Auto format on save feature for `:GoModFmt`, enabled automatically. Can be
|
||||||
|
toggled of with the setting `g:go_mod_fmt_autosave` or with the command:
|
||||||
|
`GoModFmtAutoSaveToggle`
|
||||||
|
[[GH-1931]](https://github.com/fatih/vim-go/pull/1931)
|
||||||
|
|
||||||
|
IMPROVEMENTS:
|
||||||
|
* Unify async job handling for Vim8 and Neovim.
|
||||||
|
[[GH-1864]](https://github.com/fatih/vim-go/pull/1864)
|
||||||
|
* Document Vim and Neovim requirements in README.md and help file.
|
||||||
|
[[GH-1889]](https://github.com/fatih/vim-go/pull/1889)
|
||||||
|
* Highlight `context.Context` when `g:go_highlight_extra_types` is set.
|
||||||
|
[[GH-1903]](https://github.com/fatih/vim-go/pull/1903)
|
||||||
|
* Run gometalinter asynchronously in Neovim.
|
||||||
|
[[GH-1901]](https://github.com/fatih/vim-go/pull/1901)
|
||||||
|
* Run gorename asynchronously in Vim8 and Neovim.
|
||||||
|
[[GH-1894]](https://github.com/fatih/vim-go/pull/1894)
|
||||||
|
* Install keyify from its canonical import path.
|
||||||
|
[[GH-1924]](https://github.com/fatih/vim-go/pull/1924)
|
||||||
|
* Update the tested version of Neovim to v0.3.1.
|
||||||
|
[[GH-1923]](https://github.com/fatih/vim-go/pull/1923)
|
||||||
|
* Run autocompletion asynchronously in Vim8 and Neovim.
|
||||||
|
[[GH-1926]](https://github.com/fatih/vim-go/pull/1926)
|
||||||
|
* Show statusline update when running `:GoInfo` with `g:go_info_mode='gocode'`.
|
||||||
|
[[GH-1937]](https://github.com/fatih/vim-go/pull/1937)
|
||||||
|
* Do not update statusline when highlighting sameids or showing type info via
|
||||||
|
an autocmd.
|
||||||
|
[[GH-1937]](https://github.com/fatih/vim-go/pull/1937)
|
||||||
|
* Do not indent within a raw string literal.
|
||||||
|
[[GH-1858]](https://github.com/fatih/vim-go/pull/1858)
|
||||||
|
* Highlight Go's predeclared function identifiers (the functions in `builtins`)
|
||||||
|
using keyword groups and highlight them using the `Identifiers` group.
|
||||||
|
[[GH-1939]](https://github.com/fatih/vim-go/pull/1939)
|
||||||
|
* Add a new FAQ entry to instruct users how to modify the vim-go highlight
|
||||||
|
groups.
|
||||||
|
[[GH-1939]](https://github.com/fatih/vim-go/pull/1939)
|
||||||
|
* Improve use of statusline and progress messages.
|
||||||
|
[[GH-1948]](https://github.com/fatih/vim-go/pull/1948)
|
||||||
|
* Add `tt` snippet to create a table test boilerplate (see
|
||||||
|
https://github.com/golang/go/wiki/TableDrivenTests for more information on
|
||||||
|
how to use a table driven test).
|
||||||
|
[[GH-1956]](https://github.com/fatih/vim-go/pull/1956)
|
||||||
|
* Add `<Plug>(go-decls)` and `<Plug>(go-decls-dir)` mappings.
|
||||||
|
[[GH-1964]](https://github.com/fatih/vim-go/pull/1964)
|
||||||
|
* Handle go1.11 test output.
|
||||||
|
[[GH-1978]](https://github.com/fatih/vim-go/pull/1978)
|
||||||
|
* Internal: install tools by their custom names
|
||||||
|
[[GH-1984]](https://github.com/fatih/vim-go/pull/1984)
|
||||||
|
* Support the go-debugger features in Neovim.
|
||||||
|
[[GH-2007]](https://github.com/fatih/vim-go/pull/2007)
|
||||||
|
* color the statusline for termguicolors and Neovim.
|
||||||
|
[[GH-2014]](https://github.com/fatih/vim-go/pull/2014)
|
||||||
|
* add an option to disable highlighting of breakpoints and the current line
|
||||||
|
when debugging.
|
||||||
|
[[GH-2025]](https://github.com/fatih/vim-go/pull/2025)
|
||||||
|
* Update autocompletion to work with Go modules.
|
||||||
|
[[GH-1988]](https://github.com/fatih/vim-go/pull/1988)
|
||||||
|
* Add an option to search $GOPATH/bin or $GOBIN _after_ $PATH.
|
||||||
|
[[GH-2041]](https://github.com/fatih/vim-go/pull/2041)
|
||||||
|
|
||||||
|
BUG FIXES:
|
||||||
|
* Fix `:GoRun %` on Windows.
|
||||||
|
[[GH-1900]](https://github.com/fatih/vim-go/pull/1900)
|
||||||
|
* Fix `go#complete#GetInfo()` to return a description of the identifier.
|
||||||
|
[[GH-1905]](https://github.com/fatih/vim-go/pull/1905)
|
||||||
|
* Restore support for running tests in the Neovim terminal.
|
||||||
|
[[GH-1895]](https://github.com/fatih/vim-go/pull/1895)
|
||||||
|
* Fix `:GoInfo` when `g:go_info_mode` is `gocode`
|
||||||
|
[[GH-1915]](https://github.com/fatih/vim-go/pull/1915)
|
||||||
|
* Fix highlighting of pointer type in var blocks.
|
||||||
|
[[GH-1794]](https://github.com/fatih/vim-go/pull/1794)
|
||||||
|
* Fix `:GoImport` when adding to an empty import block (i.e`import ()`)
|
||||||
|
[[GH-1938]](https://github.com/fatih/vim-go/pull/1938)
|
||||||
|
* Run shell commands with shellcmdflag set to `-c`.
|
||||||
|
[[GH-2006]](https://github.com/fatih/vim-go/pull/2006)
|
||||||
|
* Use the correct log output option for delve.
|
||||||
|
[[GH-1992]](https://github.com/fatih/vim-go/pull/1992)
|
||||||
|
* Pass empty arguments correctly in async jobs on Windows.
|
||||||
|
[[GH-2011]](https://github.com/fatih/vim-go/pull/2011)
|
||||||
|
* Don't close godoc scratch window when using arrow keys.
|
||||||
|
[[GH-2021]](https://github.com/fatih/vim-go/pull/2021)
|
||||||
|
|
||||||
|
BACKWARDS INCOMPATIBILITIES:
|
||||||
|
* Bump minimum required version of Vim to 7.4.2009.
|
||||||
|
[[GH-1899]](https://github.com/fatih/vim-go/pull/1899)
|
||||||
|
* Switch gocode to github.com/mdempsky/gocode. Several gocode options have been
|
||||||
|
removed and a new one has been added.
|
||||||
|
[[GH-1853]](https://github.com/fatih/vim-go/pull/1853)
|
||||||
|
|
||||||
|
## 1.18 - (July 18, 2018)
|
||||||
|
|
||||||
|
FEATURES:
|
||||||
|
|
||||||
|
* Add **:GoIfErr** command together with the `<Plug>(go-iferr)` plug key to
|
||||||
|
create a custom mapping. This command generates an `if err != nil { return ... }`
|
||||||
|
automatically which infer the type of return values and the numbers.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
func doSomething() (string, error) {
|
||||||
|
f, err := os.Open("file")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Becomes:
|
||||||
|
|
||||||
|
```
|
||||||
|
func doSomething() (string, error) {
|
||||||
|
f, err := os.Open("file")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Two new text objects has been added:
|
||||||
|
* `ic` (inner comment) selects the content of the comment, excluding the start/end markers (i.e: `//`, `/*`)
|
||||||
|
* `ac` (a comment) selects the content of the whole commment block, including markers
|
||||||
|
To use this new feature, make sure you use use the latest version of
|
||||||
|
[motion](https://github.com/fatih/motion). You can update the tool from Vim
|
||||||
|
via `:GoUpdateBinaries`
|
||||||
|
[[GH-1779]](https://github.com/fatih/vim-go/pull/1779)
|
||||||
|
* Add `:GoPointsTo` to show all variables to which the pointer under the cursor
|
||||||
|
may point to.
|
||||||
|
[[GH-1751]](https://github.com/fatih/vim-go/pull/1751)
|
||||||
|
* Add `:GoReportGitHubIssue` to initialize a new GitHub issue with as much data
|
||||||
|
that our template requests as possible.
|
||||||
|
[[GH-1738]](https://github.com/fatih/vim-go/pull/1738)
|
||||||
|
|
||||||
|
IMPROVEMENTS:
|
||||||
|
|
||||||
|
* Add build tags (with `g:go_build_tags`) to all commands that support it.
|
||||||
|
[[GH-1705]](https://github.com/fatih/vim-go/pull/1705)
|
||||||
|
* Some command which operate on files (rather than Vim buffers) will now show a
|
||||||
|
warning if there are unsaved buffers, similar to Vim's `:make`.
|
||||||
|
[[GH-1754]](https://github.com/fatih/vim-go/pull/1754)
|
||||||
|
* Don't return an error from `:GoGuru` functions when the import path is
|
||||||
|
unknown and scope is unneeded.
|
||||||
|
[[GH-1826]](https://github.com/fatih/vim-go/pull/1826)
|
||||||
|
* Performance improvements for the `go.vim` syntax file.
|
||||||
|
[[GH-1799]](https://github.com/fatih/vim-go/pull/1799)
|
||||||
|
* Allow `GoDebugBreakpoint` and `GoDebugCurrent` highlight groups to be
|
||||||
|
overridden by user configuration.
|
||||||
|
[[GH-1850]](https://github.com/vim-go/pull/1850)
|
||||||
|
* Strip trailing carriage returns from quickfix errors that are parsed
|
||||||
|
manually. [[GH-1861]](https://github.com/fatih/vim-go/pull/1861).
|
||||||
|
* Cleanup title of terminal window.
|
||||||
|
[[GH-1861]](https://github.com/fatih/vim-go/pull/1861).
|
||||||
|
* Add `:GoImpl` is able to complete interfaces by their full import path in
|
||||||
|
addition to the current package name (i.e: `:GoImpl t *T github.com/BurntSushi/toml.Unmarshaller`
|
||||||
|
is now possible)
|
||||||
|
[[GH-1884]](https://github.com/fatih/vim-go/pull/1884)
|
||||||
|
|
||||||
|
BUG FIXES:
|
||||||
|
|
||||||
|
* Update the correct window's location list after a long running async job
|
||||||
|
completes, even when the user changes their window layout while the job is
|
||||||
|
running.
|
||||||
|
[[GH-1734]](https://github.com/fatih/vim-go/pull/1734)
|
||||||
|
* Apply debugger mappings only for Go buffers, and not all buffers.
|
||||||
|
[[GH-1696]](https://github.com/fatih/vim-go/pull/1696)
|
||||||
|
* The `gohtmltmpl` filetype will now highlight `{{ .. }}` syntax HTML attributes
|
||||||
|
and some other locations.
|
||||||
|
[[GH-1790]](https://github.com/fatih/vim-go/pull/1790)
|
||||||
|
* Use the correct logging flag argument for delve.
|
||||||
|
[[GH-1809]](https://github.com/fatih/vim-go/pull/1809)
|
||||||
|
* Fix gocode option string values that would cause gocode settings not to set
|
||||||
|
correctly
|
||||||
|
[[GH-1818]](https://github.com/fatih/vim-go/pull/1818)
|
||||||
|
* Fix Neovim handling of guru output.
|
||||||
|
[[GH-1846]](https://github.com/fatih/vim-go/pull/1846)
|
||||||
|
* Execute commands correctly when they are in $GOBIN but not $PATH.
|
||||||
|
[[GH-1866]](https://github.com/fatih/vim-go/pull/1866)
|
||||||
|
* Open files correctly with ctrlp.
|
||||||
|
[[GH-1878]](https://github.com/fatih/vim-go/pull/1878)
|
||||||
|
* Fix checking guru binary path
|
||||||
|
[[GH-1886]](https://github.com/fatih/vim-go/pull/1886)
|
||||||
|
* Add build tags to `:GoDef` if only it's present
|
||||||
|
[[GH-1882]](https://github.com/fatih/vim-go/pull/1882)
|
||||||
|
|
||||||
## 1.17 - (March 27, 2018)
|
## 1.17 - (March 27, 2018)
|
||||||
|
|
||||||
FEATURES:
|
FEATURES:
|
||||||
|
|
||||||
* **Debugger support!** Add integrated support for the
|
* **Debugger support!** Add integrated support for the
|
||||||
[`delve`](https://github.com/derekparker/delve) debugger. Use
|
[`delve`](https://github.com/go-delve/delve) debugger. Use
|
||||||
`:GoInstallBinaries` to install `dlv`, and see `:help go-debug` to get
|
`:GoInstallBinaries` to install `dlv`, and see `:help go-debug` to get
|
||||||
started.
|
started.
|
||||||
[[GH-1390]](https://github.com/fatih/vim-go/pull/1390)
|
[[GH-1390]](https://github.com/fatih/vim-go/pull/1390)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.9.2
|
FROM golang:1.12.1
|
||||||
|
|
||||||
RUN apt-get update -y && \
|
RUN apt-get update -y && \
|
||||||
apt-get install -y build-essential curl git libncurses5-dev python3-pip && \
|
apt-get install -y build-essential curl git libncurses5-dev python3-pip && \
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
VIMS ?= vim-7.4 vim-8.0 nvim
|
VIMS ?= vim-7.4 vim-8.0 nvim
|
||||||
|
|
||||||
all: install test lint
|
all: install lint test
|
||||||
|
|
||||||
install:
|
install:
|
||||||
@echo "==> Installing Vims: $(VIMS)"
|
@echo "==> Installing Vims: $(VIMS)"
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
This plugin adds Go language support for Vim, with the following main features:
|
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
|
* Compile your package with `:GoBuild`, install it with `:GoInstall` or test it
|
||||||
with `:GoTest`. Run a single tests with `:GoTestFunc`).
|
with `:GoTest`. Run a single test with `:GoTestFunc`).
|
||||||
* Quickly execute your current file(s) with `:GoRun`.
|
* Quickly execute your current file(s) with `:GoRun`.
|
||||||
* Improved syntax highlighting and folding.
|
* Improved syntax highlighting and folding.
|
||||||
* Debug programs with integrated `delve` support with `:GoDebugStart`.
|
* Debug programs with integrated `delve` support with `:GoDebugStart`.
|
||||||
* Completion support via `gocode`.
|
* Completion support via `gocode` and `gopls`.
|
||||||
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
|
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
|
||||||
* Go to symbol/declaration with `:GoDef`.
|
* Go to symbol/declaration with `:GoDef`.
|
||||||
* Look up documentation with `:GoDoc` or `:GoDocBrowser`.
|
* Look up documentation with `:GoDoc` or `:GoDocBrowser`.
|
||||||
|
@ -33,10 +33,13 @@ This plugin adds Go language support for Vim, with the following main features:
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
|
vim-go requires at least Vim 7.4.2009 or Neovim 0.3.1.
|
||||||
|
|
||||||
The [**latest stable release**](https://github.com/fatih/vim-go/releases/latest) is the
|
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,
|
recommended version to use. If you choose to use the master branch instead,
|
||||||
please do so with caution; it is a _development_ branch.
|
please do so with caution; it is a _development_ branch.
|
||||||
|
|
||||||
|
|
||||||
vim-go follows the standard runtime path structure. Below are some helper lines
|
vim-go follows the standard runtime path structure. Below are some helper lines
|
||||||
for popular package managers:
|
for popular package managers:
|
||||||
|
|
||||||
|
@ -45,7 +48,9 @@ for popular package managers:
|
||||||
* [Pathogen](https://github.com/tpope/vim-pathogen)
|
* [Pathogen](https://github.com/tpope/vim-pathogen)
|
||||||
* `git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go`
|
* `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'`
|
* `Plug 'fatih/vim-go', { 'do': ':GoUpdateBinaries' }`
|
||||||
|
* [Vundle](https://github.com/VundleVim/Vundle.vim)
|
||||||
|
* `Plugin 'fatih/vim-go'`
|
||||||
|
|
||||||
You will also need to install all the necessary binaries. vim-go makes it easy
|
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
|
to install all of them by providing a command, `:GoInstallBinaries`, which will
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
let s:go_decls_var = {
|
let s:go_decls_var = {
|
||||||
\ 'init': 'ctrlp#decls#init()',
|
\ 'init': 'ctrlp#decls#init()',
|
||||||
\ 'exit': 'ctrlp#decls#exit()',
|
\ 'exit': 'ctrlp#decls#exit()',
|
||||||
|
@ -20,7 +24,7 @@ function! ctrlp#decls#init() abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! ctrlp#decls#exit() abort
|
function! ctrlp#decls#exit() abort
|
||||||
unlet! s:decls s:current_dir s:target
|
unlet! s:decls s:target
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" The action to perform on the selected string
|
" The action to perform on the selected string
|
||||||
|
@ -32,10 +36,6 @@ function! ctrlp#decls#accept(mode, str) abort
|
||||||
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||||
let dir = getcwd()
|
let dir = getcwd()
|
||||||
try
|
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*\(.*\)|')
|
let vals = matchlist(a:str, '|\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)|')
|
||||||
|
|
||||||
" i.e: main.go
|
" i.e: main.go
|
||||||
|
@ -50,46 +50,41 @@ function! ctrlp#decls#accept(mode, str) abort
|
||||||
call ctrlp#acceptfile(a:mode, filepath)
|
call ctrlp#acceptfile(a:mode, filepath)
|
||||||
call cursor(line, col)
|
call cursor(line, col)
|
||||||
silent! norm! zvzz
|
silent! norm! zvzz
|
||||||
finally
|
|
||||||
"jump back to old dir
|
|
||||||
execute cd . fnameescape(dir)
|
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! ctrlp#decls#enter() abort
|
function! ctrlp#decls#enter() abort
|
||||||
let s:current_dir = fnameescape(expand('%:p:h'))
|
|
||||||
let s:decls = []
|
let s:decls = []
|
||||||
|
|
||||||
let bin_path = go#path#CheckBinPath('motion')
|
let l:cmd = ['motion',
|
||||||
if empty(bin_path)
|
\ '-format', 'vim',
|
||||||
return
|
\ '-mode', 'decls',
|
||||||
endif
|
\ '-include', go#config#DeclsIncludes(),
|
||||||
let command = printf("%s -format vim -mode decls", bin_path)
|
\ ]
|
||||||
let command .= " -include ". get(g:, "go_decls_includes", "func,type")
|
|
||||||
|
|
||||||
call go#cmd#autowrite()
|
call go#cmd#autowrite()
|
||||||
|
|
||||||
if s:mode == 0
|
if s:mode == 0
|
||||||
" current file mode
|
" current file mode
|
||||||
let fname = expand("%:p")
|
let l:fname = expand("%:p")
|
||||||
if exists('s:target')
|
if exists('s:target')
|
||||||
let fname = s:target
|
let l:fname = s:target
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command .= printf(" -file %s", fname)
|
let cmd += ['-file', l:fname]
|
||||||
else
|
else
|
||||||
" all functions mode
|
" all functions mode
|
||||||
let dir = expand("%:p:h")
|
let l:dir = expand("%:p:h")
|
||||||
if exists('s:target')
|
if exists('s:target')
|
||||||
let dir = s:target
|
let l:dir = s:target
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command .= printf(" -dir %s", dir)
|
let cmd += ['-dir', l:dir]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let out = go#util#System(command)
|
let [l:out, l:err] = go#util#Exec(l:cmd)
|
||||||
if go#util#ShellError() != 0
|
if l:err
|
||||||
call go#util#EchoError(out)
|
call go#util#EchoError(l:out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -118,7 +113,7 @@ function! ctrlp#decls#enter() abort
|
||||||
call add(s:decls, printf("%s\t%s |%s:%s:%s|\t%s",
|
call add(s:decls, printf("%s\t%s |%s:%s:%s|\t%s",
|
||||||
\ decl.ident . space,
|
\ decl.ident . space,
|
||||||
\ decl.keyword,
|
\ decl.keyword,
|
||||||
\ fnamemodify(decl.filename, ":t"),
|
\ fnamemodify(decl.filename, ":p"),
|
||||||
\ decl.line,
|
\ decl.line,
|
||||||
\ decl.col,
|
\ decl.col,
|
||||||
\ decl.full,
|
\ decl.full,
|
||||||
|
@ -152,4 +147,8 @@ function! ctrlp#decls#cmd(mode, ...) abort
|
||||||
return s:id
|
return s:id
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
function! s:code(group, attr) abort
|
function! s:code(group, attr) abort
|
||||||
let code = synIDattr(synIDtrans(hlID(a:group)), a:attr, "cterm")
|
let code = synIDattr(synIDtrans(hlID(a:group)), a:attr, "cterm")
|
||||||
if code =~ '^[0-9]\+$'
|
if code =~ '^[0-9]\+$'
|
||||||
|
@ -58,35 +62,34 @@ function! s:source(mode,...) abort
|
||||||
let s:current_dir = expand('%:p:h')
|
let s:current_dir = expand('%:p:h')
|
||||||
let ret_decls = []
|
let ret_decls = []
|
||||||
|
|
||||||
let bin_path = go#path#CheckBinPath('motion')
|
let l:cmd = ['motion',
|
||||||
if empty(bin_path)
|
\ '-format', 'vim',
|
||||||
return
|
\ '-mode', 'decls',
|
||||||
endif
|
\ '-include', go#config#DeclsIncludes(),
|
||||||
let command = printf("%s -format vim -mode decls", bin_path)
|
\ ]
|
||||||
let command .= " -include ". get(g:, "go_decls_includes", "func,type")
|
|
||||||
|
|
||||||
call go#cmd#autowrite()
|
call go#cmd#autowrite()
|
||||||
|
|
||||||
if a:mode == 0
|
if a:mode == 0
|
||||||
" current file mode
|
" current file mode
|
||||||
let fname = expand("%:p")
|
let l:fname = expand("%:p")
|
||||||
if a:0 && !empty(a:1)
|
if a:0 && !empty(a:1)
|
||||||
let fname = a:1
|
let l:fname = a:1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command .= printf(" -file %s", shellescape(fname))
|
let cmd += ['-file', l:fname]
|
||||||
else
|
else
|
||||||
" all functions mode
|
" all functions mode
|
||||||
if a:0 && !empty(a:1)
|
if a:0 && !empty(a:1)
|
||||||
let s:current_dir = a:1
|
let s:current_dir = a:1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command .= printf(" -dir %s", shellescape(s:current_dir))
|
let l:cmd += ['-dir', s:current_dir]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let out = go#util#System(command)
|
let [l:out, l:err] = go#util#Exec(l:cmd)
|
||||||
if go#util#ShellError() != 0
|
if l:err
|
||||||
call go#util#EchoError(out)
|
call go#util#EchoError(l:out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -147,4 +150,8 @@ function! fzf#decls#cmd(...) abort
|
||||||
\ }))
|
\ }))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
" By default use edit (current buffer view) to switch
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
if !exists("g:go_alternate_mode")
|
let s:cpo_save = &cpo
|
||||||
let g:go_alternate_mode = "edit"
|
set cpo&vim
|
||||||
endif
|
|
||||||
|
|
||||||
" Test alternates between the implementation of code and the test code.
|
" Test alternates between the implementation of code and the test code.
|
||||||
function! go#alternate#Switch(bang, cmd) abort
|
function! go#alternate#Switch(bang, cmd) abort
|
||||||
|
@ -23,10 +22,14 @@ function! go#alternate#Switch(bang, cmd) abort
|
||||||
call go#util#EchoError("couldn't find ".alt_file)
|
call go#util#EchoError("couldn't find ".alt_file)
|
||||||
return
|
return
|
||||||
elseif empty(a:cmd)
|
elseif empty(a:cmd)
|
||||||
execute ":" . g:go_alternate_mode . " " . alt_file
|
execute ":" . go#config#AlternateMode() . " " . alt_file
|
||||||
else
|
else
|
||||||
execute ":" . a:cmd . " " . alt_file
|
execute ":" . a:cmd . " " . alt_file
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
"
|
"
|
||||||
" Flag to automatically call :Fmt when file is saved.
|
" Flag to automatically call :Fmt when file is saved.
|
||||||
|
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
let s:got_fmt_error = 0
|
let s:got_fmt_error = 0
|
||||||
|
|
||||||
" This is a trimmed-down version of the logic in fmt.vim.
|
" This is a trimmed-down version of the logic in fmt.vim.
|
||||||
|
@ -28,42 +32,45 @@ function! go#asmfmt#Format() abort
|
||||||
call writefile(go#util#GetLines(), l:tmpname)
|
call writefile(go#util#GetLines(), l:tmpname)
|
||||||
|
|
||||||
" Run asmfmt.
|
" Run asmfmt.
|
||||||
let path = go#path#CheckBinPath("asmfmt")
|
let [l:out, l:err] = go#util#Exec(['asmfmt', '-w', l:tmpname])
|
||||||
if empty(path)
|
if l:err
|
||||||
|
call go#util#EchoError(l:out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
let out = go#util#System(path . ' -w ' . l:tmpname)
|
|
||||||
|
|
||||||
" If there's no error, replace the current file with the output.
|
|
||||||
if go#util#ShellError() == 0
|
|
||||||
" Remove undo point caused by BufWritePre.
|
" Remove undo point caused by BufWritePre.
|
||||||
try | silent undojoin | catch | endtry
|
try | silent undojoin | catch | endtry
|
||||||
|
|
||||||
" Replace the current file with the temp file; then reload the buffer.
|
" Replace the current file with the temp file; then reload the buffer.
|
||||||
let old_fileformat = &fileformat
|
let old_fileformat = &fileformat
|
||||||
|
|
||||||
" save old file permissions
|
" save old file permissions
|
||||||
let original_fperm = getfperm(expand('%'))
|
let original_fperm = getfperm(expand('%'))
|
||||||
call rename(l:tmpname, expand('%'))
|
call rename(l:tmpname, expand('%'))
|
||||||
|
|
||||||
" restore old file permissions
|
" restore old file permissions
|
||||||
call setfperm(expand('%'), original_fperm)
|
call setfperm(expand('%'), original_fperm)
|
||||||
silent edit!
|
silent edit!
|
||||||
let &fileformat = old_fileformat
|
let &fileformat = old_fileformat
|
||||||
let &syntax = &syntax
|
let &syntax = &syntax
|
||||||
endif
|
|
||||||
|
|
||||||
" Restore the cursor/window positions.
|
" Restore the cursor/window positions.
|
||||||
call winrestview(l:curw)
|
call winrestview(l:curw)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#asmfmt#ToggleAsmFmtAutoSave() abort
|
function! go#asmfmt#ToggleAsmFmtAutoSave() abort
|
||||||
if get(g:, "go_asmfmt_autosave", 0)
|
if go#config#AsmfmtAutosave()
|
||||||
let g:go_asmfmt_autosave = 1
|
call go#config#SetAsmfmtAutosave(1)
|
||||||
call go#util#EchoProgress("auto asmfmt enabled")
|
call go#util#EchoProgress("auto asmfmt enabled")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
let g:go_asmfmt_autosave = 0
|
call go#config#SetAsmfmtAutosave(0)
|
||||||
call go#util#EchoProgress("auto asmfmt disabled")
|
call go#util#EchoProgress("auto asmfmt disabled")
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
93
pack/acp/start/vim-go/autoload/go/auto.vim
Normal file
93
pack/acp/start/vim-go/autoload/go/auto.vim
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
function! go#auto#template_autocreate()
|
||||||
|
if !go#config#TemplateAutocreate() || !&modifiable
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" create new template from scratch
|
||||||
|
call go#template#create()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#auto#echo_go_info()
|
||||||
|
if !go#config#EchoGoInfo()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !exists('v:completed_item') || empty(v:completed_item)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
let item = v:completed_item
|
||||||
|
|
||||||
|
if !has_key(item, "info")
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if empty(item.info)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
redraws! | echo "vim-go: " | echohl Function | echon item.info | echohl None
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#auto#auto_type_info()
|
||||||
|
if !go#config#AutoTypeInfo() || !filereadable(expand('%:p'))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" GoInfo automatic update
|
||||||
|
call go#tool#Info(0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#auto#auto_sameids()
|
||||||
|
if !go#config#AutoSameids() || !filereadable(expand('%:p'))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" GoSameId automatic update
|
||||||
|
call go#guru#SameIds(0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#auto#fmt_autosave()
|
||||||
|
if !go#config#FmtAutosave() || !filereadable(expand('%:p'))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Go code formatting on save
|
||||||
|
call go#fmt#Format(-1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#auto#metalinter_autosave()
|
||||||
|
if !go#config#MetalinterAutosave() || !filereadable(expand('%:p'))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" run gometalinter on save
|
||||||
|
call go#lint#Gometa(!g:go_jump_to_error, 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#auto#modfmt_autosave()
|
||||||
|
if !go#config#ModFmtAutosave() || !filereadable(expand('%:p'))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" go.mod code formatting on save
|
||||||
|
call go#mod#Format()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#auto#asmfmt_autosave()
|
||||||
|
if !go#config#AsmfmtAutosave() || !filereadable(expand('%:p'))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Go asm formatting on save
|
||||||
|
call go#asmfmt#Format()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -1,6 +1,21 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
function! go#cmd#autowrite() abort
|
function! go#cmd#autowrite() abort
|
||||||
if &autowrite == 1 || &autowriteall == 1
|
if &autowrite == 1 || &autowriteall == 1
|
||||||
silent! wall
|
silent! wall
|
||||||
|
else
|
||||||
|
for l:nr in range(0, bufnr('$'))
|
||||||
|
if buflisted(l:nr) && getbufvar(l:nr, '&modified')
|
||||||
|
" Sleep one second to make sure people see the message. Otherwise it is
|
||||||
|
" often immediacy overwritten by the async messages (which also don't
|
||||||
|
" invoke the "hit ENTER" prompt).
|
||||||
|
call go#util#EchoWarning('[No write since last change]')
|
||||||
|
sleep 1
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -11,34 +26,21 @@ endfunction
|
||||||
function! go#cmd#Build(bang, ...) abort
|
function! go#cmd#Build(bang, ...) abort
|
||||||
" Create our command arguments. go build discards any results when it
|
" Create our command arguments. go build discards any results when it
|
||||||
" compiles multiple packages. So we pass the `errors` package just as an
|
" compiles multiple packages. So we pass the `errors` package just as an
|
||||||
" placeholder with the current folder (indicated with '.'). We also pass -i
|
" placeholder with the current folder (indicated with '.').
|
||||||
" that tries to install the dependencies, this has the side effect that it
|
let l:args =
|
||||||
" caches the build results, so every other build is faster.
|
\ ['build', '-tags', go#config#BuildTags()] +
|
||||||
let args =
|
|
||||||
\ ["build"] +
|
|
||||||
\ map(copy(a:000), "expand(v:val)") +
|
\ map(copy(a:000), "expand(v:val)") +
|
||||||
\ [".", "errors"]
|
\ [".", "errors"]
|
||||||
|
|
||||||
" Vim async.
|
" Vim and Neovim async.
|
||||||
if go#util#has_job()
|
if go#util#has_job()
|
||||||
if get(g:, 'go_echo_command_info', 1)
|
|
||||||
call go#util#EchoProgress("building dispatched ...")
|
|
||||||
endif
|
|
||||||
|
|
||||||
call s:cmd_job({
|
call s:cmd_job({
|
||||||
\ 'cmd': ['go'] + args,
|
\ 'cmd': ['go'] + args,
|
||||||
\ 'bang': a:bang,
|
\ 'bang': a:bang,
|
||||||
\ 'for': 'GoBuild',
|
\ 'for': 'GoBuild',
|
||||||
|
\ 'statustype': 'build'
|
||||||
\})
|
\})
|
||||||
|
|
||||||
" Nvim async.
|
|
||||||
elseif has('nvim')
|
|
||||||
if get(g:, 'go_echo_command_info', 1)
|
|
||||||
call go#util#EchoProgress("building dispatched ...")
|
|
||||||
endif
|
|
||||||
|
|
||||||
call go#jobcontrol#Spawn(a:bang, "build", "GoBuild", args)
|
|
||||||
|
|
||||||
" Vim 7.4 without async
|
" Vim 7.4 without async
|
||||||
else
|
else
|
||||||
let default_makeprg = &makeprg
|
let default_makeprg = &makeprg
|
||||||
|
@ -59,6 +61,7 @@ function! go#cmd#Build(bang, ...) abort
|
||||||
redraw!
|
redraw!
|
||||||
finally
|
finally
|
||||||
execute cd . fnameescape(dir)
|
execute cd . fnameescape(dir)
|
||||||
|
let &makeprg = default_makeprg
|
||||||
endtry
|
endtry
|
||||||
|
|
||||||
let errors = go#list#Get(l:listtype)
|
let errors = go#list#Get(l:listtype)
|
||||||
|
@ -68,8 +71,6 @@ function! go#cmd#Build(bang, ...) abort
|
||||||
else
|
else
|
||||||
call go#util#EchoSuccess("[build] SUCCESS")
|
call go#util#EchoSuccess("[build] SUCCESS")
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let &makeprg = default_makeprg
|
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -77,33 +78,44 @@ endfunction
|
||||||
" BuildTags sets or shows the current build tags used for tools
|
" BuildTags sets or shows the current build tags used for tools
|
||||||
function! go#cmd#BuildTags(bang, ...) abort
|
function! go#cmd#BuildTags(bang, ...) abort
|
||||||
if a:0
|
if a:0
|
||||||
if a:0 == 1 && a:1 == '""'
|
let v = a:1
|
||||||
unlet g:go_build_tags
|
if v == '""' || v == "''"
|
||||||
|
let v = ""
|
||||||
|
endif
|
||||||
|
call go#config#SetBuildTags(v)
|
||||||
|
let tags = go#config#BuildTags()
|
||||||
|
if empty(tags)
|
||||||
call go#util#EchoSuccess("build tags are cleared")
|
call go#util#EchoSuccess("build tags are cleared")
|
||||||
else
|
else
|
||||||
let g:go_build_tags = a:1
|
call go#util#EchoSuccess("build tags are changed to: " . tags)
|
||||||
call go#util#EchoSuccess("build tags are changed to: ". a:1)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !exists('g:go_build_tags')
|
let tags = go#config#BuildTags()
|
||||||
|
if empty(tags)
|
||||||
call go#util#EchoSuccess("build tags are not set")
|
call go#util#EchoSuccess("build tags are not set")
|
||||||
else
|
else
|
||||||
call go#util#EchoSuccess("current build tags: ". g:go_build_tags)
|
call go#util#EchoSuccess("current build tags: " . tags)
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
" Run runs the current file (and their dependencies if any) in a new terminal.
|
" Run runs the current file (and their dependencies if any) in a new terminal.
|
||||||
function! go#cmd#RunTerm(bang, mode, files) abort
|
function! go#cmd#RunTerm(bang, mode, files) abort
|
||||||
if empty(a:files)
|
let cmd = "go run "
|
||||||
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
|
let tags = go#config#BuildTags()
|
||||||
else
|
if len(tags) > 0
|
||||||
let cmd = "go run ". go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1)
|
let cmd .= "-tags " . go#util#Shellescape(tags) . " "
|
||||||
endif
|
endif
|
||||||
call go#term#newmode(a:bang, cmd, a:mode)
|
|
||||||
|
if empty(a:files)
|
||||||
|
let cmd .= go#util#Shelljoin(go#tool#Files())
|
||||||
|
else
|
||||||
|
let cmd .= go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1)
|
||||||
|
endif
|
||||||
|
call go#term#newmode(a:bang, cmd, s:runerrorformat(), a:mode)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Run runs the current file (and their dependencies if any) and outputs it.
|
" Run runs the current file (and their dependencies if any) and outputs it.
|
||||||
|
@ -122,8 +134,19 @@ function! go#cmd#Run(bang, ...) abort
|
||||||
" anything. Once this is implemented we're going to make :GoRun async
|
" anything. Once this is implemented we're going to make :GoRun async
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
let cmd = "go run "
|
||||||
|
let tags = go#config#BuildTags()
|
||||||
|
if len(tags) > 0
|
||||||
|
let cmd .= "-tags " . go#util#Shellescape(tags) . " "
|
||||||
|
endif
|
||||||
|
|
||||||
if go#util#IsWin()
|
if go#util#IsWin()
|
||||||
exec '!go run ' . go#util#Shelljoin(go#tool#Files())
|
if a:0 == 0
|
||||||
|
exec '!' . cmd . go#util#Shelljoin(go#tool#Files(), 1)
|
||||||
|
else
|
||||||
|
exec '!' . cmd . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
|
||||||
|
endif
|
||||||
|
|
||||||
if v:shell_error
|
if v:shell_error
|
||||||
redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None
|
redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None
|
||||||
else
|
else
|
||||||
|
@ -136,29 +159,36 @@ function! go#cmd#Run(bang, ...) abort
|
||||||
" :make expands '%' and '#' wildcards, so they must also be escaped
|
" :make expands '%' and '#' wildcards, so they must also be escaped
|
||||||
let default_makeprg = &makeprg
|
let default_makeprg = &makeprg
|
||||||
if a:0 == 0
|
if a:0 == 0
|
||||||
let &makeprg = 'go run ' . go#util#Shelljoin(go#tool#Files(), 1)
|
let &makeprg = cmd . go#util#Shelljoin(go#tool#Files(), 1)
|
||||||
else
|
else
|
||||||
let &makeprg = "go run " . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
|
let &makeprg = cmd . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:listtype = go#list#Type("GoRun")
|
let l:listtype = go#list#Type("GoRun")
|
||||||
|
|
||||||
|
try
|
||||||
|
|
||||||
|
" backup user's errorformat, will be restored once we are finished
|
||||||
|
let l:old_errorformat = &errorformat
|
||||||
|
let &errorformat = s:runerrorformat()
|
||||||
if l:listtype == "locationlist"
|
if l:listtype == "locationlist"
|
||||||
exe 'lmake!'
|
exe 'lmake!'
|
||||||
else
|
else
|
||||||
exe 'make!'
|
exe 'make!'
|
||||||
endif
|
endif
|
||||||
|
finally
|
||||||
|
"restore errorformat
|
||||||
|
let &errorformat = l:old_errorformat
|
||||||
|
let &makeprg = default_makeprg
|
||||||
|
endtry
|
||||||
|
|
||||||
let items = go#list#Get(l:listtype)
|
let l:errors = go#list#Get(l:listtype)
|
||||||
let errors = go#tool#FilterValids(items)
|
|
||||||
|
|
||||||
call go#list#Populate(l:listtype, errors, &makeprg)
|
call go#list#Window(l:listtype, len(l:errors))
|
||||||
call go#list#Window(l:listtype, len(errors))
|
if !empty(l:errors) && !a:bang
|
||||||
if !empty(errors) && !a:bang
|
|
||||||
call go#list#JumpToFirst(l:listtype)
|
call go#list#JumpToFirst(l:listtype)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let &makeprg = default_makeprg
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Install installs the package by simple calling 'go install'. If any argument
|
" Install installs the package by simple calling 'go install'. If any argument
|
||||||
|
@ -170,14 +200,11 @@ function! go#cmd#Install(bang, ...) abort
|
||||||
" expand all wildcards(i.e: '%' to the current file name)
|
" expand all wildcards(i.e: '%' to the current file name)
|
||||||
let goargs = map(copy(a:000), "expand(v:val)")
|
let goargs = map(copy(a:000), "expand(v:val)")
|
||||||
|
|
||||||
if get(g:, 'go_echo_command_info', 1)
|
|
||||||
call go#util#EchoProgress("installing dispatched ...")
|
|
||||||
endif
|
|
||||||
|
|
||||||
call s:cmd_job({
|
call s:cmd_job({
|
||||||
\ 'cmd': ['go', 'install'] + goargs,
|
\ 'cmd': ['go', 'install', '-tags', go#config#BuildTags()] + goargs,
|
||||||
\ 'bang': a:bang,
|
\ 'bang': a:bang,
|
||||||
\ 'for': 'GoInstall',
|
\ 'for': 'GoInstall',
|
||||||
|
\ 'statustype': 'install'
|
||||||
\})
|
\})
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -203,6 +230,7 @@ function! go#cmd#Install(bang, ...) abort
|
||||||
redraw!
|
redraw!
|
||||||
finally
|
finally
|
||||||
execute cd . fnameescape(dir)
|
execute cd . fnameescape(dir)
|
||||||
|
let &makeprg = default_makeprg
|
||||||
endtry
|
endtry
|
||||||
|
|
||||||
let errors = go#list#Get(l:listtype)
|
let errors = go#list#Get(l:listtype)
|
||||||
|
@ -212,8 +240,6 @@ function! go#cmd#Install(bang, ...) abort
|
||||||
else
|
else
|
||||||
call go#util#EchoSuccess("installed to ". go#path#Default())
|
call go#util#EchoSuccess("installed to ". go#path#Default())
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let &makeprg = default_makeprg
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Generate runs 'go generate' in similar fashion to go#cmd#Build()
|
" Generate runs 'go generate' in similar fashion to go#cmd#Build()
|
||||||
|
@ -232,12 +258,17 @@ function! go#cmd#Generate(bang, ...) abort
|
||||||
let l:listtype = go#list#Type("GoGenerate")
|
let l:listtype = go#list#Type("GoGenerate")
|
||||||
|
|
||||||
echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None
|
echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None
|
||||||
|
|
||||||
|
try
|
||||||
if l:listtype == "locationlist"
|
if l:listtype == "locationlist"
|
||||||
silent! exe 'lmake!'
|
silent! exe 'lmake!'
|
||||||
else
|
else
|
||||||
silent! exe 'make!'
|
silent! exe 'make!'
|
||||||
endif
|
endif
|
||||||
|
finally
|
||||||
redraw!
|
redraw!
|
||||||
|
let &makeprg = default_makeprg
|
||||||
|
endtry
|
||||||
|
|
||||||
let errors = go#list#Get(l:listtype)
|
let errors = go#list#Get(l:listtype)
|
||||||
call go#list#Window(l:listtype, len(errors))
|
call go#list#Window(l:listtype, len(errors))
|
||||||
|
@ -249,64 +280,27 @@ function! go#cmd#Generate(bang, ...) abort
|
||||||
redraws! | echon "vim-go: " | echohl Function | echon "[generate] SUCCESS"| echohl None
|
redraws! | echon "vim-go: " | echohl Function | echon "[generate] SUCCESS"| echohl None
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let &makeprg = default_makeprg
|
endfunction
|
||||||
|
|
||||||
|
function! s:runerrorformat()
|
||||||
|
let l:panicaddress = "%\\t%#%f:%l +0x%[0-9A-Fa-f]%\\+"
|
||||||
|
let l:errorformat = '%A' . l:panicaddress . "," . &errorformat
|
||||||
|
return l:errorformat
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" ---------------------
|
" ---------------------
|
||||||
" | Vim job callbacks |
|
" | Vim job callbacks |
|
||||||
" ---------------------
|
" ---------------------
|
||||||
|
|
||||||
function s:cmd_job(args) abort
|
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",
|
|
||||||
\})
|
|
||||||
|
|
||||||
" autowrite is not enabled for jobs
|
" autowrite is not enabled for jobs
|
||||||
call go#cmd#autowrite()
|
call go#cmd#autowrite()
|
||||||
|
|
||||||
function! s:complete(job, exit_status, data) closure abort
|
call go#job#Spawn(a:args.cmd, a:args)
|
||||||
let status = {
|
|
||||||
\ 'desc': 'last status',
|
|
||||||
\ 'type': a:args.cmd[1],
|
|
||||||
\ 'state': "success",
|
|
||||||
\ }
|
|
||||||
|
|
||||||
if a:exit_status
|
|
||||||
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)
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
let a:args.complete = funcref('s:complete')
|
" restore Vi compatibility settings
|
||||||
let callbacks = go#job#Spawn(a:args)
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
let start_options = {
|
|
||||||
\ 'callback': callbacks.callback,
|
|
||||||
\ 'exit_cb': callbacks.exit_cb,
|
|
||||||
\ 'close_cb': callbacks.close_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
|
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_GoBuildErrors()
|
func! Test_GoBuildErrors()
|
||||||
try
|
try
|
||||||
let l:filename = 'cmd/bad.go'
|
let l:filename = 'cmd/bad.go'
|
||||||
|
@ -27,4 +31,8 @@ func! Test_GoBuildErrors()
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,16 +1,39 @@
|
||||||
let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
function! s:gocodeCommand(cmd, args) abort
|
function! s:gocodeCommand(cmd, args) abort
|
||||||
let bin_path = go#path#CheckBinPath("gocode")
|
let l:gocode_bin = "gocode"
|
||||||
|
let l:gomod = go#util#gomod()
|
||||||
|
if filereadable(l:gomod)
|
||||||
|
let l:gocode_bin = "gocode-gomod"
|
||||||
|
endif
|
||||||
|
|
||||||
|
let bin_path = go#path#CheckBinPath(l:gocode_bin)
|
||||||
if empty(bin_path)
|
if empty(bin_path)
|
||||||
return []
|
return []
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
|
let socket_type = go#config#GocodeSocketType()
|
||||||
|
|
||||||
let cmd = [bin_path]
|
let cmd = [bin_path]
|
||||||
let cmd = extend(cmd, ['-sock', socket_type])
|
let cmd = extend(cmd, ['-sock', socket_type])
|
||||||
let cmd = extend(cmd, ['-f', 'vim'])
|
let cmd = extend(cmd, ['-f', 'vim'])
|
||||||
|
|
||||||
|
if go#config#GocodeProposeBuiltins()
|
||||||
|
let cmd = extend(cmd, ['-builtin'])
|
||||||
|
endif
|
||||||
|
|
||||||
|
if go#config#GocodeProposeSource()
|
||||||
|
let cmd = extend(cmd, ['-source'])
|
||||||
|
else
|
||||||
|
let cmd = extend(cmd, ['-fallback-to-source', '-cache'])
|
||||||
|
endif
|
||||||
|
|
||||||
|
if go#config#GocodeUnimportedPackages()
|
||||||
|
let cmd = extend(cmd, ['-unimported-packages'])
|
||||||
|
endif
|
||||||
|
|
||||||
let cmd = extend(cmd, [a:cmd])
|
let cmd = extend(cmd, [a:cmd])
|
||||||
let cmd = extend(cmd, a:args)
|
let cmd = extend(cmd, a:args)
|
||||||
|
|
||||||
|
@ -45,32 +68,7 @@ function! s:sync_gocode(cmd, args, input) abort
|
||||||
return l:result
|
return l:result
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" TODO(bc): reset when gocode isn't running
|
|
||||||
let s:optionsEnabled = 0
|
|
||||||
function! s:gocodeEnableOptions() abort
|
|
||||||
if s:optionsEnabled
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
let bin_path = go#path#CheckBinPath("gocode")
|
|
||||||
if empty(bin_path)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
let s:optionsEnabled = 1
|
|
||||||
|
|
||||||
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! s:toBool(val) abort
|
|
||||||
if a:val | return 'true ' | else | return 'false' | endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:gocodeAutocomplete() abort
|
function! s:gocodeAutocomplete() abort
|
||||||
call s:gocodeEnableOptions()
|
|
||||||
|
|
||||||
" use the offset as is, because the cursor position is the position for
|
" use the offset as is, because the cursor position is the position for
|
||||||
" which autocomplete candidates are needed.
|
" which autocomplete candidates are needed.
|
||||||
return s:sync_gocode('autocomplete',
|
return s:sync_gocode('autocomplete',
|
||||||
|
@ -84,60 +82,20 @@ function! go#complete#GetInfo() abort
|
||||||
return s:sync_info(0)
|
return s:sync_info(0)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#complete#Info(auto) abort
|
function! go#complete#Info(showstatus) abort
|
||||||
if go#util#has_job()
|
if go#util#has_job(1)
|
||||||
return s:async_info(a:auto)
|
return s:async_info(1, a:showstatus)
|
||||||
else
|
else
|
||||||
return s:sync_info(a:auto)
|
return s:sync_info(1)
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:async_info(auto)
|
function! s:async_info(echo, showstatus)
|
||||||
if exists("s:async_info_job")
|
let state = {'echo': a:echo}
|
||||||
call job_stop(s:async_info_job)
|
|
||||||
unlet s:async_info_job
|
|
||||||
endif
|
|
||||||
|
|
||||||
let state = {
|
" explicitly bind complete to state so that within it, self will
|
||||||
\ 'exited': 0,
|
" always refer to state. See :help Partial for more information.
|
||||||
\ 'exit_status': 0,
|
let state.complete = function('s:complete', [], state)
|
||||||
\ 'closed': 0,
|
|
||||||
\ 'messages': [],
|
|
||||||
\ 'auto': a:auto
|
|
||||||
\ }
|
|
||||||
|
|
||||||
function! s:callback(chan, msg) dict
|
|
||||||
let l:msg = a:msg
|
|
||||||
if &encoding != 'utf-8'
|
|
||||||
let l:msg = iconv(l:msg, 'utf-8', &encoding)
|
|
||||||
endif
|
|
||||||
call add(self.messages, l:msg)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:exit_cb(job, exitval) dict
|
|
||||||
let self.exit_status = a:exitval
|
|
||||||
let self.exited = 1
|
|
||||||
|
|
||||||
if self.closed
|
|
||||||
call self.complete()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:close_cb(ch) dict
|
|
||||||
let self.closed = 1
|
|
||||||
if self.exited
|
|
||||||
call self.complete()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function state.complete() dict
|
|
||||||
if self.exit_status != 0
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
let result = s:info_filter(self.auto, join(self.messages, "\n"))
|
|
||||||
call s:info_complete(self.auto, result)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" add 1 to the offset, so that the position at the cursor will be included
|
" add 1 to the offset, so that the position at the cursor will be included
|
||||||
" in gocode's search
|
" in gocode's search
|
||||||
|
@ -149,23 +107,49 @@ function! s:async_info(auto)
|
||||||
\ "GOROOT": go#util#env("goroot")
|
\ "GOROOT": go#util#env("goroot")
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
|
let opts = {
|
||||||
|
\ 'bang': 1,
|
||||||
|
\ 'complete': state.complete,
|
||||||
|
\ 'for': '_',
|
||||||
|
\ }
|
||||||
|
|
||||||
|
if a:showstatus
|
||||||
|
let opts.statustype = 'gocode'
|
||||||
|
endif
|
||||||
|
|
||||||
|
let opts = go#job#Options(l:opts)
|
||||||
|
|
||||||
let cmd = s:gocodeCommand('autocomplete',
|
let cmd = s:gocodeCommand('autocomplete',
|
||||||
\ [expand('%:p'), offset])
|
\ [expand('%:p'), offset])
|
||||||
|
|
||||||
" TODO(bc): Don't write the buffer to a file; pass the buffer directrly to
|
" TODO(bc): Don't write the buffer to a file; pass the buffer directly to
|
||||||
" gocode's stdin. It shouldn't be necessary to use {in_io: 'file', in_name:
|
" gocode's stdin. It shouldn't be necessary to use {in_io: 'file', in_name:
|
||||||
" s:gocodeFile()}, but unfortunately {in_io: 'buffer', in_buf: bufnr('%')}
|
" s:gocodeFile()}, but unfortunately {in_io: 'buffer', in_buf: bufnr('%')}
|
||||||
" should work.
|
" doesn't work.
|
||||||
let options = {
|
call extend(opts, {
|
||||||
\ 'env': env,
|
\ 'env': env,
|
||||||
\ 'in_io': 'file',
|
\ 'in_io': 'file',
|
||||||
\ 'in_name': s:gocodeFile(),
|
\ 'in_name': s:gocodeFile(),
|
||||||
\ 'callback': funcref("s:callback", [], state),
|
\ })
|
||||||
\ 'exit_cb': funcref("s:exit_cb", [], state),
|
|
||||||
\ 'close_cb': funcref("s:close_cb", [], state)
|
|
||||||
\ }
|
|
||||||
|
|
||||||
let s:async_info_job = job_start(cmd, options)
|
call go#job#Start(cmd, opts)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:complete(job, exit_status, messages) abort dict
|
||||||
|
if a:exit_status != 0
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if &encoding != 'utf-8'
|
||||||
|
let i = 0
|
||||||
|
while i < len(a:messages)
|
||||||
|
let a:messages[i] = iconv(a:messages[i], 'utf-8', &encoding)
|
||||||
|
let i += 1
|
||||||
|
endwhile
|
||||||
|
endif
|
||||||
|
|
||||||
|
let result = s:info_filter(self.echo, join(a:messages, "\n"))
|
||||||
|
call s:info_complete(self.echo, result)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:gocodeFile()
|
function! s:gocodeFile()
|
||||||
|
@ -174,9 +158,7 @@ function! s:gocodeFile()
|
||||||
return file
|
return file
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:sync_info(auto)
|
function! s:sync_info(echo)
|
||||||
" auto is true if we were called by g:go_auto_type_info's autocmd
|
|
||||||
|
|
||||||
" add 1 to the offset, so that the position at the cursor will be included
|
" add 1 to the offset, so that the position at the cursor will be included
|
||||||
" in gocode's search
|
" in gocode's search
|
||||||
let offset = go#util#OffsetCursor()+1
|
let offset = go#util#OffsetCursor()+1
|
||||||
|
@ -185,11 +167,11 @@ function! s:sync_info(auto)
|
||||||
\ [expand('%:p'), offset],
|
\ [expand('%:p'), offset],
|
||||||
\ go#util#GetLines())
|
\ go#util#GetLines())
|
||||||
|
|
||||||
let result = s:info_filter(a:auto, result)
|
let result = s:info_filter(a:echo, result)
|
||||||
call s:info_complete(a:auto, result)
|
return s:info_complete(a:echo, result)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:info_filter(auto, result) abort
|
function! s:info_filter(echo, result) abort
|
||||||
if empty(a:result)
|
if empty(a:result)
|
||||||
return ""
|
return ""
|
||||||
endif
|
endif
|
||||||
|
@ -203,7 +185,7 @@ function! s:info_filter(auto, result) abort
|
||||||
if len(l:candidates) == 1
|
if len(l:candidates) == 1
|
||||||
" When gocode panics in vim mode, it returns
|
" When gocode panics in vim mode, it returns
|
||||||
" [0, [{'word': 'PANIC', 'abbr': 'PANIC PANIC PANIC', 'info': 'PANIC PANIC PANIC'}]]
|
" [0, [{'word': 'PANIC', 'abbr': 'PANIC PANIC PANIC', 'info': 'PANIC PANIC PANIC'}]]
|
||||||
if a:auto && l:candidates[0].info ==# "PANIC PANIC PANIC"
|
if a:echo && l:candidates[0].info ==# "PANIC PANIC PANIC"
|
||||||
return ""
|
return ""
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -216,17 +198,21 @@ function! s:info_filter(auto, result) abort
|
||||||
let wordMatch = substitute(wordMatch, "'", "''", "g")
|
let wordMatch = substitute(wordMatch, "'", "''", "g")
|
||||||
let filtered = filter(l:candidates, "v:val.info =~ '".wordMatch."'")
|
let filtered = filter(l:candidates, "v:val.info =~ '".wordMatch."'")
|
||||||
|
|
||||||
if len(l:filtered) != 1
|
if len(l:filtered) == 0
|
||||||
return ""
|
return "no matches"
|
||||||
|
elseif len(l:filtered) > 1
|
||||||
|
return "ambiguous match"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return l:filtered[0].info
|
return l:filtered[0].info
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:info_complete(auto, result) abort
|
function! s:info_complete(echo, result) abort
|
||||||
if !empty(a:result)
|
if a:echo
|
||||||
echo "vim-go: " | echohl Function | echon a:result | echohl None
|
call go#util#ShowInfo(a:result)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
return a:result
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:trim_bracket(val) abort
|
function! s:trim_bracket(val) abort
|
||||||
|
@ -234,12 +220,22 @@ function! s:trim_bracket(val) abort
|
||||||
return a:val
|
return a:val
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
let s:completions = ""
|
let s:completions = []
|
||||||
function! go#complete#Complete(findstart, base) abort
|
|
||||||
|
function! go#complete#GocodeComplete(findstart, base) abort
|
||||||
"findstart = 1 when we need to get the text length
|
"findstart = 1 when we need to get the text length
|
||||||
if a:findstart == 1
|
if a:findstart == 1
|
||||||
execute "silent let s:completions = " . s:gocodeAutocomplete()
|
let l:completions = []
|
||||||
return col('.') - s:completions[0] - 1
|
execute "silent let l:completions = " . s:gocodeAutocomplete()
|
||||||
|
|
||||||
|
if len(l:completions) == 0 || len(l:completions) >= 2 && len(l:completions[1]) == 0
|
||||||
|
" no matches. cancel and leave completion mode.
|
||||||
|
call go#util#EchoInfo("no matches")
|
||||||
|
return -3
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:completions = l:completions[1]
|
||||||
|
return col('.') - l:completions[0] - 1
|
||||||
"findstart = 0 when we need to return the list of completions
|
"findstart = 0 when we need to return the list of completions
|
||||||
else
|
else
|
||||||
let s = getline(".")[col('.') - 1]
|
let s = getline(".")[col('.') - 1]
|
||||||
|
@ -251,15 +247,49 @@ function! go#complete#Complete(findstart, base) abort
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! go#complete#Complete(findstart, base) abort
|
||||||
|
let l:state = {'done': 0, 'matches': []}
|
||||||
|
|
||||||
|
function! s:handler(state, matches) abort dict
|
||||||
|
let a:state.matches = a:matches
|
||||||
|
let a:state.done = 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
"findstart = 1 when we need to get the start of the match
|
||||||
|
if a:findstart == 1
|
||||||
|
call go#lsp#Completion(expand('%:p'), line('.'), col('.'), funcref('s:handler', [l:state]))
|
||||||
|
|
||||||
|
while !l:state.done
|
||||||
|
sleep 10m
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
let s:completions = l:state.matches
|
||||||
|
|
||||||
|
if len(l:state.matches) == 0
|
||||||
|
" no matches. cancel and leave completion mode.
|
||||||
|
call go#util#EchoInfo("no matches")
|
||||||
|
return -3
|
||||||
|
endif
|
||||||
|
|
||||||
|
return col('.')
|
||||||
|
else "findstart = 0 when we need to return the list of completions
|
||||||
|
return s:completions
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! go#complete#ToggleAutoTypeInfo() abort
|
function! go#complete#ToggleAutoTypeInfo() abort
|
||||||
if get(g:, "go_auto_type_info", 0)
|
if go#config#AutoTypeInfo()
|
||||||
let g:go_auto_type_info = 0
|
call go#config#SetAutoTypeInfo(0)
|
||||||
call go#util#EchoProgress("auto type info disabled")
|
call go#util#EchoProgress("auto type info disabled")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
let g:go_auto_type_info = 1
|
call go#config#SetAutoTypeInfo(1)
|
||||||
call go#util#EchoProgress("auto type info enabled")
|
call go#util#EchoProgress("auto type info enabled")
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
28
pack/acp/start/vim-go/autoload/go/complete_test.vim
Normal file
28
pack/acp/start/vim-go/autoload/go/complete_test.vim
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
func! Test_GetInfo()
|
||||||
|
let l:filename = 'complete/complete.go'
|
||||||
|
let l:tmp = gotest#load_fixture(l:filename)
|
||||||
|
|
||||||
|
call cursor(8, 3)
|
||||||
|
|
||||||
|
let g:go_info_mode = 'gocode'
|
||||||
|
let expected = 'func Example(s string)'
|
||||||
|
let actual = go#complete#GetInfo()
|
||||||
|
call assert_equal(expected, actual)
|
||||||
|
|
||||||
|
let g:go_info_mode = 'guru'
|
||||||
|
call go#config#InfoMode()
|
||||||
|
let actual = go#complete#GetInfo()
|
||||||
|
call assert_equal(expected, actual)
|
||||||
|
|
||||||
|
unlet g:go_info_mode
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
479
pack/acp/start/vim-go/autoload/go/config.vim
Normal file
479
pack/acp/start/vim-go/autoload/go/config.vim
Normal file
|
@ -0,0 +1,479 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
function! go#config#AutodetectGopath() abort
|
||||||
|
return get(g:, 'go_autodetect_gopath', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#ListTypeCommands() abort
|
||||||
|
return get(g:, 'go_list_type_commands', {})
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#VersionWarning() abort
|
||||||
|
return get(g:, 'go_version_warning', 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#BuildTags() abort
|
||||||
|
return get(g:, 'go_build_tags', '')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetBuildTags(value) abort
|
||||||
|
if a:value is ''
|
||||||
|
silent! unlet g:go_build_tags
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let g:go_build_tags = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TestTimeout() abort
|
||||||
|
return get(g:, 'go_test_timeout', '10s')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TestShowName() abort
|
||||||
|
return get(g:, 'go_test_show_name', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TermHeight() abort
|
||||||
|
return get(g:, 'go_term_height', winheight(0))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TermWidth() abort
|
||||||
|
return get(g:, 'go_term_width', winwidth(0))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TermMode() abort
|
||||||
|
return get(g:, 'go_term_mode', 'vsplit')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TermEnabled() abort
|
||||||
|
return has('nvim') && get(g:, 'go_term_enabled', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetTermEnabled(value) abort
|
||||||
|
let g:go_term_enabled = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TemplateUsePkg() abort
|
||||||
|
return get(g:, 'go_template_use_pkg', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TemplateTestFile() abort
|
||||||
|
return get(g:, 'go_template_test_file', "hello_world_test.go")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TemplateFile() abort
|
||||||
|
return get(g:, 'go_template_file', "hello_world.go")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#StatuslineDuration() abort
|
||||||
|
return get(g:, 'go_statusline_duration', 60000)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SnippetEngine() abort
|
||||||
|
return get(g:, 'go_snippet_engine', 'automatic')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#PlayBrowserCommand() abort
|
||||||
|
if go#util#IsWin()
|
||||||
|
let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%'
|
||||||
|
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
|
||||||
|
|
||||||
|
return get(g:, 'go_play_browser_command', go_play_browser_command)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#MetalinterDeadline() abort
|
||||||
|
" gometalinter has a default deadline of 5 seconds only when asynchronous
|
||||||
|
" jobs are not supported.
|
||||||
|
|
||||||
|
let deadline = '5s'
|
||||||
|
if go#util#has_job() && has('lambda')
|
||||||
|
let deadline = ''
|
||||||
|
endif
|
||||||
|
|
||||||
|
return get(g:, 'go_metalinter_deadline', deadline)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#ListType() abort
|
||||||
|
return get(g:, 'go_list_type', '')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#ListAutoclose() abort
|
||||||
|
return get(g:, 'go_list_autoclose', 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#InfoMode() abort
|
||||||
|
return get(g:, 'go_info_mode', 'gocode')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#GuruScope() abort
|
||||||
|
let scope = get(g:, 'go_guru_scope', [])
|
||||||
|
|
||||||
|
if !empty(scope)
|
||||||
|
" strip trailing slashes for each path in scope. bug:
|
||||||
|
" https://github.com/golang/go/issues/14584
|
||||||
|
let scopes = go#util#StripTrailingSlash(scope)
|
||||||
|
endif
|
||||||
|
|
||||||
|
return scope
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetGuruScope(scope) abort
|
||||||
|
if empty(a:scope)
|
||||||
|
if exists('g:go_guru_scope')
|
||||||
|
unlet g:go_guru_scope
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
let g:go_guru_scope = a:scope
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#GocodeUnimportedPackages() abort
|
||||||
|
return get(g:, 'go_gocode_unimported_packages', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
|
||||||
|
function! go#config#GocodeSocketType() abort
|
||||||
|
return get(g:, 'go_gocode_socket_type', s:sock_type)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#GocodeProposeBuiltins() abort
|
||||||
|
return get(g:, 'go_gocode_propose_builtins', 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#GocodeProposeSource() abort
|
||||||
|
return get(g:, 'go_gocode_propose_source', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#EchoCommandInfo() abort
|
||||||
|
return get(g:, 'go_echo_command_info', 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DocUrl() abort
|
||||||
|
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 expect /pkg before package names
|
||||||
|
let godoc_url .= "/pkg"
|
||||||
|
endif
|
||||||
|
return godoc_url
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DefReuseBuffer() abort
|
||||||
|
return get(g:, 'go_def_reuse_buffer', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DefMode() abort
|
||||||
|
return get(g:, 'go_def_mode', 'guru')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DeclsIncludes() abort
|
||||||
|
return get(g:, 'go_decls_includes', 'func,type')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#Debug() abort
|
||||||
|
return get(g:, 'go_debug', [])
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DebugWindows() abort
|
||||||
|
return get(g:, 'go_debug_windows', {
|
||||||
|
\ 'stack': 'leftabove 20vnew',
|
||||||
|
\ 'out': 'botright 10new',
|
||||||
|
\ 'vars': 'leftabove 30vnew',
|
||||||
|
\ }
|
||||||
|
\ )
|
||||||
|
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DebugAddress() abort
|
||||||
|
return get(g:, 'go_debug_address', '127.0.0.1:8181')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DebugCommands() abort
|
||||||
|
" make sure g:go_debug_commands is set so that it can be added to easily.
|
||||||
|
let g:go_debug_commands = get(g:, 'go_debug_commands', [])
|
||||||
|
return g:go_debug_commands
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DebugLogOutput() abort
|
||||||
|
return get(g:, 'go_debug_log_output', 'debugger, rpc')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#LspLog() abort
|
||||||
|
" make sure g:go_lsp_log is set so that it can be added to easily.
|
||||||
|
let g:go_lsp_log = get(g:, 'go_lsp_log', [])
|
||||||
|
return g:go_lsp_log
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetDebugDiag(value) abort
|
||||||
|
let g:go_debug_diag = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#AutoSameids() abort
|
||||||
|
return get(g:, 'go_auto_sameids', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetAutoSameids(value) abort
|
||||||
|
let g:go_auto_sameids = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#AddtagsTransform() abort
|
||||||
|
return get(g:, 'go_addtags_transform', "snakecase")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TemplateAutocreate() abort
|
||||||
|
return get(g:, "go_template_autocreate", 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetTemplateAutocreate(value) abort
|
||||||
|
let g:go_template_autocreate = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#MetalinterCommand() abort
|
||||||
|
return get(g:, "go_metalinter_command", "gometalinter")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#MetalinterAutosaveEnabled() abort
|
||||||
|
let l:default_enabled = ["vet", "golint"]
|
||||||
|
|
||||||
|
if go#config#MetalinterCommand() == "golangci-lint"
|
||||||
|
let l:default_enabled = ["govet", "golint"]
|
||||||
|
endif
|
||||||
|
|
||||||
|
return get(g:, "go_metalinter_autosave_enabled", default_enabled)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#MetalinterEnabled() abort
|
||||||
|
let l:default_enabled = ["vet", "golint", "errcheck"]
|
||||||
|
|
||||||
|
if go#config#MetalinterCommand() == "golangci-lint"
|
||||||
|
let l:default_enabled = ["govet", "golint"]
|
||||||
|
endif
|
||||||
|
|
||||||
|
return get(g:, "go_metalinter_enabled", default_enabled)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#MetalinterDisabled() abort
|
||||||
|
return get(g:, "go_metalinter_disabled", [])
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#GolintBin() abort
|
||||||
|
return get(g:, "go_golint_bin", "golint")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#ErrcheckBin() abort
|
||||||
|
return get(g:, "go_errcheck_bin", "errcheck")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#MetalinterAutosave() abort
|
||||||
|
return get(g:, "go_metalinter_autosave", 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetMetalinterAutosave(value) abort
|
||||||
|
let g:go_metalinter_autosave = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#ListHeight() abort
|
||||||
|
return get(g:, "go_list_height", 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#FmtAutosave() abort
|
||||||
|
return get(g:, "go_fmt_autosave", 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetFmtAutosave(value) abort
|
||||||
|
let g:go_fmt_autosave = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#AsmfmtAutosave() abort
|
||||||
|
return get(g:, "go_asmfmt_autosave", 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetAsmfmtAutosave(value) abort
|
||||||
|
let g:go_asmfmt_autosave = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#ModFmtAutosave() abort
|
||||||
|
return get(g:, "go_mod_fmt_autosave", 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetModFmtAutosave(value) abort
|
||||||
|
let g:go_mod_fmt_autosave = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DocMaxHeight() abort
|
||||||
|
return get(g:, "go_doc_max_height", 20)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#AutoTypeInfo() abort
|
||||||
|
return get(g:, "go_auto_type_info", 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SetAutoTypeInfo(value) abort
|
||||||
|
let g:go_auto_type_info = a:value
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#AlternateMode() abort
|
||||||
|
return get(g:, "go_alternate_mode", "edit")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#DeclsMode() abort
|
||||||
|
return get(g:, "go_decls_mode", "")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#FmtCommand() abort
|
||||||
|
return get(g:, "go_fmt_command", "gofmt")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#FmtOptions() abort
|
||||||
|
return get(g:, "go_fmt_options", {})
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#FmtFailSilently() abort
|
||||||
|
return get(g:, "go_fmt_fail_silently", 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#FmtExperimental() abort
|
||||||
|
return get(g:, "go_fmt_experimental", 0 )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#PlayOpenBrowser() abort
|
||||||
|
return get(g:, "go_play_open_browser", 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#GorenameBin() abort
|
||||||
|
return get(g:, "go_gorename_bin", "gorename")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#GorenamePrefill() abort
|
||||||
|
return get(g:, "go_gorename_prefill", 'expand("<cword>") =~# "^[A-Z]"' .
|
||||||
|
\ '? go#util#pascalcase(expand("<cword>"))' .
|
||||||
|
\ ': go#util#camelcase(expand("<cword>"))')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TextobjIncludeFunctionDoc() abort
|
||||||
|
return get(g:, "go_textobj_include_function_doc", 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#TextobjIncludeVariable() abort
|
||||||
|
return get(g:, "go_textobj_include_variable", 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#BinPath() abort
|
||||||
|
return get(g:, "go_bin_path", "")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#SearchBinPathFirst() abort
|
||||||
|
return get(g:, 'go_search_bin_path_first', 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightArrayWhitespaceError() abort
|
||||||
|
return get(g:, 'go_highlight_array_whitespace_error', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightChanWhitespaceError() abort
|
||||||
|
return get(g:, 'go_highlight_chan_whitespace_error', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightExtraTypes() abort
|
||||||
|
return get(g:, 'go_highlight_extra_types', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightSpaceTabError() abort
|
||||||
|
return get(g:, 'go_highlight_space_tab_error', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightTrailingWhitespaceError() abort
|
||||||
|
return get(g:, 'go_highlight_trailing_whitespace_error', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightOperators() abort
|
||||||
|
return get(g:, 'go_highlight_operators', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightFunctions() abort
|
||||||
|
return get(g:, 'go_highlight_functions', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightFunctionParameters() abort
|
||||||
|
" fallback to highlight_function_arguments for backwards compatibility
|
||||||
|
return get(g:, 'go_highlight_function_parameters', get(g:, 'go_highlight_function_arguments', 0))
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightFunctionCalls() abort
|
||||||
|
return get(g:, 'go_highlight_function_calls', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightFields() abort
|
||||||
|
return get(g:, 'go_highlight_fields', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightTypes() abort
|
||||||
|
return get(g:, 'go_highlight_types', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightBuildConstraints() abort
|
||||||
|
return get(g:, 'go_highlight_build_constraints', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightStringSpellcheck() abort
|
||||||
|
return get(g:, 'go_highlight_string_spellcheck', 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightFormatStrings() abort
|
||||||
|
return get(g:, 'go_highlight_format_strings', 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightGenerateTags() abort
|
||||||
|
return get(g:, 'go_highlight_generate_tags', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightVariableAssignments() abort
|
||||||
|
return get(g:, 'go_highlight_variable_assignments', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightVariableDeclarations() abort
|
||||||
|
return get(g:, 'go_highlight_variable_declarations', 0)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#HighlightDebug() abort
|
||||||
|
return get(g:, 'go_highlight_debug', 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#FoldEnable(...) abort
|
||||||
|
if a:0 > 0
|
||||||
|
return index(go#config#FoldEnable(), a:1) > -1
|
||||||
|
endif
|
||||||
|
return get(g:, 'go_fold_enable', ['block', 'import', 'varconst', 'package_comment'])
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#config#EchoGoInfo() abort
|
||||||
|
return get(g:, "go_echo_go_info", 1)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Set the default value. A value of "1" is a shortcut for this, for
|
||||||
|
" compatibility reasons.
|
||||||
|
if exists("g:go_gorename_prefill") && g:go_gorename_prefill == 1
|
||||||
|
unlet g:go_gorename_prefill
|
||||||
|
endif
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
let s:toggle = 0
|
let s:toggle = 0
|
||||||
|
|
||||||
" Buffer creates a new cover profile with 'go test -coverprofile' and changes
|
" Buffer creates a new cover profile with 'go test -coverprofile' and changes
|
||||||
|
@ -44,43 +48,28 @@ function! go#coverage#Buffer(bang, ...) abort
|
||||||
let s:toggle = 1
|
let s:toggle = 1
|
||||||
let l:tmpname = tempname()
|
let l:tmpname = tempname()
|
||||||
|
|
||||||
if get(g:, 'go_echo_command_info', 1)
|
|
||||||
call go#util#EchoProgress("testing...")
|
|
||||||
endif
|
|
||||||
|
|
||||||
if go#util#has_job()
|
if go#util#has_job()
|
||||||
call s:coverage_job({
|
call s:coverage_job({
|
||||||
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname] + a:000,
|
\ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname] + a:000,
|
||||||
\ 'complete': function('s:coverage_callback', [l:tmpname]),
|
\ 'complete': function('s:coverage_callback', [l:tmpname]),
|
||||||
\ 'bang': a:bang,
|
\ 'bang': a:bang,
|
||||||
\ 'for': 'GoTest',
|
\ 'for': 'GoTest',
|
||||||
|
\ 'statustype': 'coverage',
|
||||||
\ })
|
\ })
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if go#config#EchoCommandInfo()
|
||||||
|
call go#util#EchoProgress("testing...")
|
||||||
|
endif
|
||||||
|
|
||||||
let args = [a:bang, 0, "-coverprofile", l:tmpname]
|
let args = [a:bang, 0, "-coverprofile", l:tmpname]
|
||||||
if a:0
|
if a:0
|
||||||
call extend(args, a:000)
|
call extend(args, a:000)
|
||||||
endif
|
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)
|
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
|
if go#util#ShellError() == 0
|
||||||
call go#coverage#overlay(l:tmpname)
|
call go#coverage#overlay(l:tmpname)
|
||||||
endif
|
endif
|
||||||
|
@ -96,7 +85,7 @@ function! go#coverage#Clear() abort
|
||||||
|
|
||||||
" remove the autocmd we defined
|
" remove the autocmd we defined
|
||||||
augroup vim-go-coverage
|
augroup vim-go-coverage
|
||||||
autocmd!
|
autocmd! * <buffer>
|
||||||
augroup end
|
augroup end
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -106,10 +95,11 @@ function! go#coverage#Browser(bang, ...) abort
|
||||||
let l:tmpname = tempname()
|
let l:tmpname = tempname()
|
||||||
if go#util#has_job()
|
if go#util#has_job()
|
||||||
call s:coverage_job({
|
call s:coverage_job({
|
||||||
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname],
|
\ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname],
|
||||||
\ 'complete': function('s:coverage_browser_callback', [l:tmpname]),
|
\ 'complete': function('s:coverage_browser_callback', [l:tmpname]),
|
||||||
\ 'bang': a:bang,
|
\ 'bang': a:bang,
|
||||||
\ 'for': 'GoTest',
|
\ 'for': 'GoTest',
|
||||||
|
\ 'statustype': 'coverage',
|
||||||
\ })
|
\ })
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -120,16 +110,9 @@ function! go#coverage#Browser(bang, ...) abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let id = call('go#test#Test', args)
|
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
|
if go#util#ShellError() == 0
|
||||||
let openHTML = 'go tool cover -html='.l:tmpname
|
call go#util#ExecInDir(['go', 'tool', 'cover', '-html=' . l:tmpname])
|
||||||
call go#tool#ExecuteInDir(openHTML)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call delete(l:tmpname)
|
call delete(l:tmpname)
|
||||||
|
@ -259,7 +242,7 @@ function! go#coverage#overlay(file) abort
|
||||||
|
|
||||||
" clear the matches if we leave the buffer
|
" clear the matches if we leave the buffer
|
||||||
augroup vim-go-coverage
|
augroup vim-go-coverage
|
||||||
autocmd!
|
autocmd! * <buffer>
|
||||||
autocmd BufWinLeave <buffer> call go#coverage#Clear()
|
autocmd BufWinLeave <buffer> call go#coverage#Clear()
|
||||||
augroup end
|
augroup end
|
||||||
|
|
||||||
|
@ -277,48 +260,17 @@ function s:coverage_job(args)
|
||||||
" autowrite is not enabled for jobs
|
" autowrite is not enabled for jobs
|
||||||
call go#cmd#autowrite()
|
call go#cmd#autowrite()
|
||||||
|
|
||||||
let status_dir = expand('%:p:h')
|
let disabled_term = 0
|
||||||
let Complete = a:args.complete
|
if go#config#TermEnabled()
|
||||||
function! s:complete(job, exit_status, data) closure
|
let disabled_term = 1
|
||||||
let status = {
|
call go#config#SetTermEnabled(0)
|
||||||
\ 'desc': 'last status',
|
|
||||||
\ 'type': "coverage",
|
|
||||||
\ 'state': "finished",
|
|
||||||
\ }
|
|
||||||
|
|
||||||
if a:exit_status
|
|
||||||
let status.state = "failed"
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call go#statusline#Update(status_dir, status)
|
call go#job#Spawn(a:args.cmd, a:args)
|
||||||
return Complete(a:job, a:exit_status, a:data)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
let a:args.complete = funcref('s:complete')
|
if disabled_term
|
||||||
let callbacks = go#job#Spawn(a:args)
|
call go#config#SetTermEnabled(1)
|
||||||
|
endif
|
||||||
let start_options = {
|
|
||||||
\ 'callback': callbacks.callback,
|
|
||||||
\ 'exit_cb': callbacks.exit_cb,
|
|
||||||
\ 'close_cb': callbacks.close_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
|
endfunction
|
||||||
|
|
||||||
" coverage_callback is called when the coverage execution is finished
|
" coverage_callback is called when the coverage execution is finished
|
||||||
|
@ -332,47 +284,14 @@ endfunction
|
||||||
|
|
||||||
function! s:coverage_browser_callback(coverfile, job, exit_status, data)
|
function! s:coverage_browser_callback(coverfile, job, exit_status, data)
|
||||||
if a:exit_status == 0
|
if a:exit_status == 0
|
||||||
let openHTML = 'go tool cover -html='.a:coverfile
|
call go#util#ExecInDir(['go', 'tool', 'cover', '-html=' . a:coverfile])
|
||||||
call go#tool#ExecuteInDir(openHTML)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call delete(a:coverfile)
|
call delete(a:coverfile)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" -----------------------
|
" restore Vi compatibility settings
|
||||||
" | Neovim job handlers |
|
let &cpo = s:cpo_save
|
||||||
" -----------------------
|
unlet s:cpo_save
|
||||||
|
|
||||||
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
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
scriptencoding utf-8
|
scriptencoding utf-8
|
||||||
|
|
||||||
if !exists('g:go_debug_windows')
|
|
||||||
let g:go_debug_windows = {
|
|
||||||
\ 'stack': 'leftabove 20vnew',
|
|
||||||
\ 'out': 'botright 10new',
|
|
||||||
\ 'vars': 'leftabove 30vnew',
|
|
||||||
\ }
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists('g:go_debug_address')
|
|
||||||
let g:go_debug_address = '127.0.0.1:8181'
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists('s:state')
|
if !exists('s:state')
|
||||||
let s:state = {
|
let s:state = {
|
||||||
\ 'rpcid': 1,
|
\ 'rpcid': 1,
|
||||||
\ 'running': 0,
|
\ 'running': 0,
|
||||||
\ 'breakpoint': {},
|
|
||||||
\ 'currentThread': {},
|
\ 'currentThread': {},
|
||||||
\ 'localVars': {},
|
\ 'localVars': {},
|
||||||
\ 'functionArgs': {},
|
\ 'functionArgs': {},
|
||||||
|
@ -25,7 +16,7 @@ if !exists('s:state')
|
||||||
\}
|
\}
|
||||||
|
|
||||||
if go#util#HasDebug('debugger-state')
|
if go#util#HasDebug('debugger-state')
|
||||||
let g:go_debug_diag = s:state
|
call go#config#SetDebugDiag(s:state)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -37,14 +28,35 @@ function! s:groutineID() abort
|
||||||
return s:state['currentThread'].goroutineID
|
return s:state['currentThread'].goroutineID
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:exit(job, status) abort
|
function! s:complete(job, exit_status, data) abort
|
||||||
|
let l:gotready = get(s:state, 'ready', 0)
|
||||||
|
" copy messages to a:data _only_ when dlv exited non-zero and it was never
|
||||||
|
" detected as ready (e.g. there was a compiler error).
|
||||||
|
if a:exit_status > 0 && !l:gotready
|
||||||
|
" copy messages to data so that vim-go's usual handling of errors from
|
||||||
|
" async jobs will occur.
|
||||||
|
call extend(a:data, s:state['message'])
|
||||||
|
endif
|
||||||
|
|
||||||
|
" return early instead of clearing any variables when the current job is not
|
||||||
|
" a:job
|
||||||
|
if has_key(s:state, 'job') && s:state['job'] != a:job
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
if has_key(s:state, 'job')
|
if has_key(s:state, 'job')
|
||||||
call remove(s:state, 'job')
|
call remove(s:state, 'job')
|
||||||
endif
|
endif
|
||||||
call s:clearState()
|
|
||||||
if a:status > 0
|
if has_key(s:state, 'ready')
|
||||||
call go#util#EchoError(s:state['message'])
|
call remove(s:state, 'ready')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if has_key(s:state, 'ch')
|
||||||
|
call remove(s:state, 'ch')
|
||||||
|
endif
|
||||||
|
|
||||||
|
call s:clearState()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:logger(prefix, ch, msg) abort
|
function! s:logger(prefix, ch, msg) abort
|
||||||
|
@ -71,57 +83,46 @@ endfunction
|
||||||
|
|
||||||
function! s:call_jsonrpc(method, ...) abort
|
function! s:call_jsonrpc(method, ...) abort
|
||||||
if go#util#HasDebug('debugger-commands')
|
if go#util#HasDebug('debugger-commands')
|
||||||
if !exists('g:go_debug_commands')
|
|
||||||
let g:go_debug_commands = []
|
|
||||||
endif
|
|
||||||
echom 'sending to dlv ' . a:method
|
echom 'sending to dlv ' . a:method
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if len(a:000) > 0 && type(a:000[0]) == v:t_func
|
let l:args = a:000
|
||||||
let Cb = a:000[0]
|
|
||||||
let args = a:000[1:]
|
|
||||||
else
|
|
||||||
let Cb = v:none
|
|
||||||
let args = a:000
|
|
||||||
endif
|
|
||||||
let s:state['rpcid'] += 1
|
let s:state['rpcid'] += 1
|
||||||
let req_json = json_encode({
|
let l:req_json = json_encode({
|
||||||
\ 'id': s:state['rpcid'],
|
\ 'id': s:state['rpcid'],
|
||||||
\ 'method': a:method,
|
\ 'method': a:method,
|
||||||
\ 'params': args,
|
\ 'params': l:args,
|
||||||
\})
|
\})
|
||||||
|
|
||||||
try
|
try
|
||||||
" Use callback
|
let l:ch = s:state['ch']
|
||||||
if type(Cb) == v:t_func
|
if has('nvim')
|
||||||
let s:ch = ch_open('127.0.0.1:8181', {'mode': 'nl', 'callback': Cb})
|
call chansend(l:ch, l:req_json)
|
||||||
call ch_sendraw(s:ch, req_json)
|
while len(s:state.data) == 0
|
||||||
|
sleep 50m
|
||||||
if go#util#HasDebug('debugger-commands')
|
if get(s:state, 'ready', 0) == 0
|
||||||
let g:go_debug_commands = add(g:go_debug_commands, {
|
|
||||||
\ 'request': req_json,
|
|
||||||
\ 'response': Cb,
|
|
||||||
\ })
|
|
||||||
endif
|
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
endwhile
|
||||||
let ch = ch_open('127.0.0.1:8181', {'mode': 'nl', 'timeout': 20000})
|
let resp_json = s:state.data[0]
|
||||||
call ch_sendraw(ch, req_json)
|
let s:state.data = s:state.data[1:]
|
||||||
let resp_json = ch_readraw(ch)
|
else
|
||||||
|
call ch_sendraw(l:ch, req_json)
|
||||||
|
let l:resp_raw = ch_readraw(l:ch)
|
||||||
|
let resp_json = json_decode(l:resp_raw)
|
||||||
|
endif
|
||||||
|
|
||||||
if go#util#HasDebug('debugger-commands')
|
if go#util#HasDebug('debugger-commands')
|
||||||
let g:go_debug_commands = add(g:go_debug_commands, {
|
let g:go_debug_commands = add(go#config#DebugCommands(), {
|
||||||
\ 'request': req_json,
|
\ 'request': l:req_json,
|
||||||
\ 'response': resp_json,
|
\ 'response': l:resp_json,
|
||||||
\ })
|
\ })
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let obj = json_decode(resp_json)
|
if type(l:resp_json) == v:t_dict && has_key(l:resp_json, 'error') && !empty(l:resp_json.error)
|
||||||
if type(obj) == v:t_dict && has_key(obj, 'error') && !empty(obj.error)
|
throw l:resp_json.error
|
||||||
throw obj.error
|
|
||||||
endif
|
endif
|
||||||
return obj
|
return l:resp_json
|
||||||
catch
|
catch
|
||||||
throw substitute(v:exception, '^Vim', '', '')
|
throw substitute(v:exception, '^Vim', '', '')
|
||||||
endtry
|
endtry
|
||||||
|
@ -130,7 +131,7 @@ endfunction
|
||||||
" Update the location of the current breakpoint or line we're halted on based on
|
" Update the location of the current breakpoint or line we're halted on based on
|
||||||
" response from dlv.
|
" response from dlv.
|
||||||
function! s:update_breakpoint(res) abort
|
function! s:update_breakpoint(res) abort
|
||||||
if type(a:res) ==# v:t_none
|
if type(a:res) ==# type(v:null)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -227,26 +228,34 @@ function! s:clearState() abort
|
||||||
let s:state['localVars'] = {}
|
let s:state['localVars'] = {}
|
||||||
let s:state['functionArgs'] = {}
|
let s:state['functionArgs'] = {}
|
||||||
let s:state['message'] = []
|
let s:state['message'] = []
|
||||||
|
|
||||||
silent! sign unplace 9999
|
silent! sign unplace 9999
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:stop() abort
|
function! s:stop() abort
|
||||||
call s:clearState()
|
let l:res = s:call_jsonrpc('RPCServer.Detach', {'kill': v:true})
|
||||||
|
|
||||||
|
if has_key(s:state, 'job')
|
||||||
|
call go#job#Wait(s:state['job'])
|
||||||
|
|
||||||
|
" while waiting, the s:complete may have already removed job from s:state.
|
||||||
if has_key(s:state, 'job')
|
if has_key(s:state, 'job')
|
||||||
call job_stop(s:state['job'])
|
|
||||||
call remove(s:state, 'job')
|
call remove(s:state, 'job')
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(s:state, 'ready')
|
||||||
|
call remove(s:state, 'ready')
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(s:state, 'ch')
|
||||||
|
call remove(s:state, 'ch')
|
||||||
|
endif
|
||||||
|
|
||||||
|
call s:clearState()
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#debug#Stop() abort
|
function! go#debug#Stop() abort
|
||||||
" Remove signs.
|
|
||||||
for k in keys(s:state['breakpoint'])
|
|
||||||
let bt = s:state['breakpoint'][k]
|
|
||||||
if bt.id >= 0
|
|
||||||
silent exe 'sign unplace ' . bt.id
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
|
|
||||||
" Remove all commands and add back the default commands.
|
" Remove all commands and add back the default commands.
|
||||||
for k in map(split(execute('command GoDebug'), "\n")[1:], 'matchstr(v:val, "^\\s*\\zs\\S\\+")')
|
for k in map(split(execute('command GoDebug'), "\n")[1:], 'matchstr(v:val, "^\\s*\\zs\\S\\+")')
|
||||||
exe 'delcommand' k
|
exe 'delcommand' k
|
||||||
|
@ -272,8 +281,15 @@ function! go#debug#Stop() abort
|
||||||
silent! exe bufwinnr(bufnr('__GODEBUG_VARIABLES__')) 'wincmd c'
|
silent! exe bufwinnr(bufnr('__GODEBUG_VARIABLES__')) 'wincmd c'
|
||||||
silent! exe bufwinnr(bufnr('__GODEBUG_OUTPUT__')) 'wincmd c'
|
silent! exe bufwinnr(bufnr('__GODEBUG_OUTPUT__')) 'wincmd c'
|
||||||
|
|
||||||
set noballooneval
|
if has('balloon_eval')
|
||||||
set balloonexpr=
|
let &ballooneval=s:ballooneval
|
||||||
|
let &balloonexpr=s:balloonexpr
|
||||||
|
endif
|
||||||
|
|
||||||
|
augroup vim-go-debug
|
||||||
|
autocmd!
|
||||||
|
augroup END
|
||||||
|
augroup! vim-go-debug
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:goto_file() abort
|
function! s:goto_file() abort
|
||||||
|
@ -393,22 +409,8 @@ function! s:expand_var() abort
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:start_cb(ch, json) abort
|
function! s:start_cb() abort
|
||||||
let res = json_decode(a:json)
|
let l:winid = win_getid()
|
||||||
if type(res) == v:t_dict && has_key(res, 'error') && !empty(res.error)
|
|
||||||
throw res.error
|
|
||||||
endif
|
|
||||||
if empty(res) || !has_key(res, 'result')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
for bt in res.result.Breakpoints
|
|
||||||
if bt.id >= 0
|
|
||||||
let s:state['breakpoint'][bt.id] = bt
|
|
||||||
exe 'sign place '. bt.id .' line=' . bt.line . ' name=godebugbreakpoint file=' . bt.file
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
|
|
||||||
let oldbuf = bufnr('%')
|
|
||||||
silent! only!
|
silent! only!
|
||||||
|
|
||||||
let winnum = bufwinnr(bufnr('__GODEBUG_STACKTRACE__'))
|
let winnum = bufwinnr(bufnr('__GODEBUG_STACKTRACE__'))
|
||||||
|
@ -416,8 +418,9 @@ function! s:start_cb(ch, json) abort
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if exists('g:go_debug_windows["stack"]') && g:go_debug_windows['stack'] != ''
|
let debugwindows = go#config#DebugWindows()
|
||||||
exe 'silent ' . g:go_debug_windows['stack']
|
if has_key(debugwindows, "stack") && debugwindows['stack'] != ''
|
||||||
|
exe 'silent ' . debugwindows['stack']
|
||||||
silent file `='__GODEBUG_STACKTRACE__'`
|
silent file `='__GODEBUG_STACKTRACE__'`
|
||||||
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
||||||
setlocal filetype=godebugstacktrace
|
setlocal filetype=godebugstacktrace
|
||||||
|
@ -425,16 +428,16 @@ function! s:start_cb(ch, json) abort
|
||||||
nmap <buffer> q <Plug>(go-debug-stop)
|
nmap <buffer> q <Plug>(go-debug-stop)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if exists('g:go_debug_windows["out"]') && g:go_debug_windows['out'] != ''
|
if has_key(debugwindows, "out") && debugwindows['out'] != ''
|
||||||
exe 'silent ' . g:go_debug_windows['out']
|
exe 'silent ' . debugwindows['out']
|
||||||
silent file `='__GODEBUG_OUTPUT__'`
|
silent file `='__GODEBUG_OUTPUT__'`
|
||||||
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
||||||
setlocal filetype=godebugoutput
|
setlocal filetype=godebugoutput
|
||||||
nmap <buffer> q <Plug>(go-debug-stop)
|
nmap <buffer> q <Plug>(go-debug-stop)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if exists('g:go_debug_windows["vars"]') && g:go_debug_windows['vars'] != ''
|
if has_key(debugwindows, "vars") && debugwindows['vars'] != ''
|
||||||
exe 'silent ' . g:go_debug_windows['vars']
|
exe 'silent ' . debugwindows['vars']
|
||||||
silent file `='__GODEBUG_VARIABLES__'`
|
silent file `='__GODEBUG_VARIABLES__'`
|
||||||
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
||||||
setlocal filetype=godebugvariables
|
setlocal filetype=godebugvariables
|
||||||
|
@ -462,75 +465,124 @@ function! s:start_cb(ch, json) abort
|
||||||
nnoremap <silent> <Plug>(go-debug-stop) :<C-u>call go#debug#Stop()<CR>
|
nnoremap <silent> <Plug>(go-debug-stop) :<C-u>call go#debug#Stop()<CR>
|
||||||
nnoremap <silent> <Plug>(go-debug-print) :<C-u>call go#debug#Print(expand('<cword>'))<CR>
|
nnoremap <silent> <Plug>(go-debug-print) :<C-u>call go#debug#Print(expand('<cword>'))<CR>
|
||||||
|
|
||||||
nmap <F5> <Plug>(go-debug-continue)
|
if has('balloon_eval')
|
||||||
nmap <F6> <Plug>(go-debug-print)
|
let s:balloonexpr=&balloonexpr
|
||||||
nmap <F9> <Plug>(go-debug-breakpoint)
|
let s:ballooneval=&ballooneval
|
||||||
nmap <F10> <Plug>(go-debug-next)
|
|
||||||
nmap <F11> <Plug>(go-debug-step)
|
|
||||||
|
|
||||||
set balloonexpr=go#debug#BalloonExpr()
|
set balloonexpr=go#debug#BalloonExpr()
|
||||||
set ballooneval
|
set ballooneval
|
||||||
|
endif
|
||||||
|
|
||||||
exe bufwinnr(oldbuf) 'wincmd w'
|
call win_gotoid(l:winid)
|
||||||
|
|
||||||
|
augroup vim-go-debug
|
||||||
|
autocmd! * <buffer>
|
||||||
|
autocmd FileType go nmap <buffer> <F5> <Plug>(go-debug-continue)
|
||||||
|
autocmd FileType go nmap <buffer> <F6> <Plug>(go-debug-print)
|
||||||
|
autocmd FileType go nmap <buffer> <F9> <Plug>(go-debug-breakpoint)
|
||||||
|
autocmd FileType go nmap <buffer> <F10> <Plug>(go-debug-next)
|
||||||
|
autocmd FileType go nmap <buffer> <F11> <Plug>(go-debug-step)
|
||||||
|
augroup END
|
||||||
|
doautocmd vim-go-debug FileType go
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:err_cb(ch, msg) abort
|
function! s:err_cb(ch, msg) abort
|
||||||
call go#util#EchoError(a:msg)
|
if get(s:state, 'ready', 0) != 0
|
||||||
|
call call('s:logger', ['ERR: ', a:ch, a:msg])
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
let s:state['message'] += [a:msg]
|
let s:state['message'] += [a:msg]
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:out_cb(ch, msg) abort
|
function! s:out_cb(ch, msg) abort
|
||||||
call go#util#EchoProgress(a:msg)
|
if get(s:state, 'ready', 0) != 0
|
||||||
|
call call('s:logger', ['OUT: ', a:ch, a:msg])
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
let s:state['message'] += [a:msg]
|
let s:state['message'] += [a:msg]
|
||||||
|
|
||||||
" TODO: why do this in this callback?
|
if stridx(a:msg, go#config#DebugAddress()) != -1
|
||||||
if stridx(a:msg, g:go_debug_address) != -1
|
if has('nvim')
|
||||||
call ch_setoptions(a:ch, {
|
let s:state['data'] = []
|
||||||
\ 'out_cb': function('s:logger', ['OUT: ']),
|
let l:state = {'databuf': ''}
|
||||||
\ 'err_cb': function('s:logger', ['ERR: ']),
|
|
||||||
\})
|
|
||||||
|
|
||||||
" Tell dlv about the breakpoints that the user added before delve started.
|
" explicitly bind callback to state so that within it, self will
|
||||||
let l:breaks = copy(s:state.breakpoint)
|
" always refer to state. See :help Partial for more information.
|
||||||
let s:state['breakpoint'] = {}
|
let l:state.on_data = function('s:on_data', [], l:state)
|
||||||
for l:bt in values(l:breaks)
|
let l:ch = sockconnect('tcp', go#config#DebugAddress(), {'on_data': l:state.on_data, 'state': l:state})
|
||||||
call go#debug#Breakpoint(bt.line)
|
if l:ch == 0
|
||||||
|
call go#util#EchoError("could not connect to debugger")
|
||||||
|
call go#job#Stop(s:state['job'])
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
let l:ch = ch_open(go#config#DebugAddress(), {'mode': 'raw', 'timeout': 20000})
|
||||||
|
if ch_status(l:ch) !=# 'open'
|
||||||
|
call go#util#EchoError("could not connect to debugger")
|
||||||
|
call go#job#Stop(s:state['job'])
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:state['ch'] = l:ch
|
||||||
|
|
||||||
|
" After this block executes, Delve will be running with all the
|
||||||
|
" breakpoints setup, so this callback doesn't have to run again; just log
|
||||||
|
" future messages.
|
||||||
|
let s:state['ready'] = 1
|
||||||
|
|
||||||
|
" replace all the breakpoints set before delve started so that the ids won't overlap.
|
||||||
|
let l:breakpoints = s:list_breakpoints()
|
||||||
|
for l:bt in s:list_breakpoints()
|
||||||
|
exe 'sign unplace '. l:bt.id
|
||||||
|
call go#debug#Breakpoint(l:bt.line, l:bt.file)
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
call s:call_jsonrpc('RPCServer.ListBreakpoints', function('s:start_cb'))
|
call s:start_cb()
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:on_data(ch, data, event) dict abort
|
||||||
|
let l:data = self.databuf
|
||||||
|
for l:msg in a:data
|
||||||
|
let l:data .= l:msg
|
||||||
|
endfor
|
||||||
|
|
||||||
|
try
|
||||||
|
let l:res = json_decode(l:data)
|
||||||
|
let s:state['data'] = add(s:state['data'], l:res)
|
||||||
|
let self.databuf = ''
|
||||||
|
catch
|
||||||
|
" there isn't a complete message in databuf: buffer l:data and try
|
||||||
|
" again when more data comes in.
|
||||||
|
let self.databuf = l:data
|
||||||
|
finally
|
||||||
|
endtry
|
||||||
|
endfunction
|
||||||
|
|
||||||
" Start the debug mode. The first argument is the package name to compile and
|
" Start the debug mode. The first argument is the package name to compile and
|
||||||
" debug, anything else will be passed to the running program.
|
" debug, anything else will be passed to the running program.
|
||||||
function! go#debug#Start(is_test, ...) abort
|
function! go#debug#Start(is_test, ...) abort
|
||||||
if has('nvim')
|
call go#cmd#autowrite()
|
||||||
call go#util#EchoError('This feature only works in Vim for now; Neovim is not (yet) supported. Sorry :-(')
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
if !go#util#has_job()
|
if !go#util#has_job()
|
||||||
call go#util#EchoError('This feature requires Vim 8.0.0087 or newer with +job.')
|
call go#util#EchoError('This feature requires either Vim 8.0.0087 or newer with +job or Neovim.')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" It's already running.
|
" It's already running.
|
||||||
if has_key(s:state, 'job') && job_status(s:state['job']) == 'run'
|
if has_key(s:state, 'job')
|
||||||
return
|
return s:state['job']
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let s:start_args = a:000
|
let s:start_args = a:000
|
||||||
|
|
||||||
if go#util#HasDebug('debugger-state')
|
if go#util#HasDebug('debugger-state')
|
||||||
let g:go_debug_diag = s:state
|
call go#config#SetDebugDiag(s:state)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" cd in to test directory; this is also what running "go test" does.
|
|
||||||
if a:is_test
|
|
||||||
lcd %:p:h
|
|
||||||
endif
|
|
||||||
|
|
||||||
let s:state.is_test = a:is_test
|
|
||||||
|
|
||||||
let dlv = go#path#CheckBinPath("dlv")
|
let dlv = go#path#CheckBinPath("dlv")
|
||||||
if empty(dlv)
|
if empty(dlv)
|
||||||
return
|
return
|
||||||
|
@ -539,14 +591,27 @@ function! go#debug#Start(is_test, ...) abort
|
||||||
try
|
try
|
||||||
if len(a:000) > 0
|
if len(a:000) > 0
|
||||||
let l:pkgname = a:1
|
let l:pkgname = a:1
|
||||||
" Expand .; otherwise this won't work from a tmp dir.
|
|
||||||
if l:pkgname[0] == '.'
|
if l:pkgname[0] == '.'
|
||||||
let l:pkgname = go#package#FromPath(getcwd()) . l:pkgname[1:]
|
let l:pkgname = go#package#FromPath(l:pkgname)
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
let l:pkgname = go#package#FromPath(getcwd())
|
let l:pkgname = go#package#FromPath(getcwd())
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if l:pkgname is -1
|
||||||
|
call go#util#EchoError('could not determine package name')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" cd in to test directory; this is also what running "go test" does.
|
||||||
|
if a:is_test
|
||||||
|
" TODO(bc): Either remove this if it's ok to do so or else record it and
|
||||||
|
" reset cwd after the job completes.
|
||||||
|
lcd %:p:h
|
||||||
|
endif
|
||||||
|
|
||||||
|
let s:state.is_test = a:is_test
|
||||||
|
|
||||||
let l:args = []
|
let l:args = []
|
||||||
if len(a:000) > 1
|
if len(a:000) > 1
|
||||||
let l:args = ['--'] + a:000[1:]
|
let l:args = ['--'] + a:000[1:]
|
||||||
|
@ -555,29 +620,40 @@ function! go#debug#Start(is_test, ...) abort
|
||||||
let l:cmd = [
|
let l:cmd = [
|
||||||
\ dlv,
|
\ dlv,
|
||||||
\ (a:is_test ? 'test' : 'debug'),
|
\ (a:is_test ? 'test' : 'debug'),
|
||||||
|
\ l:pkgname,
|
||||||
\ '--output', tempname(),
|
\ '--output', tempname(),
|
||||||
\ '--headless',
|
\ '--headless',
|
||||||
\ '--api-version', '2',
|
\ '--api-version', '2',
|
||||||
\ '--log',
|
\ '--listen', go#config#DebugAddress(),
|
||||||
\ '--listen', g:go_debug_address,
|
|
||||||
\ '--accept-multiclient',
|
|
||||||
\]
|
\]
|
||||||
if get(g:, 'go_build_tags', '') isnot ''
|
let l:debugLogOutput = go#config#DebugLogOutput()
|
||||||
let l:cmd += ['--build-flags', '--tags=' . g:go_build_tags]
|
if l:debugLogOutput != ''
|
||||||
|
let cmd += ['--log', '--log-output', l:debugLogOutput]
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:buildtags = go#config#BuildTags()
|
||||||
|
if buildtags isnot ''
|
||||||
|
let l:cmd += ['--build-flags', '--tags=' . buildtags]
|
||||||
endif
|
endif
|
||||||
let l:cmd += l:args
|
let l:cmd += l:args
|
||||||
|
|
||||||
call go#util#EchoProgress('Starting GoDebug...')
|
|
||||||
let s:state['message'] = []
|
let s:state['message'] = []
|
||||||
let s:state['job'] = job_start(l:cmd, {
|
let l:opts = {
|
||||||
\ 'out_cb': function('s:out_cb'),
|
\ 'for': 'GoDebug',
|
||||||
\ 'err_cb': function('s:err_cb'),
|
\ 'statustype': 'debug',
|
||||||
\ 'exit_cb': function('s:exit'),
|
\ 'complete': function('s:complete'),
|
||||||
\ 'stoponexit': 'kill',
|
\ }
|
||||||
\})
|
let l:opts = go#job#Options(l:opts)
|
||||||
|
let l:opts.out_cb = function('s:out_cb')
|
||||||
|
let l:opts.err_cb = function('s:err_cb')
|
||||||
|
let l:opts.stoponexit = 'kill'
|
||||||
|
|
||||||
|
let s:state['job'] = go#job#Start(l:cmd, l:opts)
|
||||||
catch
|
catch
|
||||||
call go#util#EchoError(v:exception)
|
call go#util#EchoError(v:exception)
|
||||||
endtry
|
endtry
|
||||||
|
|
||||||
|
return s:state['job']
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Translate a reflect kind constant to a human string.
|
" Translate a reflect kind constant to a human string.
|
||||||
|
@ -670,13 +746,12 @@ endfunction
|
||||||
|
|
||||||
function! s:eval(arg) abort
|
function! s:eval(arg) abort
|
||||||
try
|
try
|
||||||
let res = s:call_jsonrpc('RPCServer.State')
|
let l:res = s:call_jsonrpc('RPCServer.State')
|
||||||
let goroutineID = res.result.State.currentThread.goroutineID
|
let l:res = s:call_jsonrpc('RPCServer.Eval', {
|
||||||
let res = s:call_jsonrpc('RPCServer.Eval', {
|
|
||||||
\ 'expr': a:arg,
|
\ 'expr': a:arg,
|
||||||
\ 'scope': {'GoroutineID': goroutineID}
|
\ 'scope': {'GoroutineID': l:res.result.State.currentThread.goroutineID}
|
||||||
\ })
|
\ })
|
||||||
return s:eval_tree(res.result.Variable, 0)
|
return s:eval_tree(l:res.result.Variable, 0)
|
||||||
catch
|
catch
|
||||||
call go#util#EchoError(v:exception)
|
call go#util#EchoError(v:exception)
|
||||||
return ''
|
return ''
|
||||||
|
@ -726,12 +801,11 @@ endfunction
|
||||||
|
|
||||||
function! go#debug#Set(symbol, value) abort
|
function! go#debug#Set(symbol, value) abort
|
||||||
try
|
try
|
||||||
let res = s:call_jsonrpc('RPCServer.State')
|
let l:res = s:call_jsonrpc('RPCServer.State')
|
||||||
let goroutineID = res.result.State.currentThread.goroutineID
|
|
||||||
call s:call_jsonrpc('RPCServer.Set', {
|
call s:call_jsonrpc('RPCServer.Set', {
|
||||||
\ 'symbol': a:symbol,
|
\ 'symbol': a:symbol,
|
||||||
\ 'value': a:value,
|
\ 'value': a:value,
|
||||||
\ 'scope': {'GoroutineID': goroutineID}
|
\ 'scope': {'GoroutineID': l:res.result.State.currentThread.goroutineID}
|
||||||
\ })
|
\ })
|
||||||
catch
|
catch
|
||||||
call go#util#EchoError(v:exception)
|
call go#util#EchoError(v:exception)
|
||||||
|
@ -742,27 +816,20 @@ endfunction
|
||||||
|
|
||||||
function! s:update_stacktrace() abort
|
function! s:update_stacktrace() abort
|
||||||
try
|
try
|
||||||
let res = s:call_jsonrpc('RPCServer.Stacktrace', {'id': s:groutineID(), 'depth': 5})
|
let l:res = s:call_jsonrpc('RPCServer.Stacktrace', {'id': s:groutineID(), 'depth': 5})
|
||||||
call s:show_stacktrace(res)
|
call s:show_stacktrace(l:res)
|
||||||
catch
|
catch
|
||||||
call go#util#EchoError(v:exception)
|
call go#util#EchoError(v:exception)
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:stack_cb(ch, json) abort
|
function! s:stack_cb(res) abort
|
||||||
let s:stack_name = ''
|
let s:stack_name = ''
|
||||||
let res = json_decode(a:json)
|
|
||||||
if type(res) == v:t_dict && has_key(res, 'error') && !empty(res.error)
|
|
||||||
call go#util#EchoError(res.error)
|
|
||||||
call s:clearState()
|
|
||||||
call go#debug#Restart()
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
if empty(res) || !has_key(res, 'result')
|
if empty(a:res) || !has_key(a:res, 'result')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
call s:update_breakpoint(res)
|
call s:update_breakpoint(a:res)
|
||||||
call s:update_stacktrace()
|
call s:update_stacktrace()
|
||||||
call s:update_variables()
|
call s:update_variables()
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -780,7 +847,7 @@ function! go#debug#Stack(name) abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" Add a breakpoint to the main.Main if the user didn't define any.
|
" Add a breakpoint to the main.Main if the user didn't define any.
|
||||||
if len(s:state['breakpoint']) is 0
|
if len(s:list_breakpoints()) is 0
|
||||||
if go#debug#Breakpoint() isnot 0
|
if go#debug#Breakpoint() isnot 0
|
||||||
let s:state.running = 0
|
let s:state.running = 0
|
||||||
return
|
return
|
||||||
|
@ -793,36 +860,34 @@ function! go#debug#Stack(name) abort
|
||||||
call s:call_jsonrpc('RPCServer.CancelNext')
|
call s:call_jsonrpc('RPCServer.CancelNext')
|
||||||
endif
|
endif
|
||||||
let s:stack_name = l:name
|
let s:stack_name = l:name
|
||||||
call s:call_jsonrpc('RPCServer.Command', function('s:stack_cb'), {'name': l:name})
|
try
|
||||||
|
let res = s:call_jsonrpc('RPCServer.Command', {'name': l:name})
|
||||||
|
call s:stack_cb(res)
|
||||||
|
catch
|
||||||
|
call go#util#EchoError(v:exception)
|
||||||
|
call s:clearState()
|
||||||
|
call go#debug#Restart()
|
||||||
|
endtry
|
||||||
catch
|
catch
|
||||||
call go#util#EchoError(v:exception)
|
call go#util#EchoError(v:exception)
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#debug#Restart() abort
|
function! go#debug#Restart() abort
|
||||||
try
|
call go#cmd#autowrite()
|
||||||
call job_stop(s:state['job'])
|
|
||||||
while has_key(s:state, 'job') && job_status(s:state['job']) is# 'run'
|
try
|
||||||
sleep 50m
|
call s:stop()
|
||||||
endwhile
|
|
||||||
|
|
||||||
let l:breaks = s:state['breakpoint']
|
|
||||||
let s:state = {
|
let s:state = {
|
||||||
\ 'rpcid': 1,
|
\ 'rpcid': 1,
|
||||||
\ 'running': 0,
|
\ 'running': 0,
|
||||||
\ 'breakpoint': {},
|
|
||||||
\ 'currentThread': {},
|
\ 'currentThread': {},
|
||||||
\ 'localVars': {},
|
\ 'localVars': {},
|
||||||
\ 'functionArgs': {},
|
\ 'functionArgs': {},
|
||||||
\ 'message': [],
|
\ 'message': [],
|
||||||
\}
|
\}
|
||||||
|
|
||||||
" Preserve breakpoints.
|
|
||||||
for bt in values(l:breaks)
|
|
||||||
" TODO: should use correct filename
|
|
||||||
exe 'sign unplace '. bt.id .' file=' . bt.file
|
|
||||||
call go#debug#Breakpoint(bt.line)
|
|
||||||
endfor
|
|
||||||
call call('go#debug#Start', s:start_args)
|
call call('go#debug#Start', s:start_args)
|
||||||
catch
|
catch
|
||||||
call go#util#EchoError(v:exception)
|
call go#util#EchoError(v:exception)
|
||||||
|
@ -837,47 +902,45 @@ endfunction
|
||||||
" Toggle breakpoint. Returns 0 on success and 1 on failure.
|
" Toggle breakpoint. Returns 0 on success and 1 on failure.
|
||||||
function! go#debug#Breakpoint(...) abort
|
function! go#debug#Breakpoint(...) abort
|
||||||
let l:filename = fnamemodify(expand('%'), ':p:gs!\\!/!')
|
let l:filename = fnamemodify(expand('%'), ':p:gs!\\!/!')
|
||||||
|
let l:linenr = line('.')
|
||||||
|
|
||||||
" Get line number from argument.
|
" Get line number from argument.
|
||||||
if len(a:000) > 0
|
if len(a:000) > 0
|
||||||
let linenr = str2nr(a:1)
|
let l:linenr = str2nr(a:1)
|
||||||
if linenr is 0
|
if l:linenr is 0
|
||||||
call go#util#EchoError('not a number: ' . a:1)
|
call go#util#EchoError('not a number: ' . a:1)
|
||||||
return 0
|
return 0
|
||||||
endif
|
endif
|
||||||
else
|
if len(a:000) > 1
|
||||||
let linenr = line('.')
|
let l:filename = a:2
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
try
|
try
|
||||||
" Check if we already have a breakpoint for this line.
|
" Check if we already have a breakpoint for this line.
|
||||||
let found = v:none
|
let l:found = {}
|
||||||
for k in keys(s:state.breakpoint)
|
for l:bt in s:list_breakpoints()
|
||||||
let bt = s:state.breakpoint[k]
|
if l:bt.file is# l:filename && l:bt.line is# l:linenr
|
||||||
if bt.file == l:filename && bt.line == linenr
|
let l:found = l:bt
|
||||||
let found = bt
|
|
||||||
break
|
break
|
||||||
endif
|
endif
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
" Remove breakpoint.
|
" Remove breakpoint.
|
||||||
if type(found) == v:t_dict
|
if type(l:found) == v:t_dict && !empty(l:found)
|
||||||
call remove(s:state['breakpoint'], bt.id)
|
exe 'sign unplace '. l:found.id .' file=' . l:found.file
|
||||||
exe 'sign unplace '. found.id .' file=' . found.file
|
|
||||||
if s:isActive()
|
if s:isActive()
|
||||||
let res = s:call_jsonrpc('RPCServer.ClearBreakpoint', {'id': found.id})
|
let res = s:call_jsonrpc('RPCServer.ClearBreakpoint', {'id': l:found.id})
|
||||||
endif
|
endif
|
||||||
" Add breakpoint.
|
" Add breakpoint.
|
||||||
else
|
else
|
||||||
if s:isActive()
|
if s:isActive()
|
||||||
let res = s:call_jsonrpc('RPCServer.CreateBreakpoint', {'Breakpoint': {'file': l:filename, 'line': linenr}})
|
let l:res = s:call_jsonrpc('RPCServer.CreateBreakpoint', {'Breakpoint': {'file': l:filename, 'line': l:linenr}})
|
||||||
let bt = res.result.Breakpoint
|
let l:bt = res.result.Breakpoint
|
||||||
exe 'sign place '. bt.id .' line=' . bt.line . ' name=godebugbreakpoint file=' . bt.file
|
exe 'sign place '. l:bt.id .' line=' . l:bt.line . ' name=godebugbreakpoint file=' . l:bt.file
|
||||||
let s:state['breakpoint'][bt.id] = bt
|
|
||||||
else
|
else
|
||||||
let id = len(s:state['breakpoint']) + 1
|
let l:id = len(s:list_breakpoints()) + 1
|
||||||
let s:state['breakpoint'][id] = {'id': id, 'file': l:filename, 'line': linenr}
|
exe 'sign place ' . l:id . ' line=' . l:linenr . ' name=godebugbreakpoint file=' . l:filename
|
||||||
exe 'sign place '. id .' line=' . linenr . ' name=godebugbreakpoint file=' . l:filename
|
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
catch
|
catch
|
||||||
|
@ -888,17 +951,43 @@ function! go#debug#Breakpoint(...) abort
|
||||||
return 0
|
return 0
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
sign define godebugbreakpoint text=> texthl=GoDebugBreakpoint
|
function! s:list_breakpoints()
|
||||||
sign define godebugcurline text== linehl=GoDebugCurrent texthl=GoDebugCurrent
|
" :sign place
|
||||||
|
" --- Signs ---
|
||||||
|
" Signs for a.go:
|
||||||
|
" line=15 id=2 name=godebugbreakpoint
|
||||||
|
" line=16 id=1 name=godebugbreakpoint
|
||||||
|
" Signs for a_test.go:
|
||||||
|
" line=6 id=3 name=godebugbreakpoint
|
||||||
|
|
||||||
fun! s:hi()
|
let l:signs = []
|
||||||
hi GoDebugBreakpoint term=standout ctermbg=117 ctermfg=0 guibg=#BAD4F5 guifg=Black
|
let l:file = ''
|
||||||
hi GoDebugCurrent term=reverse ctermbg=12 ctermfg=7 guibg=DarkBlue guifg=White
|
for l:line in split(execute('sign place'), '\n')[1:]
|
||||||
endfun
|
if l:line =~# '^Signs for '
|
||||||
augroup vim-go-breakpoint
|
let l:file = l:line[10:-2]
|
||||||
autocmd!
|
continue
|
||||||
autocmd ColorScheme * call s:hi()
|
endif
|
||||||
augroup end
|
|
||||||
call s:hi()
|
if l:line !~# 'name=godebugbreakpoint'
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:sign = matchlist(l:line, '\vline\=(\d+) +id\=(\d+)')
|
||||||
|
call add(l:signs, {
|
||||||
|
\ 'id': l:sign[2],
|
||||||
|
\ 'file': fnamemodify(l:file, ':p'),
|
||||||
|
\ 'line': str2nr(l:sign[1]),
|
||||||
|
\ })
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return l:signs
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
sign define godebugbreakpoint text=> texthl=GoDebugBreakpoint
|
||||||
|
sign define godebugcurline text== texthl=GoDebugCurrent linehl=GoDebugCurrent
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
101
pack/acp/start/vim-go/autoload/go/debug_test.vim
Normal file
101
pack/acp/start/vim-go/autoload/go/debug_test.vim
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
function! Test_GoDebugStart_Empty() abort
|
||||||
|
call s:debug()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_GoDebugStart_RelativePackage() abort
|
||||||
|
call s:debug('./debug/debugmain')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_GoDebugStart_Package() abort
|
||||||
|
call s:debug('debug/debugmain')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! Test_GoDebugStart_Errors() abort
|
||||||
|
if !go#util#has_job()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
try
|
||||||
|
let l:expected = [
|
||||||
|
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 0, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': '# debug/compilerror'},
|
||||||
|
\ {'lnum': 6, 'bufnr': 7, 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ' syntax error: unexpected newline, expecting comma or )'},
|
||||||
|
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 0, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exit status 2'}
|
||||||
|
\]
|
||||||
|
call setqflist([], 'r')
|
||||||
|
|
||||||
|
let l:tmp = gotest#load_fixture('debug/compilerror/main.go')
|
||||||
|
call assert_false(exists(':GoDebugStop'))
|
||||||
|
|
||||||
|
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
|
||||||
|
execute l:cd . ' debug/compilerror'
|
||||||
|
|
||||||
|
call go#debug#Start(0)
|
||||||
|
|
||||||
|
let l:actual = getqflist()
|
||||||
|
let l:start = reltime()
|
||||||
|
while len(l:actual) == 0 && reltimefloat(reltime(l:start)) < 10
|
||||||
|
sleep 100m
|
||||||
|
let l:actual = getqflist()
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
call gotest#assert_quickfix(l:actual, l:expected)
|
||||||
|
call assert_false(exists(':GoDebugStop'))
|
||||||
|
|
||||||
|
finally
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
" clear the quickfix lists
|
||||||
|
call setqflist([], 'r')
|
||||||
|
endtry
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:debug(...) abort
|
||||||
|
if !go#util#has_job()
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
try
|
||||||
|
let l:tmp = gotest#load_fixture('debug/debugmain/debugmain.go')
|
||||||
|
|
||||||
|
call go#debug#Breakpoint(6)
|
||||||
|
|
||||||
|
call assert_false(exists(':GoDebugStop'))
|
||||||
|
|
||||||
|
if a:0 == 0
|
||||||
|
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
|
||||||
|
execute l:cd . ' debug/debugmain'
|
||||||
|
let l:job = go#debug#Start(0)
|
||||||
|
else
|
||||||
|
let l:job = go#debug#Start(0, a:1)
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:start = reltime()
|
||||||
|
while !exists(':GoDebugStop') && reltimefloat(reltime(l:start)) < 10
|
||||||
|
sleep 100m
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
call assert_true(exists(':GoDebugStop'))
|
||||||
|
call gotest#assert_quickfix(getqflist(), [])
|
||||||
|
|
||||||
|
call go#debug#Stop()
|
||||||
|
|
||||||
|
if !has('nvim')
|
||||||
|
call assert_equal(job_status(l:job), 'dead')
|
||||||
|
endif
|
||||||
|
|
||||||
|
call assert_false(exists(':GoDebugStop'))
|
||||||
|
|
||||||
|
finally
|
||||||
|
call go#debug#Breakpoint(6)
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -1,11 +1,12 @@
|
||||||
if !exists('g:go_decls_mode')
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let g:go_decls_mode = ''
|
let s:cpo_save = &cpo
|
||||||
endif
|
set cpo&vim
|
||||||
|
|
||||||
function! go#decls#Decls(mode, ...) abort
|
function! go#decls#Decls(mode, ...) abort
|
||||||
if g:go_decls_mode == 'ctrlp'
|
let decls_mode = go#config#DeclsMode()
|
||||||
|
if decls_mode == 'ctrlp'
|
||||||
call ctrlp#init(call("ctrlp#decls#cmd", [a:mode] + a:000))
|
call ctrlp#init(call("ctrlp#decls#cmd", [a:mode] + a:000))
|
||||||
elseif g:go_decls_mode == 'fzf'
|
elseif decls_mode == 'fzf'
|
||||||
call call("fzf#decls#cmd", [a:mode] + a:000)
|
call call("fzf#decls#cmd", [a:mode] + a:000)
|
||||||
else
|
else
|
||||||
if globpath(&rtp, 'plugin/ctrlp.vim') != ""
|
if globpath(&rtp, 'plugin/ctrlp.vim') != ""
|
||||||
|
@ -18,4 +19,8 @@ function! go#decls#Decls(mode, ...) abort
|
||||||
end
|
end
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,39 +1,38 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
let s:go_stack = []
|
let s:go_stack = []
|
||||||
let s:go_stack_level = 0
|
let s:go_stack_level = 0
|
||||||
|
|
||||||
function! go#def#Jump(mode) abort
|
function! go#def#Jump(mode, type) abort
|
||||||
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
||||||
|
|
||||||
" so guru right now is slow for some people. previously we were using
|
" 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
|
" 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
|
" 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
|
" covers all edge cases, but now anyone can switch to godef if they wish
|
||||||
let bin_name = get(g:, 'go_def_mode', 'guru')
|
let bin_name = go#config#DefMode()
|
||||||
if bin_name == 'godef'
|
if bin_name == 'godef'
|
||||||
if &modified
|
let l:cmd = ['godef',
|
||||||
" Write current unsaved buffer to a temp file and use the modified content
|
\ '-f=' . l:fname,
|
||||||
let l:tmpname = tempname()
|
\ '-o=' . go#util#OffsetCursor(),
|
||||||
call writefile(go#util#GetLines(), l:tmpname)
|
\ '-t']
|
||||||
let fname = l:tmpname
|
|
||||||
endif
|
|
||||||
|
|
||||||
let bin_path = go#path#CheckBinPath("godef")
|
if &modified
|
||||||
if empty(bin_path)
|
let l:stdin_content = join(go#util#GetLines(), "\n")
|
||||||
return
|
call add(l:cmd, "-i")
|
||||||
endif
|
let [l:out, l:err] = go#util#ExecInDir(l:cmd, l:stdin_content)
|
||||||
let command = printf("%s -f=%s -o=%s -t", go#util#Shellescape(bin_path),
|
else
|
||||||
\ go#util#Shellescape(fname), go#util#OffsetCursor())
|
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
|
||||||
let out = go#util#System(command)
|
|
||||||
if exists("l:tmpname")
|
|
||||||
call delete(l:tmpname)
|
|
||||||
endif
|
endif
|
||||||
elseif bin_name == 'guru'
|
elseif bin_name == 'guru'
|
||||||
let bin_path = go#path#CheckBinPath("guru")
|
let cmd = [go#path#CheckBinPath(bin_name)]
|
||||||
if empty(bin_path)
|
let buildtags = go#config#BuildTags()
|
||||||
return
|
if buildtags isnot ''
|
||||||
|
let cmd += ['-tags', buildtags]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let cmd = [bin_path]
|
|
||||||
let stdin_content = ""
|
let stdin_content = ""
|
||||||
|
|
||||||
if &modified
|
if &modified
|
||||||
|
@ -42,42 +41,46 @@ function! go#def#Jump(mode) abort
|
||||||
call add(cmd, "-modified")
|
call add(cmd, "-modified")
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if exists('g:go_build_tags')
|
call extend(cmd, ["definition", fname . ':#' . go#util#OffsetCursor()])
|
||||||
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()
|
if go#util#has_job()
|
||||||
|
let l:state = {}
|
||||||
let l:spawn_args = {
|
let l:spawn_args = {
|
||||||
\ 'cmd': cmd,
|
\ 'cmd': cmd,
|
||||||
\ 'complete': function('s:jump_to_declaration_cb', [a:mode, bin_name]),
|
\ 'complete': function('s:jump_to_declaration_cb', [a:mode, bin_name], l:state),
|
||||||
|
\ 'for': '_',
|
||||||
|
\ 'statustype': 'searching declaration',
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
if &modified
|
if &modified
|
||||||
let l:spawn_args.input = stdin_content
|
let l:spawn_args.input = stdin_content
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call go#util#EchoProgress("searching declaration ...")
|
call s:def_job(spawn_args, l:state)
|
||||||
|
|
||||||
call s:def_job(spawn_args)
|
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command = join(cmd, " ")
|
|
||||||
if &modified
|
if &modified
|
||||||
let out = go#util#System(command, stdin_content)
|
let [l:out, l:err] = go#util#ExecInDir(l:cmd, l:stdin_content)
|
||||||
else
|
else
|
||||||
let out = go#util#System(command)
|
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
|
||||||
endif
|
endif
|
||||||
|
elseif bin_name == 'gopls'
|
||||||
|
let [l:line, l:col] = getpos('.')[1:2]
|
||||||
|
" delegate to gopls, with an empty job object and an exit status of 0
|
||||||
|
" (they're irrelevant for gopls).
|
||||||
|
if a:type
|
||||||
|
call go#lsp#TypeDef(l:fname, l:line, l:col, function('s:jump_to_declaration_cb', [a:mode, 'gopls', {}, 0]))
|
||||||
else
|
else
|
||||||
call go#util#EchoError('go_def_mode value: '. bin_name .' is not valid. Valid values are: [godef, guru]')
|
call go#lsp#Definition(l:fname, l:line, l:col, function('s:jump_to_declaration_cb', [a:mode, 'gopls', {}, 0]))
|
||||||
|
endif
|
||||||
|
return
|
||||||
|
else
|
||||||
|
call go#util#EchoError('go_def_mode value: '. bin_name .' is not valid. Valid values are: [godef, guru, gopls]')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if go#util#ShellError() != 0
|
if l:err
|
||||||
call go#util#EchoError(out)
|
call go#util#EchoError(out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -85,19 +88,26 @@ function! go#def#Jump(mode) abort
|
||||||
call go#def#jump_to_declaration(out, a:mode, bin_name)
|
call go#def#jump_to_declaration(out, a:mode, bin_name)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:jump_to_declaration_cb(mode, bin_name, job, exit_status, data) abort
|
function! s:jump_to_declaration_cb(mode, bin_name, job, exit_status, data) abort dict
|
||||||
if a:exit_status != 0
|
if a:exit_status != 0
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call go#def#jump_to_declaration(a:data[0], a:mode, a:bin_name)
|
call go#def#jump_to_declaration(a:data[0], a:mode, a:bin_name)
|
||||||
call go#util#EchoSuccess(fnamemodify(a:data[0], ":t"))
|
|
||||||
|
" capture the active window so that callbacks for jobs, exit_cb and
|
||||||
|
" close_cb, and callbacks for gopls can return to it when a:mode caused a
|
||||||
|
" split.
|
||||||
|
let self.winid = win_getid(winnr())
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" go#def#jump_to_declaration parses out (expected to be
|
||||||
|
" 'filename:line:col: message').
|
||||||
function! go#def#jump_to_declaration(out, mode, bin_name) abort
|
function! go#def#jump_to_declaration(out, mode, bin_name) abort
|
||||||
let final_out = a:out
|
let final_out = a:out
|
||||||
if a:bin_name == "godef"
|
if a:bin_name == "godef"
|
||||||
" append the type information to the same line so our we can parse it.
|
" append the type information to the same line so it will be parsed
|
||||||
|
" correctly using guru's output format.
|
||||||
" This makes it compatible with guru output.
|
" This makes it compatible with guru output.
|
||||||
let final_out = join(split(a:out, '\n'), ':')
|
let final_out = join(split(a:out, '\n'), ':')
|
||||||
endif
|
endif
|
||||||
|
@ -110,10 +120,24 @@ function! go#def#jump_to_declaration(out, mode, bin_name) abort
|
||||||
let parts = split(out, ':')
|
let parts = split(out, ':')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if len(parts) == 0
|
||||||
|
call go#util#EchoError('go jump_to_declaration '. a:bin_name .' output is not valid.')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let line = 1
|
||||||
|
let col = 1
|
||||||
|
let ident = 0
|
||||||
let filename = parts[0]
|
let filename = parts[0]
|
||||||
|
if len(parts) > 1
|
||||||
let line = parts[1]
|
let line = parts[1]
|
||||||
|
endif
|
||||||
|
if len(parts) > 2
|
||||||
let col = parts[2]
|
let col = parts[2]
|
||||||
|
endif
|
||||||
|
if len(parts) > 3
|
||||||
let ident = parts[3]
|
let ident = parts[3]
|
||||||
|
endif
|
||||||
|
|
||||||
" Remove anything newer than the current position, just like basic
|
" Remove anything newer than the current position, just like basic
|
||||||
" vim tag support
|
" vim tag support
|
||||||
|
@ -138,7 +162,7 @@ function! go#def#jump_to_declaration(out, mode, bin_name) abort
|
||||||
if filename != fnamemodify(expand("%"), ':p:gs?\\?/?')
|
if filename != fnamemodify(expand("%"), ':p:gs?\\?/?')
|
||||||
" jump to existing buffer if, 1. we have enabled it, 2. the buffer is loaded
|
" jump to existing buffer if, 1. we have enabled it, 2. the buffer is loaded
|
||||||
" and 3. there is buffer window number we switch to
|
" and 3. there is buffer window number we switch to
|
||||||
if get(g:, 'go_def_reuse_buffer', 0) && bufloaded(filename) != 0 && bufwinnr(filename) != -1
|
if go#config#DefReuseBuffer() && bufloaded(filename) != 0 && bufwinnr(filename) != -1
|
||||||
" jumpt to existing buffer if it exists
|
" jumpt to existing buffer if it exists
|
||||||
execute bufwinnr(filename) . 'wincmd w'
|
execute bufwinnr(filename) . 'wincmd w'
|
||||||
else
|
else
|
||||||
|
@ -291,14 +315,25 @@ function! go#def#Stack(...) abort
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function s:def_job(args) abort
|
function s:def_job(args, state) abort
|
||||||
let callbacks = go#job#Spawn(a:args)
|
let l:start_options = go#job#Options(a:args)
|
||||||
|
|
||||||
let start_options = {
|
let l:state = a:state
|
||||||
\ 'callback': callbacks.callback,
|
function! s:exit_cb(next, job, exitval) dict
|
||||||
\ 'exit_cb': callbacks.exit_cb,
|
call call(a:next, [a:job, a:exitval])
|
||||||
\ 'close_cb': callbacks.close_cb,
|
if has_key(self, 'winid')
|
||||||
\ }
|
call win_gotoid(self.winid)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
let l:start_options.exit_cb = funcref('s:exit_cb', [l:start_options.exit_cb], l:state)
|
||||||
|
|
||||||
|
function! s:close_cb(next, ch) dict
|
||||||
|
call call(a:next, [a:ch])
|
||||||
|
if has_key(self, 'winid')
|
||||||
|
call win_gotoid(self.winid)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
let l:start_options.close_cb = funcref('s:close_cb', [l:start_options.close_cb], l:state)
|
||||||
|
|
||||||
if &modified
|
if &modified
|
||||||
let l:tmpname = tempname()
|
let l:tmpname = tempname()
|
||||||
|
@ -307,7 +342,11 @@ function s:def_job(args) abort
|
||||||
let l:start_options.in_name = l:tmpname
|
let l:start_options.in_name = l:tmpname
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call job_start(a:args.cmd, start_options)
|
call go#job#Start(a:args.cmd, l:start_options)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_jump_to_declaration_guru() abort
|
func! Test_jump_to_declaration_guru() abort
|
||||||
try
|
try
|
||||||
let l:filename = 'def/jump.go'
|
let l:filename = 'def/jump.go'
|
||||||
|
@ -34,4 +38,37 @@ func! Test_jump_to_declaration_godef() abort
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func! Test_Jump_leaves_lists() abort
|
||||||
|
try
|
||||||
|
let filename = 'def/jump.go'
|
||||||
|
let l:tmp = gotest#load_fixture(l:filename)
|
||||||
|
|
||||||
|
let expected = [{'lnum': 10, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'quux'}]
|
||||||
|
|
||||||
|
call setloclist(winnr(), copy(expected), 'r' )
|
||||||
|
call setqflist(copy(expected), 'r' )
|
||||||
|
|
||||||
|
let l:bufnr = bufnr('%')
|
||||||
|
call cursor(6, 7)
|
||||||
|
call go#def#Jump('', 0)
|
||||||
|
|
||||||
|
let start = reltime()
|
||||||
|
while bufnr('%') == l:bufnr && reltimefloat(reltime(start)) < 10
|
||||||
|
sleep 100m
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
let actual = getloclist(winnr())
|
||||||
|
call gotest#assert_quickfix(actual, expected)
|
||||||
|
|
||||||
|
let actual = getqflist()
|
||||||
|
call gotest#assert_quickfix(actual, expected)
|
||||||
|
finally
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
" Use of this source code is governed by a BSD-style
|
" Use of this source code is governed by a BSD-style
|
||||||
" license that can be found in the LICENSE file.
|
" license that can be found in the LICENSE file.
|
||||||
|
|
||||||
let s:buf_nr = -1
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
if !exists("g:go_doc_command")
|
let s:buf_nr = -1
|
||||||
let g:go_doc_command = ["godoc"]
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! go#doc#OpenBrowser(...) abort
|
function! go#doc#OpenBrowser(...) abort
|
||||||
" check if we have gogetdoc as it gives us more and accurate information.
|
" check if we have gogetdoc as it gives us more and accurate information.
|
||||||
|
@ -14,8 +14,8 @@ function! go#doc#OpenBrowser(...) abort
|
||||||
" non-json output of gogetdoc
|
" non-json output of gogetdoc
|
||||||
let bin_path = go#path#CheckBinPath('gogetdoc')
|
let bin_path = go#path#CheckBinPath('gogetdoc')
|
||||||
if !empty(bin_path) && exists('*json_decode')
|
if !empty(bin_path) && exists('*json_decode')
|
||||||
let json_out = s:gogetdoc(1)
|
let [l:json_out, l:err] = s:gogetdoc(1)
|
||||||
if go#util#ShellError() != 0
|
if l:err
|
||||||
call go#util#EchoError(json_out)
|
call go#util#EchoError(json_out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -29,15 +29,13 @@ function! go#doc#OpenBrowser(...) abort
|
||||||
let name = out["name"]
|
let name = out["name"]
|
||||||
let decl = out["decl"]
|
let decl = out["decl"]
|
||||||
|
|
||||||
let godoc_url = s:custom_godoc_url()
|
let godoc_url = go#config#DocUrl()
|
||||||
let godoc_url .= "/" . import
|
let godoc_url .= "/" . import
|
||||||
if decl !~ "^package"
|
if decl !~ "^package"
|
||||||
let godoc_url .= "#" . name
|
let godoc_url .= "#" . name
|
||||||
endif
|
endif
|
||||||
|
|
||||||
echo godoc_url
|
call go#util#OpenBrowser(godoc_url)
|
||||||
|
|
||||||
call go#tool#OpenBrowser(godoc_url)
|
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -50,28 +48,22 @@ function! go#doc#OpenBrowser(...) abort
|
||||||
let exported_name = pkgs[1]
|
let exported_name = pkgs[1]
|
||||||
|
|
||||||
" example url: https://godoc.org/github.com/fatih/set#Set
|
" example url: https://godoc.org/github.com/fatih/set#Set
|
||||||
let godoc_url = s:custom_godoc_url() . "/" . pkg . "#" . exported_name
|
let godoc_url = go#config#DocUrl() . "/" . pkg . "#" . exported_name
|
||||||
call go#tool#OpenBrowser(godoc_url)
|
call go#util#OpenBrowser(godoc_url)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#doc#Open(newmode, mode, ...) abort
|
function! go#doc#Open(newmode, mode, ...) abort
|
||||||
" With argument: run "godoc [arg]".
|
" With argument: run "godoc [arg]".
|
||||||
if len(a:000)
|
if len(a:000)
|
||||||
if empty(go#path#CheckBinPath(g:go_doc_command[0]))
|
let [l:out, l:err] = go#util#Exec(['go', 'doc'] + a:000)
|
||||||
return
|
else " Without argument: run gogetdoc on cursor position.
|
||||||
endif
|
let [l:out, l:err] = s:gogetdoc(0)
|
||||||
|
|
||||||
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
|
if out == -1
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if go#util#ShellError() != 0
|
if l:err
|
||||||
call go#util#EchoError(out)
|
call go#util#EchoError(out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -97,7 +89,7 @@ function! s:GodocView(newposition, position, content) abort
|
||||||
if !is_visible
|
if !is_visible
|
||||||
if a:position == "split"
|
if a:position == "split"
|
||||||
" cap window height to 20, but resize it for smaller contents
|
" cap window height to 20, but resize it for smaller contents
|
||||||
let max_height = get(g:, "go_doc_max_height", 20)
|
let max_height = go#config#DocMaxHeight()
|
||||||
let content_height = len(split(a:content, "\n"))
|
let content_height = len(split(a:content, "\n"))
|
||||||
if content_height > max_height
|
if content_height > max_height
|
||||||
exe 'resize ' . max_height
|
exe 'resize ' . max_height
|
||||||
|
@ -129,39 +121,29 @@ function! s:GodocView(newposition, position, content) abort
|
||||||
setlocal nomodifiable
|
setlocal nomodifiable
|
||||||
sil normal! gg
|
sil normal! gg
|
||||||
|
|
||||||
" close easily with <esc> or enter
|
" close easily with enter
|
||||||
noremap <buffer> <silent> <CR> :<C-U>close<CR>
|
noremap <buffer> <silent> <CR> :<C-U>close<CR>
|
||||||
noremap <buffer> <silent> <Esc> :<C-U>close<CR>
|
noremap <buffer> <silent> <Esc> :<C-U>close<CR>
|
||||||
|
" make sure any key that sends an escape as a prefix (e.g. the arrow keys)
|
||||||
|
" don't cause the window to close.
|
||||||
|
nnoremap <buffer> <silent> <Esc>[ <Esc>[
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:gogetdoc(json) abort
|
function! s:gogetdoc(json) abort
|
||||||
" check if we have 'gogetdoc' and use it automatically
|
let l:cmd = [
|
||||||
let bin_path = go#path#CheckBinPath('gogetdoc')
|
\ 'gogetdoc',
|
||||||
if empty(bin_path)
|
\ '-tags', go#config#BuildTags(),
|
||||||
return -1
|
\ '-pos', expand("%:p:gs!\\!/!") . ':#' . go#util#OffsetCursor()]
|
||||||
endif
|
|
||||||
|
|
||||||
let cmd = [go#util#Shellescape(bin_path)]
|
|
||||||
|
|
||||||
let offset = go#util#OffsetCursor()
|
|
||||||
let fname = expand("%:p:gs!\\!/!")
|
|
||||||
let pos = shellescape(fname.':#'.offset)
|
|
||||||
|
|
||||||
let cmd += ["-pos", pos]
|
|
||||||
if a:json
|
if a:json
|
||||||
let cmd += ["-json"]
|
let l:cmd += ['-json']
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command = join(cmd, " ")
|
|
||||||
|
|
||||||
if &modified
|
if &modified
|
||||||
let command .= " -modified"
|
let l:cmd += ['-modified']
|
||||||
let out = go#util#System(command, go#util#archive())
|
return go#util#Exec(l:cmd, go#util#archive())
|
||||||
else
|
|
||||||
let out = go#util#System(command)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return out
|
return go#util#Exec(l:cmd)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" returns the package and exported name. exported name might be empty.
|
" returns the package and exported name. exported name might be empty.
|
||||||
|
@ -206,18 +188,8 @@ function! s:godocWord(args) abort
|
||||||
return [pkg, exported_name]
|
return [pkg, exported_name]
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:custom_godoc_url() abort
|
" restore Vi compatibility settings
|
||||||
let godoc_url = get(g:, 'go_doc_url', 'https://godoc.org')
|
let &cpo = s:cpo_save
|
||||||
if godoc_url isnot 'https://godoc.org'
|
unlet s:cpo_save
|
||||||
" 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 expect /pkg before package names
|
|
||||||
let godoc_url .= "/pkg"
|
|
||||||
endif
|
|
||||||
return godoc_url
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
function! go#fillstruct#FillStruct() abort
|
function! go#fillstruct#FillStruct() abort
|
||||||
let l:cmd = ['fillstruct',
|
let l:cmd = ['fillstruct',
|
||||||
\ '-file', bufname(''),
|
\ '-file', bufname(''),
|
||||||
\ '-offset', go#util#OffsetCursor(),
|
\ '-offset', go#util#OffsetCursor(),
|
||||||
\ '-line', line('.')]
|
\ '-line', line('.')]
|
||||||
|
" Needs: https://github.com/davidrjenni/reftools/pull/14
|
||||||
|
"\ '-tags', go#config#BuildTags()]
|
||||||
|
|
||||||
" Read from stdin if modified.
|
" Read from stdin if modified.
|
||||||
if &modified
|
if &modified
|
||||||
|
@ -59,4 +65,8 @@ function! go#fillstruct#FillStruct() abort
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_fillstruct() abort
|
func! Test_fillstruct() abort
|
||||||
try
|
try
|
||||||
let l:tmp = gotest#write_file('a/a.go', [
|
let l:tmp = gotest#write_file('a/a.go', [
|
||||||
|
@ -87,4 +91,8 @@ func! Test_fillstruct_two_cursor() abort
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -5,21 +5,9 @@
|
||||||
" fmt.vim: Vim command to format Go files with gofmt (and gofmt compatible
|
" fmt.vim: Vim command to format Go files with gofmt (and gofmt compatible
|
||||||
" toorls, such as goimports).
|
" toorls, such as goimports).
|
||||||
|
|
||||||
if !exists("g:go_fmt_command")
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let g:go_fmt_command = "gofmt"
|
let s:cpo_save = &cpo
|
||||||
endif
|
set cpo&vim
|
||||||
|
|
||||||
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_experimental")
|
|
||||||
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/12741977/prevent-vim-from-updating-its-undo-tree
|
||||||
|
@ -30,7 +18,7 @@ endif
|
||||||
" this and have VimL experience, please look at the function for
|
" this and have VimL experience, please look at the function for
|
||||||
" improvements, patches are welcome :)
|
" improvements, patches are welcome :)
|
||||||
function! go#fmt#Format(withGoimport) abort
|
function! go#fmt#Format(withGoimport) abort
|
||||||
if g:go_fmt_experimental == 1
|
if go#config#FmtExperimental()
|
||||||
" Using winsaveview to save/restore cursor state has the problem of
|
" Using winsaveview to save/restore cursor state has the problem of
|
||||||
" closing folds on save:
|
" closing folds on save:
|
||||||
" https://github.com/fatih/vim-go/issues/502
|
" https://github.com/fatih/vim-go/issues/502
|
||||||
|
@ -64,18 +52,18 @@ function! go#fmt#Format(withGoimport) abort
|
||||||
let l:tmpname = tr(l:tmpname, '\', '/')
|
let l:tmpname = tr(l:tmpname, '\', '/')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let bin_name = g:go_fmt_command
|
let bin_name = go#config#FmtCommand()
|
||||||
if a:withGoimport == 1
|
if a:withGoimport == 1
|
||||||
let bin_name = "goimports"
|
let bin_name = "goimports"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let current_col = col('.')
|
let current_col = col('.')
|
||||||
let out = go#fmt#run(bin_name, l:tmpname, expand('%'))
|
let [l:out, l:err] = go#fmt#run(bin_name, l:tmpname, expand('%'))
|
||||||
let diff_offset = len(readfile(l:tmpname)) - line('$')
|
let diff_offset = len(readfile(l:tmpname)) - line('$')
|
||||||
|
|
||||||
if go#util#ShellError() == 0
|
if l:err == 0
|
||||||
call go#fmt#update_file(l:tmpname, expand('%'))
|
call go#fmt#update_file(l:tmpname, expand('%'))
|
||||||
elseif g:go_fmt_fail_silently == 0
|
elseif !go#config#FmtFailSilently()
|
||||||
let errors = s:parse_errors(expand('%'), out)
|
let errors = s:parse_errors(expand('%'), out)
|
||||||
call s:show_errors(errors)
|
call s:show_errors(errors)
|
||||||
endif
|
endif
|
||||||
|
@ -83,7 +71,7 @@ function! go#fmt#Format(withGoimport) abort
|
||||||
" We didn't use the temp file, so clean up
|
" We didn't use the temp file, so clean up
|
||||||
call delete(l:tmpname)
|
call delete(l:tmpname)
|
||||||
|
|
||||||
if g:go_fmt_experimental == 1
|
if go#config#FmtExperimental()
|
||||||
" restore our undo history
|
" restore our undo history
|
||||||
silent! exe 'rundo ' . tmpundofile
|
silent! exe 'rundo ' . tmpundofile
|
||||||
call delete(tmpundofile)
|
call delete(tmpundofile)
|
||||||
|
@ -154,64 +142,27 @@ endfunction
|
||||||
" run runs the gofmt/goimport command for the given source file and returns
|
" 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.
|
" the output of the executed command. Target is the real file to be formatted.
|
||||||
function! go#fmt#run(bin_name, source, target)
|
function! go#fmt#run(bin_name, source, target)
|
||||||
let cmd = s:fmt_cmd(a:bin_name, a:source, a:target)
|
let l:cmd = s:fmt_cmd(a:bin_name, a:source, a:target)
|
||||||
if empty(cmd)
|
if empty(l:cmd)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
return go#util#Exec(l:cmd)
|
||||||
let command = join(cmd, " ")
|
|
||||||
|
|
||||||
" execute our command...
|
|
||||||
let out = go#util#System(command)
|
|
||||||
|
|
||||||
return out
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" fmt_cmd returns a dict that contains the command to execute gofmt (or
|
" fmt_cmd returns the command to run as a list.
|
||||||
" goimports). args is dict with
|
|
||||||
function! s:fmt_cmd(bin_name, source, target)
|
function! s:fmt_cmd(bin_name, source, target)
|
||||||
" check if the user has installed command binary.
|
let l:cmd = [a:bin_name, '-w']
|
||||||
" 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
|
|
||||||
|
|
||||||
" 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
|
" 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
|
" string, however to allow customization it's now a dictionary of binary
|
||||||
" name mapping to options.
|
" name mapping to options.
|
||||||
let opts = g:go_fmt_options
|
let opts = go#config#FmtOptions()
|
||||||
if type(g:go_fmt_options) == type({})
|
if type(opts) == type({})
|
||||||
let opts = has_key(g:go_fmt_options, a:bin_name) ? g:go_fmt_options[a:bin_name] : ""
|
let opts = has_key(opts, a:bin_name) ? opts[a:bin_name] : ""
|
||||||
endif
|
endif
|
||||||
call extend(cmd, split(opts, " "))
|
call extend(cmd, split(opts, " "))
|
||||||
|
if a:bin_name is# 'goimports'
|
||||||
if a:bin_name == "goimports"
|
call extend(cmd, ["-srcdir", a:target])
|
||||||
" 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 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
|
endif
|
||||||
|
|
||||||
call add(cmd, a:source)
|
call add(cmd, a:source)
|
||||||
|
@ -254,14 +205,18 @@ function! s:show_errors(errors) abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#fmt#ToggleFmtAutoSave() abort
|
function! go#fmt#ToggleFmtAutoSave() abort
|
||||||
if get(g:, "go_fmt_autosave", 1)
|
if go#config#FmtAutosave()
|
||||||
let g:go_fmt_autosave = 0
|
call go#config#SetFmtAutosave(0)
|
||||||
call go#util#EchoProgress("auto fmt disabled")
|
call go#util#EchoProgress("auto fmt disabled")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
let g:go_fmt_autosave = 1
|
call go#config#SetFmtAutosave(1)
|
||||||
call go#util#EchoProgress("auto fmt enabled")
|
call go#util#EchoProgress("auto fmt enabled")
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_run_fmt() abort
|
func! Test_run_fmt() abort
|
||||||
let actual_file = tempname()
|
let actual_file = tempname()
|
||||||
call writefile(readfile("test-fixtures/fmt/hello.go"), actual_file)
|
call writefile(readfile("test-fixtures/fmt/hello.go"), actual_file)
|
||||||
|
@ -46,4 +50,8 @@ func! Test_goimports() abort
|
||||||
call assert_equal(expected, actual)
|
call assert_equal(expected, actual)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
" guru.vim -- Vim integration for the Go guru.
|
" guru.vim -- Vim integration for the Go guru.
|
||||||
|
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
" guru_cmd returns a dict that contains the command to execute guru. args
|
" guru_cmd returns a dict that contains the command to execute guru. args
|
||||||
" is dict with following options:
|
" is dict with following options:
|
||||||
" mode : guru mode, such as 'implements'
|
" mode : guru mode, such as 'implements'
|
||||||
|
@ -14,14 +18,9 @@ function! s:guru_cmd(args) range abort
|
||||||
let format = a:args.format
|
let format = a:args.format
|
||||||
let needs_scope = a:args.needs_scope
|
let needs_scope = a:args.needs_scope
|
||||||
let selected = a:args.selected
|
let selected = a:args.selected
|
||||||
|
let postype = get(a:args, 'postype', 'cursor')
|
||||||
|
|
||||||
let result = {}
|
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
|
"return with a warning if the binary doesn't exist
|
||||||
let bin_path = go#path#CheckBinPath("guru")
|
let bin_path = go#path#CheckBinPath("guru")
|
||||||
|
@ -30,9 +29,8 @@ function! s:guru_cmd(args) range abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" start constructing the command
|
" start constructing the command
|
||||||
let cmd = [bin_path]
|
let cmd = [bin_path, '-tags', go#config#BuildTags()]
|
||||||
|
|
||||||
let filename = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
|
||||||
if &modified
|
if &modified
|
||||||
let result.stdin_content = go#util#archive()
|
let result.stdin_content = go#util#archive()
|
||||||
call add(cmd, "-modified")
|
call add(cmd, "-modified")
|
||||||
|
@ -43,49 +41,30 @@ function! s:guru_cmd(args) range abort
|
||||||
call add(cmd, "-json")
|
call add(cmd, "-json")
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" check for any tags
|
let scopes = go#config#GuruScope()
|
||||||
if exists('g:go_build_tags')
|
if empty(scopes)
|
||||||
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
|
" 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
|
" choose a sensible setting, which is using the current file's package
|
||||||
let scopes = []
|
|
||||||
if needs_scope
|
if needs_scope
|
||||||
|
let pkg = go#package#ImportPath()
|
||||||
|
if pkg == -1
|
||||||
|
return {'err': "current directory is not inside of a valid GOPATH"}
|
||||||
|
endif
|
||||||
let scopes = [pkg]
|
let scopes = [pkg]
|
||||||
endif
|
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
|
endif
|
||||||
|
|
||||||
let scopes = get(g:, 'go_guru_scope')
|
" Add the scope.
|
||||||
endif
|
|
||||||
|
|
||||||
" now add the scope to our command if there is any
|
|
||||||
if !empty(scopes)
|
if !empty(scopes)
|
||||||
" strip trailing slashes for each path in scoped. bug:
|
" guru expect a comma-separated list of patterns.
|
||||||
" https://github.com/golang/go/issues/14584
|
|
||||||
let scopes = go#util#StripTrailingSlash(scopes)
|
|
||||||
|
|
||||||
" create shell-safe entries of the list
|
|
||||||
if !has("nvim") && !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 l:scope = join(scopes, ",")
|
||||||
let result.scope = l:scope
|
let result.scope = l:scope
|
||||||
call extend(cmd, ["-scope", l:scope])
|
call extend(cmd, ["-scope", l:scope])
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if postype == 'balloon'
|
||||||
|
let pos = printf("#%s", go#util#Offset(v:beval_lnum, v:beval_col))
|
||||||
|
else
|
||||||
let pos = printf("#%s", go#util#OffsetCursor())
|
let pos = printf("#%s", go#util#OffsetCursor())
|
||||||
if selected != -1
|
if selected != -1
|
||||||
" means we have a range, get it
|
" means we have a range, get it
|
||||||
|
@ -93,9 +72,10 @@ function! s:guru_cmd(args) range abort
|
||||||
let pos2 = go#util#Offset(line("'>"), col("'>"))
|
let pos2 = go#util#Offset(line("'>"), col("'>"))
|
||||||
let pos = printf("#%s,#%s", pos1, pos2)
|
let pos = printf("#%s,#%s", pos1, pos2)
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
let filename .= ':'.pos
|
let l:filename = fnamemodify(expand("%"), ':p:gs?\\?/?') . ':' . pos
|
||||||
call extend(cmd, [mode, filename])
|
call extend(cmd, [mode, l:filename])
|
||||||
|
|
||||||
let result.cmd = cmd
|
let result.cmd = cmd
|
||||||
return result
|
return result
|
||||||
|
@ -119,51 +99,22 @@ function! s:sync_guru(args) abort
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
" run, forrest run!!!
|
" run, forrest run!!!
|
||||||
let command = join(result.cmd, " ")
|
if has_key(l:result, 'stdin_content')
|
||||||
if has_key(result, 'stdin_content')
|
let [l:out, l:err] = go#util#Exec(l:result.cmd, l:result.stdin_content)
|
||||||
let out = go#util#System(command, result.stdin_content)
|
|
||||||
else
|
else
|
||||||
let out = go#util#System(command)
|
let [l:out, l:err] = go#util#Exec(l:result.cmd)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if has_key(a:args, 'custom_parse')
|
if has_key(a:args, 'custom_parse')
|
||||||
call a:args.custom_parse(go#util#ShellError(), out, a:args.mode)
|
call a:args.custom_parse(l:err, l:out, a:args.mode)
|
||||||
else
|
else
|
||||||
call s:parse_guru_output(go#util#ShellError(), out, a:args.mode)
|
call s:parse_guru_output(l:err, l:out, a:args.mode)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return out
|
return l:out
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
" use vim or neovim job api as appropriate
|
|
||||||
function! s:job_start(cmd, start_options) abort
|
|
||||||
if go#util#has_job()
|
|
||||||
return job_start(a:cmd, a:start_options)
|
|
||||||
endif
|
|
||||||
|
|
||||||
let opts = {'stdout_buffered': v:true, 'stderr_buffered': v:true}
|
|
||||||
function opts.on_stdout(job_id, data, event) closure
|
|
||||||
call a:start_options.callback(a:job_id, join(a:data, "\n"))
|
|
||||||
endfunction
|
|
||||||
function opts.on_stderr(job_id, data, event) closure
|
|
||||||
call a:start_options.callback(a:job_id, join(a:data, "\n"))
|
|
||||||
endfunction
|
|
||||||
function opts.on_exit(job_id, exit_code, event) closure
|
|
||||||
call a:start_options.exit_cb(a:job_id, a:exit_code)
|
|
||||||
call a:start_options.close_cb(a:job_id)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" use a shell for input redirection if needed
|
|
||||||
let cmd = a:cmd
|
|
||||||
if has_key(a:start_options, 'in_io') && a:start_options.in_io ==# 'file' && !empty(a:start_options.in_name)
|
|
||||||
let cmd = ['/bin/sh', '-c', join(a:cmd, ' ') . ' <' . a:start_options.in_name]
|
|
||||||
endif
|
|
||||||
|
|
||||||
return jobstart(cmd, opts)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" async_guru runs guru in async mode with the given arguments
|
" async_guru runs guru in async mode with the given arguments
|
||||||
function! s:async_guru(args) abort
|
function! s:async_guru(args) abort
|
||||||
let result = s:guru_cmd(a:args)
|
let result = s:guru_cmd(a:args)
|
||||||
|
@ -172,92 +123,51 @@ function! s:async_guru(args) abort
|
||||||
return
|
return
|
||||||
endif
|
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)...")
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
|
|
||||||
let state = {
|
let state = {
|
||||||
\ 'status_dir': expand('%:p:h'),
|
|
||||||
\ 'statusline_type': printf("%s", a:args.mode),
|
|
||||||
\ 'mode': a:args.mode,
|
\ 'mode': a:args.mode,
|
||||||
\ 'status': {},
|
|
||||||
\ 'exitval': 0,
|
|
||||||
\ 'closed': 0,
|
|
||||||
\ 'exited': 0,
|
|
||||||
\ 'messages': [],
|
|
||||||
\ 'parse' : get(a:args, 'custom_parse', funcref("s:parse_guru_output"))
|
\ 'parse' : get(a:args, 'custom_parse', funcref("s:parse_guru_output"))
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
function! s:callback(chan, msg) dict
|
" explicitly bind complete to state so that within it, self will
|
||||||
call add(self.messages, a:msg)
|
" always refer to state. See :help Partial for more information.
|
||||||
endfunction
|
let state.complete = function('s:complete', [], state)
|
||||||
|
|
||||||
function! s:exit_cb(job, exitval) dict
|
let opts = {
|
||||||
let self.exited = 1
|
\ 'statustype': get(a:args, 'statustype', a:args.mode),
|
||||||
|
\ 'for': '_',
|
||||||
let status = {
|
\ 'errorformat': "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m",
|
||||||
\ 'desc': 'last status',
|
\ 'complete': state.complete,
|
||||||
\ 'type': self.statusline_type,
|
|
||||||
\ 'state': "finished",
|
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
if a:exitval
|
if has_key(a:args, 'disable_progress')
|
||||||
let self.exitval = a:exitval
|
let opts.statustype = ''
|
||||||
let status.state = "failed"
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call go#statusline#Update(self.status_dir, status)
|
let opts = go#job#Options(l:opts)
|
||||||
|
|
||||||
if self.closed
|
|
||||||
call self.complete()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:close_cb(ch) dict
|
|
||||||
let self.closed = 1
|
|
||||||
|
|
||||||
if self.exited
|
|
||||||
call self.complete()
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function state.complete() dict
|
|
||||||
let out = join(self.messages, "\n")
|
|
||||||
|
|
||||||
call self.parse(self.exitval, out, self.mode)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" explicitly bind the callbacks to state so that self within them always
|
|
||||||
" refers to state. See :help Partial for more information.
|
|
||||||
let start_options = {
|
|
||||||
\ 'callback': function('s:callback', [], state),
|
|
||||||
\ 'exit_cb': function('s:exit_cb', [], state),
|
|
||||||
\ 'close_cb': function('s:close_cb', [], state)
|
|
||||||
\ }
|
|
||||||
|
|
||||||
if has_key(result, 'stdin_content')
|
if has_key(result, 'stdin_content')
|
||||||
let l:tmpname = tempname()
|
let l:tmpname = tempname()
|
||||||
call writefile(split(result.stdin_content, "\n"), l:tmpname, "b")
|
call writefile(split(result.stdin_content, "\n"), l:tmpname, "b")
|
||||||
let l:start_options.in_io = "file"
|
let l:opts.in_io = "file"
|
||||||
let l:start_options.in_name = l:tmpname
|
let l:opts.in_name = l:tmpname
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call go#statusline#Update(state.status_dir, {
|
call go#job#Start(result.cmd, opts)
|
||||||
\ 'desc': "current status",
|
|
||||||
\ 'type': state.statusline_type,
|
|
||||||
\ 'state': "analysing",
|
|
||||||
\})
|
|
||||||
|
|
||||||
return s:job_start(result.cmd, start_options)
|
if a:args.needs_scope && go#config#EchoCommandInfo() && !has_key(a:args, 'disable_progress')
|
||||||
|
call go#util#EchoProgress("analysing with scope " . result.scope .
|
||||||
|
\ " (see ':help go-guru-scope' if this doesn't work)...")
|
||||||
|
endif
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
function! s:complete(job, exit_status, messages) dict abort
|
||||||
|
let output = join(a:messages, "\n")
|
||||||
|
call self.parse(a:exit_status, output, self.mode)
|
||||||
|
endfunction
|
||||||
|
|
||||||
" run_guru runs the given guru argument
|
" run_guru runs the given guru argument
|
||||||
function! s:run_guru(args) abort
|
function! s:run_guru(args) abort
|
||||||
if has('nvim') || go#util#has_job()
|
if go#util#has_job()
|
||||||
let res = s:async_guru(a:args)
|
let res = s:async_guru(a:args)
|
||||||
else
|
else
|
||||||
let res = s:sync_guru(a:args)
|
let res = s:sync_guru(a:args)
|
||||||
|
@ -278,6 +188,18 @@ function! go#guru#Implements(selected) abort
|
||||||
call s:run_guru(args)
|
call s:run_guru(args)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" Shows the set of possible objects to which a pointer may point.
|
||||||
|
function! go#guru#PointsTo(selected) abort
|
||||||
|
let l:args = {
|
||||||
|
\ 'mode': 'pointsto',
|
||||||
|
\ 'format': 'plain',
|
||||||
|
\ 'selected': a:selected,
|
||||||
|
\ 'needs_scope': 1,
|
||||||
|
\ }
|
||||||
|
|
||||||
|
call s:run_guru(l:args)
|
||||||
|
endfunction
|
||||||
|
|
||||||
" Report the possible constants, global variables, and concrete types that may
|
" Report the possible constants, global variables, and concrete types that may
|
||||||
" appear in a value of type error
|
" appear in a value of type error
|
||||||
function! go#guru#Whicherrs(selected) abort
|
function! go#guru#Whicherrs(selected) abort
|
||||||
|
@ -309,7 +231,7 @@ function! go#guru#Describe(selected) abort
|
||||||
call s:run_guru(args)
|
call s:run_guru(args)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#guru#DescribeInfo() abort
|
function! go#guru#DescribeInfo(showstatus) abort
|
||||||
" json_encode() and friends are introduced with this patch (7.4.1304)
|
" json_encode() and friends are introduced with this patch (7.4.1304)
|
||||||
" vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ
|
" vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ
|
||||||
" nvim: https://github.com/neovim/neovim/pull/4131
|
" nvim: https://github.com/neovim/neovim/pull/4131
|
||||||
|
@ -318,6 +240,18 @@ function! go#guru#DescribeInfo() abort
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
let args = {
|
||||||
|
\ 'mode': 'describe',
|
||||||
|
\ 'format': 'json',
|
||||||
|
\ 'selected': -1,
|
||||||
|
\ 'needs_scope': 0,
|
||||||
|
\ 'custom_parse': function('s:info'),
|
||||||
|
\ 'disable_progress': a:showstatus == 0,
|
||||||
|
\ }
|
||||||
|
|
||||||
|
call s:run_guru(args)
|
||||||
|
endfunction
|
||||||
|
|
||||||
function! s:info(exit_val, output, mode)
|
function! s:info(exit_val, output, mode)
|
||||||
if a:exit_val != 0
|
if a:exit_val != 0
|
||||||
return
|
return
|
||||||
|
@ -400,19 +334,7 @@ function! go#guru#DescribeInfo() abort
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call go#util#EchoInfo(info)
|
call go#util#ShowInfo(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
|
endfunction
|
||||||
|
|
||||||
" Show possible targets of selected function call
|
" Show possible targets of selected function call
|
||||||
|
@ -493,7 +415,7 @@ function! go#guru#Referrers(selected) abort
|
||||||
call s:run_guru(args)
|
call s:run_guru(args)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#guru#SameIds() abort
|
function! go#guru#SameIds(showstatus) abort
|
||||||
" we use matchaddpos() which was introduce with 7.4.330, be sure we have
|
" 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
|
" it: http://ftp.vim.org/vim/patches/7.4/7.4.330
|
||||||
if !exists("*matchaddpos")
|
if !exists("*matchaddpos")
|
||||||
|
@ -516,6 +438,9 @@ function! go#guru#SameIds() abort
|
||||||
\ 'needs_scope': 0,
|
\ 'needs_scope': 0,
|
||||||
\ 'custom_parse': function('s:same_ids_highlight'),
|
\ 'custom_parse': function('s:same_ids_highlight'),
|
||||||
\ }
|
\ }
|
||||||
|
if !a:showstatus
|
||||||
|
let args.disable_progress = 1
|
||||||
|
endif
|
||||||
|
|
||||||
call s:run_guru(args)
|
call s:run_guru(args)
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -524,20 +449,20 @@ function! s:same_ids_highlight(exit_val, output, mode) abort
|
||||||
call go#guru#ClearSameIds() " run after calling guru to reduce flicker.
|
call go#guru#ClearSameIds() " run after calling guru to reduce flicker.
|
||||||
|
|
||||||
if a:output[0] !=# '{'
|
if a:output[0] !=# '{'
|
||||||
if !get(g:, 'go_auto_sameids', 0)
|
if !go#config#AutoSameids()
|
||||||
call go#util#EchoError(a:output)
|
call go#util#EchoError(a:output)
|
||||||
endif
|
endif
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let result = json_decode(a:output)
|
let result = json_decode(a:output)
|
||||||
if type(result) != type({}) && !get(g:, 'go_auto_sameids', 0)
|
if type(result) != type({}) && !go#config#AutoSameids()
|
||||||
call go#util#EchoError("malformed output from guru")
|
call go#util#EchoError("malformed output from guru")
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !has_key(result, 'sameids')
|
if !has_key(result, 'sameids')
|
||||||
if !get(g:, 'go_auto_sameids', 0)
|
if !go#config#AutoSameids()
|
||||||
call go#util#EchoError("no same_ids founds for the given identifier")
|
call go#util#EchoError("no same_ids founds for the given identifier")
|
||||||
endif
|
endif
|
||||||
return
|
return
|
||||||
|
@ -563,12 +488,12 @@ function! s:same_ids_highlight(exit_val, output, mode) abort
|
||||||
call matchaddpos('goSameId', [[str2nr(pos[-2]), str2nr(pos[-1]), str2nr(poslen)]])
|
call matchaddpos('goSameId', [[str2nr(pos[-2]), str2nr(pos[-1]), str2nr(poslen)]])
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
if get(g:, "go_auto_sameids", 0)
|
if go#config#AutoSameids()
|
||||||
" re-apply SameIds at the current cursor position at the time the buffer
|
" re-apply SameIds at the current cursor position at the time the buffer
|
||||||
" is redisplayed: e.g. :edit, :GoRename, etc.
|
" is redisplayed: e.g. :edit, :GoRename, etc.
|
||||||
augroup vim-go-sameids
|
augroup vim-go-sameids
|
||||||
autocmd!
|
autocmd! * <buffer>
|
||||||
autocmd BufWinEnter <buffer> nested call go#guru#SameIds()
|
autocmd BufWinEnter <buffer> nested call go#guru#SameIds(0)
|
||||||
augroup end
|
augroup end
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -592,7 +517,7 @@ function! go#guru#ClearSameIds() abort
|
||||||
|
|
||||||
" remove the autocmds we defined
|
" remove the autocmds we defined
|
||||||
augroup vim-go-sameids
|
augroup vim-go-sameids
|
||||||
autocmd!
|
autocmd! * <buffer>
|
||||||
augroup end
|
augroup end
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
@ -600,20 +525,20 @@ endfunction
|
||||||
|
|
||||||
function! go#guru#ToggleSameIds() abort
|
function! go#guru#ToggleSameIds() abort
|
||||||
if go#guru#ClearSameIds() != 0
|
if go#guru#ClearSameIds() != 0
|
||||||
call go#guru#SameIds()
|
call go#guru#SameIds(1)
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#guru#AutoToogleSameIds() abort
|
function! go#guru#AutoToggleSameIds() abort
|
||||||
if get(g:, "go_auto_sameids", 0)
|
if go#config#AutoSameids()
|
||||||
call go#util#EchoProgress("sameids auto highlighting disabled")
|
call go#util#EchoProgress("sameids auto highlighting disabled")
|
||||||
call go#guru#ClearSameIds()
|
call go#guru#ClearSameIds()
|
||||||
let g:go_auto_sameids = 0
|
call go#config#SetAutoSameids(0)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call go#util#EchoSuccess("sameids auto highlighting enabled")
|
call go#util#EchoSuccess("sameids auto highlighting enabled")
|
||||||
let g:go_auto_sameids = 1
|
call go#config#SetAutoSameids(1)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
@ -648,22 +573,165 @@ endfun
|
||||||
|
|
||||||
function! go#guru#Scope(...) abort
|
function! go#guru#Scope(...) abort
|
||||||
if a:0
|
if a:0
|
||||||
|
let scope = a:000
|
||||||
if a:0 == 1 && a:1 == '""'
|
if a:0 == 1 && a:1 == '""'
|
||||||
unlet g:go_guru_scope
|
let scope = []
|
||||||
|
endif
|
||||||
|
|
||||||
|
call go#config#SetGuruScope(scope)
|
||||||
|
if empty(scope)
|
||||||
call go#util#EchoSuccess("guru scope is cleared")
|
call go#util#EchoSuccess("guru scope is cleared")
|
||||||
else
|
else
|
||||||
let g:go_guru_scope = a:000
|
|
||||||
call go#util#EchoSuccess("guru scope changed to: ". join(a:000, ","))
|
call go#util#EchoSuccess("guru scope changed to: ". join(a:000, ","))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !exists('g:go_guru_scope')
|
let scope = go#config#GuruScope()
|
||||||
|
if empty(scope)
|
||||||
call go#util#EchoError("guru scope is not set")
|
call go#util#EchoError("guru scope is not set")
|
||||||
else
|
else
|
||||||
call go#util#EchoSuccess("current guru scope: ". join(g:go_guru_scope, ","))
|
call go#util#EchoSuccess("current guru scope: ". join(scope, ","))
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! go#guru#DescribeBalloon() abort
|
||||||
|
" don't even try if async isn't available.
|
||||||
|
if !go#util#has_job()
|
||||||
|
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("requires 'json_decode'. Update your Vim/Neovim version.")
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" change the active window to the window where the cursor is.
|
||||||
|
let l:winid = win_getid(winnr())
|
||||||
|
call win_gotoid(v:beval_winid)
|
||||||
|
|
||||||
|
let l:args = {
|
||||||
|
\ 'mode': 'describe',
|
||||||
|
\ 'format': 'json',
|
||||||
|
\ 'selected': -1,
|
||||||
|
\ 'needs_scope': 0,
|
||||||
|
\ 'custom_parse': function('s:describe_balloon'),
|
||||||
|
\ 'disable_progress': 1,
|
||||||
|
\ 'postype': 'balloon',
|
||||||
|
\ }
|
||||||
|
|
||||||
|
call s:async_guru(args)
|
||||||
|
|
||||||
|
" make the starting window active again
|
||||||
|
call win_gotoid(l:winid)
|
||||||
|
|
||||||
|
return ''
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:describe_balloon(exit_val, output, mode)
|
||||||
|
if a:exit_val != 0
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if a:output[0] !=# '{'
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if empty(a:output) || type(a:output) != type("")
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:result = json_decode(a:output)
|
||||||
|
if type(l:result) != type({})
|
||||||
|
call go#util#EchoError(printf('malformed output from guru: %s', a:output))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:info = []
|
||||||
|
if has_key(l:result, 'desc')
|
||||||
|
if l:result['desc'] != 'identifier'
|
||||||
|
let l:info = add(l:info, l:result['desc'])
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(l:result, 'detail')
|
||||||
|
let l:detail = l:result['detail']
|
||||||
|
|
||||||
|
" guru gives different information based on the detail mode. Let try to
|
||||||
|
" extract the most useful information
|
||||||
|
|
||||||
|
if l:detail == 'value'
|
||||||
|
if !has_key(l:result, 'value')
|
||||||
|
call go#util#EchoError('value key is missing. Please open a bug report on vim-go repo.')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:val = l:result['value']
|
||||||
|
if !has_key(l:val, 'type')
|
||||||
|
call go#util#EchoError('type key is missing (value.type). Please open a bug report on vim-go repo.')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:info = add(l:info, printf('type: %s', l:val['type']))
|
||||||
|
if has_key(l:val, 'value')
|
||||||
|
let l:info = add(l:info, printf('value: %s', l:val['value']))
|
||||||
|
endif
|
||||||
|
elseif l:detail == 'type'
|
||||||
|
if !has_key(l:result, 'type')
|
||||||
|
call go#util#EchoError('type key is missing. Please open a bug report on vim-go repo.')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:type = l:result['type']
|
||||||
|
if !has_key(l:type, 'type')
|
||||||
|
call go#util#EchoError('type key is missing (type.type). Please open a bug report on vim-go repo.')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:info = add(l:info, printf('type: %s', l:type['type']))
|
||||||
|
|
||||||
|
if has_key(l:type, 'methods')
|
||||||
|
let l:info = add(l:info, 'methods:')
|
||||||
|
for l:m in l:type.methods
|
||||||
|
let l:info = add(l:info, printf("\t%s", l:m['name']))
|
||||||
|
endfor
|
||||||
|
endif
|
||||||
|
elseif l:detail == 'package'
|
||||||
|
if !has_key(l:result, 'package')
|
||||||
|
call go#util#EchoError('package key is missing. Please open a bug report on vim-go repo.')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:package = result['package']
|
||||||
|
if !has_key(l:package, 'path')
|
||||||
|
call go#util#EchoError('path key is missing (package.path). Please open a bug report on vim-go repo.')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:info = add(l:info, printf('package: %s', l:package["path"]))
|
||||||
|
elseif l:detail == 'unknown'
|
||||||
|
" the description is already included in l:info, and there's no other
|
||||||
|
" information on unknowns.
|
||||||
|
else
|
||||||
|
call go#util#EchoError(printf('unknown detail mode (%s) found. Please open a bug report on vim-go repo', l:detail))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has('balloon_eval')
|
||||||
|
call balloon_show(join(l:info, "\n"))
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
call balloon_show(l:info)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
23
pack/acp/start/vim-go/autoload/go/guru_test.vim
Normal file
23
pack/acp/start/vim-go/autoload/go/guru_test.vim
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
function Test_GuruScope_Set() abort
|
||||||
|
silent call go#guru#Scope("example.com/foo/bar")
|
||||||
|
let actual = go#config#GuruScope()
|
||||||
|
call assert_equal(["example.com/foo/bar"], actual)
|
||||||
|
|
||||||
|
silent call go#guru#Scope('""')
|
||||||
|
silent let actual = go#config#GuruScope()
|
||||||
|
call assert_equal([], actual, "setting scope to empty string should clear")
|
||||||
|
|
||||||
|
if exists('g:go_guru_scope')
|
||||||
|
unlet g:go_guru_scope
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
105
pack/acp/start/vim-go/autoload/go/highlight_test.vim
Normal file
105
pack/acp/start/vim-go/autoload/go/highlight_test.vim
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
function! Test_gomodVersion_highlight() abort
|
||||||
|
try
|
||||||
|
syntax on
|
||||||
|
|
||||||
|
let l:dir = gotest#write_file('gomodtest/go.mod', [
|
||||||
|
\ 'module github.com/fatih/vim-go',
|
||||||
|
\ '',
|
||||||
|
\ '\x1frequire (',
|
||||||
|
\ '\tversion/simple v1.0.0',
|
||||||
|
\ '\tversion/simple-pre-release v1.0.0-rc',
|
||||||
|
\ '\tversion/simple-pre-release v1.0.0+meta',
|
||||||
|
\ '\tversion/simple-pre-release v1.0.0-rc+meta',
|
||||||
|
\ '\tversion/pseudo/premajor v1.0.0-20060102150405-0123456789abcdef',
|
||||||
|
\ '\tversion/pseudo/prerelease v1.0.0-prerelease.0.20060102150405-0123456789abcdef',
|
||||||
|
\ '\tversion/pseudo/prepatch v1.0.1-0.20060102150405-0123456789abcdef',
|
||||||
|
\ '\tversion/simple/incompatible v2.0.0+incompatible',
|
||||||
|
\ '\tversion/pseudo/premajor/incompatible v2.0.0-20060102150405-0123456789abcdef+incompatible',
|
||||||
|
\ '\tversion/pseudo/prerelease/incompatible v2.0.0-prerelease.0.20060102150405-0123456789abcdef+incompatible',
|
||||||
|
\ '\tversion/pseudo/prepatch/incompatible v2.0.1-0.20060102150405-0123456789abcdef+incompatible',
|
||||||
|
\ ')'])
|
||||||
|
|
||||||
|
let l:lineno = 4
|
||||||
|
let l:lineclose = line('$')
|
||||||
|
while l:lineno < l:lineclose
|
||||||
|
let l:line = getline(l:lineno)
|
||||||
|
let l:col = col([l:lineno, '$']) - 1
|
||||||
|
let l:idx = len(l:line) - 1
|
||||||
|
let l:from = stridx(l:line, ' ') + 1
|
||||||
|
|
||||||
|
while l:idx >= l:from
|
||||||
|
call cursor(l:lineno, l:col)
|
||||||
|
let l:synname = synIDattr(synID(l:lineno, l:col, 1), 'name')
|
||||||
|
let l:errlen = len(v:errors)
|
||||||
|
|
||||||
|
call assert_equal('gomodVersion', l:synname, 'version on line ' . l:lineno)
|
||||||
|
|
||||||
|
" continue at the next line if there was an error at this column;
|
||||||
|
" there's no need to test each column once an error is detected.
|
||||||
|
if l:errlen < len(v:errors)
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:col -= 1
|
||||||
|
let l:idx -= 1
|
||||||
|
endwhile
|
||||||
|
let l:lineno += 1
|
||||||
|
endwhile
|
||||||
|
finally
|
||||||
|
call delete(l:dir, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
function! Test_gomodVersion_incompatible_highlight() abort
|
||||||
|
try
|
||||||
|
syntax on
|
||||||
|
|
||||||
|
let l:dir = gotest#write_file('gomodtest/go.mod', [
|
||||||
|
\ 'module github.com/fatih/vim-go',
|
||||||
|
\ '',
|
||||||
|
\ '\x1frequire (',
|
||||||
|
\ '\tversion/invalid/premajor/incompatible v1.0.0-20060102150405-0123456789abcdef+incompatible',
|
||||||
|
\ '\tversion/invalid/prerelease/incompatible v1.0.0-prerelease.0.20060102150405-0123456789abcdef+incompatible',
|
||||||
|
\ '\tversion/invalid/prepatch/incompatible v1.0.1-0.20060102150405-0123456789abcdef+incompatible',
|
||||||
|
\ ')'])
|
||||||
|
|
||||||
|
let l:lineno = 4
|
||||||
|
let l:lineclose = line('$')
|
||||||
|
while l:lineno < l:lineclose
|
||||||
|
let l:line = getline(l:lineno)
|
||||||
|
let l:col = col([l:lineno, '$']) - 1
|
||||||
|
let l:idx = len(l:line) - 1
|
||||||
|
let l:from = stridx(l:line, '+')
|
||||||
|
|
||||||
|
while l:idx >= l:from
|
||||||
|
call cursor(l:lineno, l:col)
|
||||||
|
let l:synname = synIDattr(synID(l:lineno, l:col, 1), 'name')
|
||||||
|
let l:errlen = len(v:errors)
|
||||||
|
|
||||||
|
call assert_notequal('gomodVersion', l:synname, 'version on line ' . l:lineno)
|
||||||
|
|
||||||
|
" continue at the next line if there was an error at this column;
|
||||||
|
" there's no need to test each column once an error is detected.
|
||||||
|
if l:errlen < len(v:errors)
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:col -= 1
|
||||||
|
let l:idx -= 1
|
||||||
|
endwhile
|
||||||
|
let l:lineno += 1
|
||||||
|
endwhile
|
||||||
|
finally
|
||||||
|
call delete(l:dir, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
26
pack/acp/start/vim-go/autoload/go/iferr.vim
Normal file
26
pack/acp/start/vim-go/autoload/go/iferr.vim
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
function! go#iferr#Generate()
|
||||||
|
let [l:out, l:err] = go#util#Exec(['iferr',
|
||||||
|
\ '-pos=' . go#util#OffsetCursor()], go#util#GetLines())
|
||||||
|
if len(l:out) == 1
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if getline('.') =~ '^\s*$'
|
||||||
|
silent delete _
|
||||||
|
silent normal! k
|
||||||
|
endif
|
||||||
|
let l:pos = getcurpos()
|
||||||
|
call append(l:pos[1], split(l:out, "\n"))
|
||||||
|
silent normal! j=2j
|
||||||
|
call setpos('.', l:pos)
|
||||||
|
silent normal! 4j
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
function! go#impl#Impl(...) abort
|
function! go#impl#Impl(...) abort
|
||||||
let recv = ""
|
let recv = ""
|
||||||
let iface = ""
|
let iface = ""
|
||||||
|
@ -101,13 +105,29 @@ function! s:root_dirs() abort
|
||||||
return dirs
|
return dirs
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:go_packages(dirs) abort
|
function! s:go_packages(dirs, arglead) abort
|
||||||
let pkgs = []
|
let pkgs = []
|
||||||
for d in a:dirs
|
for dir in a:dirs
|
||||||
let pkg_root = expand(d . '/pkg/' . go#util#osarch())
|
" this may expand to multiple lines
|
||||||
call extend(pkgs, split(globpath(pkg_root, '**/*.a', 1), "\n"))
|
let scr_root = expand(dir . '/src/')
|
||||||
|
for pkg in split(globpath(scr_root, a:arglead.'*'), "\n")
|
||||||
|
if isdirectory(pkg)
|
||||||
|
let pkg .= '/'
|
||||||
|
elseif pkg !~ '\.a$'
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
" without this the result can have duplicates in form of
|
||||||
|
" 'encoding/json' and '/encoding/json/'
|
||||||
|
let pkg = go#util#StripPathSep(pkg)
|
||||||
|
|
||||||
|
" remove the scr root and keep the package in tact
|
||||||
|
let pkg = substitute(pkg, scr_root, "", "")
|
||||||
|
call add(pkgs, pkg)
|
||||||
endfor
|
endfor
|
||||||
return map(pkgs, "fnamemodify(v:val, ':t:r')")
|
endfor
|
||||||
|
|
||||||
|
return pkgs
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:interface_list(pkg) abort
|
function! s:interface_list(pkg) abort
|
||||||
|
@ -124,17 +144,32 @@ endfunction
|
||||||
" Complete package and interface for {interface}
|
" Complete package and interface for {interface}
|
||||||
function! go#impl#Complete(arglead, cmdline, cursorpos) abort
|
function! go#impl#Complete(arglead, cmdline, cursorpos) abort
|
||||||
let words = split(a:cmdline, '\s\+', 1)
|
let words = split(a:cmdline, '\s\+', 1)
|
||||||
|
|
||||||
if words[-1] ==# ''
|
if words[-1] ==# ''
|
||||||
return s:uniq(sort(s:go_packages(s:root_dirs())))
|
" if no words are given, just start completing the first package we found
|
||||||
elseif words[-1] =~# '^\h\w*$'
|
return s:uniq(sort(s:go_packages(s:root_dirs(), a:arglead)))
|
||||||
return s:uniq(sort(filter(s:go_packages(s:root_dirs()), 'stridx(v:val, words[-1]) == 0')))
|
elseif words[-1] =~# '^\(\h\w.*\.\%(\h\w*\)\=$\)\@!\S*$'
|
||||||
elseif words[-1] =~# '^\h\w*\.\%(\h\w*\)\=$'
|
" start matching go packages. It's negate match of the below match
|
||||||
let [pkg, interface] = split(words[-1], '\.', 1)
|
return s:uniq(sort(s:go_packages(s:root_dirs(), a:arglead)))
|
||||||
echomsg pkg
|
elseif words[-1] =~# '^\h\w.*\.\%(\h\w*\)\=$'
|
||||||
|
" match the following, anything that could indicate an interface candidate
|
||||||
|
"
|
||||||
|
" io.
|
||||||
|
" io.Wr
|
||||||
|
" github.com/fatih/color.
|
||||||
|
" github.com/fatih/color.U
|
||||||
|
" github.com/fatih/color.Un
|
||||||
|
let splitted = split(words[-1], '\.', 1)
|
||||||
|
let pkg = join(splitted[:-2], '.')
|
||||||
|
let interface = splitted[-1]
|
||||||
return s:uniq(sort(filter(s:interface_list(pkg), 'v:val =~? words[-1]')))
|
return s:uniq(sort(filter(s:interface_list(pkg), 'v:val =~? words[-1]')))
|
||||||
else
|
else
|
||||||
return []
|
return []
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_impl() abort
|
func! Test_impl() abort
|
||||||
try
|
try
|
||||||
let l:tmp = gotest#write_file('a/a.go', [
|
let l:tmp = gotest#write_file('a/a.go', [
|
||||||
|
@ -35,3 +39,9 @@ func! Test_impl_get() abort
|
||||||
call delete(l:tmp, 'rf')
|
call delete(l:tmp, 'rf')
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
"
|
"
|
||||||
" Check out the docs for more information at /doc/vim-go.txt
|
" Check out the docs for more information at /doc/vim-go.txt
|
||||||
"
|
"
|
||||||
|
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
function! go#import#SwitchImport(enabled, localname, path, bang) abort
|
function! go#import#SwitchImport(enabled, localname, path, bang) abort
|
||||||
let view = winsaveview()
|
let view = winsaveview()
|
||||||
let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '')
|
let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '')
|
||||||
|
@ -27,8 +32,8 @@ function! go#import#SwitchImport(enabled, localname, path, bang) abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if a:bang == "!"
|
if a:bang == "!"
|
||||||
let out = go#util#System("go get -u -v ".shellescape(path))
|
let [l:out, l:err] = go#util#Exec(['go', 'get', '-u', '-v', path])
|
||||||
if go#util#ShellError() != 0
|
if err != 0
|
||||||
call s:Error("Can't find import: " . path . ":" . out)
|
call s:Error("Can't find import: " . path . ":" . out)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
@ -65,6 +70,9 @@ function! go#import#SwitchImport(enabled, localname, path, bang) abort
|
||||||
let packageline = line
|
let packageline = line
|
||||||
let appendline = line
|
let appendline = line
|
||||||
|
|
||||||
|
elseif linestr =~# '^import\s\+(\+)'
|
||||||
|
let appendline = line
|
||||||
|
let appendstr = qlocalpath
|
||||||
elseif linestr =~# '^import\s\+('
|
elseif linestr =~# '^import\s\+('
|
||||||
let appendstr = qlocalpath
|
let appendstr = qlocalpath
|
||||||
let indentstr = 1
|
let indentstr = 1
|
||||||
|
@ -161,8 +169,16 @@ function! go#import#SwitchImport(enabled, localname, path, bang) abort
|
||||||
let linesdelta += 3
|
let linesdelta += 3
|
||||||
let appendstr = qlocalpath
|
let appendstr = qlocalpath
|
||||||
let indentstr = 1
|
let indentstr = 1
|
||||||
endif
|
|
||||||
call append(appendline, appendstr)
|
call append(appendline, appendstr)
|
||||||
|
elseif getline(appendline) =~# '^import\s\+(\+)'
|
||||||
|
call setline(appendline, 'import (')
|
||||||
|
call append(appendline + 0, appendstr)
|
||||||
|
call append(appendline + 1, ')')
|
||||||
|
let linesdelta -= 1
|
||||||
|
let indentstr = 1
|
||||||
|
else
|
||||||
|
call append(appendline, appendstr)
|
||||||
|
endif
|
||||||
execute appendline + 1
|
execute appendline + 1
|
||||||
if indentstr
|
if indentstr
|
||||||
execute 'normal! >>'
|
execute 'normal! >>'
|
||||||
|
@ -209,5 +225,8 @@ function! s:Error(s) abort
|
||||||
echohl Error | echo a:s | echohl None
|
echohl Error | echo a:s | echohl None
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
72
pack/acp/start/vim-go/autoload/go/indent_test.vim
Normal file
72
pack/acp/start/vim-go/autoload/go/indent_test.vim
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
func! Test_indent_raw_string() abort
|
||||||
|
" The goRawString discovery requires that syntax be enabled.
|
||||||
|
syntax on
|
||||||
|
|
||||||
|
try
|
||||||
|
let l:dir= gotest#write_file('indent/indent.go', [
|
||||||
|
\ 'package main',
|
||||||
|
\ '',
|
||||||
|
\ 'import "fmt"',
|
||||||
|
\ '',
|
||||||
|
\ 'func main() {',
|
||||||
|
\ "\t\x1fconst msg = `",
|
||||||
|
\ '`',
|
||||||
|
\ '\tfmt.Println(msg)',
|
||||||
|
\ '}'])
|
||||||
|
|
||||||
|
silent execute "normal o" . "not indented\<Esc>"
|
||||||
|
let l:indent = indent(line('.'))
|
||||||
|
call assert_equal(0, l:indent)
|
||||||
|
finally
|
||||||
|
call delete(l:dir, 'rf')
|
||||||
|
endtry
|
||||||
|
|
||||||
|
try
|
||||||
|
let l:dir= gotest#write_file('indent/indent.go', [
|
||||||
|
\ 'package main',
|
||||||
|
\ '',
|
||||||
|
\ 'import "fmt"',
|
||||||
|
\ '',
|
||||||
|
\ 'func main() {',
|
||||||
|
\ "\t\x1fmsg := `",
|
||||||
|
\ '`',
|
||||||
|
\ '\tfmt.Println(msg)',
|
||||||
|
\ '}'])
|
||||||
|
|
||||||
|
silent execute "normal o" . "not indented\<Esc>"
|
||||||
|
let l:indent = indent(line('.'))
|
||||||
|
call assert_equal(0, l:indent)
|
||||||
|
finally
|
||||||
|
call delete(l:dir, 'rf')
|
||||||
|
endtry
|
||||||
|
|
||||||
|
try
|
||||||
|
let l:dir= gotest#write_file('indent/indent.go', [
|
||||||
|
\ 'package main',
|
||||||
|
\ '',
|
||||||
|
\ 'import "fmt"',
|
||||||
|
\ '',
|
||||||
|
\ 'func main() {',
|
||||||
|
\ "\tconst msg = `",
|
||||||
|
\ "\t\x1findented",
|
||||||
|
\ '`',
|
||||||
|
\ '\tfmt.Println(msg)',
|
||||||
|
\ '}'])
|
||||||
|
|
||||||
|
silent execute "normal o" . "indented\<Esc>"
|
||||||
|
let l:indent = indent(line('.'))
|
||||||
|
call assert_equal(shiftwidth(), l:indent)
|
||||||
|
finally
|
||||||
|
call delete(l:dir, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
42
pack/acp/start/vim-go/autoload/go/issue.vim
Normal file
42
pack/acp/start/vim-go/autoload/go/issue.vim
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
let s:templatepath = go#util#Join(expand('<sfile>:p:h:h:h'), '.github', 'ISSUE_TEMPLATE.md')
|
||||||
|
|
||||||
|
function! go#issue#New() abort
|
||||||
|
let body = go#uri#Encode(s:issuebody())
|
||||||
|
let url = "https://github.com/fatih/vim-go/issues/new?body=" . l:body
|
||||||
|
call go#util#OpenBrowser(l:url)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:issuebody() abort
|
||||||
|
let lines = readfile(s:templatepath)
|
||||||
|
|
||||||
|
let rtrimpat = '[[:space:]]\+$'
|
||||||
|
let body = []
|
||||||
|
for l in lines
|
||||||
|
let body = add(body, l)
|
||||||
|
|
||||||
|
if l =~ '^\* Vim version'
|
||||||
|
redir => out
|
||||||
|
silent version
|
||||||
|
redir END
|
||||||
|
let body = extend(body, split(out, "\n")[0:2])
|
||||||
|
elseif l =~ '^\* Go version'
|
||||||
|
let [out, err] = go#util#Exec(['go', 'version'])
|
||||||
|
let body = add(body, substitute(l:out, rtrimpat, '', ''))
|
||||||
|
elseif l =~ '^\* Go environment'
|
||||||
|
let [out, err] = go#util#Exec(['go', 'env'])
|
||||||
|
let body = add(body, substitute(l:out, rtrimpat, '', ''))
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return join(body, "\n")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -1,24 +1,46 @@
|
||||||
" Spawn returns callbacks to be used with job_start. It is abstracted to be
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
" Spawn starts an asynchronous job. See the description of go#job#Options to
|
||||||
|
" understand the args parameter.
|
||||||
|
"
|
||||||
|
" Spawn returns a job.
|
||||||
|
function! go#job#Spawn(cmd, args)
|
||||||
|
let l:options = go#job#Options(a:args)
|
||||||
|
return go#job#Start(a:cmd, l:options)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Options returns callbacks to be used with job_start. It is abstracted to be
|
||||||
" used with various go commands, such as build, test, install, etc.. This
|
" used with various go commands, such as build, test, install, etc.. This
|
||||||
" allows us to avoid writing the same callback over and over for some
|
" allows us to avoid writing the same callback over and over for some
|
||||||
" commands. It's fully customizable so each command can change it to it's own
|
" commands. It's fully customizable so each command can change it to its own
|
||||||
" logic.
|
" logic.
|
||||||
"
|
"
|
||||||
" args is a dictionary with the these keys:
|
" args is a dictionary with the these keys:
|
||||||
" 'cmd':
|
|
||||||
" The value to pass to job_start().
|
|
||||||
" 'bang':
|
" 'bang':
|
||||||
" Set to 0 to jump to the first error in the error list.
|
" Set to 0 to jump to the first error in the error list.
|
||||||
" Defaults to 0.
|
" Defaults to 0.
|
||||||
|
" 'statustype':
|
||||||
|
" The status type to use when updating the status.
|
||||||
|
" See statusline.vim.
|
||||||
" 'for':
|
" 'for':
|
||||||
" The g:go_list_type_command key to use to get the error list type to use.
|
" The g:go_list_type_command key to use to get the error list type to use.
|
||||||
|
" Errors will not be handled when the value is '_'.
|
||||||
" Defaults to '_job'
|
" Defaults to '_job'
|
||||||
|
" 'errorformat':
|
||||||
|
" The errorformat string to use when parsing errors. Defaults to
|
||||||
|
" &errorformat.
|
||||||
|
" See :help 'errorformat'.
|
||||||
" 'complete':
|
" 'complete':
|
||||||
" A function to call after the job exits and the channel is closed. The
|
" A function to call after the job exits and the channel is closed. The
|
||||||
" function will be passed three arguments: the job, its exit code, and the
|
" function will be passed three arguments: the job, its exit code, and the
|
||||||
" list of messages received from the channel. The default value will
|
" list of messages received from the channel. The default is a no-op. A
|
||||||
" process the messages and manage the error list after the job exits and
|
" custom value can modify the messages before they are processed by the
|
||||||
" the channel is closed.
|
" returned exit_cb and close_cb callbacks. When the function is called,
|
||||||
|
" the current window will be the window that was hosting the buffer when
|
||||||
|
" the job was started. After it returns, the current window will be
|
||||||
|
" restored to what it was before the function was called.
|
||||||
|
|
||||||
" The return value is a dictionary with these keys:
|
" The return value is a dictionary with these keys:
|
||||||
" 'callback':
|
" 'callback':
|
||||||
|
@ -30,22 +52,30 @@
|
||||||
" 'close_cb':
|
" 'close_cb':
|
||||||
" A function suitable to be passed as a job close_cb handler. See
|
" A function suitable to be passed as a job close_cb handler. See
|
||||||
" job-close_cb.
|
" job-close_cb.
|
||||||
function go#job#Spawn(args)
|
" 'cwd':
|
||||||
|
" The path to the directory which contains the current buffer. The
|
||||||
|
" callbacks are configured to expect this directory is the working
|
||||||
|
" directory for the job; it should not be modified by callers.
|
||||||
|
function! go#job#Options(args)
|
||||||
let cbs = {}
|
let cbs = {}
|
||||||
let state = {
|
let state = {
|
||||||
\ 'winnr': winnr(),
|
\ 'winid': win_getid(winnr()),
|
||||||
\ 'dir': getcwd(),
|
\ 'dir': getcwd(),
|
||||||
\ 'jobdir': fnameescape(expand("%:p:h")),
|
\ 'jobdir': fnameescape(expand("%:p:h")),
|
||||||
\ 'messages': [],
|
\ 'messages': [],
|
||||||
\ 'args': a:args.cmd,
|
|
||||||
\ 'bang': 0,
|
\ 'bang': 0,
|
||||||
\ 'for': "_job",
|
\ 'for': "_job",
|
||||||
\ 'exited': 0,
|
\ 'exited': 0,
|
||||||
\ 'exit_status': 0,
|
\ 'exit_status': 0,
|
||||||
\ 'closed': 0,
|
\ 'closed': 0,
|
||||||
\ 'errorformat': &errorformat
|
\ 'errorformat': &errorformat,
|
||||||
|
\ 'statustype' : ''
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
|
if has("patch-8.0.0902") || has('nvim')
|
||||||
|
let cbs.cwd = state.jobdir
|
||||||
|
endif
|
||||||
|
|
||||||
if has_key(a:args, 'bang')
|
if has_key(a:args, 'bang')
|
||||||
let state.bang = a:args.bang
|
let state.bang = a:args.bang
|
||||||
endif
|
endif
|
||||||
|
@ -54,90 +84,139 @@ function go#job#Spawn(args)
|
||||||
let state.for = a:args.for
|
let state.for = a:args.for
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" do nothing in state.complete by default.
|
if has_key(a:args, 'statustype')
|
||||||
|
let state.statustype = a:args.statustype
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(a:args, 'errorformat')
|
||||||
|
let state.errorformat = a:args.errorformat
|
||||||
|
endif
|
||||||
|
|
||||||
function state.complete(job, exit_status, data)
|
function state.complete(job, exit_status, data)
|
||||||
|
if has_key(self, 'custom_complete')
|
||||||
|
let l:winid = win_getid(winnr())
|
||||||
|
" Always set the active window to the window that was active when the job
|
||||||
|
" was started. Among other things, this makes sure that the correct
|
||||||
|
" window's location list will be populated when the list type is
|
||||||
|
" 'location' and the user has moved windows since starting the job.
|
||||||
|
call win_gotoid(self.winid)
|
||||||
|
call self.custom_complete(a:job, a:exit_status, a:data)
|
||||||
|
call win_gotoid(l:winid)
|
||||||
|
endif
|
||||||
|
|
||||||
|
call self.show_errors(a:job, a:exit_status, a:data)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function state.show_status(job, exit_status) dict
|
||||||
|
if self.statustype == ''
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if go#config#EchoCommandInfo()
|
||||||
|
let prefix = '[' . self.statustype . '] '
|
||||||
|
if a:exit_status == 0
|
||||||
|
call go#util#EchoSuccess(prefix . "SUCCESS")
|
||||||
|
else
|
||||||
|
call go#util#EchoError(prefix . "FAIL")
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
let status = {
|
||||||
|
\ 'desc': 'last status',
|
||||||
|
\ 'type': self.statustype,
|
||||||
|
\ 'state': "success",
|
||||||
|
\ }
|
||||||
|
|
||||||
|
if a:exit_status
|
||||||
|
let status.state = "failed"
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(self, 'started_at')
|
||||||
|
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)
|
||||||
|
endif
|
||||||
|
|
||||||
|
call go#statusline#Update(self.jobdir, status)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
if has_key(a:args, 'complete')
|
if has_key(a:args, 'complete')
|
||||||
let state.complete = a:args.complete
|
let state.custom_complete = a:args.complete
|
||||||
endif
|
endif
|
||||||
|
|
||||||
function! s:callback(chan, msg) dict
|
" explicitly bind _start to state so that within it, self will
|
||||||
call add(self.messages, a:msg)
|
" always refer to state. See :help Partial for more information.
|
||||||
endfunction
|
"
|
||||||
|
" _start is intended only for internal use and should not be referenced
|
||||||
|
" outside of this file.
|
||||||
|
let cbs._start = function('s:start', [''], state)
|
||||||
|
|
||||||
" explicitly bind callback to state so that within it, self will
|
" explicitly bind callback to state so that within it, self will
|
||||||
" always refer to state. See :help Partial for more information.
|
" always refer to state. See :help Partial for more information.
|
||||||
let cbs.callback = function('s:callback', [], state)
|
let cbs.callback = function('s:callback', [], state)
|
||||||
|
|
||||||
function! s:exit_cb(job, exitval) dict
|
|
||||||
let self.exit_status = a:exitval
|
|
||||||
let self.exited = 1
|
|
||||||
|
|
||||||
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 self.closed
|
|
||||||
call self.complete(a:job, self.exit_status, self.messages)
|
|
||||||
call self.show_errors(a:job, self.exit_status, self.messages)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
" explicitly bind exit_cb to state so that within it, self will always refer
|
" explicitly bind exit_cb to state so that within it, self will always refer
|
||||||
" to state. See :help Partial for more information.
|
" to state. See :help Partial for more information.
|
||||||
let cbs.exit_cb = function('s:exit_cb', [], state)
|
let cbs.exit_cb = function('s:exit_cb', [], state)
|
||||||
|
|
||||||
function! s:close_cb(ch) dict
|
|
||||||
let self.closed = 1
|
|
||||||
|
|
||||||
if self.exited
|
|
||||||
let job = ch_getjob(a:ch)
|
|
||||||
call self.complete(job, self.exit_status, self.messages)
|
|
||||||
call self.show_errors(job, self.exit_status, self.messages)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
" explicitly bind close_cb to state so that within it, self will
|
" explicitly bind close_cb to state so that within it, self will
|
||||||
" always refer to state. See :help Partial for more information.
|
" always refer to state. See :help Partial for more information.
|
||||||
let cbs.close_cb = function('s:close_cb', [], state)
|
let cbs.close_cb = function('s:close_cb', [], state)
|
||||||
|
|
||||||
function state.show_errors(job, exit_status, data)
|
function state.show_errors(job, exit_status, data)
|
||||||
|
if self.for == '_'
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:winid = win_getid(winnr())
|
||||||
|
" Always set the active window to the window that was active when the job
|
||||||
|
" was started. Among other things, this makes sure that the correct
|
||||||
|
" window's location list will be populated when the list type is
|
||||||
|
" 'location' and the user has moved windows since starting the job.
|
||||||
|
call win_gotoid(self.winid)
|
||||||
|
|
||||||
let l:listtype = go#list#Type(self.for)
|
let l:listtype = go#list#Type(self.for)
|
||||||
if a:exit_status == 0
|
if a:exit_status == 0
|
||||||
call go#list#Clean(l:listtype)
|
call go#list#Clean(l:listtype)
|
||||||
|
call win_gotoid(l:winid)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:listtype = go#list#Type(self.for)
|
let l:listtype = go#list#Type(self.for)
|
||||||
if len(a:data) == 0
|
if len(a:data) == 0
|
||||||
call go#list#Clean(l:listtype)
|
call go#list#Clean(l:listtype)
|
||||||
|
call win_gotoid(l:winid)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let out = join(self.messages, "\n")
|
let out = join(self.messages, "\n")
|
||||||
|
|
||||||
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
|
||||||
try
|
try
|
||||||
" parse the errors relative to self.jobdir
|
" parse the errors relative to self.jobdir
|
||||||
execute cd self.jobdir
|
execute l:cd self.jobdir
|
||||||
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
|
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
|
||||||
let errors = go#list#Get(l:listtype)
|
let errors = go#list#Get(l:listtype)
|
||||||
finally
|
finally
|
||||||
execute cd . fnameescape(self.dir)
|
execute l:cd fnameescape(self.dir)
|
||||||
endtry
|
endtry
|
||||||
|
|
||||||
|
|
||||||
if empty(errors)
|
if empty(errors)
|
||||||
" failed to parse errors, output the original content
|
" failed to parse errors, output the original content
|
||||||
call go#util#EchoError(self.messages + [self.dir])
|
call go#util#EchoError([self.dir] + self.messages)
|
||||||
|
call win_gotoid(l:winid)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if self.winnr == winnr()
|
" only open the error window if user was still in the window from which
|
||||||
|
" the job was started.
|
||||||
|
if self.winid == l:winid
|
||||||
call go#list#Window(l:listtype, len(errors))
|
call go#list#Window(l:listtype, len(errors))
|
||||||
if !self.bang
|
if self.bang
|
||||||
|
call win_gotoid(l:winid)
|
||||||
|
else
|
||||||
call go#list#JumpToFirst(l:listtype)
|
call go#list#JumpToFirst(l:listtype)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
@ -146,4 +225,311 @@ function go#job#Spawn(args)
|
||||||
return cbs
|
return cbs
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:start(args) dict
|
||||||
|
if go#config#EchoCommandInfo() && self.statustype != ""
|
||||||
|
let prefix = '[' . self.statustype . '] '
|
||||||
|
call go#util#EchoSuccess(prefix . "dispatched")
|
||||||
|
endif
|
||||||
|
|
||||||
|
if self.statustype != ''
|
||||||
|
let status = {
|
||||||
|
\ 'desc': 'current status',
|
||||||
|
\ 'type': self.statustype,
|
||||||
|
\ 'state': "started",
|
||||||
|
\ }
|
||||||
|
|
||||||
|
call go#statusline#Update(self.jobdir, status)
|
||||||
|
endif
|
||||||
|
let self.started_at = reltime()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:callback(chan, msg) dict
|
||||||
|
call add(self.messages, a:msg)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:exit_cb(job, exitval) dict
|
||||||
|
let self.exit_status = a:exitval
|
||||||
|
let self.exited = 1
|
||||||
|
|
||||||
|
call self.show_status(a:job, a:exitval)
|
||||||
|
|
||||||
|
if self.closed || has('nvim')
|
||||||
|
call self.complete(a:job, self.exit_status, self.messages)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:close_cb(ch) dict
|
||||||
|
let self.closed = 1
|
||||||
|
|
||||||
|
if self.exited
|
||||||
|
let job = ch_getjob(a:ch)
|
||||||
|
call self.complete(job, self.exit_status, self.messages)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" go#job#Start runs a job. The options are expected to be the options
|
||||||
|
" suitable for Vim8 jobs. When called from Neovim, Vim8 options will be
|
||||||
|
" transformed to their Neovim equivalents.
|
||||||
|
function! go#job#Start(cmd, options)
|
||||||
|
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
|
||||||
|
let l:options = copy(a:options)
|
||||||
|
|
||||||
|
if has('nvim')
|
||||||
|
let l:options = s:neooptions(l:options)
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Verify that the working directory for the job actually exists. Return
|
||||||
|
" early if the directory does not exist. This helps avoid errors when
|
||||||
|
" working with plugins that use virtual files that don't actually exist on
|
||||||
|
" the file system.
|
||||||
|
let l:filedir = expand("%:p:h")
|
||||||
|
if has_key(l:options, 'cwd') && !isdirectory(l:options.cwd)
|
||||||
|
return
|
||||||
|
elseif !isdirectory(l:filedir)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:manualcd = 0
|
||||||
|
if !has_key(l:options, 'cwd')
|
||||||
|
" pre start
|
||||||
|
let l:manualcd = 1
|
||||||
|
let dir = getcwd()
|
||||||
|
execute l:cd fnameescape(filedir)
|
||||||
|
elseif !(has("patch-8.0.0902") || has('nvim'))
|
||||||
|
let l:manualcd = 1
|
||||||
|
let l:dir = l:options.cwd
|
||||||
|
execute l:cd fnameescape(l:dir)
|
||||||
|
call remove(l:options, 'cwd')
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(l:options, '_start')
|
||||||
|
call l:options._start()
|
||||||
|
" remove _start to play nicely with vim (when vim encounters an unexpected
|
||||||
|
" job option it reports an "E475: invalid argument" error).
|
||||||
|
unlet l:options._start
|
||||||
|
endif
|
||||||
|
|
||||||
|
" noblock was added in 8.1.350; remove it if it's not supported.
|
||||||
|
if has_key(l:options, 'noblock') && (has('nvim') || !has("patch-8.1.350"))
|
||||||
|
call remove(l:options, 'noblock')
|
||||||
|
endif
|
||||||
|
|
||||||
|
if go#util#HasDebug('shell-commands')
|
||||||
|
call go#util#EchoInfo('job command: ' . string(a:cmd))
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has('nvim')
|
||||||
|
let l:input = []
|
||||||
|
if has_key(a:options, 'in_io') && a:options.in_io ==# 'file' && !empty(a:options.in_name)
|
||||||
|
let l:input = readfile(a:options.in_name, "b")
|
||||||
|
endif
|
||||||
|
|
||||||
|
let job = jobstart(a:cmd, l:options)
|
||||||
|
|
||||||
|
if len(l:input) > 0
|
||||||
|
call chansend(job, l:input)
|
||||||
|
" close stdin to signal that no more bytes will be sent.
|
||||||
|
call chanclose(job, 'stdin')
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
let l:cmd = a:cmd
|
||||||
|
if go#util#IsWin()
|
||||||
|
let l:cmd = join(map(copy(a:cmd), function('s:winjobarg')), " ")
|
||||||
|
endif
|
||||||
|
|
||||||
|
let job = job_start(l:cmd, l:options)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if l:manualcd
|
||||||
|
" post start
|
||||||
|
execute l:cd fnameescape(l:dir)
|
||||||
|
endif
|
||||||
|
|
||||||
|
return job
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" s:neooptions returns a dictionary of job options suitable for use by Neovim
|
||||||
|
" based on a dictionary of job options suitable for Vim8.
|
||||||
|
function! s:neooptions(options)
|
||||||
|
let l:options = {}
|
||||||
|
let l:options['stdout_buf'] = ''
|
||||||
|
let l:options['stderr_buf'] = ''
|
||||||
|
|
||||||
|
let l:err_mode = get(a:options, 'err_mode', get(a:options, 'mode', ''))
|
||||||
|
let l:out_mode = get(a:options, 'out_mode', get(a:options, 'mode', ''))
|
||||||
|
|
||||||
|
for key in keys(a:options)
|
||||||
|
if key == 'cwd'
|
||||||
|
let l:options['cwd'] = a:options['cwd']
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if key == 'callback'
|
||||||
|
let l:options['callback'] = a:options['callback']
|
||||||
|
|
||||||
|
if !has_key(a:options, 'out_cb')
|
||||||
|
let l:options['on_stdout'] = function('s:callback2on_stdout', [l:out_mode], l:options)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !has_key(a:options, 'err_cb')
|
||||||
|
let l:options['on_stderr'] = function('s:callback2on_stderr', [l:err_mode], l:options)
|
||||||
|
endif
|
||||||
|
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if key == 'out_cb'
|
||||||
|
let l:options['out_cb'] = a:options['out_cb']
|
||||||
|
let l:options['on_stdout'] = function('s:on_stdout', [l:out_mode], l:options)
|
||||||
|
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if key == 'err_cb'
|
||||||
|
let l:options['err_cb'] = a:options['err_cb']
|
||||||
|
let l:options['on_stderr'] = function('s:on_stderr', [l:err_mode], l:options)
|
||||||
|
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if key == 'exit_cb'
|
||||||
|
let l:options['exit_cb'] = a:options['exit_cb']
|
||||||
|
let l:options['on_exit'] = function('s:on_exit', [], l:options)
|
||||||
|
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if key == 'close_cb'
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if key == 'stoponexit'
|
||||||
|
if a:options['stoponexit'] == ''
|
||||||
|
let l:options['detach'] = 1
|
||||||
|
endif
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
return l:options
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:callback2on_stdout(mode, ch, data, event) dict
|
||||||
|
let self.stdout_buf = s:neocb(a:mode, a:ch, self.stdout_buf, a:data, self.callback)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:callback2on_stderr(mode, ch, data, event) dict
|
||||||
|
let self.stderr_buf = s:neocb(a:mode, a:ch, self.stderr_buf, a:data, self.callback)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:on_stdout(mode, ch, data, event) dict
|
||||||
|
let self.stdout_buf = s:neocb(a:mode, a:ch, self.stdout_buf, a:data, self.out_cb)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:on_stderr(mode, ch, data, event) dict
|
||||||
|
let self.stderr_buf = s:neocb(a:mode, a:ch, self.stderr_buf, a:data, self.err_cb )
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:on_exit(jobid, exitval, event) dict
|
||||||
|
call self.exit_cb(a:jobid, a:exitval)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#job#Stop(job) abort
|
||||||
|
if has('nvim')
|
||||||
|
call jobstop(a:job)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
call job_stop(a:job)
|
||||||
|
call go#job#Wait(a:job)
|
||||||
|
return
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#job#Wait(job) abort
|
||||||
|
if has('nvim')
|
||||||
|
call jobwait([a:job])
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
while job_status(a:job) is# 'run'
|
||||||
|
sleep 50m
|
||||||
|
endwhile
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:winjobarg(idx, val) abort
|
||||||
|
if empty(a:val)
|
||||||
|
return '""'
|
||||||
|
endif
|
||||||
|
return a:val
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:neocb(mode, ch, buf, data, callback)
|
||||||
|
" dealing with the channel lines of Neovim is awful. The docs (:help
|
||||||
|
" channel-lines) say:
|
||||||
|
" stream event handlers may receive partial (incomplete) lines. For a
|
||||||
|
" given invocation of on_stdout etc, `a:data` is not guaranteed to end
|
||||||
|
" with a newline.
|
||||||
|
" - `abcdefg` may arrive as `['abc']`, `['defg']`.
|
||||||
|
" - `abc\nefg` may arrive as `['abc', '']`, `['efg']` or `['abc']`,
|
||||||
|
" `['','efg']`, or even `['ab']`, `['c','efg']`.
|
||||||
|
"
|
||||||
|
" Thankfully, though, this is explained a bit better in an issue:
|
||||||
|
" https://github.com/neovim/neovim/issues/3555. Specifically in these two
|
||||||
|
" comments:
|
||||||
|
" * https://github.com/neovim/neovim/issues/3555#issuecomment-152290804
|
||||||
|
" * https://github.com/neovim/neovim/issues/3555#issuecomment-152588749
|
||||||
|
"
|
||||||
|
" The key is
|
||||||
|
" Every item in the list passed to job control callbacks represents a
|
||||||
|
" string after a newline(Except the first, of course). If the program
|
||||||
|
" outputs: "hello\nworld" the corresponding list is ["hello", "world"].
|
||||||
|
" If the program outputs "hello\nworld\n", the corresponding list is
|
||||||
|
" ["hello", "world", ""]. In other words, you can always determine if
|
||||||
|
" the last line received is complete or not.
|
||||||
|
" and
|
||||||
|
" for every list you receive in a callback, all items except the first
|
||||||
|
" represent newlines.
|
||||||
|
|
||||||
|
let l:buf = ''
|
||||||
|
|
||||||
|
" A single empty string means EOF was reached. The first item will never be
|
||||||
|
" an empty string except for when it's the only item and is signaling that
|
||||||
|
" EOF was reached.
|
||||||
|
if len(a:data) == 1 && a:data[0] == ''
|
||||||
|
" when there's nothing buffered, return early so that an
|
||||||
|
" erroneous message will not be added.
|
||||||
|
if a:buf == ''
|
||||||
|
return ''
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:data = [a:buf]
|
||||||
|
else
|
||||||
|
let l:data = copy(a:data)
|
||||||
|
let l:data[0] = a:buf . l:data[0]
|
||||||
|
|
||||||
|
" The last element may be a partial line; save it for next time.
|
||||||
|
if a:mode != 'raw'
|
||||||
|
let l:buf = l:data[-1]
|
||||||
|
let l:data = l:data[:-2]
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:i = 0
|
||||||
|
let l:last = len(l:data) - 1
|
||||||
|
while l:i <= l:last
|
||||||
|
let l:msg = l:data[l:i]
|
||||||
|
if a:mode == 'raw' && l:i < l:last
|
||||||
|
let l:msg = l:msg . "\n"
|
||||||
|
endif
|
||||||
|
call a:callback(a:ch, l:msg)
|
||||||
|
|
||||||
|
let l:i += 1
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
return l:buf
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,190 +0,0 @@
|
||||||
" s:jobs is a global reference to all jobs started with Spawn() or with the
|
|
||||||
" 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, for, args) abort
|
|
||||||
" autowrite is not enabled for jobs
|
|
||||||
call go#cmd#autowrite()
|
|
||||||
|
|
||||||
let job = s:spawn(a:bang, a:desc, a:for, a:args)
|
|
||||||
return job.id
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" 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
|
|
||||||
|
|
||||||
" 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. 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(),
|
|
||||||
\ '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,
|
|
||||||
\ 'errorformat': &errorformat,
|
|
||||||
\ }
|
|
||||||
|
|
||||||
" execute go build in the files directory
|
|
||||||
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
|
||||||
|
|
||||||
" cleanup previous jobs for this file
|
|
||||||
for jb in values(s:jobs)
|
|
||||||
if jb.importpath == job.importpath
|
|
||||||
unlet s:jobs[jb.id]
|
|
||||||
endif
|
|
||||||
endfor
|
|
||||||
|
|
||||||
let dir = getcwd()
|
|
||||||
let jobdir = fnameescape(expand("%:p:h"))
|
|
||||||
execute cd . jobdir
|
|
||||||
|
|
||||||
" append the subcommand, such as 'build'
|
|
||||||
let argv = ['go'] + a:args
|
|
||||||
|
|
||||||
" run, forrest, run!
|
|
||||||
let id = jobstart(argv, job)
|
|
||||||
let job.id = id
|
|
||||||
let job.dir = jobdir
|
|
||||||
let s:jobs[id] = job
|
|
||||||
|
|
||||||
execute cd . fnameescape(dir)
|
|
||||||
|
|
||||||
return job
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" on_exit is the exit handler for jobstart(). It handles cleaning up the job
|
|
||||||
" references and also displaying errors in the quickfix window collected by
|
|
||||||
" on_stderr handler. If there are no errors and a quickfix window is open,
|
|
||||||
" it'll be closed.
|
|
||||||
function! s:on_exit(job_id, exit_status, 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(l:listtype)
|
|
||||||
|
|
||||||
let self.state = "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"
|
|
||||||
|
|
||||||
if get(g:, 'go_echo_command_info', 1)
|
|
||||||
call go#util#EchoError("[" . self.status_type . "] FAILED")
|
|
||||||
endif
|
|
||||||
|
|
||||||
" parse the errors relative to self.jobdir
|
|
||||||
call go#list#ParseFormat(l:listtype, self.errorformat, std_combined, self.for)
|
|
||||||
let errors = go#list#Get(l:listtype)
|
|
||||||
|
|
||||||
execute cd . fnameescape(dir)
|
|
||||||
|
|
||||||
if !len(errors)
|
|
||||||
" failed to parse errors, output the original content
|
|
||||||
call go#util#EchoError(std_combined[0])
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
" if we are still in the same windows show the list
|
|
||||||
if self.winnr == winnr()
|
|
||||||
call go#list#Window(l:listtype, len(errors))
|
|
||||||
if !empty(errors) && !self.bang
|
|
||||||
call go#list#JumpToFirst(l:listtype)
|
|
||||||
endif
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" 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 s:handler in values(s:handlers)
|
|
||||||
call s:handler(a:job, a:exit_status, a:data)
|
|
||||||
endfor
|
|
||||||
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, event) dict abort
|
|
||||||
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, event) dict abort
|
|
||||||
call extend(self.stderr, a:data)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
|
|
@ -1,20 +1,24 @@
|
||||||
function! go#keyify#Keyify()
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let bin_path = go#path#CheckBinPath("keyify")
|
let s:cpo_save = &cpo
|
||||||
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
set cpo&vim
|
||||||
|
|
||||||
if empty(bin_path) || !exists('*json_decode')
|
function! go#keyify#Keyify()
|
||||||
|
" Needs: https://github.com/dominikh/go-tools/pull/272
|
||||||
|
"\ '-tags', go#config#BuildTags(),
|
||||||
|
let l:cmd = ['keyify',
|
||||||
|
\ '-json',
|
||||||
|
\ printf('%s:#%s', fnamemodify(expand('%'), ':p:gs?\\?/?'), go#util#OffsetCursor())]
|
||||||
|
|
||||||
|
let [l:out, l:err] = go#util#Exec(l:cmd)
|
||||||
|
if l:err
|
||||||
|
call go#util#EchoError("non-zero exit code: " . l:out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
silent! let result = json_decode(l:out)
|
||||||
" 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
|
" We want to output the error message in case the result isn't a JSON
|
||||||
if type(result) != type({})
|
if type(result) != type({})
|
||||||
call go#util#EchoError(s:chomp(output))
|
call go#util#EchoError(s:chomp(l:out))
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -53,4 +57,8 @@ function! s:chomp(string)
|
||||||
return substitute(a:string, '\n\+$', '', '')
|
return substitute(a:string, '\n\+$', '', '')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,62 +1,34 @@
|
||||||
if !exists("g:go_metalinter_command")
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let g:go_metalinter_command = ""
|
let s:cpo_save = &cpo
|
||||||
endif
|
set cpo&vim
|
||||||
|
|
||||||
if !exists("g:go_metalinter_autosave_enabled")
|
function! go#lint#Gometa(bang, autosave, ...) abort
|
||||||
let g:go_metalinter_autosave_enabled = ['vet', 'golint']
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists("g:go_metalinter_enabled")
|
|
||||||
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists("g:go_metalinter_disabled")
|
|
||||||
let g:go_metalinter_disabled = []
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists("g:go_golint_bin")
|
|
||||||
let g:go_golint_bin = "golint"
|
|
||||||
endif
|
|
||||||
|
|
||||||
if !exists("g:go_errcheck_bin")
|
|
||||||
let g:go_errcheck_bin = "errcheck"
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! go#lint#Gometa(autosave, ...) abort
|
|
||||||
if a:0 == 0
|
if a:0 == 0
|
||||||
let goargs = [expand('%:p:h')]
|
let goargs = [expand('%:p:h')]
|
||||||
else
|
else
|
||||||
let goargs = a:000
|
let goargs = a:000
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let bin_path = go#path#CheckBinPath("gometalinter")
|
let l:metalinter = go#config#MetalinterCommand()
|
||||||
if empty(bin_path)
|
|
||||||
|
if l:metalinter == 'gometalinter' || l:metalinter == 'golangci-lint'
|
||||||
|
let cmd = s:metalintercmd(l:metalinter)
|
||||||
|
if empty(cmd)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let cmd = [bin_path]
|
|
||||||
let cmd += ["--disable-all"]
|
|
||||||
|
|
||||||
if a:autosave || empty(g:go_metalinter_command)
|
|
||||||
" linters
|
" linters
|
||||||
let linters = a:autosave ? g:go_metalinter_autosave_enabled : g:go_metalinter_enabled
|
let linters = a:autosave ? go#config#MetalinterAutosaveEnabled() : go#config#MetalinterEnabled()
|
||||||
for linter in linters
|
for linter in linters
|
||||||
let cmd += ["--enable=".linter]
|
let cmd += ["--enable=".linter]
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
for linter in g:go_metalinter_disabled
|
for linter in go#config#MetalinterDisabled()
|
||||||
let cmd += ["--disable=".linter]
|
let cmd += ["--disable=".linter]
|
||||||
endfor
|
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"]
|
|
||||||
else
|
else
|
||||||
" the user wants something else, let us use it.
|
" the user wants something else, let us use it.
|
||||||
let cmd += split(g:go_metalinter_command, " ")
|
let cmd = split(go#config#MetalinterCommand(), " ")
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if a:autosave
|
if a:autosave
|
||||||
|
@ -64,36 +36,43 @@ function! go#lint#Gometa(autosave, ...) abort
|
||||||
" will be cleared
|
" will be cleared
|
||||||
redraw
|
redraw
|
||||||
|
|
||||||
|
if l:metalinter == "gometalinter"
|
||||||
" Include only messages for the active buffer for autosave.
|
" Include only messages for the active buffer for autosave.
|
||||||
let cmd += [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
|
let include = [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
|
||||||
|
if go#util#has_job()
|
||||||
|
let include = [printf('--include=^%s:.*$', expand('%:p:t'))]
|
||||||
|
endif
|
||||||
|
let cmd += include
|
||||||
|
elseif l:metalinter == "golangci-lint"
|
||||||
|
let goargs[0] = expand('%:p')
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" gometalinter has a default deadline of 5 seconds.
|
" Call metalinter asynchronously.
|
||||||
"
|
let deadline = go#config#MetalinterDeadline()
|
||||||
" For async mode (s:lint_job), we want to override the default deadline only
|
if deadline != ''
|
||||||
" 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]
|
let cmd += ["--deadline=" . deadline]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let cmd += goargs
|
let cmd += goargs
|
||||||
|
|
||||||
call s:lint_job({'cmd': cmd}, a:autosave)
|
if l:metalinter == "gometalinter"
|
||||||
return
|
" Gometalinter can output one of the two, so we look for both:
|
||||||
|
" <file>:<line>:<column>:<severity>: <message> (<linter>)
|
||||||
|
" <file>:<line>::<severity>: <message> (<linter>)
|
||||||
|
" This can be defined by the following errorformat:
|
||||||
|
let errformat = "%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m"
|
||||||
|
else
|
||||||
|
" Golangci-lint can output the following:
|
||||||
|
" <file>:<line>:<column>: <message> (<linter>)
|
||||||
|
" This can be defined by the following errorformat:
|
||||||
|
let errformat = "%f:%l:%c:\ %m"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" We're calling gometalinter synchronously.
|
if go#util#has_job()
|
||||||
let cmd += ["--deadline=" . get(g:, 'go_metalinter_deadline', "5s")]
|
call s:lint_job({'cmd': cmd, 'statustype': l:metalinter, 'errformat': errformat}, a:bang, a:autosave)
|
||||||
|
return
|
||||||
let cmd += goargs
|
endif
|
||||||
|
|
||||||
let [l:out, l:err] = go#util#Exec(cmd)
|
let [l:out, l:err] = go#util#Exec(cmd)
|
||||||
|
|
||||||
|
@ -107,48 +86,46 @@ function! go#lint#Gometa(autosave, ...) abort
|
||||||
call go#list#Clean(l:listtype)
|
call go#list#Clean(l:listtype)
|
||||||
echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
|
echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
|
||||||
else
|
else
|
||||||
" GoMetaLinter can output one of the two, so we look for both:
|
let l:winid = win_getid(winnr())
|
||||||
" <file>:<line>:[<column>]: <message> (<linter>)
|
|
||||||
" <file>:<line>:: <message> (<linter>)
|
|
||||||
" 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
|
" Parse and populate our location list
|
||||||
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter')
|
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter')
|
||||||
|
|
||||||
let errors = go#list#Get(l:listtype)
|
let errors = go#list#Get(l:listtype)
|
||||||
call go#list#Window(l:listtype, len(errors))
|
call go#list#Window(l:listtype, len(errors))
|
||||||
|
|
||||||
if !a:autosave
|
if a:autosave || a:bang
|
||||||
call go#list#JumpToFirst(l:listtype)
|
call win_gotoid(l:winid)
|
||||||
|
return
|
||||||
endif
|
endif
|
||||||
|
call go#list#JumpToFirst(l:listtype)
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Golint calls 'golint' on the current directory. Any warnings are populated in
|
" Golint calls 'golint' on the current directory. Any warnings are populated in
|
||||||
" the location list
|
" the location list
|
||||||
function! go#lint#Golint(...) abort
|
function! go#lint#Golint(bang, ...) abort
|
||||||
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
|
if a:0 == 0
|
||||||
let out = go#util#System(bin_path . " " . go#util#Shellescape(go#package#ImportPath()))
|
let [l:out, l:err] = go#util#Exec([go#config#GolintBin(), go#package#ImportPath()])
|
||||||
else
|
else
|
||||||
let out = go#util#System(bin_path . " " . go#util#Shelljoin(a:000))
|
let [l:out, l:err] = go#util#Exec([go#config#GolintBin()] + a:000)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if empty(out)
|
if empty(l:out)
|
||||||
echon "vim-go: " | echohl Function | echon "[lint] PASS" | echohl None
|
call go#util#EchoSuccess('[lint] PASS')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
let l:winid = win_getid(winnr())
|
||||||
let l:listtype = go#list#Type("GoLint")
|
let l:listtype = go#list#Type("GoLint")
|
||||||
call go#list#Parse(l:listtype, out, "GoLint")
|
call go#list#Parse(l:listtype, l:out, "GoLint")
|
||||||
let errors = go#list#Get(l:listtype)
|
let l:errors = go#list#Get(l:listtype)
|
||||||
call go#list#Window(l:listtype, len(errors))
|
call go#list#Window(l:listtype, len(l:errors))
|
||||||
|
|
||||||
|
if a:bang
|
||||||
|
call win_gotoid(l:winid)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
call go#list#JumpToFirst(l:listtype)
|
call go#list#JumpToFirst(l:listtype)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -156,202 +133,152 @@ endfunction
|
||||||
" the location list
|
" the location list
|
||||||
function! go#lint#Vet(bang, ...) abort
|
function! go#lint#Vet(bang, ...) abort
|
||||||
call go#cmd#autowrite()
|
call go#cmd#autowrite()
|
||||||
echon "vim-go: " | echohl Identifier | echon "calling vet..." | echohl None
|
|
||||||
|
if go#config#EchoCommandInfo()
|
||||||
|
call go#util#EchoProgress('calling vet...')
|
||||||
|
endif
|
||||||
|
|
||||||
if a:0 == 0
|
if a:0 == 0
|
||||||
let out = go#util#System('go vet ' . go#util#Shellescape(go#package#ImportPath()))
|
let [l:out, l:err] = go#util#Exec(['go', 'vet', go#package#ImportPath()])
|
||||||
else
|
else
|
||||||
let out = go#util#System('go tool vet ' . go#util#Shelljoin(a:000))
|
let [l:out, l:err] = go#util#Exec(['go', 'tool', 'vet'] + a:000)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:listtype = go#list#Type("GoVet")
|
let l:listtype = go#list#Type("GoVet")
|
||||||
if go#util#ShellError() != 0
|
if l:err != 0
|
||||||
|
let l:winid = win_getid(winnr())
|
||||||
let errorformat = "%-Gexit status %\\d%\\+," . &errorformat
|
let errorformat = "%-Gexit status %\\d%\\+," . &errorformat
|
||||||
call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet")
|
call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet")
|
||||||
let errors = go#list#Get(l:listtype)
|
let errors = go#list#Get(l:listtype)
|
||||||
call go#list#Window(l:listtype, len(errors))
|
call go#list#Window(l:listtype, len(errors))
|
||||||
if !empty(errors) && !a:bang
|
if !empty(errors) && !a:bang
|
||||||
call go#list#JumpToFirst(l:listtype)
|
call go#list#JumpToFirst(l:listtype)
|
||||||
|
else
|
||||||
|
call win_gotoid(l:winid)
|
||||||
endif
|
endif
|
||||||
echon "vim-go: " | echohl ErrorMsg | echon "[vet] FAIL" | echohl None
|
|
||||||
else
|
else
|
||||||
call go#list#Clean(l:listtype)
|
call go#list#Clean(l:listtype)
|
||||||
redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None
|
call go#util#EchoSuccess('[vet] PASS')
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" ErrCheck calls 'errcheck' for the given packages. Any warnings are populated in
|
" ErrCheck calls 'errcheck' for the given packages. Any warnings are populated in
|
||||||
" the location list
|
" the location list
|
||||||
function! go#lint#Errcheck(...) abort
|
function! go#lint#Errcheck(bang, ...) abort
|
||||||
if a:0 == 0
|
if a:0 == 0
|
||||||
let import_path = go#package#ImportPath()
|
let l:import_path = go#package#ImportPath()
|
||||||
if import_path == -1
|
if import_path == -1
|
||||||
echohl Error | echomsg "vim-go: package is not inside GOPATH src" | echohl None
|
call go#util#EchoError('package is not inside GOPATH src')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
let import_path = go#util#Shelljoin(a:000)
|
let l:import_path = join(a:000, ' ')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let bin_path = go#path#CheckBinPath(g:go_errcheck_bin)
|
call go#util#EchoProgress('[errcheck] analysing ...')
|
||||||
if empty(bin_path)
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
|
|
||||||
echon "vim-go: " | echohl Identifier | echon "errcheck analysing ..." | echohl None
|
|
||||||
redraw
|
redraw
|
||||||
|
|
||||||
let command = go#util#Shellescape(bin_path) . ' -abspath ' . import_path
|
let [l:out, l:err] = go#util#Exec([go#config#ErrcheckBin(), '-abspath', l:import_path])
|
||||||
let out = go#tool#ExecuteInDir(command)
|
|
||||||
|
|
||||||
let l:listtype = go#list#Type("GoErrCheck")
|
let l:listtype = go#list#Type("GoErrCheck")
|
||||||
if go#util#ShellError() != 0
|
if l:err != 0
|
||||||
|
let l:winid = win_getid(winnr())
|
||||||
let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m"
|
let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m"
|
||||||
|
|
||||||
" Parse and populate our location list
|
" Parse and populate our location list
|
||||||
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'Errcheck')
|
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'Errcheck')
|
||||||
|
|
||||||
let errors = go#list#Get(l:listtype)
|
let l:errors = go#list#Get(l:listtype)
|
||||||
if empty(errors)
|
if empty(l:errors)
|
||||||
echohl Error | echomsg "GoErrCheck returned error" | echohl None
|
call go#util#EchoError(l:out)
|
||||||
echo out
|
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !empty(errors)
|
if !empty(errors)
|
||||||
echohl Error | echomsg "GoErrCheck found errors" | echohl None
|
|
||||||
call go#list#Populate(l:listtype, errors, 'Errcheck')
|
call go#list#Populate(l:listtype, errors, 'Errcheck')
|
||||||
call go#list#Window(l:listtype, len(errors))
|
call go#list#Window(l:listtype, len(errors))
|
||||||
if !empty(errors)
|
if !a:bang
|
||||||
call go#list#JumpToFirst(l:listtype)
|
call go#list#JumpToFirst(l:listtype)
|
||||||
|
else
|
||||||
|
call win_gotoid(l:winid)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
else
|
else
|
||||||
call go#list#Clean(l:listtype)
|
call go#list#Clean(l:listtype)
|
||||||
echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None
|
call go#util#EchoSuccess('[errcheck] PASS')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#lint#ToggleMetaLinterAutoSave() abort
|
function! go#lint#ToggleMetaLinterAutoSave() abort
|
||||||
if get(g:, "go_metalinter_autosave", 0)
|
if go#config#MetalinterAutosave()
|
||||||
let g:go_metalinter_autosave = 0
|
call go#config#SetMetalinterAutosave(0)
|
||||||
call go#util#EchoProgress("auto metalinter disabled")
|
call go#util#EchoProgress("auto metalinter disabled")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
let g:go_metalinter_autosave = 1
|
call go#config#SetMetalinterAutosave(1)
|
||||||
call go#util#EchoProgress("auto metalinter enabled")
|
call go#util#EchoProgress("auto metalinter enabled")
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:lint_job(args, autosave)
|
function! s:lint_job(args, bang, autosave)
|
||||||
let state = {
|
let l:opts = {
|
||||||
\ 'status_dir': expand('%:p:h'),
|
\ 'statustype': a:args.statustype,
|
||||||
\ 'started_at': reltime(),
|
\ 'errorformat': a:args.errformat,
|
||||||
\ 'messages': [],
|
\ 'for': "GoMetaLinter",
|
||||||
\ 'exited': 0,
|
\ 'bang': a:bang,
|
||||||
\ 'closed': 0,
|
|
||||||
\ 'exit_status': 0,
|
|
||||||
\ 'winnr': winnr(),
|
|
||||||
\ 'autosave': a:autosave
|
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
call go#statusline#Update(state.status_dir, {
|
if a:autosave
|
||||||
\ 'desc': "current status",
|
let l:opts.for = "GoMetaLinterAutoSave"
|
||||||
\ 'type': "gometalinter",
|
endif
|
||||||
\ 'state': "analysing",
|
|
||||||
\})
|
|
||||||
|
|
||||||
" autowrite is not enabled for jobs
|
" autowrite is not enabled for jobs
|
||||||
call go#cmd#autowrite()
|
call go#cmd#autowrite()
|
||||||
|
|
||||||
if a:autosave
|
call go#job#Spawn(a:args.cmd, l:opts)
|
||||||
let state.listtype = go#list#Type("GoMetaLinterAutoSave")
|
|
||||||
else
|
|
||||||
let state.listtype = go#list#Type("GoMetaLinter")
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! s:callback(chan, msg) dict closure
|
|
||||||
call add(self.messages, a:msg)
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:exit_cb(job, exitval) dict
|
function! s:metalintercmd(metalinter)
|
||||||
let self.exited = 1
|
let l:cmd = []
|
||||||
let self.exit_status = a:exitval
|
let bin_path = go#path#CheckBinPath(a:metalinter)
|
||||||
|
if !empty(bin_path)
|
||||||
let status = {
|
if a:metalinter == "gometalinter"
|
||||||
\ 'desc': 'last status',
|
let l:cmd = s:gometalintercmd(bin_path)
|
||||||
\ 'type': "gometaliner",
|
elseif a:metalinter == "golangci-lint"
|
||||||
\ 'state': "finished",
|
let l:cmd = s:golangcilintcmd(bin_path)
|
||||||
\ }
|
endif
|
||||||
|
|
||||||
if a:exitval
|
|
||||||
let status.state = "failed"
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let elapsed_time = reltimestr(reltime(self.started_at))
|
return cmd
|
||||||
" 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)
|
|
||||||
|
|
||||||
if self.closed
|
|
||||||
call self.show_errors()
|
|
||||||
endif
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:close_cb(ch) dict
|
function! s:gometalintercmd(bin_path)
|
||||||
let self.closed = 1
|
let cmd = [a:bin_path]
|
||||||
|
let cmd += ["--disable-all"]
|
||||||
|
|
||||||
if self.exited
|
" gometalinter has a --tests flag to tell its linters whether to run
|
||||||
call self.show_errors()
|
" against tests. While not all of its linters respect this flag, for those
|
||||||
endif
|
" 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"]
|
||||||
|
return cmd
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:golangcilintcmd(bin_path)
|
||||||
function state.show_errors()
|
let cmd = [a:bin_path]
|
||||||
let l:winnr = winnr()
|
let cmd += ["run"]
|
||||||
|
let cmd += ["--print-issued-lines=false"]
|
||||||
" make sure the current window is the window from which gometalinter was
|
let cmd += ["--disable-all"]
|
||||||
" run when the listtype is locationlist so that the location list for the
|
" do not use the default exclude patterns, because doing so causes golint
|
||||||
" correct window will be populated.
|
" problems about missing doc strings to be ignored and other things that
|
||||||
if self.listtype == 'locationlist'
|
" golint identifies.
|
||||||
exe self.winnr . "wincmd w"
|
let cmd += ["--exclude-use-default=false"]
|
||||||
endif
|
return cmd
|
||||||
|
|
||||||
let l:errorformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
|
|
||||||
call go#list#ParseFormat(self.listtype, l:errorformat, self.messages, 'GoMetaLinter')
|
|
||||||
|
|
||||||
let errors = go#list#Get(self.listtype)
|
|
||||||
call go#list#Window(self.listtype, len(errors))
|
|
||||||
|
|
||||||
" move to the window that was active before processing the errors, because
|
|
||||||
" the user may have moved around within the window or even moved to a
|
|
||||||
" different window since saving. Moving back to current window as of the
|
|
||||||
" start of this function avoids the perception that the quickfix window
|
|
||||||
" steals focus when linting takes a while.
|
|
||||||
if self.autosave
|
|
||||||
exe l:winnr . "wincmd w"
|
|
||||||
endif
|
|
||||||
|
|
||||||
if get(g:, 'go_echo_command_info', 1)
|
|
||||||
call go#util#EchoSuccess("linting finished")
|
|
||||||
endif
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" explicitly bind the callbacks to state so that self within them always
|
" restore Vi compatibility settings
|
||||||
" refers to state. See :help Partial for more information.
|
let &cpo = s:cpo_save
|
||||||
let start_options = {
|
unlet s:cpo_save
|
||||||
\ 'callback': funcref("s:callback", [], state),
|
|
||||||
\ 'exit_cb': funcref("s:exit_cb", [], state),
|
|
||||||
\ 'close_cb': funcref("s:close_cb", [], state),
|
|
||||||
\ }
|
|
||||||
|
|
||||||
call job_start(a:args.cmd, start_options)
|
|
||||||
|
|
||||||
if get(g:, 'go_echo_command_info', 1)
|
|
||||||
call go#util#EchoProgress("linting started ...")
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,7 +1,21 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_Gometa() abort
|
func! Test_Gometa() abort
|
||||||
|
call s:gometa('gometaliner')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_GometaGolangciLint() abort
|
||||||
|
call s:gometa('golangci-lint')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! s:gometa(metalinter) abort
|
||||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||||
|
|
||||||
|
try
|
||||||
|
let g:go_metalinter_comand = a:metalinter
|
||||||
let expected = [
|
let expected = [
|
||||||
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
|
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
|
||||||
\ ]
|
\ ]
|
||||||
|
@ -9,17 +23,9 @@ func! Test_Gometa() abort
|
||||||
" clear the quickfix lists
|
" clear the quickfix lists
|
||||||
call setqflist([], 'r')
|
call setqflist([], 'r')
|
||||||
|
|
||||||
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
|
|
||||||
" be autoloaded and the default for g:go_metalinter_enabled will be set so
|
|
||||||
" we can capture it to restore it after the test is run.
|
|
||||||
call go#lint#ToggleMetaLinterAutoSave()
|
|
||||||
" And restore it back to its previous value
|
|
||||||
call go#lint#ToggleMetaLinterAutoSave()
|
|
||||||
|
|
||||||
let orig_go_metalinter_enabled = g:go_metalinter_enabled
|
|
||||||
let g:go_metalinter_enabled = ['golint']
|
let g:go_metalinter_enabled = ['golint']
|
||||||
|
|
||||||
call go#lint#Gometa(0, $GOPATH . '/src/foo')
|
call go#lint#Gometa(0, 0, $GOPATH . '/src/foo')
|
||||||
|
|
||||||
let actual = getqflist()
|
let actual = getqflist()
|
||||||
let start = reltime()
|
let start = reltime()
|
||||||
|
@ -29,13 +35,25 @@ func! Test_Gometa() abort
|
||||||
endwhile
|
endwhile
|
||||||
|
|
||||||
call gotest#assert_quickfix(actual, expected)
|
call gotest#assert_quickfix(actual, expected)
|
||||||
let g:go_metalinter_enabled = orig_go_metalinter_enabled
|
finally
|
||||||
|
unlet g:go_metalinter_enabled
|
||||||
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func! Test_GometaWithDisabled() abort
|
func! Test_GometaWithDisabled() abort
|
||||||
|
call s:gometawithdisabled('gometalinter')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_GometaWithDisabledGolangciLint() abort
|
||||||
|
call s:gometawithdisabled('golangci-lint')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! s:gometawithdisabled(metalinter) abort
|
||||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||||
|
|
||||||
|
try
|
||||||
|
let g:go_metalinter_comand = a:metalinter
|
||||||
let expected = [
|
let expected = [
|
||||||
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
|
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
|
||||||
\ ]
|
\ ]
|
||||||
|
@ -43,17 +61,9 @@ func! Test_GometaWithDisabled() abort
|
||||||
" clear the quickfix lists
|
" clear the quickfix lists
|
||||||
call setqflist([], 'r')
|
call setqflist([], 'r')
|
||||||
|
|
||||||
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
|
|
||||||
" be autoloaded and the default for g:go_metalinter_disabled will be set so
|
|
||||||
" we can capture it to restore it after the test is run.
|
|
||||||
call go#lint#ToggleMetaLinterAutoSave()
|
|
||||||
" And restore it back to its previous value
|
|
||||||
call go#lint#ToggleMetaLinterAutoSave()
|
|
||||||
|
|
||||||
let orig_go_metalinter_disabled = g:go_metalinter_disabled
|
|
||||||
let g:go_metalinter_disabled = ['vet']
|
let g:go_metalinter_disabled = ['vet']
|
||||||
|
|
||||||
call go#lint#Gometa(0, $GOPATH . '/src/foo')
|
call go#lint#Gometa(0, 0, $GOPATH . '/src/foo')
|
||||||
|
|
||||||
let actual = getqflist()
|
let actual = getqflist()
|
||||||
let start = reltime()
|
let start = reltime()
|
||||||
|
@ -63,13 +73,25 @@ func! Test_GometaWithDisabled() abort
|
||||||
endwhile
|
endwhile
|
||||||
|
|
||||||
call gotest#assert_quickfix(actual, expected)
|
call gotest#assert_quickfix(actual, expected)
|
||||||
let g:go_metalinter_disabled = orig_go_metalinter_disabled
|
finally
|
||||||
|
unlet g:go_metalinter_disabled
|
||||||
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func! Test_GometaAutoSave() abort
|
func! Test_GometaAutoSave() abort
|
||||||
|
call s:gometaautosave('gometalinter')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_GometaAutoSaveGolangciLint() abort
|
||||||
|
call s:gometaautosave('golangci-lint')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! s:gometaautosave(metalinter) abort
|
||||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||||
|
|
||||||
|
try
|
||||||
|
let g:go_metalinter_comand = a:metalinter
|
||||||
let expected = [
|
let expected = [
|
||||||
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
|
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
|
||||||
\ ]
|
\ ]
|
||||||
|
@ -79,17 +101,9 @@ func! Test_GometaAutoSave() abort
|
||||||
" clear the location lists
|
" clear the location lists
|
||||||
call setloclist(l:winnr, [], 'r')
|
call setloclist(l:winnr, [], 'r')
|
||||||
|
|
||||||
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
|
|
||||||
" be autoloaded and the default for g:go_metalinter_autosave_enabled will be
|
|
||||||
" set so we can capture it to restore it after the test is run.
|
|
||||||
call go#lint#ToggleMetaLinterAutoSave()
|
|
||||||
" And restore it back to its previous value
|
|
||||||
call go#lint#ToggleMetaLinterAutoSave()
|
|
||||||
|
|
||||||
let orig_go_metalinter_autosave_enabled = g:go_metalinter_autosave_enabled
|
|
||||||
let g:go_metalinter_autosave_enabled = ['golint']
|
let g:go_metalinter_autosave_enabled = ['golint']
|
||||||
|
|
||||||
call go#lint#Gometa(1)
|
call go#lint#Gometa(0, 1)
|
||||||
|
|
||||||
let actual = getloclist(l:winnr)
|
let actual = getloclist(l:winnr)
|
||||||
let start = reltime()
|
let start = reltime()
|
||||||
|
@ -99,16 +113,19 @@ func! Test_GometaAutoSave() abort
|
||||||
endwhile
|
endwhile
|
||||||
|
|
||||||
call gotest#assert_quickfix(actual, expected)
|
call gotest#assert_quickfix(actual, expected)
|
||||||
let g:go_metalinter_autosave_enabled = orig_go_metalinter_autosave_enabled
|
finally
|
||||||
|
unlet g:go_metalinter_autosave_enabled
|
||||||
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func! Test_Vet()
|
func! Test_Vet() abort
|
||||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||||
silent exe 'e ' . $GOPATH . '/src/vet/vet.go'
|
silent exe 'e ' . $GOPATH . '/src/vet/vet.go'
|
||||||
compiler go
|
compiler go
|
||||||
|
|
||||||
let expected = [
|
let expected = [
|
||||||
\ {'lnum': 7, 'bufnr': bufnr('%'), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'arg str for printf verb %d of wrong type: string'}
|
\ {'lnum': 7, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '',
|
||||||
|
\ 'text': 'Printf format %d has arg str of wrong type string'}
|
||||||
\ ]
|
\ ]
|
||||||
|
|
||||||
let winnr = winnr()
|
let winnr = winnr()
|
||||||
|
@ -128,4 +145,8 @@ func! Test_Vet()
|
||||||
call gotest#assert_quickfix(actual, expected)
|
call gotest#assert_quickfix(actual, expected)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
if !exists("g:go_list_type")
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let g:go_list_type = ""
|
let s:cpo_save = &cpo
|
||||||
endif
|
set cpo&vim
|
||||||
|
|
||||||
if !exists("g:go_list_type_commands")
|
|
||||||
let g:go_list_type_commands = {}
|
|
||||||
endif
|
|
||||||
|
|
||||||
" Window opens the list with the given height up to 10 lines maximum.
|
" Window opens the list with the given height up to 10 lines maximum.
|
||||||
" Otherwise g:go_loclist_height is used.
|
" Otherwise g:go_loclist_height is used.
|
||||||
|
@ -22,7 +18,7 @@ function! go#list#Window(listtype, ...) abort
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let height = get(g:, "go_list_height", 0)
|
let height = go#config#ListHeight()
|
||||||
if height == 0
|
if height == 0
|
||||||
" prevent creating a large location height for a large set of numbers
|
" prevent creating a large location height for a large set of numbers
|
||||||
if a:1 > 10
|
if a:1 > 10
|
||||||
|
@ -113,7 +109,7 @@ endfunction
|
||||||
|
|
||||||
" Close closes the location list
|
" Close closes the location list
|
||||||
function! go#list#Close(listtype) abort
|
function! go#list#Close(listtype) abort
|
||||||
let autoclose_window = get(g:, 'go_list_autoclose', 1)
|
let autoclose_window = go#config#ListAutoclose()
|
||||||
if !autoclose_window
|
if !autoclose_window
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -126,13 +122,12 @@ function! go#list#Close(listtype) abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:listtype(listtype) abort
|
function! s:listtype(listtype) abort
|
||||||
if g:go_list_type == "locationlist"
|
let listtype = go#config#ListType()
|
||||||
return "locationlist"
|
if empty(listtype)
|
||||||
elseif g:go_list_type == "quickfix"
|
return a:listtype
|
||||||
return "quickfix"
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return a:listtype
|
return listtype
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" s:default_list_type_commands is the defaults that will be used for each of
|
" s:default_list_type_commands is the defaults that will be used for each of
|
||||||
|
@ -143,6 +138,7 @@ endfunction
|
||||||
" in g:go_list_type_commands.
|
" in g:go_list_type_commands.
|
||||||
let s:default_list_type_commands = {
|
let s:default_list_type_commands = {
|
||||||
\ "GoBuild": "quickfix",
|
\ "GoBuild": "quickfix",
|
||||||
|
\ "GoDebug": "quickfix",
|
||||||
\ "GoErrCheck": "quickfix",
|
\ "GoErrCheck": "quickfix",
|
||||||
\ "GoFmt": "locationlist",
|
\ "GoFmt": "locationlist",
|
||||||
\ "GoGenerate": "quickfix",
|
\ "GoGenerate": "quickfix",
|
||||||
|
@ -150,6 +146,7 @@ let s:default_list_type_commands = {
|
||||||
\ "GoLint": "quickfix",
|
\ "GoLint": "quickfix",
|
||||||
\ "GoMetaLinter": "quickfix",
|
\ "GoMetaLinter": "quickfix",
|
||||||
\ "GoMetaLinterAutoSave": "locationlist",
|
\ "GoMetaLinterAutoSave": "locationlist",
|
||||||
|
\ "GoModFmt": "locationlist",
|
||||||
\ "GoModifyTags": "locationlist",
|
\ "GoModifyTags": "locationlist",
|
||||||
\ "GoRename": "quickfix",
|
\ "GoRename": "quickfix",
|
||||||
\ "GoRun": "quickfix",
|
\ "GoRun": "quickfix",
|
||||||
|
@ -169,7 +166,11 @@ function! go#list#Type(for) abort
|
||||||
let l:listtype = "quickfix"
|
let l:listtype = "quickfix"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return get(g:go_list_type_commands, a:for, l:listtype)
|
return get(go#config#ListTypeCommands(), a:for, l:listtype)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
533
pack/acp/start/vim-go/autoload/go/lsp.vim
Normal file
533
pack/acp/start/vim-go/autoload/go/lsp.vim
Normal file
|
@ -0,0 +1,533 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
scriptencoding utf-8
|
||||||
|
|
||||||
|
let s:lspfactory = {}
|
||||||
|
|
||||||
|
function! s:lspfactory.get() dict abort
|
||||||
|
if !has_key(self, 'current')
|
||||||
|
" TODO(bc): check that the lsp is still running.
|
||||||
|
let self.current = s:newlsp()
|
||||||
|
endif
|
||||||
|
|
||||||
|
return self.current
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:lspfactory.reset() dict abort
|
||||||
|
if has_key(self, 'current')
|
||||||
|
call remove(self, 'current')
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:newlsp() abort
|
||||||
|
if !go#util#has_job()
|
||||||
|
" TODO(bc): start the server in the background using a shell that waits for the right output before returning.
|
||||||
|
call go#util#EchoError('This feature requires either Vim 8.0.0087 or newer with +job or Neovim.')
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" job is the job used to talk to the backing instance of gopls.
|
||||||
|
" ready is 0 until the initialize response has been received. 1 afterwards.
|
||||||
|
" queue is messages to send after initialization
|
||||||
|
" last_request_id is id of the most recently sent request.
|
||||||
|
" buf is unprocessed/incomplete responses
|
||||||
|
" handlers is a mapping of request ids to dictionaries of functions.
|
||||||
|
" request id -> {start, requestComplete, handleResult, error}
|
||||||
|
" * start is a function that takes no arguments
|
||||||
|
" * requestComplete is a function that takes 1 argument. The parameter will be 1
|
||||||
|
" if the call was succesful.
|
||||||
|
" * handleResult takes a single argument, the result message received from gopls
|
||||||
|
" * error takes a single argument, the error message received from gopls.
|
||||||
|
" The error method is optional.
|
||||||
|
let l:lsp = {
|
||||||
|
\ 'job': '',
|
||||||
|
\ 'ready': 0,
|
||||||
|
\ 'queue': [],
|
||||||
|
\ 'last_request_id': 0,
|
||||||
|
\ 'buf': '',
|
||||||
|
\ 'handlers': {},
|
||||||
|
\ }
|
||||||
|
|
||||||
|
function! l:lsp.readMessage(data) dict abort
|
||||||
|
let l:responses = []
|
||||||
|
let l:rest = a:data
|
||||||
|
|
||||||
|
while 1
|
||||||
|
" Look for the end of the HTTP headers
|
||||||
|
let l:body_start_idx = matchend(l:rest, "\r\n\r\n")
|
||||||
|
|
||||||
|
if l:body_start_idx < 0
|
||||||
|
" incomplete header
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Parse the Content-Length header.
|
||||||
|
let l:header = l:rest[:l:body_start_idx - 4]
|
||||||
|
let l:length_match = matchlist(
|
||||||
|
\ l:header,
|
||||||
|
\ '\vContent-Length: *(\d+)'
|
||||||
|
\)
|
||||||
|
|
||||||
|
if empty(l:length_match)
|
||||||
|
" TODO(bc): shutdown gopls?
|
||||||
|
throw "invalid JSON-RPC header:\n" . l:header
|
||||||
|
endif
|
||||||
|
|
||||||
|
" get the start of the rest
|
||||||
|
let l:rest_start_idx = l:body_start_idx + str2nr(l:length_match[1])
|
||||||
|
|
||||||
|
if len(l:rest) < l:rest_start_idx
|
||||||
|
" incomplete response body
|
||||||
|
break
|
||||||
|
endif
|
||||||
|
|
||||||
|
if go#util#HasDebug('lsp')
|
||||||
|
let g:go_lsp_log = add(go#config#LspLog(), "<-\n" . l:rest[:l:rest_start_idx - 1])
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:body = l:rest[l:body_start_idx : l:rest_start_idx - 1]
|
||||||
|
let l:rest = l:rest[l:rest_start_idx :]
|
||||||
|
|
||||||
|
try
|
||||||
|
" add the json body to the list.
|
||||||
|
call add(l:responses, json_decode(l:body))
|
||||||
|
catch
|
||||||
|
" TODO(bc): log the message and/or show an error message.
|
||||||
|
finally
|
||||||
|
" intentionally left blank.
|
||||||
|
endtry
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
return [l:rest, l:responses]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! l:lsp.handleMessage(ch, data) dict abort
|
||||||
|
let self.buf .= a:data
|
||||||
|
|
||||||
|
let [self.buf, l:responses] = self.readMessage(self.buf)
|
||||||
|
|
||||||
|
" TODO(bc): handle notifications (e.g. window/showMessage).
|
||||||
|
|
||||||
|
for l:response in l:responses
|
||||||
|
if has_key(l:response, 'id') && has_key(self.handlers, l:response.id)
|
||||||
|
try
|
||||||
|
let l:handler = self.handlers[l:response.id]
|
||||||
|
|
||||||
|
if has_key(l:response, 'error')
|
||||||
|
call l:handler.requestComplete(0)
|
||||||
|
if has_key(l:handler, 'error')
|
||||||
|
call call(l:handler.error, [l:response.error.message])
|
||||||
|
else
|
||||||
|
call go#util#EchoError(l:response.error.message)
|
||||||
|
endif
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
call l:handler.requestComplete(1)
|
||||||
|
call call(l:handler.handleResult, [l:response.result])
|
||||||
|
finally
|
||||||
|
call remove(self.handlers, l:response.id)
|
||||||
|
endtry
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! l:lsp.handleInitializeResult(result) dict abort
|
||||||
|
let self.ready = 1
|
||||||
|
" TODO(bc): send initialized message to the server?
|
||||||
|
|
||||||
|
" send messages queued while waiting for ready.
|
||||||
|
for l:item in self.queue
|
||||||
|
call self.sendMessage(l:item.data, l:item.handler)
|
||||||
|
endfor
|
||||||
|
|
||||||
|
" reset the queue
|
||||||
|
let self.queue = []
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! l:lsp.sendMessage(data, handler) dict abort
|
||||||
|
if !self.last_request_id
|
||||||
|
" TODO(bc): run a server per module and one per GOPATH? (may need to
|
||||||
|
" keep track of servers by rootUri).
|
||||||
|
let l:msg = self.newMessage(go#lsp#message#Initialize(getcwd()))
|
||||||
|
|
||||||
|
let l:state = s:newHandlerState('')
|
||||||
|
let l:state.handleResult = funcref('self.handleInitializeResult', [], l:self)
|
||||||
|
let self.handlers[l:msg.id] = l:state
|
||||||
|
|
||||||
|
call l:state.start()
|
||||||
|
call self.write(l:msg)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !self.ready
|
||||||
|
call add(self.queue, {'data': a:data, 'handler': a:handler})
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:msg = self.newMessage(a:data)
|
||||||
|
if has_key(l:msg, 'id')
|
||||||
|
let self.handlers[l:msg.id] = a:handler
|
||||||
|
endif
|
||||||
|
|
||||||
|
call a:handler.start()
|
||||||
|
call self.write(l:msg)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" newMessage returns a message constructed from data. data should be a dict
|
||||||
|
" with 2 or 3 keys: notification, method, and optionally params.
|
||||||
|
function! l:lsp.newMessage(data) dict abort
|
||||||
|
let l:msg = {
|
||||||
|
\ 'method': a:data.method,
|
||||||
|
\ 'jsonrpc': '2.0',
|
||||||
|
\ }
|
||||||
|
|
||||||
|
if !a:data.notification
|
||||||
|
let self.last_request_id += 1
|
||||||
|
let l:msg.id = self.last_request_id
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(a:data, 'params')
|
||||||
|
let l:msg.params = a:data.params
|
||||||
|
endif
|
||||||
|
|
||||||
|
return l:msg
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! l:lsp.write(msg) dict abort
|
||||||
|
let l:body = json_encode(a:msg)
|
||||||
|
let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
|
||||||
|
|
||||||
|
if go#util#HasDebug('lsp')
|
||||||
|
let g:go_lsp_log = add(go#config#LspLog(), "->\n" . l:data)
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has('nvim')
|
||||||
|
call chansend(self.job, l:data)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
call ch_sendraw(self.job, l:data)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! l:lsp.exit_cb(job, exit_status) dict abort
|
||||||
|
call s:lspfactory.reset()
|
||||||
|
endfunction
|
||||||
|
" explicitly bind close_cb to state so that within it, self will always refer
|
||||||
|
|
||||||
|
function! l:lsp.close_cb(ch) dict abort
|
||||||
|
" TODO(bc): does anything need to be done here?
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! l:lsp.err_cb(ch, msg) dict abort
|
||||||
|
if go#util#HasDebug('lsp')
|
||||||
|
let g:go_lsp_log = add(go#config#LspLog(), "<-stderr\n" . a:msg)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" explicitly bind callbacks to l:lsp so that within it, self will always refer
|
||||||
|
" to l:lsp instead of l:opts. See :help Partial for more information.
|
||||||
|
let l:opts = {
|
||||||
|
\ 'in_mode': 'raw',
|
||||||
|
\ 'out_mode': 'raw',
|
||||||
|
\ 'err_mode': 'nl',
|
||||||
|
\ 'noblock': 1,
|
||||||
|
\ 'err_cb': funcref('l:lsp.err_cb', [], l:lsp),
|
||||||
|
\ 'out_cb': funcref('l:lsp.handleMessage', [], l:lsp),
|
||||||
|
\ 'close_cb': funcref('l:lsp.close_cb', [], l:lsp),
|
||||||
|
\ 'exit_cb': funcref('l:lsp.exit_cb', [], l:lsp),
|
||||||
|
\ 'cwd': getcwd(),
|
||||||
|
\}
|
||||||
|
|
||||||
|
let l:bin_path = go#path#CheckBinPath("gopls")
|
||||||
|
if empty(l:bin_path)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" TODO(bc): output a message indicating which directory lsp is going to
|
||||||
|
" start in.
|
||||||
|
let l:lsp.job = go#job#Start([l:bin_path], l:opts)
|
||||||
|
|
||||||
|
" TODO(bc): send the initialize message now?
|
||||||
|
return l:lsp
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:noop(...) abort
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:newHandlerState(statustype) abort
|
||||||
|
let l:state = {
|
||||||
|
\ 'winid': win_getid(winnr()),
|
||||||
|
\ 'statustype': a:statustype,
|
||||||
|
\ 'jobdir': getcwd(),
|
||||||
|
\ }
|
||||||
|
|
||||||
|
" explicitly bind requestComplete to state so that within it, self will
|
||||||
|
" always refer to state. See :help Partial for more information.
|
||||||
|
let l:state.requestComplete = funcref('s:requestComplete', [], l:state)
|
||||||
|
|
||||||
|
" explicitly bind start to state so that within it, self will
|
||||||
|
" always refer to state. See :help Partial for more information.
|
||||||
|
let l:state.start = funcref('s:start', [], l:state)
|
||||||
|
|
||||||
|
return l:state
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:requestComplete(ok) abort dict
|
||||||
|
if self.statustype == ''
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if go#config#EchoCommandInfo()
|
||||||
|
let prefix = '[' . self.statustype . '] '
|
||||||
|
if a:ok
|
||||||
|
call go#util#EchoSuccess(prefix . "SUCCESS")
|
||||||
|
else
|
||||||
|
call go#util#EchoError(prefix . "FAIL")
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
let status = {
|
||||||
|
\ 'desc': 'last status',
|
||||||
|
\ 'type': self.statustype,
|
||||||
|
\ 'state': "success",
|
||||||
|
\ }
|
||||||
|
|
||||||
|
if !a:ok
|
||||||
|
let status.state = "failed"
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(self, 'started_at')
|
||||||
|
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)
|
||||||
|
endif
|
||||||
|
|
||||||
|
call go#statusline#Update(self.jobdir, status)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:start() abort dict
|
||||||
|
if self.statustype != ''
|
||||||
|
let status = {
|
||||||
|
\ 'desc': 'current status',
|
||||||
|
\ 'type': self.statustype,
|
||||||
|
\ 'state': "started",
|
||||||
|
\ }
|
||||||
|
|
||||||
|
call go#statusline#Update(self.jobdir, status)
|
||||||
|
endif
|
||||||
|
let self.started_at = reltime()
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" go#lsp#Definition calls gopls to get the definition of the identifier at
|
||||||
|
" line and col in fname. handler should be a dictionary function that takes a
|
||||||
|
" list of strings in the form 'file:line:col: message'. handler will be
|
||||||
|
" attached to a dictionary that manages state (statuslines, sets the winid,
|
||||||
|
" etc.)
|
||||||
|
function! go#lsp#Definition(fname, line, col, handler) abort
|
||||||
|
call go#lsp#DidChange(a:fname)
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
let l:state = s:newHandlerState('definition')
|
||||||
|
let l:state.handleResult = funcref('s:definitionHandler', [function(a:handler, [], l:state)], l:state)
|
||||||
|
let l:msg = go#lsp#message#Definition(fnamemodify(a:fname, ':p'), a:line, a:col)
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:definitionHandler(next, msg) abort dict
|
||||||
|
" gopls returns a []Location; just take the first one.
|
||||||
|
let l:msg = a:msg[0]
|
||||||
|
let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, l:msg.range.start.character+1, 'lsp does not supply a description')]]
|
||||||
|
call call(a:next, l:args)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" go#lsp#Type calls gopls to get the type definition of the identifier at
|
||||||
|
" line and col in fname. handler should be a dictionary function that takes a
|
||||||
|
" list of strings in the form 'file:line:col: message'. handler will be
|
||||||
|
" attached to a dictionary that manages state (statuslines, sets the winid,
|
||||||
|
" etc.)
|
||||||
|
function! go#lsp#TypeDef(fname, line, col, handler) abort
|
||||||
|
call go#lsp#DidChange(a:fname)
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
let l:state = s:newHandlerState('type definition')
|
||||||
|
let l:msg = go#lsp#message#TypeDefinition(fnamemodify(a:fname, ':p'), a:line, a:col)
|
||||||
|
let l:state.handleResult = funcref('s:typeDefinitionHandler', [function(a:handler, [], l:state)], l:state)
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:typeDefinitionHandler(next, msg) abort dict
|
||||||
|
" gopls returns a []Location; just take the first one.
|
||||||
|
let l:msg = a:msg[0]
|
||||||
|
let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, l:msg.range.start.character+1, 'lsp does not supply a description')]]
|
||||||
|
call call(a:next, l:args)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#DidOpen(fname) abort
|
||||||
|
if get(b:, 'go_lsp_did_open', 0)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !filereadable(a:fname)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
let l:msg = go#lsp#message#DidOpen(fnamemodify(a:fname, ':p'), join(go#util#GetLines(), "\n") . "\n")
|
||||||
|
let l:state = s:newHandlerState('')
|
||||||
|
let l:state.handleResult = funcref('s:noop')
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
|
||||||
|
let b:go_lsp_did_open = 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#DidChange(fname) abort
|
||||||
|
" DidChange is called even when fname isn't open in a buffer (e.g. via
|
||||||
|
" go#lsp#Info); don't report the file as open or as having changed when it's
|
||||||
|
" not actually a buffer.
|
||||||
|
if bufnr(a:fname) == -1
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
call go#lsp#DidOpen(a:fname)
|
||||||
|
|
||||||
|
if !filereadable(a:fname)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
let l:msg = go#lsp#message#DidChange(fnamemodify(a:fname, ':p'), join(go#util#GetLines(), "\n") . "\n")
|
||||||
|
let l:state = s:newHandlerState('')
|
||||||
|
let l:state.handleResult = funcref('s:noop')
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#DidClose(fname) abort
|
||||||
|
if !filereadable(a:fname)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !get(b:, 'go_lsp_did_open', 0)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
let l:msg = go#lsp#message#DidClose(fnamemodify(a:fname, ':p'))
|
||||||
|
let l:state = s:newHandlerState('')
|
||||||
|
let l:state.handleResult = funcref('s:noop')
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
|
||||||
|
let b:go_lsp_did_open = 0
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#Completion(fname, line, col, handler) abort
|
||||||
|
call go#lsp#DidChange(a:fname)
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
let l:msg = go#lsp#message#Completion(a:fname, a:line, a:col)
|
||||||
|
let l:state = s:newHandlerState('completion')
|
||||||
|
let l:state.handleResult = funcref('s:completionHandler', [function(a:handler, [], l:state)], l:state)
|
||||||
|
let l:state.error = funcref('s:completionErrorHandler', [function(a:handler, [], l:state)], l:state)
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:completionHandler(next, msg) abort dict
|
||||||
|
" gopls returns a CompletionList.
|
||||||
|
let l:matches = []
|
||||||
|
for l:item in a:msg.items
|
||||||
|
let l:match = {'abbr': l:item.label, 'word': l:item.textEdit.newText, 'info': '', 'kind': go#lsp#completionitemkind#Vim(l:item.kind)}
|
||||||
|
if has_key(l:item, 'detail')
|
||||||
|
let l:item.info = l:item.detail
|
||||||
|
endif
|
||||||
|
|
||||||
|
if has_key(l:item, 'documentation')
|
||||||
|
let l:match.info .= "\n\n" . l:item.documentation
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:matches = add(l:matches, l:match)
|
||||||
|
endfor
|
||||||
|
let l:args = [l:matches]
|
||||||
|
call call(a:next, l:args)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:completionErrorHandler(next, error) abort dict
|
||||||
|
call call(a:next, [[]])
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#Hover(fname, line, col, handler) abort
|
||||||
|
call go#lsp#DidChange(a:fname)
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
let l:msg = go#lsp#message#Hover(a:fname, a:line, a:col)
|
||||||
|
let l:state = s:newHandlerState('')
|
||||||
|
let l:state.handleResult = funcref('s:hoverHandler', [function(a:handler, [], l:state)], l:state)
|
||||||
|
let l:state.error = funcref('s:noop')
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:hoverHandler(next, msg) abort dict
|
||||||
|
let l:content = split(a:msg.contents.value, '; ')
|
||||||
|
if len(l:content) > 1
|
||||||
|
let l:curly = stridx(l:content[0], '{')
|
||||||
|
let l:content = extend([l:content[0][0:l:curly]], map(extend([l:content[0][l:curly+1:]], l:content[1:]), '"\t" . v:val'))
|
||||||
|
let l:content[len(l:content)-1] = '}'
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:args = [l:content]
|
||||||
|
call call(a:next, l:args)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#Info(showstatus)
|
||||||
|
let l:fname = expand('%:p')
|
||||||
|
let [l:line, l:col] = getpos('.')[1:2]
|
||||||
|
|
||||||
|
call go#lsp#DidChange(l:fname)
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
|
||||||
|
if a:showstatus
|
||||||
|
let l:state = s:newHandlerState('info')
|
||||||
|
else
|
||||||
|
let l:state = s:newHandlerState('')
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:state.handleResult = funcref('s:infoDefinitionHandler', [function('s:info', []), a:showstatus], l:state)
|
||||||
|
let l:state.error = funcref('s:noop')
|
||||||
|
let l:msg = go#lsp#message#Definition(l:fname, l:line, l:col)
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:infoDefinitionHandler(next, showstatus, msg) abort dict
|
||||||
|
" gopls returns a []Location; just take the first one.
|
||||||
|
let l:msg = a:msg[0]
|
||||||
|
|
||||||
|
let l:fname = go#path#FromURI(l:msg.uri)
|
||||||
|
let l:line = l:msg.range.start.line+1
|
||||||
|
let l:col = l:msg.range.start.character+1
|
||||||
|
|
||||||
|
let l:lsp = s:lspfactory.get()
|
||||||
|
let l:msg = go#lsp#message#Hover(l:fname, l:line, l:col)
|
||||||
|
|
||||||
|
if a:showstatus
|
||||||
|
let l:state = s:newHandlerState('info')
|
||||||
|
else
|
||||||
|
let l:state = s:newHandlerState('')
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:state.handleResult = funcref('s:hoverHandler', [function('s:info', [], l:state)], l:state)
|
||||||
|
let l:state.error = funcref('s:noop')
|
||||||
|
call l:lsp.sendMessage(l:msg, l:state)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:info(content) abort dict
|
||||||
|
" strip off the method set and fields of structs and interfaces.
|
||||||
|
let l:content = substitute(a:content[0], '{.*', '', '')
|
||||||
|
call go#util#ShowInfo(l:content)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
47
pack/acp/start/vim-go/autoload/go/lsp/completionitemkind.vim
Normal file
47
pack/acp/start/vim-go/autoload/go/lsp/completionitemkind.vim
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
let s:Text = 1
|
||||||
|
let s:Method = 2
|
||||||
|
let s:Function = 3
|
||||||
|
let s:Constructor = 4
|
||||||
|
let s:Field = 5
|
||||||
|
let s:Variable = 6
|
||||||
|
let s:Class = 7
|
||||||
|
let s:Interface = 8
|
||||||
|
let s:Module = 9
|
||||||
|
let s:Property = 10
|
||||||
|
let s:Unit = 11
|
||||||
|
let s:Value = 12
|
||||||
|
let s:Enum = 13
|
||||||
|
let s:Keyword = 14
|
||||||
|
let s:Snippet = 15
|
||||||
|
let s:Color = 16
|
||||||
|
let s:File = 17
|
||||||
|
let s:Reference = 18
|
||||||
|
let s:Folder = 19
|
||||||
|
let s:EnumMember = 20
|
||||||
|
let s:Constant = 21
|
||||||
|
let s:Struct = 22
|
||||||
|
let s:Event = 23
|
||||||
|
let s:Operator = 24
|
||||||
|
let s:TypeParameter = 25
|
||||||
|
|
||||||
|
function! go#lsp#completionitemkind#Vim(kind)
|
||||||
|
if a:kind == s:Method || a:kind == s:Function || a:kind == s:Constructor
|
||||||
|
return 'f'
|
||||||
|
elseif a:kind == s:Variable || a:kind == s:Constant
|
||||||
|
return 'v'
|
||||||
|
elseif a:kind == s:Field || a:kind == s:Property
|
||||||
|
return 'm'
|
||||||
|
elseif a:kind == s:Class || a:kind == s:Interface || a:kind == s:Struct
|
||||||
|
return 't'
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
127
pack/acp/start/vim-go/autoload/go/lsp/message.vim
Normal file
127
pack/acp/start/vim-go/autoload/go/lsp/message.vim
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
function! go#lsp#message#Initialize(wd) abort
|
||||||
|
return {
|
||||||
|
\ 'notification': 0,
|
||||||
|
\ 'method': 'initialize',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'processId': getpid(),
|
||||||
|
\ 'rootUri': go#path#ToURI(a:wd),
|
||||||
|
\ 'capabilities': {
|
||||||
|
\ 'workspace': {},
|
||||||
|
\ 'textDocument': {
|
||||||
|
\ 'hover': {
|
||||||
|
\ 'contentFormat': ['plaintext'],
|
||||||
|
\ },
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#message#Definition(file, line, col) abort
|
||||||
|
return {
|
||||||
|
\ 'notification': 0,
|
||||||
|
\ 'method': 'textDocument/definition',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'textDocument': {
|
||||||
|
\ 'uri': go#path#ToURI(a:file)
|
||||||
|
\ },
|
||||||
|
\ 'position': s:position(a:line, a:col)
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#message#TypeDefinition(file, line, col) abort
|
||||||
|
return {
|
||||||
|
\ 'notification': 0,
|
||||||
|
\ 'method': 'textDocument/typeDefinition',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'textDocument': {
|
||||||
|
\ 'uri': go#path#ToURI(a:file)
|
||||||
|
\ },
|
||||||
|
\ 'position': s:position(a:line, a:col)
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#message#DidOpen(file, content) abort
|
||||||
|
return {
|
||||||
|
\ 'notification': 1,
|
||||||
|
\ 'method': 'textDocument/didOpen',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'textDocument': {
|
||||||
|
\ 'uri': go#path#ToURI(a:file),
|
||||||
|
\ 'languageId': 'go',
|
||||||
|
\ 'text': a:content,
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#message#DidChange(file, content) abort
|
||||||
|
return {
|
||||||
|
\ 'notification': 1,
|
||||||
|
\ 'method': 'textDocument/didChange',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'textDocument': {
|
||||||
|
\ 'uri': go#path#ToURI(a:file),
|
||||||
|
\ },
|
||||||
|
\ 'contentChanges': [
|
||||||
|
\ {
|
||||||
|
\ 'text': a:content,
|
||||||
|
\ }
|
||||||
|
\ ]
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#message#DidClose(file) abort
|
||||||
|
return {
|
||||||
|
\ 'notification': 1,
|
||||||
|
\ 'method': 'textDocument/didClose',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'textDocument': {
|
||||||
|
\ 'uri': go#path#ToURI(a:file),
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#message#Completion(file, line, col) abort
|
||||||
|
return {
|
||||||
|
\ 'notification': 0,
|
||||||
|
\ 'method': 'textDocument/completion',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'textDocument': {
|
||||||
|
\ 'uri': go#path#ToURI(a:file)
|
||||||
|
\ },
|
||||||
|
\ 'position': s:position(a:line, a:col),
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#lsp#message#Hover(file, line, col) abort
|
||||||
|
return {
|
||||||
|
\ 'notification': 0,
|
||||||
|
\ 'method': 'textDocument/hover',
|
||||||
|
\ 'params': {
|
||||||
|
\ 'textDocument': {
|
||||||
|
\ 'uri': go#path#ToURI(a:file)
|
||||||
|
\ },
|
||||||
|
\ 'position': s:position(a:line, a:col),
|
||||||
|
\ }
|
||||||
|
\ }
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:position(line, col) abort
|
||||||
|
return {'line': a:line - 1, 'character': a:col-1}
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
150
pack/acp/start/vim-go/autoload/go/mod.vim
Normal file
150
pack/acp/start/vim-go/autoload/go/mod.vim
Normal file
|
@ -0,0 +1,150 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
let s:go_major_version = ""
|
||||||
|
|
||||||
|
function! go#mod#Format() abort
|
||||||
|
" go mod only exists in `v1.11`
|
||||||
|
if empty(s:go_major_version)
|
||||||
|
let tokens = matchlist(go#util#System("go version"), '\d\+.\(\d\+\)\(\.\d\+\)\? ')
|
||||||
|
let s:go_major_version = str2nr(tokens[1])
|
||||||
|
endif
|
||||||
|
|
||||||
|
if s:go_major_version < "11"
|
||||||
|
call go#util#EchoError("Go v1.11 is required to format go.mod file")
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
||||||
|
|
||||||
|
" Save cursor position and many other things.
|
||||||
|
let l:curw = winsaveview()
|
||||||
|
|
||||||
|
" Write current unsaved buffer to a temp file
|
||||||
|
let l:tmpname = tempname() . '.mod'
|
||||||
|
call writefile(go#util#GetLines(), l:tmpname)
|
||||||
|
if go#util#IsWin()
|
||||||
|
let l:tmpname = tr(l:tmpname, '\', '/')
|
||||||
|
endif
|
||||||
|
|
||||||
|
let current_col = col('.')
|
||||||
|
let l:args = ['go', 'mod', 'edit', '--fmt', l:tmpname]
|
||||||
|
let [l:out, l:err] = go#util#Exec(l:args)
|
||||||
|
let diff_offset = len(readfile(l:tmpname)) - line('$')
|
||||||
|
|
||||||
|
if l:err == 0
|
||||||
|
call go#mod#update_file(l:tmpname, fname)
|
||||||
|
else
|
||||||
|
let errors = s:parse_errors(fname, l:out)
|
||||||
|
call s:show_errors(errors)
|
||||||
|
endif
|
||||||
|
|
||||||
|
" We didn't use the temp file, so clean up
|
||||||
|
call delete(l:tmpname)
|
||||||
|
|
||||||
|
" Restore our cursor/windows positions.
|
||||||
|
call winrestview(l:curw)
|
||||||
|
|
||||||
|
" be smart and jump to the line the new statement was added/removed
|
||||||
|
call cursor(line('.') + diff_offset, current_col)
|
||||||
|
|
||||||
|
" Syntax highlighting breaks less often.
|
||||||
|
syntax sync fromstart
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" update_file updates the target file with the given formatted source
|
||||||
|
function! go#mod#update_file(source, target)
|
||||||
|
" remove undo point caused via BufWritePre
|
||||||
|
try | silent undojoin | catch | endtry
|
||||||
|
|
||||||
|
let old_fileformat = &fileformat
|
||||||
|
if exists("*getfperm")
|
||||||
|
" save file permissions
|
||||||
|
let original_fperm = getfperm(a:target)
|
||||||
|
endif
|
||||||
|
|
||||||
|
call rename(a:source, a:target)
|
||||||
|
|
||||||
|
" restore file permissions
|
||||||
|
if exists("*setfperm") && original_fperm != ''
|
||||||
|
call setfperm(a:target , original_fperm)
|
||||||
|
endif
|
||||||
|
|
||||||
|
" reload buffer to reflect latest changes
|
||||||
|
silent edit!
|
||||||
|
|
||||||
|
let &fileformat = old_fileformat
|
||||||
|
let &syntax = &syntax
|
||||||
|
|
||||||
|
let l:listtype = go#list#Type("GoModFmt")
|
||||||
|
|
||||||
|
" 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)
|
||||||
|
endif
|
||||||
|
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\+\):\s*\(.*\)')
|
||||||
|
if !empty(tokens)
|
||||||
|
call add(errors,{
|
||||||
|
\"filename": a:filename,
|
||||||
|
\"lnum": tokens[2],
|
||||||
|
\"text": tokens[3],
|
||||||
|
\ })
|
||||||
|
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("GoModFmt")
|
||||||
|
if !empty(a:errors)
|
||||||
|
call go#list#Populate(l:listtype, a:errors, 'Format')
|
||||||
|
call go#util#EchoError("GoModFmt returned error")
|
||||||
|
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#mod#ToggleModFmtAutoSave() abort
|
||||||
|
if go#config#ModFmtAutosave()
|
||||||
|
call go#config#SetModFmtAutosave(0)
|
||||||
|
call go#util#EchoProgress("auto mod fmt disabled")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
call go#config#SetModFmtAutosave(1)
|
||||||
|
call go#util#EchoProgress("auto mod fmt enabled")
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -5,6 +5,10 @@
|
||||||
" This file provides a utility function that performs auto-completion of
|
" This file provides a utility function that performs auto-completion of
|
||||||
" package names, for use by other commands.
|
" package names, for use by other commands.
|
||||||
|
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
let s:goos = $GOOS
|
let s:goos = $GOOS
|
||||||
let s:goarch = $GOARCH
|
let s:goarch = $GOARCH
|
||||||
|
|
||||||
|
@ -28,7 +32,7 @@ if len(s:goarch) == 0
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
function! go#package#Paths() abort
|
function! s:paths() abort
|
||||||
let dirs = []
|
let dirs = []
|
||||||
|
|
||||||
if !exists("s:goroot")
|
if !exists("s:goroot")
|
||||||
|
@ -54,63 +58,119 @@ function! go#package#Paths() abort
|
||||||
return dirs
|
return dirs
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! s:module() abort
|
||||||
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-m', '-f', '{{.Dir}}'])
|
||||||
|
if l:err != 0
|
||||||
|
return {}
|
||||||
|
endif
|
||||||
|
let l:dir = split(l:out, '\n')[0]
|
||||||
|
|
||||||
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-m', '-f', '{{.Path}}'])
|
||||||
|
if l:err != 0
|
||||||
|
return {}
|
||||||
|
endif
|
||||||
|
let l:path = split(l:out, '\n')[0]
|
||||||
|
|
||||||
|
return {'dir': l:dir, 'path': l:path}
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:vendordirs() abort
|
||||||
|
let l:vendorsuffix = go#util#PathSep() . 'vendor'
|
||||||
|
let l:module = s:module()
|
||||||
|
if empty(l:module)
|
||||||
|
let [l:root, l:err] = go#util#ExecInDir(['go', 'list', '-f', '{{.Root}}'])
|
||||||
|
if l:err != 0
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
let l:root = split(l:root, '\n')[0] . go#util#PathSep() . 'src'
|
||||||
|
|
||||||
|
let [l:dir, l:err] = go#util#ExecInDir(['go', 'list', '-f', '{{.Dir}}'])
|
||||||
|
if l:err != 0
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
let l:dir = split(l:dir, '\n')[0]
|
||||||
|
|
||||||
|
let l:vendordirs = []
|
||||||
|
while l:dir != l:root
|
||||||
|
let l:vendordir = l:dir . l:vendorsuffix
|
||||||
|
if isdirectory(l:vendordir)
|
||||||
|
let l:vendordirs = add(l:vendordirs, l:vendordir)
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:dir = fnamemodify(l:dir, ':h')
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
return l:vendordirs
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:vendordir = l:module.dir . l:vendorsuffix
|
||||||
|
if !isdirectory(l:vendordir)
|
||||||
|
return []
|
||||||
|
endif
|
||||||
|
return [l:vendordir]
|
||||||
|
endfunction
|
||||||
|
|
||||||
let s:import_paths = {}
|
let s:import_paths = {}
|
||||||
" ImportPath returns the import path in the current directory it was executed
|
" ImportPath returns the import path of the package for current buffer.
|
||||||
function! go#package#ImportPath() abort
|
function! go#package#ImportPath() abort
|
||||||
let dir = expand("%:p:h")
|
let dir = expand("%:p:h")
|
||||||
if has_key(s:import_paths, dir)
|
if has_key(s:import_paths, dir)
|
||||||
return s:import_paths[dir]
|
return s:import_paths[dir]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let out = go#tool#ExecuteInDir("go list")
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list'])
|
||||||
if go#util#ShellError() != 0
|
if l:err != 0
|
||||||
return -1
|
return -1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let import_path = split(out, '\n')[0]
|
let l:importpath = split(out, '\n')[0]
|
||||||
|
|
||||||
" go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH.
|
" go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH.
|
||||||
" Check it and retun an error if that is the case
|
" Check it and retun an error if that is the case
|
||||||
if import_path[0] ==# '_'
|
if l:importpath[0] ==# '_'
|
||||||
return -1
|
return -1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let s:import_paths[dir] = import_path
|
let s:import_paths[dir] = l:importpath
|
||||||
|
|
||||||
return import_path
|
return l:importpath
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
|
" FromPath returns the import path of arg.
|
||||||
function! go#package#FromPath(arg) abort
|
function! go#package#FromPath(arg) abort
|
||||||
let path = fnamemodify(resolve(a:arg), ':p')
|
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
|
||||||
let dirs = go#package#Paths()
|
let l:dir = getcwd()
|
||||||
|
|
||||||
for dir in dirs
|
let l:path = a:arg
|
||||||
if len(dir) && match(path, dir) == 0
|
if !isdirectory(l:path)
|
||||||
let workspace = dir
|
let l:path = fnamemodify(l:path, ':h')
|
||||||
break
|
|
||||||
endif
|
endif
|
||||||
endfor
|
|
||||||
|
|
||||||
if !exists('workspace')
|
execute l:cd fnameescape(l:path)
|
||||||
|
let [l:out, l:err] = go#util#Exec(['go', 'list'])
|
||||||
|
execute l:cd fnameescape(l:dir)
|
||||||
|
if l:err != 0
|
||||||
return -1
|
return -1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let path = substitute(path, '/*$', '', '')
|
let l:importpath = split(l:out, '\n')[0]
|
||||||
let workspace = substitute(workspace . '/src/', '/+', '', '')
|
|
||||||
if isdirectory(path)
|
" go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH.
|
||||||
return substitute(path, workspace, '', '')
|
" Check it and retun an error if that is the case
|
||||||
else
|
if l:importpath[0] ==# '_'
|
||||||
return substitute(substitute(path, workspace, '', ''),
|
return -1
|
||||||
\ '/' . fnamemodify(path, ':t'), '', '')
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
return l:importpath
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#package#CompleteMembers(package, member) abort
|
function! go#package#CompleteMembers(package, member) abort
|
||||||
silent! let content = go#util#System('godoc ' . a:package)
|
let [l:content, l:err] = go#util#Exec(['go', 'doc', a:package])
|
||||||
if go#util#ShellError() || !len(content)
|
if l:err || !len(content)
|
||||||
return []
|
return []
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let lines = filter(split(content, "\n"),"v:val !~ '^\\s\\+$'")
|
let lines = filter(split(content, "\n"),"v:val !~ '^\\s\\+$'")
|
||||||
try
|
try
|
||||||
let mx1 = '^\s\+\(\S+\)\s\+=\s\+.*'
|
let mx1 = '^\s\+\(\S+\)\s\+=\s\+.*'
|
||||||
|
@ -136,37 +196,88 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
|
||||||
return go#package#CompleteMembers(words[1], words[2])
|
return go#package#CompleteMembers(words[1], words[2])
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let dirs = go#package#Paths()
|
let dirs = s:paths()
|
||||||
|
let module = s:module()
|
||||||
|
|
||||||
if len(dirs) == 0
|
if len(dirs) == 0 && empty(module)
|
||||||
" should not happen
|
" should not happen
|
||||||
return []
|
return []
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
let vendordirs = s:vendordirs()
|
||||||
|
|
||||||
let ret = {}
|
let ret = {}
|
||||||
for dir in dirs
|
for dir in dirs
|
||||||
" this may expand to multiple lines
|
" this may expand to multiple lines
|
||||||
let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
|
let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
|
||||||
call add(root, expand(dir . '/src'))
|
let root = add(root, expand(dir . '/src'), )
|
||||||
for r in root
|
let root = extend(root, vendordirs)
|
||||||
for i in split(globpath(r, a:ArgLead.'*'), "\n")
|
let root = add(root, module)
|
||||||
if isdirectory(i)
|
for item in root
|
||||||
let i .= '/'
|
" item may be a dictionary when operating in a module.
|
||||||
elseif i !~ '\.a$'
|
if type(item) == type({})
|
||||||
|
if empty(item)
|
||||||
continue
|
continue
|
||||||
endif
|
endif
|
||||||
let i = substitute(substitute(i[len(r)+1:], '[\\]', '/', 'g'),
|
let dir = item.dir
|
||||||
|
let path = item.path
|
||||||
|
else
|
||||||
|
let dir = item
|
||||||
|
let path = item
|
||||||
|
endif
|
||||||
|
|
||||||
|
if !empty(module) && dir ==# module.dir
|
||||||
|
if stridx(a:ArgLead, module.path) == 0
|
||||||
|
if len(a:ArgLead) != len(module.path)
|
||||||
|
let glob = globpath(module.dir, substitute(a:ArgLead, module.path . '/\?', '', '').'*')
|
||||||
|
else
|
||||||
|
let glob = module.dir
|
||||||
|
endif
|
||||||
|
elseif stridx(module.path, a:ArgLead) == 0 && stridx(module.path, '/', len(a:ArgLead)) < 0
|
||||||
|
" use the module directory when a:ArgLead is contained in
|
||||||
|
" module.path and module.path does not have any path segments after
|
||||||
|
" a:ArgLead.
|
||||||
|
let glob = module.dir
|
||||||
|
else
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
let glob = globpath(dir, a:ArgLead.'*')
|
||||||
|
endif
|
||||||
|
for candidate in split(glob)
|
||||||
|
if isdirectory(candidate)
|
||||||
|
" TODO(bc): use wildignore instead of filtering out vendor
|
||||||
|
" directories manually?
|
||||||
|
if fnamemodify(candidate, ':t') == 'vendor'
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
let candidate .= '/'
|
||||||
|
elseif candidate !~ '\.a$'
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if dir !=# path
|
||||||
|
let candidate = substitute(candidate, '^' . dir, path, 'g')
|
||||||
|
else
|
||||||
|
let candidate = candidate[len(dir)+1:]
|
||||||
|
endif
|
||||||
|
" replace a backslash with a forward slash and drop .a suffixes
|
||||||
|
let candidate = substitute(substitute(candidate, '[\\]', '/', 'g'),
|
||||||
\ '\.a$', '', 'g')
|
\ '\.a$', '', 'g')
|
||||||
|
|
||||||
" without this the result can have duplicates in form of
|
" without this the result can have duplicates in form of
|
||||||
" 'encoding/json' and '/encoding/json/'
|
" 'encoding/json' and '/encoding/json/'
|
||||||
let i = go#util#StripPathSep(i)
|
let candidate = go#util#StripPathSep(candidate)
|
||||||
|
|
||||||
let ret[i] = i
|
let ret[candidate] = candidate
|
||||||
endfor
|
endfor
|
||||||
endfor
|
endfor
|
||||||
endfor
|
endfor
|
||||||
return sort(keys(ret))
|
return sort(keys(ret))
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
58
pack/acp/start/vim-go/autoload/go/package_test.vim
Normal file
58
pack/acp/start/vim-go/autoload/go/package_test.vim
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
func! Test_Complete_GOPATH_simple() abort
|
||||||
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
|
||||||
|
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
|
||||||
|
call s:complete('package', ['package'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_Complete_Module_simple() abort
|
||||||
|
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
|
||||||
|
call s:complete('package', ['package'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_Complete_GOPATH_subdirs() abort
|
||||||
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
|
||||||
|
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
|
||||||
|
call s:complete('package/', ['package/bar', 'package/baz'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_Complete_Module_subdirs() abort
|
||||||
|
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
|
||||||
|
call s:complete('package/', ['package/bar', 'package/baz'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_Complete_GOPATH_baronly() abort
|
||||||
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
|
||||||
|
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
|
||||||
|
call s:complete('package/bar', ['package/bar'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_Complete_Module_baronly() abort
|
||||||
|
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
|
||||||
|
call s:complete('package/bar', ['package/bar'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_Complete_GOPATH_vendor() abort
|
||||||
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
|
||||||
|
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
|
||||||
|
call s:complete('foo', ['foo'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_Complete_Module_vendor() abort
|
||||||
|
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
|
||||||
|
call s:complete('foo', ['foo'])
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! s:complete(arglead, expected) abort
|
||||||
|
let l:candidates = go#package#Complete(a:arglead, '', 1)
|
||||||
|
call assert_equal(a:expected, l:candidates)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
" initial_go_path is used to store the initial GOPATH that was set when Vim
|
" initial_go_path is used to store the initial GOPATH that was set when Vim
|
||||||
" was started. It's used with :GoPathClear to restore the GOPATH when the user
|
" was started. It's used with :GoPathClear to restore the GOPATH when the user
|
||||||
" changed it explicitly via :GoPath. Initially it's empty. It's being set when
|
" changed it explicitly via :GoPath. Initially it's empty. It's being set when
|
||||||
|
@ -121,13 +125,14 @@ endfunction
|
||||||
|
|
||||||
" BinPath returns the binary path of installed go tools.
|
" BinPath returns the binary path of installed go tools.
|
||||||
function! go#path#BinPath() abort
|
function! go#path#BinPath() abort
|
||||||
let bin_path = ""
|
let bin_path = go#config#BinPath()
|
||||||
|
if bin_path != ""
|
||||||
|
return bin_path
|
||||||
|
endif
|
||||||
|
|
||||||
" check if our global custom path is set, if not check if $GOBIN is set so
|
" check if our global custom path is set, if not check if $GOBIN is set so
|
||||||
" we can use it, otherwise use default GOPATH
|
" we can use it, otherwise use default GOPATH
|
||||||
if exists("g:go_bin_path")
|
if $GOBIN != ""
|
||||||
let bin_path = g:go_bin_path
|
|
||||||
elseif $GOBIN != ""
|
|
||||||
let bin_path = $GOBIN
|
let bin_path = $GOBIN
|
||||||
else
|
else
|
||||||
let go_paths = split(go#path#Default(), go#util#PathListSep())
|
let go_paths = split(go#path#Default(), go#util#PathListSep())
|
||||||
|
@ -141,11 +146,13 @@ function! go#path#BinPath() abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" CheckBinPath checks whether the given binary exists or not and returns the
|
" CheckBinPath checks whether the given binary exists or not and returns the
|
||||||
" path of the binary. It returns an empty string doesn't exists.
|
" path of the binary, respecting the go_bin_path and go_search_bin_path_first
|
||||||
|
" settings. It returns an empty string if the binary doesn't exist.
|
||||||
function! go#path#CheckBinPath(binpath) abort
|
function! go#path#CheckBinPath(binpath) abort
|
||||||
" remove whitespaces if user applied something like 'goimports '
|
" remove whitespaces if user applied something like 'goimports '
|
||||||
let binpath = substitute(a:binpath, '^\s*\(.\{-}\)\s*$', '\1', '')
|
let binpath = substitute(a:binpath, '^\s*\(.\{-}\)\s*$', '\1', '')
|
||||||
" save off original path
|
|
||||||
|
" save original path
|
||||||
let old_path = $PATH
|
let old_path = $PATH
|
||||||
|
|
||||||
" check if we have an appropriate bin_path
|
" check if we have an appropriate bin_path
|
||||||
|
@ -153,7 +160,12 @@ function! go#path#CheckBinPath(binpath) abort
|
||||||
if !empty(go_bin_path)
|
if !empty(go_bin_path)
|
||||||
" append our GOBIN and GOPATH paths and be sure they can be found there...
|
" append our GOBIN and GOPATH paths and be sure they can be found there...
|
||||||
" let us search in our GOBIN and GOPATH paths
|
" let us search in our GOBIN and GOPATH paths
|
||||||
|
" respect the ordering specified by go_search_bin_path_first
|
||||||
|
if go#config#SearchBinPathFirst()
|
||||||
let $PATH = go_bin_path . go#util#PathListSep() . $PATH
|
let $PATH = go_bin_path . go#util#PathListSep() . $PATH
|
||||||
|
else
|
||||||
|
let $PATH = $PATH . go#util#PathListSep() . go_bin_path
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" if it's in PATH just return it
|
" if it's in PATH just return it
|
||||||
|
@ -193,4 +205,44 @@ function! s:CygwinPath(path)
|
||||||
return substitute(a:path, '\\', '/', "g")
|
return substitute(a:path, '\\', '/', "g")
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" go#path#ToURI converts path to a file URI. path should be an absolute path.
|
||||||
|
" Relative paths cannot be properly converted to a URI; when path is a
|
||||||
|
" relative path, the file scheme will not be prepended.
|
||||||
|
function! go#path#ToURI(path)
|
||||||
|
let l:absolute = !go#util#IsWin() && a:path[0] is# '/'
|
||||||
|
let l:prefix = ''
|
||||||
|
let l:path = a:path
|
||||||
|
|
||||||
|
if go#util#IsWin() && l:path[1:2] is# ':\'
|
||||||
|
let l:absolute = 1
|
||||||
|
let l:prefix = '/' . l:path[0:1]
|
||||||
|
let l:path = l:path[2:]
|
||||||
|
endif
|
||||||
|
|
||||||
|
return substitute(
|
||||||
|
\ (l:absolute ? 'file://' : '') . l:prefix . go#uri#EncodePath(l:path),
|
||||||
|
\ '\\',
|
||||||
|
\ '/',
|
||||||
|
\ 'g',
|
||||||
|
\)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#path#FromURI(uri) abort
|
||||||
|
let l:i = len('file://')
|
||||||
|
let l:encoded_path = a:uri[: l:i - 1] is# 'file://' ? a:uri[l:i :] : a:uri
|
||||||
|
|
||||||
|
let l:path = go#uri#Decode(l:encoded_path)
|
||||||
|
|
||||||
|
" If the path is like /C:/foo/bar, it should be C:\foo\bar instead.
|
||||||
|
if go#util#IsWin() && l:path =~# '^/[a-zA-Z]:'
|
||||||
|
let l:path = substitute(l:path[1:], '/', '\\', 'g')
|
||||||
|
endif
|
||||||
|
|
||||||
|
return l:path
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
if !exists("g:go_play_open_browser")
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let g:go_play_open_browser = 1
|
let s:cpo_save = &cpo
|
||||||
endif
|
set cpo&vim
|
||||||
|
|
||||||
|
|
||||||
function! go#play#Share(count, line1, line2) abort
|
function! go#play#Share(count, line1, line2) abort
|
||||||
if !executable('curl')
|
if !executable('curl')
|
||||||
|
@ -13,15 +12,16 @@ function! go#play#Share(count, line1, line2) abort
|
||||||
let share_file = tempname()
|
let share_file = tempname()
|
||||||
call writefile(split(content, "\n"), share_file, "b")
|
call writefile(split(content, "\n"), share_file, "b")
|
||||||
|
|
||||||
let command = "curl -s -X POST https://play.golang.org/share --data-binary '@".share_file."'"
|
let l:cmd = ['curl', '-s', '-X', 'POST', 'https://play.golang.org/share',
|
||||||
let snippet_id = go#util#System(command)
|
\ '--data-binary', '@' . l:share_file]
|
||||||
|
let [l:snippet_id, l:err] = go#util#Exec(l:cmd)
|
||||||
|
|
||||||
" we can remove the temp file because it's now posted.
|
" we can remove the temp file because it's now posted.
|
||||||
call delete(share_file)
|
call delete(share_file)
|
||||||
|
|
||||||
if go#util#ShellError() != 0
|
if l:err != 0
|
||||||
echo 'A error has occurred. Run this command to see what the problem is:'
|
echom 'A error has occurred. Run this command to see what the problem is:'
|
||||||
echo command
|
echom go#util#Shelljoin(l:cmd)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ function! go#play#Share(count, line1, line2) abort
|
||||||
let @+ = url
|
let @+ = url
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if g:go_play_open_browser != 0
|
if go#config#PlayOpenBrowser()
|
||||||
call go#tool#OpenBrowser(url)
|
call go#util#OpenBrowser(url)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
echo "vim-go: snippet uploaded: ".url
|
echo "vim-go: snippet uploaded: ".url
|
||||||
|
@ -70,4 +70,8 @@ function! s:get_visual_selection() abort
|
||||||
return join(lines, "\n")
|
return join(lines, "\n")
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,26 +1,14 @@
|
||||||
if !exists("g:go_gorename_bin")
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let g:go_gorename_bin = "gorename"
|
let s:cpo_save = &cpo
|
||||||
endif
|
set cpo&vim
|
||||||
|
|
||||||
" 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("<cword>") =~# "^[A-Z]"' .
|
|
||||||
\ '? go#util#pascalcase(expand("<cword>"))' .
|
|
||||||
\ ': go#util#camelcase(expand("<cword>"))'
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
call s:default()
|
|
||||||
|
|
||||||
function! go#rename#Rename(bang, ...) abort
|
function! go#rename#Rename(bang, ...) abort
|
||||||
call s:default()
|
|
||||||
|
|
||||||
let to_identifier = ""
|
let to_identifier = ""
|
||||||
if a:0 == 0
|
if a:0 == 0
|
||||||
let ask = printf("vim-go: rename '%s' to: ", expand("<cword>"))
|
let ask = printf("vim-go: rename '%s' to: ", expand("<cword>"))
|
||||||
if g:go_gorename_prefill != ''
|
let prefill = go#config#GorenamePrefill()
|
||||||
let to_identifier = input(ask, eval(g:go_gorename_prefill))
|
if prefill != ''
|
||||||
|
let to_identifier = input(ask, eval(prefill))
|
||||||
else
|
else
|
||||||
let to_identifier = input(ask)
|
let to_identifier = input(ask)
|
||||||
endif
|
endif
|
||||||
|
@ -33,7 +21,7 @@ function! go#rename#Rename(bang, ...) abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" return with a warning if the bin doesn't exist
|
" return with a warning if the bin doesn't exist
|
||||||
let bin_path = go#path#CheckBinPath(g:go_gorename_bin)
|
let bin_path = go#path#CheckBinPath(go#config#GorenameBin())
|
||||||
if empty(bin_path)
|
if empty(bin_path)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -41,22 +29,9 @@ function! go#rename#Rename(bang, ...) abort
|
||||||
let fname = expand('%:p')
|
let fname = expand('%:p')
|
||||||
let pos = go#util#OffsetCursor()
|
let pos = go#util#OffsetCursor()
|
||||||
let offset = printf('%s:#%d', fname, pos)
|
let offset = printf('%s:#%d', fname, pos)
|
||||||
|
let cmd = [bin_path, "-offset", offset, "-to", to_identifier, '-tags', go#config#BuildTags()]
|
||||||
" 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()
|
if go#util#has_job()
|
||||||
call go#util#EchoProgress(printf("renaming to '%s' ...", to_identifier))
|
|
||||||
call s:rename_job({
|
call s:rename_job({
|
||||||
\ 'cmd': cmd,
|
\ 'cmd': cmd,
|
||||||
\ 'bang': a:bang,
|
\ 'bang': a:bang,
|
||||||
|
@ -64,71 +39,42 @@ function! go#rename#Rename(bang, ...) abort
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command = join(cmd, " ")
|
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
|
||||||
let out = go#tool#ExecuteInDir(command)
|
call s:parse_errors(l:err, a:bang, split(l:out, '\n'))
|
||||||
|
|
||||||
let splitted = split(out, '\n')
|
|
||||||
call s:parse_errors(go#util#ShellError(), a:bang, splitted)
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function s:rename_job(args)
|
function s:rename_job(args)
|
||||||
let state = {
|
let l:job_opts = {
|
||||||
\ 'exited': 0,
|
\ 'bang': a:args.bang,
|
||||||
\ 'closed': 0,
|
\ 'for': 'GoRename',
|
||||||
\ 'exitval': 0,
|
\ 'statustype': 'gorename',
|
||||||
\ 'messages': [],
|
|
||||||
\ 'status_dir': expand('%:p:h'),
|
|
||||||
\ 'bang': a:args.bang
|
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
function! s:callback(chan, msg) dict
|
" autowrite is not enabled for jobs
|
||||||
call add(self.messages, a:msg)
|
call go#cmd#autowrite()
|
||||||
|
let l:cbs = go#job#Options(l:job_opts)
|
||||||
|
|
||||||
|
" wrap l:cbs.exit_cb in s:exit_cb.
|
||||||
|
let l:cbs.exit_cb = funcref('s:exit_cb', [l:cbs.exit_cb])
|
||||||
|
|
||||||
|
call go#job#Start(a:args.cmd, l:cbs)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:exit_cb(job, exitval) dict
|
function! s:reload_changed() abort
|
||||||
let self.exited = 1
|
" reload all files to reflect the new changes. We explicitly call
|
||||||
let self.exitval = a:exitval
|
" checktime to trigger a reload of all files. See
|
||||||
|
" http://www.mail-archive.com/vim@vim.org/msg05900.html for more info
|
||||||
let status = {
|
" about the autoread bug
|
||||||
\ 'desc': 'last status',
|
let current_autoread = &autoread
|
||||||
\ 'type': "gorename",
|
set autoread
|
||||||
\ 'state': "finished",
|
silent! checktime
|
||||||
\ }
|
let &autoread = current_autoread
|
||||||
|
|
||||||
if a:exitval
|
|
||||||
let status.state = "failed"
|
|
||||||
endif
|
|
||||||
|
|
||||||
call go#statusline#Update(self.status_dir, status)
|
|
||||||
|
|
||||||
if self.closed
|
|
||||||
call s:parse_errors(self.exitval, self.bang, self.messages)
|
|
||||||
endif
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:close_cb(ch) dict
|
" s:exit_cb reloads any changed buffers and then calls next.
|
||||||
let self.closed = 1
|
function! s:exit_cb(next, job, exitval) abort
|
||||||
|
call s:reload_changed()
|
||||||
if self.exited
|
call call(a:next, [a:job, a:exitval])
|
||||||
call s:parse_errors(self.exitval, self.bang, self.messages)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" explicitly bind the callbacks to state so that self within them always
|
|
||||||
" refers to state. See :help Partial for more information.
|
|
||||||
let start_options = {
|
|
||||||
\ 'callback': funcref("s:callback", [], state),
|
|
||||||
\ 'exit_cb': funcref("s:exit_cb", [], state),
|
|
||||||
\ 'close_cb': funcref("s:close_cb", [], state),
|
|
||||||
\ }
|
|
||||||
|
|
||||||
call go#statusline#Update(state.status_dir, {
|
|
||||||
\ 'desc': "current status",
|
|
||||||
\ 'type': "gorename",
|
|
||||||
\ 'state': "started",
|
|
||||||
\})
|
|
||||||
|
|
||||||
call job_start(a:args.cmd, start_options)
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function s:parse_errors(exit_val, bang, out)
|
function s:parse_errors(exit_val, bang, out)
|
||||||
|
@ -143,8 +89,7 @@ function s:parse_errors(exit_val, bang, out)
|
||||||
|
|
||||||
let l:listtype = go#list#Type("GoRename")
|
let l:listtype = go#list#Type("GoRename")
|
||||||
if a:exit_val != 0
|
if a:exit_val != 0
|
||||||
call go#util#EchoError("FAILED")
|
let errors = go#util#ParseErrors(a:out)
|
||||||
let errors = go#tool#ParseErrors(a:out)
|
|
||||||
call go#list#Populate(l:listtype, errors, 'Rename')
|
call go#list#Populate(l:listtype, errors, 'Rename')
|
||||||
call go#list#Window(l:listtype, len(errors))
|
call go#list#Window(l:listtype, len(errors))
|
||||||
if !empty(errors) && !a:bang
|
if !empty(errors) && !a:bang
|
||||||
|
@ -163,9 +108,6 @@ function s:parse_errors(exit_val, bang, out)
|
||||||
call go#util#EchoSuccess(a:out[0])
|
call go#util#EchoSuccess(a:out[0])
|
||||||
|
|
||||||
" refresh the buffer so we can see the new content
|
" 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"
|
silent execute ":e"
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -178,4 +120,8 @@ function! go#rename#Complete(lead, cmdline, cursor)
|
||||||
\ 'strpart(v:val, 0, len(a:lead)) == a:lead')
|
\ 'strpart(v:val, 0, len(a:lead)) == a:lead')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
" Statusline
|
" Statusline
|
||||||
""""""""""""""""""""""""""""""""
|
""""""""""""""""""""""""""""""""
|
||||||
|
|
||||||
|
@ -26,7 +30,7 @@ function! go#statusline#Show() abort
|
||||||
" lazy initialiation of the cleaner
|
" lazy initialiation of the cleaner
|
||||||
if !s:timer_id
|
if !s:timer_id
|
||||||
" clean every 60 seconds all statuses
|
" clean every 60 seconds all statuses
|
||||||
let interval = get(g:, 'go_statusline_duration', 60000)
|
let interval = go#config#StatuslineDuration()
|
||||||
let s:timer_id = timer_start(interval, function('go#statusline#Clear'), {'repeat': -1})
|
let s:timer_id = timer_start(interval, function('go#statusline#Clear'), {'repeat': -1})
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -54,11 +58,11 @@ function! go#statusline#Show() abort
|
||||||
" only update highlight if status has changed.
|
" only update highlight if status has changed.
|
||||||
if status_text != s:last_status
|
if status_text != s:last_status
|
||||||
if status.state =~ "success" || status.state =~ "finished" || status.state =~ "pass"
|
if status.state =~ "success" || status.state =~ "finished" || status.state =~ "pass"
|
||||||
hi goStatusLineColor cterm=bold ctermbg=76 ctermfg=22
|
hi goStatusLineColor cterm=bold ctermbg=76 ctermfg=22 guibg=#5fd700 guifg=#005f00
|
||||||
elseif status.state =~ "started" || status.state =~ "analysing" || status.state =~ "compiling"
|
elseif status.state =~ "started" || status.state =~ "analysing" || status.state =~ "compiling"
|
||||||
hi goStatusLineColor cterm=bold ctermbg=208 ctermfg=88
|
hi goStatusLineColor cterm=bold ctermbg=208 ctermfg=88 guibg=#ff8700 guifg=#870000
|
||||||
elseif status.state =~ "failed"
|
elseif status.state =~ "failed"
|
||||||
hi goStatusLineColor cterm=bold ctermbg=196 ctermfg=52
|
hi goStatusLineColor cterm=bold ctermbg=196 ctermfg=52 guibg=#ff0000 guifg=#5f0000
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -109,4 +113,8 @@ function! go#statusline#Clear(timer_id) abort
|
||||||
exe 'let &ro = &ro'
|
exe 'let &ro = &ro'
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
" mapped to :GoAddTags
|
" mapped to :GoAddTags
|
||||||
function! go#tags#Add(start, end, count, ...) abort
|
function! go#tags#Add(start, end, count, ...) abort
|
||||||
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
||||||
|
@ -31,24 +35,22 @@ function! go#tags#run(start, end, offset, mode, fname, test_mode, ...) abort
|
||||||
let args["modified"] = 1
|
let args["modified"] = 1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let result = s:create_cmd(args)
|
let l:result = s:create_cmd(args)
|
||||||
if has_key(result, 'err')
|
if has_key(result, 'err')
|
||||||
call go#util#EchoError(result.err)
|
call go#util#EchoError(result.err)
|
||||||
return -1
|
return -1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command = join(result.cmd, " ")
|
|
||||||
|
|
||||||
if &modified
|
if &modified
|
||||||
let filename = expand("%:p:gs!\\!/!")
|
let filename = expand("%:p:gs!\\!/!")
|
||||||
let content = join(go#util#GetLines(), "\n")
|
let content = join(go#util#GetLines(), "\n")
|
||||||
let in = filename . "\n" . strlen(content) . "\n" . content
|
let in = filename . "\n" . strlen(content) . "\n" . content
|
||||||
let out = go#util#System(command, in)
|
let [l:out, l:err] = go#util#Exec(l:result.cmd, in)
|
||||||
else
|
else
|
||||||
let out = go#util#System(command)
|
let [l:out, l:err] = go#util#Exec(l:result.cmd)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if go#util#ShellError() != 0
|
if l:err != 0
|
||||||
call go#util#EchoError(out)
|
call go#util#EchoError(out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -115,19 +117,18 @@ func s:create_cmd(args) abort
|
||||||
if empty(bin_path)
|
if empty(bin_path)
|
||||||
return {'err': "gomodifytags does not exist"}
|
return {'err': "gomodifytags does not exist"}
|
||||||
endif
|
endif
|
||||||
let bin_path = go#util#Shellescape(bin_path)
|
|
||||||
|
|
||||||
let l:start = a:args.start
|
let l:start = a:args.start
|
||||||
let l:end = a:args.end
|
let l:end = a:args.end
|
||||||
let l:offset = a:args.offset
|
let l:offset = a:args.offset
|
||||||
let l:mode = a:args.mode
|
let l:mode = a:args.mode
|
||||||
let l:cmd_args = a:args.cmd_args
|
let l:cmd_args = a:args.cmd_args
|
||||||
let l:modifytags_transform = get(g:, 'go_addtags_transform', "snakecase")
|
let l:modifytags_transform = go#config#AddtagsTransform()
|
||||||
|
|
||||||
" start constructing the command
|
" start constructing the command
|
||||||
let cmd = [bin_path]
|
let cmd = [bin_path]
|
||||||
call extend(cmd, ["-format", "json"])
|
call extend(cmd, ["-format", "json"])
|
||||||
call extend(cmd, ["-file", go#util#Shellescape(a:args.fname)])
|
call extend(cmd, ["-file", a:args.fname])
|
||||||
call extend(cmd, ["-transform", l:modifytags_transform])
|
call extend(cmd, ["-transform", l:modifytags_transform])
|
||||||
|
|
||||||
if has_key(a:args, "modified")
|
if has_key(a:args, "modified")
|
||||||
|
@ -162,10 +163,6 @@ func s:create_cmd(args) abort
|
||||||
endfor
|
endfor
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" construct options
|
|
||||||
if !empty(l:options)
|
|
||||||
call extend(cmd, ["-add-options", join(l:options, ",")])
|
|
||||||
else
|
|
||||||
" default value
|
" default value
|
||||||
if empty(l:tags)
|
if empty(l:tags)
|
||||||
let l:tags = ["json"]
|
let l:tags = ["json"]
|
||||||
|
@ -173,6 +170,10 @@ func s:create_cmd(args) abort
|
||||||
|
|
||||||
" construct tags
|
" construct tags
|
||||||
call extend(cmd, ["-add-tags", join(l:tags, ",")])
|
call extend(cmd, ["-add-tags", join(l:tags, ",")])
|
||||||
|
|
||||||
|
" construct options
|
||||||
|
if !empty(l:options)
|
||||||
|
call extend(cmd, ["-add-options", join(l:options, ",")])
|
||||||
endif
|
endif
|
||||||
elseif l:mode == "remove"
|
elseif l:mode == "remove"
|
||||||
if empty(l:cmd_args)
|
if empty(l:cmd_args)
|
||||||
|
@ -211,4 +212,8 @@ func s:create_cmd(args) abort
|
||||||
return {'cmd': cmd}
|
return {'cmd': cmd}
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
func! Test_add_tags() abort
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
func! TestAddTags() abort
|
||||||
try
|
try
|
||||||
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
|
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
|
||||||
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1)
|
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1)
|
||||||
|
@ -9,6 +13,28 @@ func! Test_add_tags() abort
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
|
||||||
|
func! TestAddTags_WithOptions() abort
|
||||||
|
try
|
||||||
|
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
|
||||||
|
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1, 'json,omitempty')
|
||||||
|
call gotest#assert_fixture('tags/add_all_golden_options.go')
|
||||||
|
finally
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! TestAddTags_AddOptions() abort
|
||||||
|
try
|
||||||
|
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
|
||||||
|
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1, 'json')
|
||||||
|
call gotest#assert_fixture('tags/add_all_golden.go')
|
||||||
|
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1, 'json,omitempty')
|
||||||
|
call gotest#assert_fixture('tags/add_all_golden_options.go')
|
||||||
|
finally
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
func! Test_remove_tags() abort
|
func! Test_remove_tags() abort
|
||||||
try
|
try
|
||||||
let l:tmp = gotest#load_fixture('tags/remove_all_input.go')
|
let l:tmp = gotest#load_fixture('tags/remove_all_input.go')
|
||||||
|
@ -19,4 +45,8 @@ func! Test_remove_tags() abort
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim:ts=2:sts=2:sw=2:et
|
" vim:ts=2:sts=2:sw=2:et
|
||||||
|
|
|
@ -1,53 +1,56 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
let s:current_file = expand("<sfile>")
|
let s:current_file = expand("<sfile>")
|
||||||
|
|
||||||
function! go#template#create() abort
|
function! go#template#create() abort
|
||||||
let l:go_template_use_pkg = get(g:, 'go_template_use_pkg', 0)
|
let l:go_template_use_pkg = go#config#TemplateUsePkg()
|
||||||
let l:root_dir = fnamemodify(s:current_file, ':h:h:h')
|
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()
|
let l:package_name = go#tool#PackageName()
|
||||||
endif
|
|
||||||
|
|
||||||
" if we can't figure out any package name(no Go files or non Go package
|
" if we can't figure out any package name (i.e. no Go files in the directory)
|
||||||
" files) from the directory create the template or use the cwd
|
" from the directory create the template or use the directory as the name.
|
||||||
" as the name
|
if l:package_name == -1
|
||||||
if l:package_name == -1 && l:go_template_use_pkg != 1
|
if l:go_template_use_pkg == 1
|
||||||
let l:filename = fnamemodify(expand("%"), ':t')
|
let l:path = fnamemodify(expand('%:p:h'), ':t')
|
||||||
if l:filename =~ "_test.go$"
|
let l:content = printf("package %s", l:path)
|
||||||
let l:template_file = get(g:, 'go_template_test_file', "hello_world_test.go")
|
call append(0, l:content)
|
||||||
else
|
else
|
||||||
let l:template_file = get(g:, 'go_template_file', "hello_world.go")
|
let l:filename = expand('%:t')
|
||||||
|
if l:filename =~ "_test.go$"
|
||||||
|
let l:template_file = go#config#TemplateTestFile()
|
||||||
|
else
|
||||||
|
let l:template_file = go#config#TemplateFile()
|
||||||
endif
|
endif
|
||||||
let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file)
|
let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file)
|
||||||
silent exe 'keepalt 0r ' . fnameescape(l:template_path)
|
silent exe 'keepalt 0r ' . fnameescape(l:template_path)
|
||||||
elseif l:package_name == -1 && l:go_template_use_pkg == 1
|
endif
|
||||||
" 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
|
else
|
||||||
let l:content = printf("package %s", l:package_name)
|
let l:content = printf("package %s", l:package_name)
|
||||||
call append(0, l:content)
|
call append(0, l:content)
|
||||||
endif
|
endif
|
||||||
|
" checking that the last line is empty shouldn't be necessary, but for some
|
||||||
|
" reason the last line isn't the expected empty line when run via tests.
|
||||||
|
if getline('$') is ''
|
||||||
$delete _
|
$delete _
|
||||||
|
endif
|
||||||
execute cd . fnameescape(dir)
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#template#ToggleAutoCreate() abort
|
function! go#template#ToggleAutoCreate() abort
|
||||||
if get(g:, "go_template_autocreate", 1)
|
if go#config#TemplateAutocreate()
|
||||||
let g:go_template_autocreate = 0
|
call go#config#SetTemplateAutocreate(0)
|
||||||
call go#util#EchoProgress("auto template create disabled")
|
call go#util#EchoProgress("auto template create disabled")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
let g:go_template_autocreate = 1
|
call go#config#SetTemplateAutocreate(1)
|
||||||
call go#util#EchoProgress("auto template create enabled")
|
call go#util#EchoProgress("auto template create enabled")
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
62
pack/acp/start/vim-go/autoload/go/template_test.vim
Normal file
62
pack/acp/start/vim-go/autoload/go/template_test.vim
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
func! Test_TemplateCreate() abort
|
||||||
|
try
|
||||||
|
let l:tmp = gotest#write_file('foo/empty.txt', [''])
|
||||||
|
|
||||||
|
edit foo/bar.go
|
||||||
|
|
||||||
|
call gotest#assert_buffer(1, [
|
||||||
|
\ 'func main() {',
|
||||||
|
\ '\tfmt.Println("vim-go")',
|
||||||
|
\ '}'])
|
||||||
|
finally
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
endtry
|
||||||
|
|
||||||
|
try
|
||||||
|
let l:tmp = gotest#write_file('foo/empty.txt', [''])
|
||||||
|
edit foo/bar_test.go
|
||||||
|
|
||||||
|
call gotest#assert_buffer(1, [
|
||||||
|
\ 'func TestHelloWorld(t *testing.T) {',
|
||||||
|
\ '\t// t.Fatal("not implemented")',
|
||||||
|
\ '}'])
|
||||||
|
finally
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_TemplateCreate_UsePkg() abort
|
||||||
|
try
|
||||||
|
let l:tmp = gotest#write_file('foo/empty.txt', [''])
|
||||||
|
|
||||||
|
let g:go_template_use_pkg = 1
|
||||||
|
edit foo/bar.go
|
||||||
|
|
||||||
|
call gotest#assert_buffer(0, ['package foo'])
|
||||||
|
finally
|
||||||
|
unlet g:go_template_use_pkg
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_TemplateCreate_PackageExists() abort
|
||||||
|
try
|
||||||
|
let l:tmp = gotest#write_file('quux/quux.go', ['package foo'])
|
||||||
|
|
||||||
|
edit quux/bar.go
|
||||||
|
|
||||||
|
call gotest#assert_buffer(0, ['package foo'])
|
||||||
|
finally
|
||||||
|
call delete(l:tmp, 'rf')
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -1,34 +1,36 @@
|
||||||
if has('nvim') && !exists("g:go_term_mode")
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let g:go_term_mode = 'vsplit'
|
let s:cpo_save = &cpo
|
||||||
endif
|
set cpo&vim
|
||||||
|
|
||||||
" new creates a new terminal with the given command. Mode is set based on the
|
" 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
|
" global variable g:go_term_mode, which is by default set to :vsplit
|
||||||
function! go#term#new(bang, cmd) abort
|
function! go#term#new(bang, cmd, errorformat) abort
|
||||||
return go#term#newmode(a:bang, a:cmd, g:go_term_mode)
|
return go#term#newmode(a:bang, a:cmd, a:errorformat, go#config#TermMode())
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" new creates a new terminal with the given command and window mode.
|
" go#term#newmode creates a new terminal with the given command and window mode.
|
||||||
function! go#term#newmode(bang, cmd, mode) abort
|
function! go#term#newmode(bang, cmd, errorformat, mode) abort
|
||||||
let mode = a:mode
|
let l:mode = a:mode
|
||||||
if empty(mode)
|
if empty(l:mode)
|
||||||
let mode = g:go_term_mode
|
let l:mode = go#config#TermMode()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let state = {
|
let l:state = {
|
||||||
\ 'cmd': a:cmd,
|
\ 'cmd': a:cmd,
|
||||||
\ 'bang' : a:bang,
|
\ 'bang' : a:bang,
|
||||||
\ 'winid': win_getid(winnr()),
|
\ 'winid': win_getid(winnr()),
|
||||||
\ 'stdout': []
|
\ 'stdout': [],
|
||||||
|
\ 'stdout_buf': '',
|
||||||
|
\ 'errorformat': a:errorformat,
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
" execute go build in the files directory
|
" execute go build in the files directory
|
||||||
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||||
let dir = getcwd()
|
let l:dir = getcwd()
|
||||||
|
|
||||||
execute cd . fnameescape(expand("%:p:h"))
|
execute l:cd . fnameescape(expand("%:p:h"))
|
||||||
|
|
||||||
execute mode.' __go_term__'
|
execute l:mode . ' __go_term__'
|
||||||
|
|
||||||
setlocal filetype=goterm
|
setlocal filetype=goterm
|
||||||
setlocal bufhidden=delete
|
setlocal bufhidden=delete
|
||||||
|
@ -41,79 +43,107 @@ function! go#term#newmode(bang, cmd, mode) abort
|
||||||
"
|
"
|
||||||
" Don't set an on_stderr, because it will be passed the same data as
|
" Don't set an on_stderr, because it will be passed the same data as
|
||||||
" on_stdout. See https://github.com/neovim/neovim/issues/2836
|
" on_stdout. See https://github.com/neovim/neovim/issues/2836
|
||||||
let job = {
|
let l:job = {
|
||||||
\ 'on_stdout': function('s:on_stdout', [], state),
|
\ 'on_stdout': function('s:on_stdout', [], state),
|
||||||
\ 'on_exit' : function('s:on_exit', [], state),
|
\ 'on_exit' : function('s:on_exit', [], state),
|
||||||
\ }
|
\ }
|
||||||
|
|
||||||
let state.id = termopen(a:cmd, job)
|
let l:state.id = termopen(a:cmd, l:job)
|
||||||
let state.termwinid = win_getid(winnr())
|
let l:state.termwinid = win_getid(winnr())
|
||||||
|
|
||||||
execute cd . fnameescape(dir)
|
execute l:cd . fnameescape(l:dir)
|
||||||
|
|
||||||
" resize new term if needed.
|
" resize new term if needed.
|
||||||
let height = get(g:, 'go_term_height', winheight(0))
|
let l:height = go#config#TermHeight()
|
||||||
let width = get(g:, 'go_term_width', winwidth(0))
|
let l:width = go#config#TermWidth()
|
||||||
|
|
||||||
" Adjust the window width or height depending on whether it's a vertical or
|
" Adjust the window width or height depending on whether it's a vertical or
|
||||||
" horizontal split.
|
" horizontal split.
|
||||||
if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew"
|
if l:mode =~ "vertical" || l:mode =~ "vsplit" || l:mode =~ "vnew"
|
||||||
exe 'vertical resize ' . width
|
exe 'vertical resize ' . l:width
|
||||||
elseif mode =~ "split" || mode =~ "new"
|
elseif mode =~ "split" || mode =~ "new"
|
||||||
exe 'resize ' . height
|
exe 'resize ' . l:height
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" we also need to resize the pty, so there you go...
|
" we also need to resize the pty, so there you go...
|
||||||
call jobresize(state.id, width, height)
|
call jobresize(l:state.id, l:width, l:height)
|
||||||
|
|
||||||
call win_gotoid(state.winid)
|
call win_gotoid(l:state.winid)
|
||||||
|
|
||||||
return state.id
|
return l:state.id
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:on_stdout(job_id, data, event) dict abort
|
function! s:on_stdout(job_id, data, event) dict abort
|
||||||
call extend(self.stdout, a:data)
|
" A single empty string means EOF was reached. The first item will never be
|
||||||
|
" the empty string except for when it's the only item and is signaling that
|
||||||
|
" EOF was reached.
|
||||||
|
if len(a:data) == 1 && a:data[0] == ''
|
||||||
|
" when there's nothing buffered, return early so that an
|
||||||
|
" erroneous message will not be added.
|
||||||
|
if self.stdout_buf == ''
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
let self.stdout = add(self.stdout, self.stdout_buf)
|
||||||
|
else
|
||||||
|
let l:data = copy(a:data)
|
||||||
|
let l:data[0] = self.stdout_buf . l:data[0]
|
||||||
|
|
||||||
|
" The last element may be a partial line; save it for next time.
|
||||||
|
let self.stdout_buf = l:data[-1]
|
||||||
|
let self.stdout = extend(self.stdout, l:data[:-2])
|
||||||
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:on_exit(job_id, exit_status, event) dict abort
|
function! s:on_exit(job_id, exit_status, event) dict abort
|
||||||
|
let l:winid = win_getid(winnr())
|
||||||
|
call win_gotoid(self.winid)
|
||||||
let l:listtype = go#list#Type("_term")
|
let l:listtype = go#list#Type("_term")
|
||||||
|
|
||||||
" usually there is always output so never branch into this clause
|
if a:exit_status == 0
|
||||||
if empty(self.stdout)
|
call go#list#Clean(l:listtype)
|
||||||
call s:cleanlist(self.winid, l:listtype)
|
call win_gotoid(l:winid)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let errors = go#tool#ParseErrors(self.stdout)
|
|
||||||
let errors = go#tool#FilterValids(errors)
|
|
||||||
|
|
||||||
if !empty(errors)
|
|
||||||
" close terminal; we don't need it anymore
|
|
||||||
call win_gotoid(self.termwinid)
|
|
||||||
close
|
|
||||||
|
|
||||||
call win_gotoid(self.winid)
|
call win_gotoid(self.winid)
|
||||||
|
|
||||||
call go#list#Populate(l:listtype, errors, self.cmd)
|
let l:title = self.cmd
|
||||||
call go#list#Window(l:listtype, len(errors))
|
if type(l:title) == v:t_list
|
||||||
if !self.bang
|
let l:title = join(self.cmd)
|
||||||
call go#list#JumpToFirst(l:listtype)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
let l:i = 0
|
||||||
|
while l:i < len(self.stdout)
|
||||||
|
let self.stdout[l:i] = substitute(self.stdout[l:i], "\r$", '', 'g')
|
||||||
|
let l:i += 1
|
||||||
|
endwhile
|
||||||
|
|
||||||
|
call go#list#ParseFormat(l:listtype, self.errorformat, self.stdout, l:title)
|
||||||
|
let l:errors = go#list#Get(l:listtype)
|
||||||
|
call go#list#Window(l:listtype, len(l:errors))
|
||||||
|
|
||||||
|
if empty(l:errors)
|
||||||
|
call go#util#EchoError( '[' . l:title . '] ' . "FAIL")
|
||||||
|
call win_gotoid(l:winid)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call s:cleanlist(self.winid, l:listtype)
|
" close terminal; we don't need it anymore
|
||||||
|
call win_gotoid(self.termwinid)
|
||||||
|
close!
|
||||||
|
|
||||||
|
if self.bang
|
||||||
|
call win_gotoid(l:winid)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
call win_gotoid(self.winid)
|
||||||
|
call go#list#JumpToFirst(l:listtype)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:cleanlist(winid, listtype) abort
|
" restore Vi compatibility settings
|
||||||
" There are no errors. Clean and close the list. Jump to the window to which
|
let &cpo = s:cpo_save
|
||||||
" the location list is attached, close the list, and then jump back to the
|
unlet s:cpo_save
|
||||||
" current window.
|
|
||||||
let winid = win_getid(winnr())
|
|
||||||
call win_gotoid(a:winid)
|
|
||||||
call go#list#Clean(a:listtype)
|
|
||||||
call win_gotoid(l:winid)
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_GoTermNewMode()
|
func! Test_GoTermNewMode()
|
||||||
if !has('nvim')
|
if !has('nvim')
|
||||||
return
|
return
|
||||||
|
@ -13,7 +17,7 @@ func! Test_GoTermNewMode()
|
||||||
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
|
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
|
||||||
|
|
||||||
set nosplitright
|
set nosplitright
|
||||||
call go#term#newmode(0, cmd, '')
|
call go#term#new(0, cmd, &errorformat)
|
||||||
let actual = expand('%:p')
|
let actual = expand('%:p')
|
||||||
call assert_equal(actual, l:expected)
|
call assert_equal(actual, l:expected)
|
||||||
|
|
||||||
|
@ -37,7 +41,7 @@ func! Test_GoTermNewMode_SplitRight()
|
||||||
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
|
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
|
||||||
|
|
||||||
set splitright
|
set splitright
|
||||||
call go#term#newmode(0, cmd, '')
|
call go#term#new(0, cmd, &errorformat)
|
||||||
let actual = expand('%:p')
|
let actual = expand('%:p')
|
||||||
call assert_equal(actual, l:expected)
|
call assert_equal(actual, l:expected)
|
||||||
|
|
||||||
|
@ -47,4 +51,8 @@ func! Test_GoTermNewMode_SplitRight()
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package complete
|
||||||
|
|
||||||
|
type T struct {
|
||||||
|
V string
|
||||||
|
}
|
||||||
|
|
||||||
|
func Example(s string) {
|
||||||
|
Example("")
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("vim-go"
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("vim-go")
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
../imports/
|
|
|
@ -0,0 +1 @@
|
||||||
|
../imports/
|
|
@ -0,0 +1,3 @@
|
||||||
|
module package
|
||||||
|
|
||||||
|
go 1.12
|
|
@ -0,0 +1,7 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Println("vim-go")
|
||||||
|
}
|
0
pack/acp/start/vim-go/autoload/go/test-fixtures/package/src/package/vendor/foo/.gitkeep
vendored
Normal file
0
pack/acp/start/vim-go/autoload/go/test-fixtures/package/src/package/vendor/foo/.gitkeep
vendored
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
ID int `json:"id,omitempty"`
|
||||||
|
MyHomeAddress string `json:"my_home_address,omitempty"`
|
||||||
|
SubDomains []string `json:"sub_domains,omitempty"`
|
||||||
|
Empty string `json:"empty,omitempty"`
|
||||||
|
Example int64 `json:"example,omitempty"`
|
||||||
|
Example2 string `json:"example_2,omitempty"`
|
||||||
|
Bar struct {
|
||||||
|
Four string `json:"four,omitempty"`
|
||||||
|
Five string `json:"five,omitempty"`
|
||||||
|
} `json:"bar,omitempty"`
|
||||||
|
Lala interface{} `json:"lala,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSomething(t *testing.T) {
|
||||||
|
r := struct{}{}
|
||||||
|
ioutil.ReadAll(r)
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fmt.Errorf("%v")
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
" Test runs `go test` in the current directory. If compile is true, it'll
|
" 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
|
" 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.
|
" test files). Any other argument is appended to the final `go test` command.
|
||||||
function! go#test#Test(bang, compile, ...) abort
|
function! go#test#Test(bang, compile, ...) abort
|
||||||
let args = ["test"]
|
let args = ["test", '-tags', go#config#BuildTags()]
|
||||||
|
|
||||||
" don't run the test, only compile it. Useful to capture and fix errors.
|
" don't run the test, only compile it. Useful to capture and fix errors.
|
||||||
if a:compile
|
if a:compile
|
||||||
|
@ -10,11 +14,6 @@ function! go#test#Test(bang, compile, ...) abort
|
||||||
call extend(args, ["-c", "-o", testfile])
|
call extend(args, ["-c", "-o", testfile])
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if exists('g:go_build_tags')
|
|
||||||
let tags = get(g:, 'go_build_tags')
|
|
||||||
call extend(args, ["-tags", tags])
|
|
||||||
endif
|
|
||||||
|
|
||||||
if a:0
|
if a:0
|
||||||
let goargs = a:000
|
let goargs = a:000
|
||||||
|
|
||||||
|
@ -24,18 +23,35 @@ function! go#test#Test(bang, compile, ...) abort
|
||||||
let goargs = map(copy(a:000), "expand(v:val)")
|
let goargs = map(copy(a:000), "expand(v:val)")
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if !(has('nvim') || go#util#has_job())
|
|
||||||
let goargs = go#util#Shelllist(goargs, 1)
|
|
||||||
endif
|
|
||||||
|
|
||||||
call extend(args, goargs, 1)
|
call extend(args, goargs, 1)
|
||||||
else
|
else
|
||||||
" only add this if no custom flags are passed
|
" only add this if no custom flags are passed
|
||||||
let timeout = get(g:, 'go_test_timeout', '10s')
|
let timeout = go#config#TestTimeout()
|
||||||
call add(args, printf("-timeout=%s", timeout))
|
call add(args, printf("-timeout=%s", timeout))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(g:, 'go_echo_command_info', 1)
|
if go#config#TermEnabled()
|
||||||
|
call go#term#new(a:bang, ["go"] + args, s:errorformat())
|
||||||
|
endif
|
||||||
|
|
||||||
|
if go#util#has_job()
|
||||||
|
" use vim's job functionality to call it asynchronously
|
||||||
|
let job_options = {
|
||||||
|
\ 'bang': a:bang,
|
||||||
|
\ 'for': 'GoTest',
|
||||||
|
\ 'statustype': 'test',
|
||||||
|
\ 'errorformat': s:errorformat(),
|
||||||
|
\ }
|
||||||
|
|
||||||
|
if a:compile
|
||||||
|
let job_options.statustype = 'compile ' . job_options.statustype
|
||||||
|
endif
|
||||||
|
|
||||||
|
call s:test_job(['go'] + args, job_options)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
if go#config#EchoCommandInfo()
|
||||||
if a:compile
|
if a:compile
|
||||||
call go#util#EchoProgress("compiling tests ...")
|
call go#util#EchoProgress("compiling tests ...")
|
||||||
else
|
else
|
||||||
|
@ -43,35 +59,12 @@ function! go#test#Test(bang, compile, ...) abort
|
||||||
endif
|
endif
|
||||||
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()
|
call go#cmd#autowrite()
|
||||||
redraw
|
redraw
|
||||||
|
|
||||||
let command = "go " . join(args, ' ')
|
let l:cmd = ['go'] + l:args
|
||||||
let out = go#tool#ExecuteInDir(command)
|
|
||||||
|
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
|
||||||
" TODO(bc): When the output is JSON, the JSON should be run through a
|
" 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.
|
" filter to produce lines that are more easily described by errorformat.
|
||||||
|
|
||||||
|
@ -81,17 +74,19 @@ function! go#test#Test(bang, compile, ...) abort
|
||||||
let dir = getcwd()
|
let dir = getcwd()
|
||||||
execute cd fnameescape(expand("%:p:h"))
|
execute cd fnameescape(expand("%:p:h"))
|
||||||
|
|
||||||
if go#util#ShellError() != 0
|
if l:err != 0
|
||||||
call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), command)
|
let l:winid = win_getid(winnr())
|
||||||
|
call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), l:cmd)
|
||||||
let errors = go#list#Get(l:listtype)
|
let errors = go#list#Get(l:listtype)
|
||||||
call go#list#Window(l:listtype, len(errors))
|
call go#list#Window(l:listtype, len(errors))
|
||||||
if !empty(errors) && !a:bang
|
if empty(errors)
|
||||||
call go#list#JumpToFirst(l:listtype)
|
|
||||||
elseif empty(errors)
|
|
||||||
" failed to parse errors, output the original content
|
" failed to parse errors, output the original content
|
||||||
call go#util#EchoError(out)
|
call go#util#EchoError(out)
|
||||||
|
elseif a:bang
|
||||||
|
call win_gotoid(l:winid)
|
||||||
|
else
|
||||||
|
call go#list#JumpToFirst(l:listtype)
|
||||||
endif
|
endif
|
||||||
call go#util#EchoError("[test] FAIL")
|
|
||||||
else
|
else
|
||||||
call go#list#Clean(l:listtype)
|
call go#list#Clean(l:listtype)
|
||||||
|
|
||||||
|
@ -130,154 +125,20 @@ function! go#test#Func(bang, ...) abort
|
||||||
call extend(args, a:000)
|
call extend(args, a:000)
|
||||||
else
|
else
|
||||||
" only add this if no custom flags are passed
|
" only add this if no custom flags are passed
|
||||||
let timeout = get(g:, 'go_test_timeout', '10s')
|
let timeout = go#config#TestTimeout()
|
||||||
call add(args, printf("-timeout=%s", timeout))
|
call add(args, printf("-timeout=%s", timeout))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
call call('go#test#Test', args)
|
call call('go#test#Test', args)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:test_job(args) abort
|
function! s:test_job(cmd, args) abort
|
||||||
let status = {
|
|
||||||
\ 'desc': 'current status',
|
|
||||||
\ 'type': "test",
|
|
||||||
\ 'state': "started",
|
|
||||||
\ }
|
|
||||||
|
|
||||||
if a:args.compile_test
|
|
||||||
let status.state = "compiling"
|
|
||||||
endif
|
|
||||||
|
|
||||||
" autowrite is not enabled for jobs
|
" autowrite is not enabled for jobs
|
||||||
call go#cmd#autowrite()
|
call go#cmd#autowrite()
|
||||||
|
|
||||||
let state = {
|
call go#job#Spawn(a:cmd, a:args)
|
||||||
\ 'exited': 0,
|
|
||||||
\ 'closed': 0,
|
|
||||||
\ 'exitval': 0,
|
|
||||||
\ 'messages': [],
|
|
||||||
\ 'args': a:args,
|
|
||||||
\ 'compile_test': a:args.compile_test,
|
|
||||||
\ 'status_dir': expand('%:p:h'),
|
|
||||||
\ 'started_at': reltime()
|
|
||||||
\ }
|
|
||||||
|
|
||||||
call go#statusline#Update(state.status_dir, status)
|
|
||||||
|
|
||||||
function! s:callback(chan, msg) dict
|
|
||||||
call add(self.messages, a:msg)
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! s:exit_cb(job, exitval) dict
|
|
||||||
let self.exited = 1
|
|
||||||
let self.exitval = a:exitval
|
|
||||||
|
|
||||||
let status = {
|
|
||||||
\ 'desc': 'last status',
|
|
||||||
\ 'type': "test",
|
|
||||||
\ 'state': "pass",
|
|
||||||
\ }
|
|
||||||
|
|
||||||
if self.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 self.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(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)
|
|
||||||
|
|
||||||
if self.closed
|
|
||||||
call s:show_errors(self.args, self.exitval, self.messages)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! s:close_cb(ch) dict
|
|
||||||
let self.closed = 1
|
|
||||||
|
|
||||||
if self.exited
|
|
||||||
call s:show_errors(self.args, self.exitval, self.messages)
|
|
||||||
endif
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" explicitly bind the callbacks to state so that self within them always
|
|
||||||
" refers to state. See :help Partial for more information.
|
|
||||||
let start_options = {
|
|
||||||
\ 'callback': funcref("s:callback", [], state),
|
|
||||||
\ 'exit_cb': funcref("s:exit_cb", [], state),
|
|
||||||
\ 'close_cb': funcref("s:close_cb", [], state)
|
|
||||||
\ }
|
|
||||||
|
|
||||||
" 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")
|
|
||||||
if a:exit_val == 0
|
|
||||||
call go#list#Clean(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.
|
|
||||||
|
|
||||||
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 = ""
|
let s:efm = ""
|
||||||
let s:go_test_show_name = 0
|
let s:go_test_show_name = 0
|
||||||
|
|
||||||
|
@ -288,7 +149,7 @@ function! s:errorformat() abort
|
||||||
" https://github.com/golang/go/issues/2981.
|
" https://github.com/golang/go/issues/2981.
|
||||||
let goroot = go#util#goroot()
|
let goroot = go#util#goroot()
|
||||||
|
|
||||||
let show_name=get(g:, 'go_test_show_name', 0)
|
let show_name = go#config#TestShowName()
|
||||||
if s:efm != "" && s:go_test_show_name == show_name
|
if s:efm != "" && s:go_test_show_name == show_name
|
||||||
return s:efm
|
return s:efm
|
||||||
endif
|
endif
|
||||||
|
@ -297,15 +158,12 @@ function! s:errorformat() abort
|
||||||
" each level of test indents the test output 4 spaces. Capturing groups
|
" each level of test indents the test output 4 spaces. Capturing groups
|
||||||
" (e.g. \(\)) cannot be used in an errorformat, but non-capturing groups can
|
" (e.g. \(\)) cannot be used in an errorformat, but non-capturing groups can
|
||||||
" (e.g. \%(\)).
|
" (e.g. \%(\)).
|
||||||
let indent = '%\\%( %\\)%#'
|
let indent = '%\\%( %\\)'
|
||||||
|
|
||||||
" match compiler errors
|
|
||||||
let format = "%f:%l:%c: %m"
|
|
||||||
|
|
||||||
" ignore `go test -v` output for starting tests
|
" ignore `go test -v` output for starting tests
|
||||||
let format .= ",%-G=== RUN %.%#"
|
let format = "%-G=== RUN %.%#"
|
||||||
" ignore `go test -v` output for passing tests
|
" ignore `go test -v` output for passing tests
|
||||||
let format .= ",%-G" . indent . "--- PASS: %.%#"
|
let format .= ",%-G" . indent . "%#--- PASS: %.%#"
|
||||||
|
|
||||||
" Match failure lines.
|
" Match failure lines.
|
||||||
"
|
"
|
||||||
|
@ -315,24 +173,25 @@ function! s:errorformat() abort
|
||||||
" e.g.:
|
" e.g.:
|
||||||
" '--- FAIL: TestSomething (0.00s)'
|
" '--- FAIL: TestSomething (0.00s)'
|
||||||
if show_name
|
if show_name
|
||||||
let format .= ",%G" . indent . "--- FAIL: %m (%.%#)"
|
let format .= ",%G" . indent . "%#--- FAIL: %m (%.%#)"
|
||||||
else
|
else
|
||||||
let format .= ",%-G" . indent . "--- FAIL: %.%#"
|
let format .= ",%-G" . indent . "%#--- FAIL: %.%#"
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" Go 1.10 test output {{{1
|
||||||
" Matches test output lines.
|
" Matches test output lines.
|
||||||
"
|
"
|
||||||
" All test output lines start with the test indentation and a tab, followed
|
" 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
|
" by the filename, a colon, the line number, another colon, a space, and the
|
||||||
" message. e.g.:
|
" message. e.g.:
|
||||||
" '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
|
" '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
|
||||||
let format .= ",%A" . indent . "%\\t%\\+%f:%l: %m"
|
let format .= ",%A" . indent . "%#%\\t%\\+%f:%l: %m"
|
||||||
" also match lines that don't have a message (i.e. the message begins with a
|
" also match lines that don't have a message (i.e. the message begins with a
|
||||||
" newline or is the empty string):
|
" newline or is the empty string):
|
||||||
" e.g.:
|
" e.g.:
|
||||||
" t.Errorf("\ngot %v; want %v", actual, expected)
|
" t.Errorf("\ngot %v; want %v", actual, expected)
|
||||||
" t.Error("")
|
" t.Error("")
|
||||||
let format .= ",%A" . indent . "%\\t%\\+%f:%l: "
|
let format .= ",%A" . indent . "%#%\\t%\\+%f:%l: "
|
||||||
|
|
||||||
" Match the 2nd and later lines of multi-line output. These lines are
|
" 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,
|
" indented the number of spaces for the level of nesting of the test,
|
||||||
|
@ -345,7 +204,17 @@ function! s:errorformat() abort
|
||||||
" indicate that they're multiple lines of output, but in that case the lines
|
" 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
|
" get concatenated in the quickfix list, which is not what users typically
|
||||||
" want when writing a newline into their test output.
|
" want when writing a newline into their test output.
|
||||||
let format .= ",%G" . indent . "%\\t%\\{2}%m"
|
let format .= ",%G" . indent . "%#%\\t%\\{2}%m"
|
||||||
|
" }}}1
|
||||||
|
|
||||||
|
" Go 1.11 test output {{{1
|
||||||
|
" Match test output lines similarly to Go 1.10 test output lines, but they
|
||||||
|
" use an indent level where the Go 1.10 test output uses tabs, so they'll
|
||||||
|
" always have at least one level indentation...
|
||||||
|
let format .= ",%A" . indent . "%\\+%f:%l: %m"
|
||||||
|
let format .= ",%A" . indent . "%\\+%f:%l: "
|
||||||
|
let format .= ",%G" . indent . "%\\{2\\,}%m"
|
||||||
|
" }}}1
|
||||||
|
|
||||||
" set the format for panics.
|
" set the format for panics.
|
||||||
|
|
||||||
|
@ -375,7 +244,7 @@ function! s:errorformat() abort
|
||||||
" e.g.:
|
" e.g.:
|
||||||
" '\t/usr/local/go/src/time.go:1313 +0x5d'
|
" '\t/usr/local/go/src/time.go:1313 +0x5d'
|
||||||
|
|
||||||
" panicaddress, and readyaddress are identical except for
|
" panicaddress and readyaddress are identical except for
|
||||||
" panicaddress sets the filename and line number.
|
" panicaddress sets the filename and line number.
|
||||||
let panicaddress = "%\\t%f:%l +0x%[0-9A-Fa-f]%\\+"
|
let panicaddress = "%\\t%f:%l +0x%[0-9A-Fa-f]%\\+"
|
||||||
let readyaddress = "%\\t%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
|
let readyaddress = "%\\t%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
|
||||||
|
@ -398,6 +267,11 @@ function! s:errorformat() abort
|
||||||
" the running goroutine's stack.
|
" the running goroutine's stack.
|
||||||
let format .= ",%Z" . panicaddress
|
let format .= ",%Z" . panicaddress
|
||||||
|
|
||||||
|
" Match and ignore errors from runtime.goparkunlock(). These started
|
||||||
|
" appearing in stack traces from Go 1.12 test timeouts.
|
||||||
|
let format .= ",%-Gruntime.goparkunlock(%.%#"
|
||||||
|
let format .= ",%-G%\\t" . goroot . "%\\f%\\+:%\\d%\\+"
|
||||||
|
|
||||||
" Match and ignore panic address without being part of a multi-line message.
|
" 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
|
" This is to catch those lines that come after the top most non-standard
|
||||||
" library line in stack traces.
|
" library line in stack traces.
|
||||||
|
@ -409,12 +283,26 @@ function! s:errorformat() abort
|
||||||
let format .= ",%-Cexit status %[0-9]%\\+"
|
let format .= ",%-Cexit status %[0-9]%\\+"
|
||||||
"let format .= ",exit status %[0-9]%\\+"
|
"let format .= ",exit status %[0-9]%\\+"
|
||||||
|
|
||||||
" Match and ignore exit failure lines whether part of a multi-line message
|
" Match and ignore failure lines whether part of a multi-line message
|
||||||
" or not, because these lines sometimes come before and sometimes after
|
" or not, because these lines sometimes come before and sometimes after
|
||||||
" panic stacktraces.
|
" panic stacktraces.
|
||||||
let format .= ",%-CFAIL%\\t%.%#"
|
let format .= ",%-CFAIL%\\t%.%#"
|
||||||
"let format .= ",FAIL%\\t%.%#"
|
"let format .= ",FAIL%\\t%.%#"
|
||||||
|
|
||||||
|
" match compiler errors.
|
||||||
|
" These are very smilar to errors from <=go1.10 test output, but lack
|
||||||
|
" leading tabs for the first line of an error, and subsequent lines only
|
||||||
|
" have one tab instead of two.
|
||||||
|
let format .= ",%A%f:%l:%c: %m"
|
||||||
|
let format .= ",%A%f:%l: %m"
|
||||||
|
" It would be nice if this weren't necessary, but panic lines from tests are
|
||||||
|
" prefixed with a single leading tab, making them very similar to 2nd and
|
||||||
|
" later lines of a multi-line compiler error. Swallow it so that it doesn't
|
||||||
|
" cause a quickfix entry since the next %G entry can add a quickfix entry
|
||||||
|
" for 2nd and later lines of a multi-line compiler error.
|
||||||
|
let format .= ",%-C%\\tpanic: %.%#"
|
||||||
|
let format .= ",%G%\\t%m"
|
||||||
|
|
||||||
" Match and ignore everything else in multi-line messages.
|
" Match and ignore everything else in multi-line messages.
|
||||||
let format .= ",%-C%.%#"
|
let format .= ",%-C%.%#"
|
||||||
" Match and ignore everything else not in a multi-line message:
|
" Match and ignore everything else not in a multi-line message:
|
||||||
|
@ -425,4 +313,8 @@ function! s:errorformat() abort
|
||||||
return s:efm
|
return s:efm
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_GoTest() abort
|
func! Test_GoTest() abort
|
||||||
let expected = [
|
let expected = [
|
||||||
\ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'},
|
\ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'},
|
||||||
|
@ -62,26 +66,33 @@ endfunc
|
||||||
func! Test_GoTestShowName() abort
|
func! Test_GoTestShowName() abort
|
||||||
let expected = [
|
let expected = [
|
||||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld'},
|
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld'},
|
||||||
\ {'lnum': 6, 'bufnr': 9, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'so long'},
|
\ {'lnum': 6, 'bufnr': 7, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'so long'},
|
||||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld/sub'},
|
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld/sub'},
|
||||||
\ {'lnum': 9, 'bufnr': 9, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
|
\ {'lnum': 9, 'bufnr': 7, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
|
||||||
\ ]
|
\ ]
|
||||||
|
|
||||||
let g:go_test_show_name=1
|
let g:go_test_show_name=1
|
||||||
call s:test('showname/showname_test.go', expected)
|
call s:test('showname/showname_test.go', expected)
|
||||||
let g:go_test_show_name=0
|
unlet g:go_test_show_name
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_GoTestVet() abort
|
||||||
|
let expected = [
|
||||||
|
\ {'lnum': 6, 'bufnr': 10, 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'Errorf format %v reads arg #1, but call has 0 args'},
|
||||||
|
\ ]
|
||||||
|
call s:test('veterror/veterror.go', expected)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func! Test_GoTestTestCompilerError() abort
|
||||||
|
let expected = [
|
||||||
|
\ {'lnum': 10, 'bufnr': 8, 'col': 16, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'cannot use r (type struct {}) as type io.Reader in argument to ioutil.ReadAll:'},
|
||||||
|
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'struct {} does not implement io.Reader (missing Read method)'}
|
||||||
|
\ ]
|
||||||
|
|
||||||
|
call s:test('testcompilerror/testcompilerror_test.go', expected)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
func! s:test(file, expected, ...) abort
|
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(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test'
|
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test'
|
||||||
silent exe 'e ' . $GOPATH . '/src/' . a:file
|
silent exe 'e ' . $GOPATH . '/src/' . a:file
|
||||||
|
|
||||||
|
@ -94,7 +105,7 @@ func! s:test(file, expected, ...) abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" run the tests
|
" run the tests
|
||||||
call call(function('go#test#Test'), args)
|
silent call call(function('go#test#Test'), args)
|
||||||
|
|
||||||
let actual = getqflist()
|
let actual = getqflist()
|
||||||
let start = reltime()
|
let start = reltime()
|
||||||
|
@ -118,4 +129,8 @@ func! s:normalize_durations(str) abort
|
||||||
return substitute(a:str, '[0-9]\+\(\.[0-9]\+\)\?s', '0.000s', 'g')
|
return substitute(a:str, '[0-9]\+\(\.[0-9]\+\)\?s', '0.000s', 'g')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,48 +1,132 @@
|
||||||
if !exists("g:go_textobj_enabled")
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
let g:go_textobj_enabled = 1
|
let s:cpo_save = &cpo
|
||||||
endif
|
set cpo&vim
|
||||||
|
|
||||||
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
|
||||||
" { } motions
|
" { } motions
|
||||||
" s for sentence
|
" s for sentence
|
||||||
" p for parapgrah
|
" p for paragraph
|
||||||
" < >
|
" < >
|
||||||
" t for tag
|
" t for tag
|
||||||
|
|
||||||
" Select a function in visual mode.
|
function! go#textobj#Comment(mode) abort
|
||||||
function! go#textobj#Function(mode) abort
|
let l:fname = expand('%:p')
|
||||||
let offset = go#util#OffsetCursor()
|
|
||||||
|
|
||||||
let fname = shellescape(expand("%:p"))
|
try
|
||||||
if &modified
|
if &modified
|
||||||
" Write current unsaved buffer to a temp file and use the modified content
|
|
||||||
let l:tmpname = tempname()
|
let l:tmpname = tempname()
|
||||||
call writefile(go#util#GetLines(), l:tmpname)
|
call writefile(go#util#GetLines(), l:tmpname)
|
||||||
let fname = l:tmpname
|
let l:fname = l:tmpname
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let bin_path = go#path#CheckBinPath('motion')
|
let l:cmd = ['motion',
|
||||||
if empty(bin_path)
|
\ '-format', 'json',
|
||||||
|
\ '-file', l:fname,
|
||||||
|
\ '-offset', go#util#OffsetCursor(),
|
||||||
|
\ '-mode', 'comment',
|
||||||
|
\ ]
|
||||||
|
|
||||||
|
let [l:out, l:err] = go#util#Exec(l:cmd)
|
||||||
|
if l:err
|
||||||
|
call go#util#EchoError(l:out)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
finally
|
||||||
|
if exists("l:tmpname")
|
||||||
|
call delete(l:tmpname)
|
||||||
|
endif
|
||||||
|
endtry
|
||||||
|
|
||||||
|
let l:result = json_decode(l:out)
|
||||||
|
if type(l:result) != 4 || !has_key(l:result, 'comment')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command = printf("%s -format vim -file %s -offset %s", bin_path, fname, offset)
|
let l:info = l:result.comment
|
||||||
let command .= " -mode enclosing"
|
call cursor(l:info.startLine, l:info.startCol)
|
||||||
|
|
||||||
if g:go_textobj_include_function_doc
|
" Adjust cursor to exclude start comment markers. Try to be a little bit
|
||||||
let command .= " -parse-comments"
|
" clever when using multi-line '/*' markers.
|
||||||
|
if a:mode is# 'i'
|
||||||
|
" trim whitespace so matching below works correctly
|
||||||
|
let l:line = substitute(getline('.'), '^\s*\(.\{-}\)\s*$', '\1', '')
|
||||||
|
|
||||||
|
" //text
|
||||||
|
if l:line[:2] is# '// '
|
||||||
|
call cursor(l:info.startLine, l:info.startCol+3)
|
||||||
|
" // text
|
||||||
|
elseif l:line[:1] is# '//'
|
||||||
|
call cursor(l:info.startLine, l:info.startCol+2)
|
||||||
|
" /*
|
||||||
|
" text
|
||||||
|
elseif l:line =~# '^/\* *$'
|
||||||
|
call cursor(l:info.startLine+1, 0)
|
||||||
|
" /*
|
||||||
|
" * text
|
||||||
|
if getline('.')[:2] is# ' * '
|
||||||
|
call cursor(l:info.startLine+1, 4)
|
||||||
|
" /*
|
||||||
|
" *text
|
||||||
|
elseif getline('.')[:1] is# ' *'
|
||||||
|
call cursor(l:info.startLine+1, 3)
|
||||||
|
endif
|
||||||
|
" /* text
|
||||||
|
elseif l:line[:2] is# '/* '
|
||||||
|
call cursor(l:info.startLine, l:info.startCol+3)
|
||||||
|
" /*text
|
||||||
|
elseif l:line[:1] is# '/*'
|
||||||
|
call cursor(l:info.startLine, l:info.startCol+2)
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let out = go#util#System(command)
|
normal! v
|
||||||
if go#util#ShellError() != 0
|
|
||||||
|
" Exclude trailing newline.
|
||||||
|
if a:mode is# 'i'
|
||||||
|
let l:info.endCol -= 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
call cursor(l:info.endLine, l:info.endCol)
|
||||||
|
|
||||||
|
" Exclude trailing '*/'.
|
||||||
|
if a:mode is# 'i'
|
||||||
|
let l:line = getline('.')
|
||||||
|
" text
|
||||||
|
" */
|
||||||
|
if l:line =~# '^ *\*/$'
|
||||||
|
call cursor(l:info.endLine - 1, len(getline(l:info.endLine - 1)))
|
||||||
|
" text */
|
||||||
|
elseif l:line[-3:] is# ' */'
|
||||||
|
call cursor(l:info.endLine, l:info.endCol - 3)
|
||||||
|
" text*/
|
||||||
|
elseif l:line[-2:] is# '*/'
|
||||||
|
call cursor(l:info.endLine, l:info.endCol - 2)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Select a function in visual mode.
|
||||||
|
function! go#textobj#Function(mode) abort
|
||||||
|
let l:fname = expand("%:p")
|
||||||
|
if &modified
|
||||||
|
let l:tmpname = tempname()
|
||||||
|
call writefile(go#util#GetLines(), l:tmpname)
|
||||||
|
let l:fname = l:tmpname
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:cmd = ['motion',
|
||||||
|
\ '-format', 'vim',
|
||||||
|
\ '-file', l:fname,
|
||||||
|
\ '-offset', go#util#OffsetCursor(),
|
||||||
|
\ '-mode', 'enclosing',
|
||||||
|
\ ]
|
||||||
|
|
||||||
|
if go#config#TextobjIncludeFunctionDoc()
|
||||||
|
let l:cmd += ['-parse-comments']
|
||||||
|
endif
|
||||||
|
|
||||||
|
let [l:out, l:err] = go#util#Exec(l:cmd)
|
||||||
|
if l:err
|
||||||
call go#util#EchoError(out)
|
call go#util#EchoError(out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -63,9 +147,9 @@ function! go#textobj#Function(mode) abort
|
||||||
if a:mode == 'a'
|
if a:mode == 'a'
|
||||||
" anonymous functions doesn't have associated doc. Also check if the user
|
" anonymous functions doesn't have associated doc. Also check if the user
|
||||||
" want's to include doc comments for function declarations
|
" want's to include doc comments for function declarations
|
||||||
if has_key(info, 'doc') && g:go_textobj_include_function_doc
|
if has_key(info, 'doc') && go#config#TextobjIncludeFunctionDoc()
|
||||||
call cursor(info.doc.line, info.doc.col)
|
call cursor(info.doc.line, info.doc.col)
|
||||||
elseif info['sig']['name'] == '' && g:go_textobj_include_variable
|
elseif info['sig']['name'] == '' && go#config#TextobjIncludeVariable()
|
||||||
" one liner anonymous functions
|
" one liner anonymous functions
|
||||||
if info.lbrace.line == info.rbrace.line
|
if info.lbrace.line == info.rbrace.line
|
||||||
" jump to first nonblack char, to get the correct column
|
" jump to first nonblack char, to get the correct column
|
||||||
|
@ -101,36 +185,28 @@ endfunction
|
||||||
|
|
||||||
" Get the location of the previous or next function.
|
" Get the location of the previous or next function.
|
||||||
function! go#textobj#FunctionLocation(direction, cnt) abort
|
function! go#textobj#FunctionLocation(direction, cnt) abort
|
||||||
let offset = go#util#OffsetCursor()
|
let l:fname = expand("%:p")
|
||||||
|
|
||||||
let fname = shellescape(expand("%:p"))
|
|
||||||
if &modified
|
if &modified
|
||||||
" Write current unsaved buffer to a temp file and use the modified content
|
" Write current unsaved buffer to a temp file and use the modified content
|
||||||
let l:tmpname = tempname()
|
let l:tmpname = tempname()
|
||||||
call writefile(go#util#GetLines(), l:tmpname)
|
call writefile(go#util#GetLines(), l:tmpname)
|
||||||
let fname = l:tmpname
|
let l:fname = l:tmpname
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let bin_path = go#path#CheckBinPath('motion')
|
let l:cmd = ['motion',
|
||||||
if empty(bin_path)
|
\ '-format', 'vim',
|
||||||
return
|
\ '-file', l:fname,
|
||||||
|
\ '-offset', go#util#OffsetCursor(),
|
||||||
|
\ '-shift', a:cnt,
|
||||||
|
\ '-mode', a:direction,
|
||||||
|
\ ]
|
||||||
|
|
||||||
|
if go#config#TextobjIncludeFunctionDoc()
|
||||||
|
let l:cmd += ['-parse-comments']
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let command = printf("%s -format vim -file %s -offset %s", bin_path, fname, offset)
|
let [l:out, l:err] = go#util#Exec(l:cmd)
|
||||||
let command .= ' -shift ' . a:cnt
|
if l:err
|
||||||
|
|
||||||
if a:direction == 'next'
|
|
||||||
let command .= ' -mode next'
|
|
||||||
else " 'prev'
|
|
||||||
let command .= ' -mode prev'
|
|
||||||
endif
|
|
||||||
|
|
||||||
if g:go_textobj_include_function_doc
|
|
||||||
let command .= " -parse-comments"
|
|
||||||
endif
|
|
||||||
|
|
||||||
let out = go#util#System(command)
|
|
||||||
if go#util#ShellError() != 0
|
|
||||||
call go#util#EchoError(out)
|
call go#util#EchoError(out)
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
|
@ -190,7 +266,7 @@ function! go#textobj#FunctionJump(mode, direction) abort
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if a:mode == 'v' && a:direction == 'prev'
|
if a:mode == 'v' && a:direction == 'prev'
|
||||||
if has_key(info, 'doc') && g:go_textobj_include_function_doc
|
if has_key(info, 'doc') && go#config#TextobjIncludeFunctionDoc()
|
||||||
keepjumps call cursor(info.doc.line, 1)
|
keepjumps call cursor(info.doc.line, 1)
|
||||||
else
|
else
|
||||||
keepjumps call cursor(info.func.line, 1)
|
keepjumps call cursor(info.func.line, 1)
|
||||||
|
@ -201,4 +277,8 @@ function! go#textobj#FunctionJump(mode, direction) abort
|
||||||
keepjumps call cursor(info.func.line, 1)
|
keepjumps call cursor(info.func.line, 1)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
" From "go list -h".
|
" From "go list -h".
|
||||||
function! go#tool#ValidFiles(...)
|
function! go#tool#ValidFiles(...)
|
||||||
let l:list = ["GoFiles", "CgoFiles", "IgnoredGoFiles", "CFiles", "CXXFiles",
|
let l:list = ["GoFiles", "CgoFiles", "IgnoredGoFiles", "CFiles", "CXXFiles",
|
||||||
|
@ -36,8 +40,8 @@ function! go#tool#Files(...) abort
|
||||||
endif
|
endif
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
let out = go#tool#ExecuteInDir('go list -f ' . shellescape(combined))
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:combined])
|
||||||
return split(out, '\n')
|
return split(l:out, '\n')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#tool#Deps() abort
|
function! go#tool#Deps() abort
|
||||||
|
@ -46,9 +50,8 @@ function! go#tool#Deps() abort
|
||||||
else
|
else
|
||||||
let format = "{{range $f := .Deps}}{{$f}}\n{{end}}"
|
let format = "{{range $f := .Deps}}{{$f}}\n{{end}}"
|
||||||
endif
|
endif
|
||||||
let command = 'go list -f '.shellescape(format)
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:format])
|
||||||
let out = go#tool#ExecuteInDir(command)
|
return split(l:out, '\n')
|
||||||
return split(out, '\n')
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#tool#Imports() abort
|
function! go#tool#Imports() abort
|
||||||
|
@ -58,188 +61,79 @@ function! go#tool#Imports() abort
|
||||||
else
|
else
|
||||||
let format = "{{range $f := .Imports}}{{$f}}{{printf \"\\n\"}}{{end}}"
|
let format = "{{range $f := .Imports}}{{$f}}{{printf \"\\n\"}}{{end}}"
|
||||||
endif
|
endif
|
||||||
let command = 'go list -f '.shellescape(format)
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', l:format])
|
||||||
let out = go#tool#ExecuteInDir(command)
|
if l:err != 0
|
||||||
if go#util#ShellError() != 0
|
|
||||||
echo out
|
echo out
|
||||||
return imports
|
return imports
|
||||||
endif
|
endif
|
||||||
|
|
||||||
for package_path in split(out, '\n')
|
for package_path in split(out, '\n')
|
||||||
let cmd = "go list -f '{{.Name}}' " . shellescape(package_path)
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', '{{.Name}}', l:package_path])
|
||||||
let package_name = substitute(go#tool#ExecuteInDir(cmd), '\n$', '', '')
|
if l:err != 0
|
||||||
|
echo out
|
||||||
|
return imports
|
||||||
|
endif
|
||||||
|
let package_name = substitute(l:out, '\n$', '', '')
|
||||||
let imports[package_name] = package_path
|
let imports[package_name] = package_path
|
||||||
endfor
|
endfor
|
||||||
|
|
||||||
return imports
|
return imports
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#tool#Info(auto) abort
|
function! go#tool#Info(showstatus) abort
|
||||||
let l:mode = get(g:, 'go_info_mode', 'gocode')
|
let l:mode = go#config#InfoMode()
|
||||||
if l:mode == 'gocode'
|
if l:mode == 'gocode'
|
||||||
call go#complete#Info(a:auto)
|
call go#complete#Info(a:showstatus)
|
||||||
elseif l:mode == 'guru'
|
elseif l:mode == 'guru'
|
||||||
call go#guru#DescribeInfo()
|
call go#guru#DescribeInfo(a:showstatus)
|
||||||
|
elseif l:mode == 'gopls'
|
||||||
|
call go#lsp#Info(a:showstatus)
|
||||||
else
|
else
|
||||||
call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [gocode, guru]')
|
call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [gocode, guru, gopls]')
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#tool#PackageName() abort
|
function! go#tool#PackageName() abort
|
||||||
let command = "go list -f \"{{.Name}}\""
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-tags', go#config#BuildTags(), '-f', '{{.Name}}'])
|
||||||
let out = go#tool#ExecuteInDir(command)
|
if l:err != 0
|
||||||
if go#util#ShellError() != 0
|
|
||||||
return -1
|
return -1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return split(out, '\n')[0]
|
return split(out, '\n')[0]
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
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) 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
|
|
||||||
|
|
||||||
for k in keys(filter(is_readable, '!v:val'))
|
|
||||||
echo "vim-go: " | echohl Identifier | echon "[run] Dropped " | echohl Constant | echon '"' . k . '"'
|
|
||||||
echohl Identifier | echon " from location list (nonvalid filename)" | echohl None
|
|
||||||
endfor
|
|
||||||
|
|
||||||
return filtered
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
function! go#tool#ExecuteInDir(cmd) abort
|
|
||||||
" 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
|
" Exists checks whether the given importpath exists or not. It returns 0 if
|
||||||
" the importpath exists under GOPATH.
|
" the importpath exists under GOPATH.
|
||||||
function! go#tool#Exists(importpath) abort
|
function! go#tool#Exists(importpath) abort
|
||||||
let command = "go list ". a:importpath
|
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', a:importpath])
|
||||||
let out = go#tool#ExecuteInDir(command)
|
if l:err != 0
|
||||||
|
|
||||||
if go#util#ShellError() != 0
|
|
||||||
return -1
|
return -1
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" following two functions are from: https://github.com/mattn/gist-vim
|
function! go#tool#DescribeBalloon()
|
||||||
" thanks @mattn
|
let l:fname = fnamemodify(bufname(v:beval_bufnr), ':p')
|
||||||
function! s:get_browser_command() abort
|
call go#lsp#Hover(l:fname, v:beval_lnum, v:beval_col, funcref('s:balloon', []))
|
||||||
let go_play_browser_command = get(g:, 'go_play_browser_command', '')
|
return ''
|
||||||
if go_play_browser_command == ''
|
|
||||||
if go#util#IsWin()
|
|
||||||
let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%'
|
|
||||||
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
|
|
||||||
endif
|
|
||||||
return go_play_browser_command
|
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#tool#OpenBrowser(url) abort
|
function! s:balloon(msg)
|
||||||
let cmd = s:get_browser_command()
|
let l:msg = a:msg
|
||||||
if len(cmd) == 0
|
if has('balloon_eval')
|
||||||
redraw
|
if has('balloon_multiline')
|
||||||
echohl WarningMsg
|
let l:msg = join(a:msg, "\n")
|
||||||
echo "It seems that you don't have general web browser. Open URL below."
|
|
||||||
echohl None
|
|
||||||
echo a:url
|
|
||||||
return
|
|
||||||
endif
|
|
||||||
if cmd =~ '^!'
|
|
||||||
let cmd = substitute(cmd, '%URL%', '\=escape(shellescape(a:url),"#")', 'g')
|
|
||||||
silent! exec cmd
|
|
||||||
elseif cmd =~ '^:[A-Z]'
|
|
||||||
let cmd = substitute(cmd, '%URL%', '\=escape(a:url,"#")', 'g')
|
|
||||||
exec cmd
|
|
||||||
else
|
else
|
||||||
let cmd = substitute(cmd, '%URL%', '\=shellescape(a:url)', 'g')
|
let l:msg = substitute(join(map(deepcopy(a:msg), 'substitute(v:val, "\t", "", "")'), '; '), '{;', '{', '')
|
||||||
call go#util#System(cmd)
|
|
||||||
endif
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
call balloon_show(l:msg)
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
func! Test_ExecuteInDir() abort
|
func! Test_ExecuteInDir() abort
|
||||||
let l:tmp = gotest#write_file('a/a.go', ['package a'])
|
let l:tmp = gotest#write_file('a/a.go', ['package a'])
|
||||||
try
|
try
|
||||||
let l:out = go#tool#ExecuteInDir("pwd")
|
let l:out = go#util#ExecInDir(['pwd'])
|
||||||
call assert_equal(l:tmp . "/src/a\n", l:out)
|
call assert_equal([l:tmp . "/src/a\n", 0], l:out)
|
||||||
finally
|
finally
|
||||||
call delete(l:tmp, 'rf')
|
call delete(l:tmp, 'rf')
|
||||||
endtry
|
endtry
|
||||||
|
@ -13,11 +17,15 @@ func! Test_ExecuteInDir_nodir() abort
|
||||||
exe ':e ' . l:tmp . '/new-dir/a'
|
exe ':e ' . l:tmp . '/new-dir/a'
|
||||||
|
|
||||||
try
|
try
|
||||||
let l:out = go#tool#ExecuteInDir("pwd")
|
let l:out = go#util#ExecInDir(['pwd'])
|
||||||
call assert_equal('', l:out)
|
call assert_equal(['', 1], l:out)
|
||||||
finally
|
finally
|
||||||
call delete(l:tmp, 'rf')
|
call delete(l:tmp, 'rf')
|
||||||
endtry
|
endtry
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
let s:buf_nr = -1
|
let s:buf_nr = -1
|
||||||
|
|
||||||
"OpenWindow opens a new scratch window and put's the content into the window
|
"OpenWindow opens a new scratch window and put's the content into the window
|
||||||
|
@ -111,4 +115,8 @@ function! go#ui#OpenDefinition(filter) abort
|
||||||
norm! zz
|
norm! zz
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
38
pack/acp/start/vim-go/autoload/go/uri.vim
Normal file
38
pack/acp/start/vim-go/autoload/go/uri.vim
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
function! go#uri#Encode(value) abort
|
||||||
|
return s:encode(a:value, '[^A-Za-z0-9_.~-]')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#uri#EncodePath(value) abort
|
||||||
|
let l:separator = '/'
|
||||||
|
if go#util#IsWin()
|
||||||
|
let l:separator = '\\'
|
||||||
|
endif
|
||||||
|
return s:encode(a:value, '[^' . l:separator . 'A-Za-z0-9_.~-]')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:encode(value, unreserved)
|
||||||
|
return substitute(
|
||||||
|
\ a:value,
|
||||||
|
\ a:unreserved,
|
||||||
|
\ '\="%".printf(''%02X'', char2nr(submatch(0)))',
|
||||||
|
\ 'g'
|
||||||
|
\)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#uri#Decode(value) abort
|
||||||
|
return substitute(
|
||||||
|
\ a:value,
|
||||||
|
\ '%\(\x\x\)',
|
||||||
|
\ '\=nr2char(''0X'' . submatch(1))',
|
||||||
|
\ 'g'
|
||||||
|
\)
|
||||||
|
endfunction
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
" PathSep returns the appropriate OS specific path separator.
|
" PathSep returns the appropriate OS specific path separator.
|
||||||
function! go#util#PathSep() abort
|
function! go#util#PathSep() abort
|
||||||
if go#util#IsWin()
|
if go#util#IsWin()
|
||||||
|
@ -48,7 +52,7 @@ function! go#util#IsMac() abort
|
||||||
return has('mac') ||
|
return has('mac') ||
|
||||||
\ has('macunix') ||
|
\ has('macunix') ||
|
||||||
\ has('gui_macvim') ||
|
\ has('gui_macvim') ||
|
||||||
\ go#util#System('uname') =~? '^darwin'
|
\ go#util#Exec(['uname'])[0] =~? '^darwin'
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Checks if using:
|
" Checks if using:
|
||||||
|
@ -59,7 +63,20 @@ function! go#util#IsUsingCygwinShell()
|
||||||
return go#util#IsWin() && executable('cygpath') && &shell =~ '.*sh.*'
|
return go#util#IsWin() && executable('cygpath') && &shell =~ '.*sh.*'
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function! go#util#has_job() abort
|
" Check if Vim jobs API is supported.
|
||||||
|
"
|
||||||
|
" The (optional) first parameter can be added to indicate the 'cwd' or 'env'
|
||||||
|
" parameters will be used, which wasn't added until a later version.
|
||||||
|
function! go#util#has_job(...) abort
|
||||||
|
if has('nvim')
|
||||||
|
return 1
|
||||||
|
endif
|
||||||
|
|
||||||
|
" cwd and env parameters to job_start was added in this version.
|
||||||
|
if a:0 > 0 && a:1 is 1
|
||||||
|
return has('job') && has("patch-8.0.0902")
|
||||||
|
endif
|
||||||
|
|
||||||
" job was introduced in 7.4.xxx however there are multiple bug fixes and one
|
" 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.
|
" of the latest is 8.0.0087 which is required for a stable async API.
|
||||||
return has('job') && has("patch-8.0.0087")
|
return has('job') && has("patch-8.0.0087")
|
||||||
|
@ -93,27 +110,34 @@ endfunction
|
||||||
" goarch returns 'go env GOARCH'. This is an internal function and shouldn't
|
" goarch returns 'go env GOARCH'. This is an internal function and shouldn't
|
||||||
" be used. Instead use 'go#util#env("goarch")'
|
" be used. Instead use 'go#util#env("goarch")'
|
||||||
function! go#util#goarch() abort
|
function! go#util#goarch() abort
|
||||||
return substitute(go#util#System('go env GOARCH'), '\n', '', 'g')
|
return substitute(s:exec(['go', 'env', 'GOARCH'])[0], '\n', '', 'g')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" goos returns 'go env GOOS'. This is an internal function and shouldn't
|
" goos returns 'go env GOOS'. This is an internal function and shouldn't
|
||||||
" be used. Instead use 'go#util#env("goos")'
|
" be used. Instead use 'go#util#env("goos")'
|
||||||
function! go#util#goos() abort
|
function! go#util#goos() abort
|
||||||
return substitute(go#util#System('go env GOOS'), '\n', '', 'g')
|
return substitute(s:exec(['go', 'env', 'GOOS'])[0], '\n', '', 'g')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" goroot returns 'go env GOROOT'. This is an internal function and shouldn't
|
" goroot returns 'go env GOROOT'. This is an internal function and shouldn't
|
||||||
" be used. Instead use 'go#util#env("goroot")'
|
" be used. Instead use 'go#util#env("goroot")'
|
||||||
function! go#util#goroot() abort
|
function! go#util#goroot() abort
|
||||||
return substitute(go#util#System('go env GOROOT'), '\n', '', 'g')
|
return substitute(s:exec(['go', 'env', 'GOROOT'])[0], '\n', '', 'g')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" gopath returns 'go env GOPATH'. This is an internal function and shouldn't
|
" gopath returns 'go env GOPATH'. This is an internal function and shouldn't
|
||||||
" be used. Instead use 'go#util#env("gopath")'
|
" be used. Instead use 'go#util#env("gopath")'
|
||||||
function! go#util#gopath() abort
|
function! go#util#gopath() abort
|
||||||
return substitute(go#util#System('go env GOPATH'), '\n', '', 'g')
|
return substitute(s:exec(['go', 'env', 'GOPATH'])[0], '\n', '', 'g')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" gomod returns 'go env GOMOD'. gomod changes depending on the folder. Don't
|
||||||
|
" use go#util#env as it caches the value.
|
||||||
|
function! go#util#gomod() abort
|
||||||
|
return substitute(s:exec(['go', 'env', 'GOMOD'])[0], '\n', '', 'g')
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
function! go#util#osarch() abort
|
function! go#util#osarch() abort
|
||||||
return go#util#env("goos") . '_' . go#util#env("goarch")
|
return go#util#env("goos") . '_' . go#util#env("goarch")
|
||||||
endfunction
|
endfunction
|
||||||
|
@ -124,12 +148,13 @@ endfunction
|
||||||
" so that we always use a standard POSIX-compatible Bourne shell (and not e.g.
|
" so that we always use a standard POSIX-compatible Bourne shell (and not e.g.
|
||||||
" csh, fish, etc.) See #988 and #1276.
|
" csh, fish, etc.) See #988 and #1276.
|
||||||
function! s:system(cmd, ...) abort
|
function! s:system(cmd, ...) abort
|
||||||
" Preserve original shell and shellredir values
|
" Preserve original shell, shellredir and shellcmdflag values
|
||||||
let l:shell = &shell
|
let l:shell = &shell
|
||||||
let l:shellredir = &shellredir
|
let l:shellredir = &shellredir
|
||||||
|
let l:shellcmdflag = &shellcmdflag
|
||||||
|
|
||||||
if !go#util#IsWin() && executable('/bin/sh')
|
if !go#util#IsWin() && executable('/bin/sh')
|
||||||
set shell=/bin/sh shellredir=>%s\ 2>&1
|
set shell=/bin/sh shellredir=>%s\ 2>&1 shellcmdflag=-c
|
||||||
endif
|
endif
|
||||||
|
|
||||||
try
|
try
|
||||||
|
@ -138,6 +163,7 @@ function! s:system(cmd, ...) abort
|
||||||
" Restore original values
|
" Restore original values
|
||||||
let &shell = l:shell
|
let &shell = l:shell
|
||||||
let &shellredir = l:shellredir
|
let &shellredir = l:shellredir
|
||||||
|
let &shellcmdflag = l:shellcmdflag
|
||||||
endtry
|
endtry
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -153,16 +179,47 @@ endfunction
|
||||||
function! go#util#Exec(cmd, ...) abort
|
function! go#util#Exec(cmd, ...) abort
|
||||||
if len(a:cmd) == 0
|
if len(a:cmd) == 0
|
||||||
call go#util#EchoError("go#util#Exec() called with empty a:cmd")
|
call go#util#EchoError("go#util#Exec() called with empty a:cmd")
|
||||||
return
|
return ['', 1]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
let l:bin = a:cmd[0]
|
||||||
|
|
||||||
|
" Lookup the full path, respecting settings such as 'go_bin_path'. On errors,
|
||||||
" CheckBinPath will show a warning for us.
|
" CheckBinPath will show a warning for us.
|
||||||
let l:bin = go#path#CheckBinPath(a:cmd[0])
|
let l:bin = go#path#CheckBinPath(l:bin)
|
||||||
if empty(l:bin)
|
if empty(l:bin)
|
||||||
return ["", 1]
|
return ['', 1]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:out = call('s:system', [go#util#Shelljoin([l:bin] + a:cmd[1:])] + a:000)
|
" Finally execute the command using the full, resolved path. Do not pass the
|
||||||
|
" unmodified command as the correct program might not exist in $PATH.
|
||||||
|
return call('s:exec', [[l:bin] + a:cmd[1:]] + a:000)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#util#ExecInDir(cmd, ...) abort
|
||||||
|
if !isdirectory(expand("%:p:h"))
|
||||||
|
return ['', 1]
|
||||||
|
endif
|
||||||
|
|
||||||
|
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||||
|
let dir = getcwd()
|
||||||
|
try
|
||||||
|
execute cd . fnameescape(expand("%:p:h"))
|
||||||
|
let [l:out, l:err] = call('go#util#Exec', [a:cmd] + a:000)
|
||||||
|
finally
|
||||||
|
execute cd . fnameescape(l:dir)
|
||||||
|
endtry
|
||||||
|
return [l:out, l:err]
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! s:exec(cmd, ...) abort
|
||||||
|
let l:bin = a:cmd[0]
|
||||||
|
let l:cmd = go#util#Shelljoin([l:bin] + a:cmd[1:])
|
||||||
|
if go#util#HasDebug('shell-commands')
|
||||||
|
call go#util#EchoInfo('shell command: ' . l:cmd)
|
||||||
|
endif
|
||||||
|
|
||||||
|
let l:out = call('s:system', [l:cmd] + a:000)
|
||||||
return [l:out, go#util#ShellError()]
|
return [l:out, go#util#ShellError()]
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
@ -259,7 +316,7 @@ endfunction
|
||||||
" snippetcase converts the given word to given preferred snippet setting type
|
" snippetcase converts the given word to given preferred snippet setting type
|
||||||
" case.
|
" case.
|
||||||
function! go#util#snippetcase(word) abort
|
function! go#util#snippetcase(word) abort
|
||||||
let l:snippet_case = get(g:, 'go_addtags_transform', "snakecase")
|
let l:snippet_case = go#config#AddtagsTransform()
|
||||||
if l:snippet_case == "snakecase"
|
if l:snippet_case == "snakecase"
|
||||||
return go#util#snakecase(a:word)
|
return go#util#snakecase(a:word)
|
||||||
elseif l:snippet_case == "camelcase"
|
elseif l:snippet_case == "camelcase"
|
||||||
|
@ -397,7 +454,73 @@ endfunction
|
||||||
|
|
||||||
" Report if the user enabled a debug flag in g:go_debug.
|
" Report if the user enabled a debug flag in g:go_debug.
|
||||||
function! go#util#HasDebug(flag)
|
function! go#util#HasDebug(flag)
|
||||||
return index(get(g:, 'go_debug', []), a:flag) >= 0
|
return index(go#config#Debug(), a:flag) >= 0
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
function! go#util#OpenBrowser(url) abort
|
||||||
|
let l:cmd = go#config#PlayBrowserCommand()
|
||||||
|
if len(l:cmd) == 0
|
||||||
|
redraw
|
||||||
|
echohl WarningMsg
|
||||||
|
echo "It seems that you don't have general web browser. Open URL below."
|
||||||
|
echohl None
|
||||||
|
echo a:url
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
" if setting starts with a !.
|
||||||
|
if l:cmd =~ '^!'
|
||||||
|
let l:cmd = substitute(l:cmd, '%URL%', '\=escape(shellescape(a:url), "#")', 'g')
|
||||||
|
silent! exec l:cmd
|
||||||
|
elseif cmd =~ '^:[A-Z]'
|
||||||
|
let l:cmd = substitute(l:cmd, '%URL%', '\=escape(a:url,"#")', 'g')
|
||||||
|
exec l:cmd
|
||||||
|
else
|
||||||
|
let l:cmd = substitute(l:cmd, '%URL%', '\=shellescape(a:url)', 'g')
|
||||||
|
call go#util#System(l:cmd)
|
||||||
|
endif
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#util#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": substitute(line, '\r$', '', '')})
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
|
return errors
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function! go#util#ShowInfo(info)
|
||||||
|
if empty(a:info)
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
|
||||||
|
echo "vim-go: " | echohl Function | echon a:info | echohl None
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
" Write a Go file to a temporary directory and append this directory to $GOPATH.
|
" 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,
|
" The file will written to a:path, which is relative to the temporary directory,
|
||||||
|
@ -127,4 +131,8 @@ func! gotest#assert_quickfix(got, want) abort
|
||||||
endwhile
|
endwhile
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -28,7 +28,7 @@ function! s:source.gather_candidates(args, context) abort
|
||||||
return []
|
return []
|
||||||
endif
|
endif
|
||||||
|
|
||||||
let l:include = get(g:, 'go_decls_includes', 'func,type')
|
let l:include = go#config#DeclsIncludes()
|
||||||
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:command = printf('%s -format vim -mode decls -include %s -%s %s', l:bin_path, l:include, l:mode, shellescape(l:path))
|
||||||
let l:candidates = []
|
let l:candidates = []
|
||||||
try
|
try
|
||||||
|
|
|
@ -9,6 +9,10 @@ if exists("g:current_compiler")
|
||||||
endif
|
endif
|
||||||
let g:current_compiler = "go"
|
let g:current_compiler = "go"
|
||||||
|
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
if exists(":CompilerSet") != 2
|
if exists(":CompilerSet") != 2
|
||||||
command -nargs=* CompilerSet setlocal <args>
|
command -nargs=* CompilerSet setlocal <args>
|
||||||
endif
|
endif
|
||||||
|
@ -38,4 +42,8 @@ CompilerSet errorformat+=%-G%.%# " All lines not matching a
|
||||||
let &cpo = s:save_cpo
|
let &cpo = s:save_cpo
|
||||||
unlet s:save_cpo
|
unlet s:save_cpo
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -38,11 +38,11 @@ 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.
|
tools developed by the Go community to provide a seamless Vim experience.
|
||||||
|
|
||||||
* Compile your package with |:GoBuild|, install it with |:GoInstall| or
|
* Compile your package with |:GoBuild|, install it with |:GoInstall| or
|
||||||
test it with |:GoTest|. Run a single tests with |:GoTestFunc|).
|
test it with |:GoTest|. Run a single test with |:GoTestFunc|).
|
||||||
* Quickly execute your current file(s) with |:GoRun|.
|
* Quickly execute your current file(s) with |:GoRun|.
|
||||||
* Improved syntax highlighting and folding.
|
* Improved syntax highlighting and folding.
|
||||||
* Debug programs with integrated `delve` support with |:GoDebugStart|.
|
* Debug programs with integrated `delve` support with |:GoDebugStart|.
|
||||||
* Completion support via `gocode`.
|
* Completion support via `gocode` and `gopls`.
|
||||||
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
|
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
|
||||||
* Go to symbol/declaration with |:GoDef|.
|
* Go to symbol/declaration with |:GoDef|.
|
||||||
* Look up documentation with |:GoDoc| or |:GoDocBrowser|.
|
* Look up documentation with |:GoDoc| or |:GoDocBrowser|.
|
||||||
|
@ -76,6 +76,12 @@ tools developed by the Go community to provide a seamless Vim experience.
|
||||||
==============================================================================
|
==============================================================================
|
||||||
INSTALL *go-install*
|
INSTALL *go-install*
|
||||||
|
|
||||||
|
vim-go requires at least Vim 7.4.2009 or Neovim 0.3.1. On macOS, if you are
|
||||||
|
still using your system version of vim, you can use homebrew to keep your
|
||||||
|
version of Vim up-to-date with the following terminal command:
|
||||||
|
>
|
||||||
|
brew install vim
|
||||||
|
|
||||||
The latest stable release, https://github.com/fatih/vim-go/releases/latest, is
|
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
|
the recommended version to use. If you choose to use the master branch
|
||||||
instead, please do so with caution; it is a _development_ branch.
|
instead, please do so with caution; it is a _development_ branch.
|
||||||
|
@ -106,12 +112,13 @@ manager's install command.
|
||||||
<
|
<
|
||||||
* https://github.com/gmarik/vundle >
|
* https://github.com/gmarik/vundle >
|
||||||
|
|
||||||
Plugin 'fatih/vim-go'
|
Plugin 'fatih/vim-go', { 'do': ':GoUpdateBinaries' }
|
||||||
<
|
<
|
||||||
* Manual (not recommended) >
|
* Manual (not recommended) >
|
||||||
|
|
||||||
Copy all of the files into your `~/.vim` directory
|
Copy all of the files into your `~/.vim` directory
|
||||||
<
|
<
|
||||||
|
|
||||||
You will also need to install all the necessary binaries. vim-go makes it easy
|
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
|
to install all of them by providing a command, |:GoInstallBinaries|, to
|
||||||
`go get` all the required binaries. The binaries will be installed to $GOBIN
|
`go get` all the required binaries. The binaries will be installed to $GOBIN
|
||||||
|
@ -131,8 +138,12 @@ The following plugins are supported for use with vim-go:
|
||||||
https://github.com/Shougo/neocomplete.vim
|
https://github.com/Shougo/neocomplete.vim
|
||||||
|
|
||||||
* Real-time completion (Neovim and Vim 8):
|
* Real-time completion (Neovim and Vim 8):
|
||||||
https://github.com/Shougo/deoplete.nvim and
|
https://github.com/Shougo/deoplete.nvim
|
||||||
https://github.com/zchee/deoplete-go
|
|
||||||
|
Add the following line to your vimrc. This instructs deoplete to use omni
|
||||||
|
completion for Go files.
|
||||||
|
|
||||||
|
call deoplete#custom#option('omni_patterns', { 'go': '[^. *\t]\.\w*' })
|
||||||
|
|
||||||
* Display source code navigation in a sidebar:
|
* Display source code navigation in a sidebar:
|
||||||
https://github.com/majutsushi/tagbar
|
https://github.com/majutsushi/tagbar
|
||||||
|
@ -142,12 +153,6 @@ The following plugins are supported for use with vim-go:
|
||||||
https://github.com/SirVer/ultisnips or
|
https://github.com/SirVer/ultisnips or
|
||||||
https://github.com/joereynolds/vim-minisnip
|
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|:
|
* Interactive |:GoDecls| and |:GoDeclsDir|:
|
||||||
https://github.com/ctrlpvim/ctrlp.vim or
|
https://github.com/ctrlpvim/ctrlp.vim or
|
||||||
https://github.com/junegunn/fzf.vim or
|
https://github.com/junegunn/fzf.vim or
|
||||||
|
@ -157,6 +162,11 @@ The following plugins are supported for use with vim-go:
|
||||||
==============================================================================
|
==============================================================================
|
||||||
COMMANDS *go-commands*
|
COMMANDS *go-commands*
|
||||||
|
|
||||||
|
*:GoReportGitHubIssue*
|
||||||
|
:GoReportGitHubIssue
|
||||||
|
GoReportGitHubIssue opens the default browser and starts a new bug report
|
||||||
|
with useful system information.
|
||||||
|
|
||||||
*:GoPath*
|
*:GoPath*
|
||||||
:GoPath [path]
|
:GoPath [path]
|
||||||
|
|
||||||
|
@ -187,11 +197,13 @@ COMMANDS *go-commands*
|
||||||
displayed and the buffer will be untouched.
|
displayed and the buffer will be untouched.
|
||||||
|
|
||||||
*:GoLint*
|
*:GoLint*
|
||||||
:GoLint [packages]
|
:GoLint! [packages]
|
||||||
|
|
||||||
Run golint for the directory under your current file, or for the given
|
Run golint for the directory under your current file, or for the given
|
||||||
packages.
|
packages.
|
||||||
|
|
||||||
|
If [!] is not given the first error is jumped to.
|
||||||
|
|
||||||
*:GoDoc*
|
*:GoDoc*
|
||||||
:GoDoc [word]
|
:GoDoc [word]
|
||||||
|
|
||||||
|
@ -249,7 +261,7 @@ CTRL-]
|
||||||
g<C-LeftMouse>
|
g<C-LeftMouse>
|
||||||
<C-LeftMouse>
|
<C-LeftMouse>
|
||||||
|
|
||||||
Goto declaration/definition for the declaration under the cursor. By
|
Go to declaration/definition for the identifier under the cursor. By
|
||||||
default the CTRL-] shortcut, the mapping `gd` and <C-LeftMouse>,
|
default the CTRL-] shortcut, the mapping `gd` and <C-LeftMouse>,
|
||||||
g<LeftMouse> are enabled to invoke :GoDef for the identifier under the
|
g<LeftMouse> are enabled to invoke :GoDef for the identifier under the
|
||||||
cursor. See |'g:go_def_mapping_enabled'| to disable them. No explicit
|
cursor. See |'g:go_def_mapping_enabled'| to disable them. No explicit
|
||||||
|
@ -261,6 +273,14 @@ g<C-LeftMouse>
|
||||||
list of file locations you have visited with :GoDef that is retained to
|
list of file locations you have visited with :GoDef that is retained to
|
||||||
help you navigate software.
|
help you navigate software.
|
||||||
|
|
||||||
|
The per-window location stack is shared with |:GoDefType|.
|
||||||
|
|
||||||
|
*:GoDefType*
|
||||||
|
:GoDefType
|
||||||
|
|
||||||
|
Go to type definition for the identifier under the cursor.
|
||||||
|
|
||||||
|
The per-window location stack is shared with |:GoDef|.
|
||||||
*:GoDefStack*
|
*:GoDefStack*
|
||||||
:GoDefStack [number]
|
:GoDefStack [number]
|
||||||
|
|
||||||
|
@ -455,7 +475,7 @@ CTRL-t
|
||||||
If [!] is not given the first error is jumped to.
|
If [!] is not given the first error is jumped to.
|
||||||
|
|
||||||
*:GoErrCheck*
|
*:GoErrCheck*
|
||||||
:GoErrCheck [options]
|
:GoErrCheck! [options]
|
||||||
|
|
||||||
Check for unchecked errors in you current package. Errors are populated in
|
Check for unchecked errors in you current package. Errors are populated in
|
||||||
the quickfix window.
|
the quickfix window.
|
||||||
|
@ -463,6 +483,8 @@ CTRL-t
|
||||||
You may optionally pass any valid errcheck flags/options. See
|
You may optionally pass any valid errcheck flags/options. See
|
||||||
`errcheck -h` for a full list.
|
`errcheck -h` for a full list.
|
||||||
|
|
||||||
|
If [!] is not given the first error is jumped to.
|
||||||
|
|
||||||
*:GoFiles*
|
*:GoFiles*
|
||||||
:GoFiles [source_files]
|
:GoFiles [source_files]
|
||||||
|
|
||||||
|
@ -629,7 +651,7 @@ CTRL-t
|
||||||
disabled it clears and stops automatic highlighting.
|
disabled it clears and stops automatic highlighting.
|
||||||
|
|
||||||
*:GoMetaLinter*
|
*:GoMetaLinter*
|
||||||
:GoMetaLinter [path]
|
:GoMetaLinter! [path]
|
||||||
|
|
||||||
Calls the underlying `gometalinter` tool and displays all warnings and
|
Calls the underlying `gometalinter` tool and displays all warnings and
|
||||||
errors in the |quickfix| window. By default the following linters are
|
errors in the |quickfix| window. By default the following linters are
|
||||||
|
@ -638,16 +660,18 @@ CTRL-t
|
||||||
use the variable |'g:go_metalinter_command'|. To override the maximum
|
use the variable |'g:go_metalinter_command'|. To override the maximum
|
||||||
linters execution time use |'g:go_metalinter_deadline'| variable.
|
linters execution time use |'g:go_metalinter_deadline'| variable.
|
||||||
|
|
||||||
|
If [!] is not given the first error is jumped to.
|
||||||
|
|
||||||
*:GoBuildTags*
|
*:GoBuildTags*
|
||||||
:GoBuildTags [tags]
|
:GoBuildTags [tags]
|
||||||
|
|
||||||
Changes the build tags for various commands. If you have any file that
|
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
|
uses a custom build tag, such as `// +build integration` , this command
|
||||||
be used to pass it to all tools that accepts tags, such as guru, gorename,
|
can be used to pass it to all tools that accepts tags, such as guru,
|
||||||
etc..
|
gorename, etc.
|
||||||
|
|
||||||
The build tags is cleared (unset) if `""` is given. If no arguments is
|
The build tags is cleared (unset) if `""` is given. If no arguments are
|
||||||
given it prints the current custom build tags.
|
given it prints the current build tags.
|
||||||
|
|
||||||
*:AsmFmt*
|
*:AsmFmt*
|
||||||
:AsmFmt
|
:AsmFmt
|
||||||
|
@ -676,6 +700,12 @@ CTRL-t
|
||||||
\| command! -bang AS call go#alternate#Switch(<bang>0, 'split')
|
\| command! -bang AS call go#alternate#Switch(<bang>0, 'split')
|
||||||
augroup END
|
augroup END
|
||||||
<
|
<
|
||||||
|
|
||||||
|
*:GoPointsTo*
|
||||||
|
:GoPointsTo
|
||||||
|
|
||||||
|
Show all variables to which the pointer under the cursor may point to.
|
||||||
|
|
||||||
*:GoWhicherrs*
|
*:GoWhicherrs*
|
||||||
:GoWhicherrs
|
:GoWhicherrs
|
||||||
|
|
||||||
|
@ -762,7 +792,7 @@ CTRL-t
|
||||||
*:GoRemoveTags*
|
*:GoRemoveTags*
|
||||||
:[range]GoRemoveTags [key],[option] [key1],[option1] ...
|
:[range]GoRemoveTags [key],[option] [key1],[option1] ...
|
||||||
|
|
||||||
Rmove field tags for the fields of a struct. If called inside a struct it
|
Remove 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
|
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
|
called outside a struct definition or if the file is not correctly
|
||||||
formatted
|
formatted
|
||||||
|
@ -789,6 +819,11 @@ CTRL-t
|
||||||
|
|
||||||
Toggles |'g:go_fmt_autosave'|.
|
Toggles |'g:go_fmt_autosave'|.
|
||||||
|
|
||||||
|
*:GoModFmtAutoSaveToggle*
|
||||||
|
:GoModFmtAutoSaveToggle
|
||||||
|
|
||||||
|
Toggles |'g:go_mod_fmt_autosave'|.
|
||||||
|
|
||||||
*:GoAsmFmtAutoSaveToggle*
|
*:GoAsmFmtAutoSaveToggle*
|
||||||
:GoAsmFmtAutoSaveToggle
|
:GoAsmFmtAutoSaveToggle
|
||||||
|
|
||||||
|
@ -842,6 +877,34 @@ CTRL-t
|
||||||
}
|
}
|
||||||
<
|
<
|
||||||
|
|
||||||
|
*:GoIfErr*
|
||||||
|
:GoIfErr
|
||||||
|
|
||||||
|
Generate if err != nil { return ... } automatically which infer the type
|
||||||
|
of return values and the numbers.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
>
|
||||||
|
func doSomething() (string, error) {
|
||||||
|
f, err := os.Open("file")
|
||||||
|
}
|
||||||
|
<
|
||||||
|
Becomes:
|
||||||
|
>
|
||||||
|
func doSomething() (string, error) {
|
||||||
|
f, err := os.Open("file")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<
|
||||||
|
*:GoModFmt*
|
||||||
|
:GoModFmt
|
||||||
|
|
||||||
|
Filter the current go.mod buffer through "go mod edit -fmt" command. It
|
||||||
|
tries to preserve cursor position and avoids replacing the buffer with
|
||||||
|
stderr output.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
MAPPINGS *go-mappings*
|
MAPPINGS *go-mappings*
|
||||||
|
|
||||||
|
@ -1030,6 +1093,10 @@ Show send/receive corresponding to selected channel op
|
||||||
|
|
||||||
Show all refs to entity denoted by selected identifier
|
Show all refs to entity denoted by selected identifier
|
||||||
|
|
||||||
|
*(go-pointsto)*
|
||||||
|
|
||||||
|
Show all variables to which the pointer under the cursor may point to.
|
||||||
|
|
||||||
*(go-metalinter)*
|
*(go-metalinter)*
|
||||||
|
|
||||||
Calls `go-metalinter` for the current directory
|
Calls `go-metalinter` for the current directory
|
||||||
|
@ -1050,6 +1117,14 @@ Alternates between the implementation and test code in a new vertical split
|
||||||
|
|
||||||
Calls `:GoImport` for the current package
|
Calls `:GoImport` for the current package
|
||||||
|
|
||||||
|
*(go-iferr)*
|
||||||
|
|
||||||
|
Generate if err != nil { return ... } automatically which infer the type of
|
||||||
|
return values and the numbers.
|
||||||
|
|
||||||
|
*(go-mod-fmt)*
|
||||||
|
|
||||||
|
Calls |:GoModFmt| for the current buffer
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
TEXT OBJECTS *go-text-objects*
|
TEXT OBJECTS *go-text-objects*
|
||||||
|
@ -1070,6 +1145,12 @@ if "inside a function", select contents of a function,
|
||||||
excluding the function definition and the closing bracket. This
|
excluding the function definition and the closing bracket. This
|
||||||
text-object also supports literal functions
|
text-object also supports literal functions
|
||||||
|
|
||||||
|
*go-v_ac* *go-ac*
|
||||||
|
ac "a comment", select contents of the current comment block.
|
||||||
|
|
||||||
|
*go-v_ic* *go-ic*
|
||||||
|
ic "inner comment", select contents of the current comment block,
|
||||||
|
excluding the start and end comment markers.
|
||||||
|
|
||||||
vim-go also defines the following text motion objects:
|
vim-go also defines the following text motion objects:
|
||||||
|
|
||||||
|
@ -1090,10 +1171,10 @@ FUNCTIONS *go-functions*
|
||||||
*go#statusline#Show()*
|
*go#statusline#Show()*
|
||||||
|
|
||||||
Shows the status of a job running asynchronously. Can be used to plug into the
|
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
|
statusline. It works to show the status per package instead of per file.
|
||||||
file. Assume you have three files open, all belonging to the same package, if
|
Assume you have three files open, all belonging to the same package, if the
|
||||||
the package build (`:GoBuild`) is successful, all statusline's will show
|
package build (`:GoBuild`) is successful, all statuslines will show `success`,
|
||||||
`success`, if you it fails all file's statusline will show `failed`.
|
if it fails all windows' statuslines will show `failed`.
|
||||||
|
|
||||||
To avoid always showing old status information, the status information is
|
To avoid always showing old status information, the status information is
|
||||||
cleaned for each package after `60` seconds. This can be changed with the
|
cleaned for each package after `60` seconds. This can be changed with the
|
||||||
|
@ -1104,6 +1185,21 @@ cleaned for each package after `60` seconds. This can be changed with the
|
||||||
Returns the description of the identifer under the cursor. Can be used to plug
|
Returns the description of the identifer under the cursor. Can be used to plug
|
||||||
into the statusline.
|
into the statusline.
|
||||||
|
|
||||||
|
*go#complete#Complete()*
|
||||||
|
|
||||||
|
Uses `gopls` for autocompletion. By default, it is hooked up to |'omnifunc'|
|
||||||
|
for Vim8 and Neovim.
|
||||||
|
|
||||||
|
*go#complete#GocodeComplete()*
|
||||||
|
|
||||||
|
Uses `gocode` for autocompletion. By default, it is hooked up to |'omnifunc'|
|
||||||
|
for Vim 7.4.
|
||||||
|
|
||||||
|
*go#tool#DescribeBalloon()*
|
||||||
|
|
||||||
|
Suitable to be used as an expression to show the evaluation balloon. See `help
|
||||||
|
balloonexpr`.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
SETTINGS *go-settings*
|
SETTINGS *go-settings*
|
||||||
|
|
||||||
|
@ -1156,8 +1252,8 @@ updated. By default it's disabled. The delay can be configured with the
|
||||||
|
|
||||||
Use this option to define the command to be used for |:GoInfo|. By default
|
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
|
`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
|
`gopls` or `guru` as they cover more cases and are more accurate. Current
|
||||||
are: `[gocode, guru]` >
|
valid options are: `[gocode, guru, gopls]` >
|
||||||
|
|
||||||
let g:go_info_mode = 'gocode'
|
let g:go_info_mode = 'gocode'
|
||||||
<
|
<
|
||||||
|
@ -1231,11 +1327,19 @@ fails. By default the location list is shown. >
|
||||||
|
|
||||||
Use this option to enable fmt's experimental mode. This experimental mode is
|
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
|
superior to the current mode as it fully saves the undo history, so undo/redo
|
||||||
doesn't break. However it's slows (creates/deletes a file for every save) and
|
doesn't break. However, it's slow (creates/deletes a file for every save) and
|
||||||
it's causing problems on some Vim versions. By default it's disabled. >
|
it's causing problems on some Vim versions. By default it's disabled. >
|
||||||
|
|
||||||
let g:go_fmt_experimental = 0
|
let g:go_fmt_experimental = 0
|
||||||
|
|
||||||
<
|
<
|
||||||
|
*'g:go_mod_fmt_autosave'*
|
||||||
|
|
||||||
|
Use this option to auto |:GoModFmt| on save. By default it's enabled >
|
||||||
|
|
||||||
|
let g:go_mod_fmt_autosave = 1
|
||||||
|
<
|
||||||
|
|
||||||
*'g:go_doc_keywordprg_enabled'*
|
*'g:go_doc_keywordprg_enabled'*
|
||||||
|
|
||||||
Use this option to run `godoc` on words under the cursor with |K|; this will
|
Use this option to run `godoc` on words under the cursor with |K|; this will
|
||||||
|
@ -1264,7 +1368,7 @@ a private internal service. Default is 'https://godoc.org'.
|
||||||
|
|
||||||
Use this option to define the command to be used for |:GoDef|. By default
|
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
|
`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]` >
|
`godef` as it's faster. Current valid options are: `[guru, godef, gopls]` >
|
||||||
|
|
||||||
let g:go_def_mode = 'guru'
|
let g:go_def_mode = 'guru'
|
||||||
<
|
<
|
||||||
|
@ -1284,21 +1388,26 @@ mappings of |:GoDef|. By default it's disabled. >
|
||||||
|
|
||||||
let g:go_def_reuse_buffer = 0
|
let g:go_def_reuse_buffer = 0
|
||||||
<
|
<
|
||||||
*'g:go_doc_command'*
|
*'g:go_bin_path'*
|
||||||
|
|
||||||
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|). >
|
|
||||||
|
|
||||||
let g:go_doc_command = ["godoc"]
|
|
||||||
|
|
||||||
< *'g:go_bin_path'*
|
|
||||||
|
|
||||||
Use this option to change default path for vim-go tools when using
|
Use this option to change default path for vim-go tools when using
|
||||||
|:GoInstallBinaries| and |:GoUpdateBinaries|. If not set `$GOBIN` or
|
|:GoInstallBinaries| and |:GoUpdateBinaries|. If not set `$GOBIN` or
|
||||||
`$GOPATH/bin` is used. >
|
`$GOPATH/bin` is used. >
|
||||||
|
|
||||||
let g:go_bin_path = ""
|
let g:go_bin_path = ""
|
||||||
|
<
|
||||||
|
*'g:go_search_bin_path_first'*
|
||||||
|
|
||||||
|
This option lets |'g:go_bin_path'| (or its default value) take precedence over
|
||||||
|
$PATH when invoking a tool command such as |:GoFmt| or |:GoImports|.
|
||||||
|
|
||||||
|
Enabling this option ensures that the binaries installed via
|
||||||
|
|:GoInstallBinaries| and |:GoUpdateBinaries| are the same ones that are
|
||||||
|
invoked via the tool commands.
|
||||||
|
|
||||||
|
By default it is enabled. >
|
||||||
|
|
||||||
|
let g:go_search_bin_path_first = 1
|
||||||
<
|
<
|
||||||
*'g:go_snippet_engine'*
|
*'g:go_snippet_engine'*
|
||||||
|
|
||||||
|
@ -1336,10 +1445,10 @@ By default it's not set, so the relevant commands defaults are being used.
|
||||||
<
|
<
|
||||||
*'g:go_build_tags'*
|
*'g:go_build_tags'*
|
||||||
|
|
||||||
These options that will be automatically passed to the `-tags` option of
|
Space-separated list of build tags passed to the `-tags` flag of tools that
|
||||||
various tools, such as `guru`, `gorename`, etc... This is a permanent
|
support it.
|
||||||
setting. A more useful way is to use |:GoBuildTags| to dynamically change or
|
There is also the |:GoBuildTags| convenience command to change or remove build
|
||||||
remove build tags. By default it's not set.
|
tags.
|
||||||
>
|
>
|
||||||
let g:go_build_tags = ''
|
let g:go_build_tags = ''
|
||||||
<
|
<
|
||||||
|
@ -1407,11 +1516,12 @@ it's empty
|
||||||
<
|
<
|
||||||
*'g:go_metalinter_command'*
|
*'g:go_metalinter_command'*
|
||||||
|
|
||||||
Overrides the command to be executed when |:GoMetaLinter| is called. This is
|
Overrides the command to be executed when |:GoMetaLinter| is called. By
|
||||||
an advanced settings and is for users who want to have a complete control
|
default it's `gometalinter`. `golangci-lint` is also supported. It can also be
|
||||||
over how `gometalinter` should be executed. By default it's empty.
|
used as an advanced setting for users who want to have more control over
|
||||||
|
the metalinter.
|
||||||
>
|
>
|
||||||
let g:go_metalinter_command = ""
|
let g:go_metalinter_command = "gometalinter"
|
||||||
<
|
<
|
||||||
*'g:go_metalinter_deadline'*
|
*'g:go_metalinter_deadline'*
|
||||||
|
|
||||||
|
@ -1445,10 +1555,10 @@ that was called. Supported values are "", "quickfix", and "locationlist".
|
||||||
Specifies the type of list to use for command outputs (such as errors from
|
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
|
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.
|
is not present in the dictionary, |'g:go_list_type'| will be used instead.
|
||||||
Supported keys are "GoBuild", "GoErrCheck", "GoFmt", "GoInstall", "GoLint",
|
Supported keys are "GoBuild", "GoErrCheck", "GoFmt", "GoModFmt", "GoInstall",
|
||||||
"GoMetaLinter", "GoMetaLinterAutoSave", "GoModifyTags" (used for both
|
"GoLint", "GoMetaLinter", "GoMetaLinterAutoSave", "GoModifyTags" (used for
|
||||||
:GoAddTags and :GoRemoveTags), "GoRename", "GoRun", and "GoTest". Supported
|
both :GoAddTags and :GoRemoveTags), "GoRename", "GoRun", and "GoTest".
|
||||||
values for each command are "quickfix" and "locationlist".
|
Supported values for each command are "quickfix" and "locationlist".
|
||||||
>
|
>
|
||||||
let g:go_list_type_commands = {}
|
let g:go_list_type_commands = {}
|
||||||
<
|
<
|
||||||
|
@ -1522,14 +1632,6 @@ same.
|
||||||
let g:go_gorename_prefill = 'expand("<cword>") =~# "^[A-Z]"' .
|
let g:go_gorename_prefill = 'expand("<cword>") =~# "^[A-Z]"' .
|
||||||
\ '? go#util#pascalcase(expand("<cword>"))' .
|
\ '? go#util#pascalcase(expand("<cword>"))' .
|
||||||
\ ': go#util#camelcase(expand("<cword>"))'
|
\ ': go#util#camelcase(expand("<cword>"))'
|
||||||
<
|
|
||||||
*'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'*
|
*'g:go_gocode_propose_builtins'*
|
||||||
|
|
||||||
|
@ -1537,6 +1639,14 @@ Specifies whether `gocode` should add built-in types, functions and constants
|
||||||
to an autocompletion proposals. By default it is enabled.
|
to an autocompletion proposals. By default it is enabled.
|
||||||
>
|
>
|
||||||
let g:go_gocode_propose_builtins = 1
|
let g:go_gocode_propose_builtins = 1
|
||||||
|
<
|
||||||
|
*'g:go_gocode_propose_source'*
|
||||||
|
|
||||||
|
Specifies whether `gocode` should use source files instead of binary packages
|
||||||
|
for autocompletion proposals. When disabled, only identifiers from the current
|
||||||
|
package and packages that have been installed will proposed.
|
||||||
|
>
|
||||||
|
let g:go_gocode_propose_source = 0
|
||||||
<
|
<
|
||||||
*'g:go_gocode_unimported_packages'*
|
*'g:go_gocode_unimported_packages'*
|
||||||
|
|
||||||
|
@ -1666,9 +1776,11 @@ A list of options to debug; useful for development and/or reporting bugs.
|
||||||
|
|
||||||
Currently accepted values:
|
Currently accepted values:
|
||||||
|
|
||||||
|
shell-commands Echo all shell commands that vim-go runs.
|
||||||
debugger-state Expose debugger state in 'g:go_debug_diag'.
|
debugger-state Expose debugger state in 'g:go_debug_diag'.
|
||||||
debugger-commands Echo communication between vim-go and `dlv`; requests and
|
debugger-commands Echo communication between vim-go and `dlv`; requests and
|
||||||
responses are recorded in `g:go_debug_commands`.
|
responses are recorded in `g:go_debug_commands`.
|
||||||
|
lsp Record lsp requests and responses in g:go_lsp_log.
|
||||||
>
|
>
|
||||||
let g:go_debug = []
|
let g:go_debug = []
|
||||||
<
|
<
|
||||||
|
@ -1750,13 +1862,13 @@ Highlight function and method declarations.
|
||||||
>
|
>
|
||||||
let g:go_highlight_functions = 0
|
let g:go_highlight_functions = 0
|
||||||
<
|
<
|
||||||
*'g:go_highlight_function_arguments'*
|
*'g:go_highlight_function_parameters'*
|
||||||
|
|
||||||
Highlight the variable names in arguments and return values in function
|
Highlight the variable names in parameters (including named return parameters)
|
||||||
declarations. Setting this implies the functionality from
|
in function declarations. Setting this implies the functionality from
|
||||||
|'g:go_highlight_functions'|.
|
|'g:go_highlight_functions'|.
|
||||||
>
|
>
|
||||||
let g:go_highlight_function_arguments = 0
|
let g:go_highlight_function_parameters = 0
|
||||||
<
|
<
|
||||||
*'g:go_highlight_function_calls'*
|
*'g:go_highlight_function_calls'*
|
||||||
|
|
||||||
|
@ -1828,6 +1940,13 @@ filetype.
|
||||||
The `gohtmltmpl` filetype is automatically set for `*.tmpl` files; the
|
The `gohtmltmpl` filetype is automatically set for `*.tmpl` files; the
|
||||||
`gotexttmpl` is never automatically set and needs to be set manually.
|
`gotexttmpl` is never automatically set and needs to be set manually.
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*gomod* *ft-gomod-syntax*
|
||||||
|
go.mod file syntax~
|
||||||
|
|
||||||
|
The `gomod` 'filetype' provides syntax highlighting for Go's module file
|
||||||
|
`go.mod`
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
DEBUGGER *go-debug*
|
DEBUGGER *go-debug*
|
||||||
|
@ -1847,10 +1966,10 @@ features:
|
||||||
* Toggle breakpoint.
|
* Toggle breakpoint.
|
||||||
* Stack operation continue/next/step out.
|
* Stack operation continue/next/step out.
|
||||||
|
|
||||||
This feature requires Vim 8.0.0087 or newer with the |+job| feature. Neovim
|
This feature requires either Vim 8.0.0087 or newer with the |+job| feature or
|
||||||
does _not_ work (yet).
|
Neovim. This features also requires Delve 1.0.0 or newer, and it is
|
||||||
This requires Delve 1.0.0 or newer, and it is recommended to use Go 1.10 or
|
recommended to use Go 1.10 or newer, as its new caching will speed up
|
||||||
newer, as its new caching will speed up recompiles.
|
recompiles.
|
||||||
|
|
||||||
*go-debug-intro*
|
*go-debug-intro*
|
||||||
GETTING STARTED WITH THE DEBUGGER~
|
GETTING STARTED WITH THE DEBUGGER~
|
||||||
|
@ -2035,6 +2154,24 @@ Defaults to `127.0.0.1:8181`:
|
||||||
let g:go_debug_address = '127.0.0.1:8181'
|
let g:go_debug_address = '127.0.0.1:8181'
|
||||||
<
|
<
|
||||||
|
|
||||||
|
*'g:go_debug_log_output'*
|
||||||
|
|
||||||
|
Specifies log output options for `dlv`. Value should be a single string
|
||||||
|
of comma-separated options suitable for passing to `dlv`. An empty string
|
||||||
|
(`''`) will suppress logging entirely.
|
||||||
|
Default: `'debugger, rpc'`:
|
||||||
|
>
|
||||||
|
let g:go_debug_log = 'debugger, rpc'
|
||||||
|
<
|
||||||
|
|
||||||
|
*'g:go_highlight_debug'*
|
||||||
|
|
||||||
|
Highlight the current line and breakpoints in the debugger.
|
||||||
|
|
||||||
|
>
|
||||||
|
let g:go_highlight_debug = 1
|
||||||
|
<
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
FAQ TROUBLESHOOTING *go-troubleshooting*
|
FAQ TROUBLESHOOTING *go-troubleshooting*
|
||||||
|
|
||||||
|
@ -2067,7 +2204,7 @@ Many vim-go commands use the `guru` commandline tool to get information. Some
|
||||||
a reasonable amount of performance `guru` limits this analysis to a selected
|
a reasonable amount of performance `guru` limits this analysis to a selected
|
||||||
list of packages. This is known as the "guru scope".
|
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
|
The default is to use the package the current buffer belongs to, but this may
|
||||||
not always be correct. For example for the file `guthub.com/user/pkg/a/a.go`
|
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
|
the scope will be set to `github.com/user/pkg/a`, but you probably want
|
||||||
`github.com/user/pkg`
|
`github.com/user/pkg`
|
||||||
|
@ -2093,11 +2230,22 @@ Also see |:GoGuruScope| and |'g:go_guru_scope'|.
|
||||||
|
|
||||||
Vim becomes slow while editing Go files~
|
Vim becomes slow while editing Go files~
|
||||||
|
|
||||||
This is usually caused by `g:go_highlight_*` options. Try disabling them if
|
The most common cause for this is using an older version of Vim that doesn't
|
||||||
you've enabled some of them.
|
support asynchronous jobs. |'g:go_auto_sameids'| and |'g:go_auto_type_info'|
|
||||||
|
run jobs that can cause noticable delays when used with vim74. The problem is
|
||||||
|
most pronounced on vim74, but can occur on vim8 and nvim. On vim8 and nvim,
|
||||||
|
the problem should be restricted to a short period when the first buffer in a
|
||||||
|
package is first loaded.
|
||||||
|
|
||||||
Other common culprits are |'g:go_auto_sameids'| and |go#statusline#Show()|.
|
If you see unexpected characters rendered in the current window, the problem
|
||||||
|
is most likely due to |'g:go_auto_sameids'| or |'g:go_auto_type_info'|. First,
|
||||||
|
try using another mode for |'g:go_info_mode'|. If that doesn't work, try
|
||||||
|
disabling |'g:go_auto_sameids'| and |'g:go_auto_type_info'|.
|
||||||
|
|
||||||
|
To a lesser extent, this can be caused by `g:go_highlight_*` options. If Vim
|
||||||
|
is just slower than normal, but doesn't render unexpected characters in the
|
||||||
|
currrent window, then the problem is most likely the `g:go_highlight_*`
|
||||||
|
options. Try disabling them if you've enabled some of them.
|
||||||
|
|
||||||
I get errors when using GoInstallBinaries~
|
I get errors when using GoInstallBinaries~
|
||||||
|
|
||||||
|
@ -2165,9 +2313,9 @@ Using with NeoVim~
|
||||||
|
|
||||||
Note: Neovim currently is not a first class citizen for vim-go. You are free
|
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.
|
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
|
vim-go might not work as well in Neovim as it does in Vim. I'm happy to accept
|
||||||
requests or very detailed bug reports. If you're interested to improve the
|
pull requests or very detailed bug reports. If you're interested to improve
|
||||||
state of Neovim in vim-go you're always welcome!
|
the state of Neovim in vim-go you're always welcome!
|
||||||
|
|
||||||
Run `:GoRun` in a new tab, horizontal split or vertical split terminal
|
Run `:GoRun` in a new tab, horizontal split or vertical split terminal
|
||||||
>
|
>
|
||||||
|
@ -2180,6 +2328,18 @@ By default new terminals are opened in a vertical split. To change it
|
||||||
let g:go_term_mode = "split"
|
let g:go_term_mode = "split"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
How can I customize the highlighting?~
|
||||||
|
|
||||||
|
All the highlight groups used by vim-go are prefixed with `go` (e.g.
|
||||||
|
`goType`) and are defined in the files in the `syntax` directory. To change
|
||||||
|
the highlighting for any group, add a `highlight` command for the group to
|
||||||
|
your vimrc. To turn off the highlighting for any group, add `highlight link
|
||||||
|
group-name NONE` (where `group-name` is the name of the group whose highlight
|
||||||
|
you'd like to turn off) to your vimrc.
|
||||||
|
|
||||||
|
Some people may wish to highlight Go's builtins as keywords. To do so, one
|
||||||
|
should simply add `highlight link goBuiltins Keyword` to the `vimrc` file.
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
DEVELOPMENT *go-development*
|
DEVELOPMENT *go-development*
|
||||||
|
|
||||||
|
@ -2215,14 +2375,14 @@ You can install and test all Vim versions by running `make`.
|
||||||
DONATION *go-donation*
|
DONATION *go-donation*
|
||||||
|
|
||||||
People have asked for this for a long time, now you can be a fully supporter
|
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 patreon at: https://www.patreon.com/bhcleek
|
||||||
|
|
||||||
By being a patron, you are enabling vim-go to grow and mature, helping me to
|
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
|
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
|
features. It's completely optional and is just a direct way to support
|
||||||
vim-go's ongoing development. Thanks!
|
vim-go's ongoing development. Thanks!
|
||||||
|
|
||||||
Check it out: https://www.patreon.com/fatih
|
Check it out: https://www.patreon.com/bhcleek
|
||||||
|
|
||||||
|
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
|
@ -1,34 +1,40 @@
|
||||||
" vint: -ProhibitAutocmdWithNoGroup
|
" vint: -ProhibitAutocmdWithNoGroup
|
||||||
|
|
||||||
" We take care to preserve the user's fileencodings and fileformats,
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
" because those settings are global (not buffer local), yet we want
|
let s:cpo_save = &cpo
|
||||||
" to override them for loading Go files, which are defined to be UTF-8.
|
set cpo&vim
|
||||||
let s:current_fileformats = ''
|
|
||||||
let s:current_fileencodings = ''
|
|
||||||
|
|
||||||
" define fileencodings to open as utf-8 encoding even if it's ascii.
|
|
||||||
function! s:gofiletype_pre(type)
|
|
||||||
let s:current_fileformats = &g:fileformats
|
|
||||||
let s:current_fileencodings = &g:fileencodings
|
|
||||||
set fileencodings=utf-8 fileformats=unix
|
|
||||||
let &l:filetype = a:type
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" restore fileencodings as others
|
|
||||||
function! s:gofiletype_post()
|
|
||||||
let &g:fileformats = s:current_fileformats
|
|
||||||
let &g:fileencodings = s:current_fileencodings
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
" Note: should not use augroup in ftdetect (see :help ftdetect)
|
" Note: should not use augroup in ftdetect (see :help ftdetect)
|
||||||
au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix
|
au BufRead,BufNewFile *.go setfiletype go
|
||||||
au BufRead *.go call s:gofiletype_pre("go")
|
au BufRead,BufNewFile *.s setfiletype asm
|
||||||
au BufReadPost *.go call s:gofiletype_post()
|
au BufRead,BufNewFile *.tmpl setfiletype gohtmltmpl
|
||||||
|
|
||||||
au BufNewFile *.s setfiletype asm | setlocal fileencoding=utf-8 fileformat=unix
|
" remove the autocommands for modsim3, and lprolog files so that their
|
||||||
au BufRead *.s call s:gofiletype_pre("asm")
|
" highlight groups, syntax, etc. will not be loaded. *.MOD is included, so
|
||||||
au BufReadPost *.s call s:gofiletype_post()
|
" that on case insensitive file systems the module2 autocmds will not be
|
||||||
|
" executed.
|
||||||
|
au! BufRead,BufNewFile *.mod,*.MOD
|
||||||
|
" Set the filetype if the first non-comment and non-blank line starts with
|
||||||
|
" 'module <path>'.
|
||||||
|
au BufRead,BufNewFile go.mod call s:gomod()
|
||||||
|
|
||||||
au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl
|
fun! s:gomod()
|
||||||
|
for l:i in range(1, line('$'))
|
||||||
|
let l:l = getline(l:i)
|
||||||
|
if l:l ==# '' || l:l[:1] ==# '//'
|
||||||
|
continue
|
||||||
|
endif
|
||||||
|
|
||||||
|
if l:l =~# '^module .\+'
|
||||||
|
setfiletype gomod
|
||||||
|
endif
|
||||||
|
|
||||||
|
break
|
||||||
|
endfor
|
||||||
|
endfun
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -5,7 +5,12 @@ if exists("b:did_ftplugin")
|
||||||
endif
|
endif
|
||||||
let b:did_ftplugin = 1
|
let b:did_ftplugin = 1
|
||||||
|
|
||||||
let b:undo_ftplugin = "setl fo< com< cms<"
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
let b:undo_ftplugin = "setl fo< com< cms<
|
||||||
|
\ | exe 'au! vim-go-asm-buffer * <buffer>'"
|
||||||
|
|
||||||
setlocal formatoptions-=t
|
setlocal formatoptions-=t
|
||||||
|
|
||||||
|
@ -16,4 +21,17 @@ setlocal noexpandtab
|
||||||
|
|
||||||
command! -nargs=0 AsmFmt call go#asmfmt#Format()
|
command! -nargs=0 AsmFmt call go#asmfmt#Format()
|
||||||
|
|
||||||
|
" Autocommands
|
||||||
|
" ============================================================================
|
||||||
|
|
||||||
|
augroup vim-go-asm-buffer
|
||||||
|
autocmd! * <buffer>
|
||||||
|
|
||||||
|
autocmd BufWritePre <buffer> call go#auto#asmfmt_autosave()
|
||||||
|
augroup end
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -9,7 +9,12 @@ if exists("b:did_ftplugin")
|
||||||
endif
|
endif
|
||||||
let b:did_ftplugin = 1
|
let b:did_ftplugin = 1
|
||||||
|
|
||||||
let b:undo_ftplugin = "setl fo< com< cms<"
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
let b:undo_ftplugin = "setl fo< com< cms<
|
||||||
|
\ | exe 'au! vim-go-buffer * <buffer>'"
|
||||||
|
|
||||||
setlocal formatoptions-=t
|
setlocal formatoptions-=t
|
||||||
|
|
||||||
|
@ -20,8 +25,11 @@ setlocal noexpandtab
|
||||||
|
|
||||||
compiler go
|
compiler go
|
||||||
|
|
||||||
" Set gocode completion
|
" Set autocompletion
|
||||||
setlocal omnifunc=go#complete#Complete
|
setlocal omnifunc=go#complete#Complete
|
||||||
|
if !go#util#has_job()
|
||||||
|
setlocal omnifunc=go#complete#GocodeComplete
|
||||||
|
endif
|
||||||
|
|
||||||
if get(g:, "go_doc_keywordprg_enabled", 1)
|
if get(g:, "go_doc_keywordprg_enabled", 1)
|
||||||
" keywordprg doesn't allow to use vim commands, override it
|
" keywordprg doesn't allow to use vim commands, override it
|
||||||
|
@ -35,18 +43,24 @@ if get(g:, "go_def_mapping_enabled", 1)
|
||||||
nnoremap <buffer> <silent> <C-]> :GoDef<cr>
|
nnoremap <buffer> <silent> <C-]> :GoDef<cr>
|
||||||
nnoremap <buffer> <silent> <C-LeftMouse> <LeftMouse>:GoDef<cr>
|
nnoremap <buffer> <silent> <C-LeftMouse> <LeftMouse>:GoDef<cr>
|
||||||
nnoremap <buffer> <silent> g<LeftMouse> <LeftMouse>:GoDef<cr>
|
nnoremap <buffer> <silent> g<LeftMouse> <LeftMouse>:GoDef<cr>
|
||||||
nnoremap <buffer> <silent> <C-w><C-]> :<C-u>call go#def#Jump("split")<CR>
|
nnoremap <buffer> <silent> <C-w><C-]> :<C-u>call go#def#Jump("split", 0)<CR>
|
||||||
nnoremap <buffer> <silent> <C-w>] :<C-u>call go#def#Jump("split")<CR>
|
nnoremap <buffer> <silent> <C-w>] :<C-u>call go#def#Jump("split", 0)<CR>
|
||||||
nnoremap <buffer> <silent> <C-t> :<C-U>call go#def#StackPop(v:count1)<cr>
|
nnoremap <buffer> <silent> <C-t> :<C-U>call go#def#StackPop(v:count1)<cr>
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(g:, "go_textobj_enabled", 1)
|
if get(g:, "go_textobj_enabled", 1)
|
||||||
onoremap <buffer> <silent> af :<c-u>call go#textobj#Function('a')<cr>
|
onoremap <buffer> <silent> af :<c-u>call go#textobj#Function('a')<cr>
|
||||||
onoremap <buffer> <silent> if :<c-u>call go#textobj#Function('i')<cr>
|
|
||||||
|
|
||||||
xnoremap <buffer> <silent> af :<c-u>call go#textobj#Function('a')<cr>
|
xnoremap <buffer> <silent> af :<c-u>call go#textobj#Function('a')<cr>
|
||||||
|
|
||||||
|
onoremap <buffer> <silent> if :<c-u>call go#textobj#Function('i')<cr>
|
||||||
xnoremap <buffer> <silent> if :<c-u>call go#textobj#Function('i')<cr>
|
xnoremap <buffer> <silent> if :<c-u>call go#textobj#Function('i')<cr>
|
||||||
|
|
||||||
|
onoremap <buffer> <silent> ac :<c-u>call go#textobj#Comment('a')<cr>
|
||||||
|
xnoremap <buffer> <silent> ac :<c-u>call go#textobj#Comment('a')<cr>
|
||||||
|
|
||||||
|
onoremap <buffer> <silent> ic :<c-u>call go#textobj#Comment('i')<cr>
|
||||||
|
xnoremap <buffer> <silent> ic :<c-u>call go#textobj#Comment('i')<cr>
|
||||||
|
|
||||||
" Remap ]] and [[ to jump betweeen functions as they are useless in Go
|
" Remap ]] and [[ to jump betweeen functions as they are useless in Go
|
||||||
nnoremap <buffer> <silent> ]] :<c-u>call go#textobj#FunctionJump('n', 'next')<cr>
|
nnoremap <buffer> <silent> ]] :<c-u>call go#textobj#FunctionJump('n', 'next')<cr>
|
||||||
nnoremap <buffer> <silent> [[ :<c-u>call go#textobj#FunctionJump('n', 'prev')<cr>
|
nnoremap <buffer> <silent> [[ :<c-u>call go#textobj#FunctionJump('n', 'prev')<cr>
|
||||||
|
@ -58,76 +72,60 @@ if get(g:, "go_textobj_enabled", 1)
|
||||||
xnoremap <buffer> <silent> [[ :<c-u>call go#textobj#FunctionJump('v', 'prev')<cr>
|
xnoremap <buffer> <silent> [[ :<c-u>call go#textobj#FunctionJump('v', 'prev')<cr>
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if get(g:, "go_auto_type_info", 0) || get(g:, "go_auto_sameids", 0)
|
if go#config#AutoTypeInfo() || go#config#AutoSameids()
|
||||||
let &l:updatetime= get(g:, "go_updatetime", 800)
|
let &l:updatetime= get(g:, "go_updatetime", 800)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" NOTE(arslan): experimental, disabled by default, doesn't work well. No
|
" Autocommands
|
||||||
" 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)
|
augroup vim-go-buffer
|
||||||
" autocmd FileType go nnoremap <buffer> <silent> * :<c-u>call Sameids_search(0)<CR>
|
autocmd! * <buffer>
|
||||||
" autocmd FileType go nnoremap <buffer> <silent> # :<c-u>call Sameids_search(1)<CR>
|
|
||||||
" autocmd FileType go nnoremap <buffer> <silent> n :<c-u>call Sameids_repeat(0)<CR>
|
|
||||||
" autocmd FileType go nnoremap <buffer> <silent> N :<c-u>call Sameids_repeat(1)<CR>
|
|
||||||
" autocmd FileType go cabbrev nohlsearch <C-r>=Sameids_nohlsearch()<CR>
|
|
||||||
" endif
|
|
||||||
|
|
||||||
" " mode 0: next 1: prev
|
" The file is registered (textDocument/DidOpen) with gopls in in
|
||||||
" function! Sameids_repeat(mode)
|
" plugin/go.vim on the FileType event.
|
||||||
" let matches = getmatches()
|
" TODO(bc): handle all the other events that may be of interest to gopls,
|
||||||
" if empty(matches)
|
" too (e.g. BufFilePost , CursorHold , CursorHoldI, FileReadPost,
|
||||||
" return
|
" StdinReadPre, BufWritePost, TextChange, TextChangedI)
|
||||||
" endif
|
if go#util#has_job()
|
||||||
" let cur_offset = go#util#OffsetCursor()
|
autocmd BufWritePost <buffer> call go#lsp#DidChange(expand('<afile>:p'))
|
||||||
|
autocmd FileChangedShell <buffer> call go#lsp#DidChange(expand('<afile>:p'))
|
||||||
|
autocmd BufDelete <buffer> call go#lsp#DidClose(expand('<afile>:p'))
|
||||||
|
endif
|
||||||
|
|
||||||
" " reverse list to make it easy to find the prev occurrence
|
autocmd CursorHold <buffer> call go#auto#auto_type_info()
|
||||||
" if a:mode
|
autocmd CursorHold <buffer> call go#auto#auto_sameids()
|
||||||
" call reverse(matches)
|
|
||||||
" endif
|
|
||||||
|
|
||||||
" for m in matches
|
" Echo the identifier information when completion is done. Useful to see
|
||||||
" if !has_key(m, "group")
|
" the signature of a function, etc...
|
||||||
" return
|
if exists('##CompleteDone')
|
||||||
" endif
|
autocmd CompleteDone <buffer> call go#auto#echo_go_info()
|
||||||
|
endif
|
||||||
|
|
||||||
" if m.group != "goSameId"
|
autocmd BufWritePre <buffer> call go#auto#fmt_autosave()
|
||||||
" return
|
autocmd BufWritePost <buffer> call go#auto#metalinter_autosave()
|
||||||
" endif
|
|
||||||
|
|
||||||
" let offset = go#util#Offset(m.pos1[0], m.pos1[1])
|
" 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 <buffer> call go#guru#ClearSameIds()
|
||||||
|
|
||||||
" if a:mode && cur_offset > offset
|
autocmd BufEnter <buffer>
|
||||||
" call cursor(m.pos1[0], m.pos1[1])
|
\ if go#config#AutodetectGopath() && !exists('b:old_gopath')
|
||||||
" return
|
\| let b:old_gopath = exists('$GOPATH') ? $GOPATH : -1
|
||||||
" elseif !a:mode && cur_offset < offset
|
\| let $GOPATH = go#path#Detect()
|
||||||
" call cursor(m.pos1[0], m.pos1[1])
|
\| endif
|
||||||
" return
|
autocmd BufLeave <buffer>
|
||||||
" endif
|
\ if exists('b:old_gopath')
|
||||||
" endfor
|
\| if b:old_gopath isnot -1
|
||||||
|
\| let $GOPATH = b:old_gopath
|
||||||
|
\| endif
|
||||||
|
\| unlet b:old_gopath
|
||||||
|
\| endif
|
||||||
|
augroup end
|
||||||
|
|
||||||
" " reached start/end, jump to the end/start
|
" restore Vi compatibility settings
|
||||||
" let initial_match = matches[0]
|
let &cpo = s:cpo_save
|
||||||
" if !has_key(initial_match, "group")
|
unlet s:cpo_save
|
||||||
" 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
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -4,6 +4,7 @@ command! -nargs=? -complete=customlist,go#rename#Complete GoRename call go#renam
|
||||||
" -- guru
|
" -- guru
|
||||||
command! -nargs=* -complete=customlist,go#package#Complete GoGuruScope call go#guru#Scope(<f-args>)
|
command! -nargs=* -complete=customlist,go#package#Complete GoGuruScope call go#guru#Scope(<f-args>)
|
||||||
command! -range=% GoImplements call go#guru#Implements(<count>)
|
command! -range=% GoImplements call go#guru#Implements(<count>)
|
||||||
|
command! -range=% GoPointsTo call go#guru#PointsTo(<count>)
|
||||||
command! -range=% GoWhicherrs call go#guru#Whicherrs(<count>)
|
command! -range=% GoWhicherrs call go#guru#Whicherrs(<count>)
|
||||||
command! -range=% GoCallees call go#guru#Callees(<count>)
|
command! -range=% GoCallees call go#guru#Callees(<count>)
|
||||||
command! -range=% GoDescribe call go#guru#Describe(<count>)
|
command! -range=% GoDescribe call go#guru#Describe(<count>)
|
||||||
|
@ -13,19 +14,22 @@ command! -range=% GoFreevars call go#guru#Freevars(<count>)
|
||||||
command! -range=% GoChannelPeers call go#guru#ChannelPeers(<count>)
|
command! -range=% GoChannelPeers call go#guru#ChannelPeers(<count>)
|
||||||
command! -range=% GoReferrers call go#guru#Referrers(<count>)
|
command! -range=% GoReferrers call go#guru#Referrers(<count>)
|
||||||
|
|
||||||
command! -range=0 GoSameIds call go#guru#SameIds()
|
command! -range=0 GoSameIds call go#guru#SameIds(1)
|
||||||
command! -range=0 GoSameIdsClear call go#guru#ClearSameIds()
|
command! -range=0 GoSameIdsClear call go#guru#ClearSameIds()
|
||||||
command! -range=0 GoSameIdsToggle call go#guru#ToggleSameIds()
|
command! -range=0 GoSameIdsToggle call go#guru#ToggleSameIds()
|
||||||
command! -range=0 GoSameIdsAutoToggle call go#guru#AutoToogleSameIds()
|
command! -range=0 GoSameIdsAutoToggle call go#guru#AutoToggleSameIds()
|
||||||
|
|
||||||
" -- tags
|
" -- tags
|
||||||
command! -nargs=* -range GoAddTags call go#tags#Add(<line1>, <line2>, <count>, <f-args>)
|
command! -nargs=* -range GoAddTags call go#tags#Add(<line1>, <line2>, <count>, <f-args>)
|
||||||
command! -nargs=* -range GoRemoveTags call go#tags#Remove(<line1>, <line2>, <count>, <f-args>)
|
command! -nargs=* -range GoRemoveTags call go#tags#Remove(<line1>, <line2>, <count>, <f-args>)
|
||||||
|
|
||||||
|
" -- mod
|
||||||
|
command! -nargs=0 -range GoModFmt call go#mod#Format()
|
||||||
|
|
||||||
" -- tool
|
" -- tool
|
||||||
command! -nargs=* -complete=customlist,go#tool#ValidFiles GoFiles echo go#tool#Files(<f-args>)
|
command! -nargs=* -complete=customlist,go#tool#ValidFiles GoFiles echo go#tool#Files(<f-args>)
|
||||||
command! -nargs=0 GoDeps echo go#tool#Deps()
|
command! -nargs=0 GoDeps echo go#tool#Deps()
|
||||||
command! -nargs=* GoInfo call go#tool#Info(0)
|
command! -nargs=0 GoInfo call go#tool#Info(1)
|
||||||
command! -nargs=0 GoAutoTypeInfoToggle call go#complete#ToggleAutoTypeInfo()
|
command! -nargs=0 GoAutoTypeInfoToggle call go#complete#ToggleAutoTypeInfo()
|
||||||
|
|
||||||
" -- cmd
|
" -- cmd
|
||||||
|
@ -50,7 +54,8 @@ command! -nargs=* -bang GoCoverageBrowser call go#coverage#Browser(<bang>0, <f-a
|
||||||
command! -nargs=0 -range=% GoPlay call go#play#Share(<count>, <line1>, <line2>)
|
command! -nargs=0 -range=% GoPlay call go#play#Share(<count>, <line1>, <line2>)
|
||||||
|
|
||||||
" -- def
|
" -- def
|
||||||
command! -nargs=* -range GoDef :call go#def#Jump('')
|
command! -nargs=* -range GoDef :call go#def#Jump('', 0)
|
||||||
|
command! -nargs=* -range GoDefType :call go#def#Jump('', 1)
|
||||||
command! -nargs=? GoDefPop :call go#def#StackPop(<f-args>)
|
command! -nargs=? GoDefPop :call go#def#StackPop(<f-args>)
|
||||||
command! -nargs=? GoDefStack :call go#def#Stack(<f-args>)
|
command! -nargs=? GoDefStack :call go#def#Stack(<f-args>)
|
||||||
command! -nargs=? GoDefStackClear :call go#def#StackClear(<f-args>)
|
command! -nargs=? GoDefStackClear :call go#def#StackClear(<f-args>)
|
||||||
|
@ -73,11 +78,11 @@ command! -nargs=1 -bang -complete=customlist,go#package#Complete GoImport call g
|
||||||
command! -nargs=* -bang -complete=customlist,go#package#Complete GoImportAs call go#import#SwitchImport(1, <f-args>, '<bang>')
|
command! -nargs=* -bang -complete=customlist,go#package#Complete GoImportAs call go#import#SwitchImport(1, <f-args>, '<bang>')
|
||||||
|
|
||||||
" -- linters
|
" -- linters
|
||||||
command! -nargs=* GoMetaLinter call go#lint#Gometa(0, <f-args>)
|
command! -nargs=* -bang GoMetaLinter call go#lint#Gometa(<bang>0, 0, <f-args>)
|
||||||
command! -nargs=0 GoMetaLinterAutoSaveToggle call go#lint#ToggleMetaLinterAutoSave()
|
command! -nargs=0 GoMetaLinterAutoSaveToggle call go#lint#ToggleMetaLinterAutoSave()
|
||||||
command! -nargs=* GoLint call go#lint#Golint(<f-args>)
|
command! -nargs=* -bang GoLint call go#lint#Golint(<bang>0, <f-args>)
|
||||||
command! -nargs=* -bang GoVet call go#lint#Vet(<bang>0, <f-args>)
|
command! -nargs=* -bang GoVet call go#lint#Vet(<bang>0, <f-args>)
|
||||||
command! -nargs=* -complete=customlist,go#package#Complete GoErrCheck call go#lint#Errcheck(<f-args>)
|
command! -nargs=* -bang -complete=customlist,go#package#Complete GoErrCheck call go#lint#Errcheck(<bang>0, <f-args>)
|
||||||
|
|
||||||
" -- alternate
|
" -- alternate
|
||||||
command! -bang GoAlternate call go#alternate#Switch(<bang>0, '')
|
command! -bang GoAlternate call go#alternate#Switch(<bang>0, '')
|
||||||
|
@ -105,4 +110,10 @@ if !exists(':GoDebugStart')
|
||||||
command! -nargs=? GoDebugBreakpoint call go#debug#Breakpoint(<f-args>)
|
command! -nargs=? GoDebugBreakpoint call go#debug#Breakpoint(<f-args>)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" -- issue
|
||||||
|
command! -nargs=0 GoReportGitHubIssue call go#issue#New()
|
||||||
|
|
||||||
|
" -- iferr
|
||||||
|
command! -nargs=0 GoIfErr call go#iferr#Generate()
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -31,7 +31,7 @@ nnoremap <silent> <Plug>(go-coverage-browser) :<C-u>call go#coverage#Browser(!g:
|
||||||
|
|
||||||
nnoremap <silent> <Plug>(go-files) :<C-u>call go#tool#Files()<CR>
|
nnoremap <silent> <Plug>(go-files) :<C-u>call go#tool#Files()<CR>
|
||||||
nnoremap <silent> <Plug>(go-deps) :<C-u>call go#tool#Deps()<CR>
|
nnoremap <silent> <Plug>(go-deps) :<C-u>call go#tool#Deps()<CR>
|
||||||
nnoremap <silent> <Plug>(go-info) :<C-u>call go#tool#Info(0)<CR>
|
nnoremap <silent> <Plug>(go-info) :<C-u>call go#tool#Info(1)<CR>
|
||||||
nnoremap <silent> <Plug>(go-import) :<C-u>call go#import#SwitchImport(1, '', expand('<cword>'), '')<CR>
|
nnoremap <silent> <Plug>(go-import) :<C-u>call go#import#SwitchImport(1, '', expand('<cword>'), '')<CR>
|
||||||
nnoremap <silent> <Plug>(go-imports) :<C-u>call go#fmt#Format(1)<CR>
|
nnoremap <silent> <Plug>(go-imports) :<C-u>call go#fmt#Format(1)<CR>
|
||||||
|
|
||||||
|
@ -43,16 +43,25 @@ nnoremap <silent> <Plug>(go-callstack) :<C-u>call go#guru#Callstack(-1)<CR>
|
||||||
xnoremap <silent> <Plug>(go-freevars) :<C-u>call go#guru#Freevars(0)<CR>
|
xnoremap <silent> <Plug>(go-freevars) :<C-u>call go#guru#Freevars(0)<CR>
|
||||||
nnoremap <silent> <Plug>(go-channelpeers) :<C-u>call go#guru#ChannelPeers(-1)<CR>
|
nnoremap <silent> <Plug>(go-channelpeers) :<C-u>call go#guru#ChannelPeers(-1)<CR>
|
||||||
nnoremap <silent> <Plug>(go-referrers) :<C-u>call go#guru#Referrers(-1)<CR>
|
nnoremap <silent> <Plug>(go-referrers) :<C-u>call go#guru#Referrers(-1)<CR>
|
||||||
nnoremap <silent> <Plug>(go-sameids) :<C-u>call go#guru#SameIds()<CR>
|
nnoremap <silent> <Plug>(go-sameids) :<C-u>call go#guru#SameIds(1)<CR>
|
||||||
|
nnoremap <silent> <Plug>(go-pointsto) :<C-u>call go#guru#PointsTo(-1)<CR>
|
||||||
nnoremap <silent> <Plug>(go-whicherrs) :<C-u>call go#guru#Whicherrs(-1)<CR>
|
nnoremap <silent> <Plug>(go-whicherrs) :<C-u>call go#guru#Whicherrs(-1)<CR>
|
||||||
nnoremap <silent> <Plug>(go-sameids-toggle) :<C-u>call go#guru#ToggleSameIds()<CR>
|
nnoremap <silent> <Plug>(go-sameids-toggle) :<C-u>call go#guru#ToggleSameIds()<CR>
|
||||||
|
|
||||||
nnoremap <silent> <Plug>(go-rename) :<C-u>call go#rename#Rename(!g:go_jump_to_error)<CR>
|
nnoremap <silent> <Plug>(go-rename) :<C-u>call go#rename#Rename(!g:go_jump_to_error)<CR>
|
||||||
|
|
||||||
nnoremap <silent> <Plug>(go-def) :<C-u>call go#def#Jump('')<CR>
|
nnoremap <silent> <Plug>(go-decls) :<C-u>call go#decls#Decls(0, '')<CR>
|
||||||
nnoremap <silent> <Plug>(go-def-vertical) :<C-u>call go#def#Jump("vsplit")<CR>
|
nnoremap <silent> <Plug>(go-decls-dir) :<C-u>call go#decls#Decls(1, '')<CR>
|
||||||
nnoremap <silent> <Plug>(go-def-split) :<C-u>call go#def#Jump("split")<CR>
|
|
||||||
nnoremap <silent> <Plug>(go-def-tab) :<C-u>call go#def#Jump("tab")<CR>
|
nnoremap <silent> <Plug>(go-def) :<C-u>call go#def#Jump('', 0)<CR>
|
||||||
|
nnoremap <silent> <Plug>(go-def-vertical) :<C-u>call go#def#Jump("vsplit", 0)<CR>
|
||||||
|
nnoremap <silent> <Plug>(go-def-split) :<C-u>call go#def#Jump("split", 0)<CR>
|
||||||
|
nnoremap <silent> <Plug>(go-def-tab) :<C-u>call go#def#Jump("tab", 0)<CR>
|
||||||
|
|
||||||
|
nnoremap <silent> <Plug>(go-def-type) :<C-u>call go#def#Jump('', 1)<CR>
|
||||||
|
nnoremap <silent> <Plug>(go-def-type-vertical) :<C-u>call go#def#Jump("vsplit", 1)<CR>
|
||||||
|
nnoremap <silent> <Plug>(go-def-type-split) :<C-u>call go#def#Jump("split", 1)<CR>
|
||||||
|
nnoremap <silent> <Plug>(go-def-type-tab) :<C-u>call go#def#Jump("tab", 1)<CR>
|
||||||
|
|
||||||
nnoremap <silent> <Plug>(go-def-pop) :<C-u>call go#def#StackPop()<CR>
|
nnoremap <silent> <Plug>(go-def-pop) :<C-u>call go#def#StackPop()<CR>
|
||||||
nnoremap <silent> <Plug>(go-def-stack) :<C-u>call go#def#Stack()<CR>
|
nnoremap <silent> <Plug>(go-def-stack) :<C-u>call go#def#Stack()<CR>
|
||||||
|
@ -64,12 +73,14 @@ nnoremap <silent> <Plug>(go-doc-vertical) :<C-u>call go#doc#Open("vnew", "vsplit
|
||||||
nnoremap <silent> <Plug>(go-doc-split) :<C-u>call go#doc#Open("new", "split")<CR>
|
nnoremap <silent> <Plug>(go-doc-split) :<C-u>call go#doc#Open("new", "split")<CR>
|
||||||
nnoremap <silent> <Plug>(go-doc-browser) :<C-u>call go#doc#OpenBrowser()<CR>
|
nnoremap <silent> <Plug>(go-doc-browser) :<C-u>call go#doc#OpenBrowser()<CR>
|
||||||
|
|
||||||
nnoremap <silent> <Plug>(go-metalinter) :<C-u>call go#lint#Gometa(0)<CR>
|
nnoremap <silent> <Plug>(go-metalinter) :<C-u>call go#lint#Gometa(!g:go_jump_to_error, 0)<CR>
|
||||||
nnoremap <silent> <Plug>(go-lint) :<C-u>call go#lint#Golint()<CR>
|
nnoremap <silent> <Plug>(go-lint) :<C-u>call go#lint#Golint(!g:go_jump_to_error)<CR>
|
||||||
nnoremap <silent> <Plug>(go-vet) :<C-u>call go#lint#Vet(!g:go_jump_to_error)<CR>
|
nnoremap <silent> <Plug>(go-vet) :<C-u>call go#lint#Vet(!g:go_jump_to_error)<CR>
|
||||||
|
|
||||||
nnoremap <silent> <Plug>(go-alternate-edit) :<C-u>call go#alternate#Switch(0, "edit")<CR>
|
nnoremap <silent> <Plug>(go-alternate-edit) :<C-u>call go#alternate#Switch(0, "edit")<CR>
|
||||||
nnoremap <silent> <Plug>(go-alternate-vertical) :<C-u>call go#alternate#Switch(0, "vsplit")<CR>
|
nnoremap <silent> <Plug>(go-alternate-vertical) :<C-u>call go#alternate#Switch(0, "vsplit")<CR>
|
||||||
nnoremap <silent> <Plug>(go-alternate-split) :<C-u>call go#alternate#Switch(0, "split")<CR>
|
nnoremap <silent> <Plug>(go-alternate-split) :<C-u>call go#alternate#Switch(0, "split")<CR>
|
||||||
|
|
||||||
|
nnoremap <silent> <Plug>(go-iferr) :<C-u>call go#iferr#Generate()<CR>
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
if exists("g:go_loaded_gosnippets")
|
if exists("g:go_loaded_gosnippets")
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
|
@ -47,7 +51,7 @@ function! s:GoMinisnip() abort
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
|
||||||
let s:engine = get(g:, 'go_snippet_engine', 'automatic')
|
let s:engine = go#config#SnippetEngine()
|
||||||
if s:engine is? "automatic"
|
if s:engine is? "automatic"
|
||||||
if get(g:, 'did_plugin_ultisnips') is 1
|
if get(g:, 'did_plugin_ultisnips') is 1
|
||||||
call s:GoUltiSnips()
|
call s:GoUltiSnips()
|
||||||
|
@ -64,4 +68,8 @@ elseif s:engine is? "minisnip"
|
||||||
call s:GoMinisnip()
|
call s:GoMinisnip()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
|
@ -9,6 +9,10 @@ elseif globpath(&rtp, 'plugin/tagbar.vim') == ""
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
if !exists("g:go_gotags_bin")
|
if !exists("g:go_gotags_bin")
|
||||||
let g:go_gotags_bin = "gotags"
|
let g:go_gotags_bin = "gotags"
|
||||||
endif
|
endif
|
||||||
|
@ -54,4 +58,8 @@ endfunction
|
||||||
|
|
||||||
call s:SetTagbar()
|
call s:SetTagbar()
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
33
pack/acp/start/vim-go/ftplugin/gomod.vim
Normal file
33
pack/acp/start/vim-go/ftplugin/gomod.vim
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
" gomod.vim: Vim filetype plugin for Go assembler.
|
||||||
|
|
||||||
|
if exists("b:did_ftplugin")
|
||||||
|
finish
|
||||||
|
endif
|
||||||
|
let b:did_ftplugin = 1
|
||||||
|
|
||||||
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
|
let s:cpo_save = &cpo
|
||||||
|
set cpo&vim
|
||||||
|
|
||||||
|
let b:undo_ftplugin = "setl fo< com< cms<
|
||||||
|
\ | exe 'au! vim-go-gomod-buffer * <buffer>'"
|
||||||
|
|
||||||
|
setlocal formatoptions-=t
|
||||||
|
|
||||||
|
setlocal comments=s1:/*,mb:*,ex:*/,://
|
||||||
|
setlocal commentstring=//\ %s
|
||||||
|
|
||||||
|
" Autocommands
|
||||||
|
" ============================================================================
|
||||||
|
|
||||||
|
augroup vim-go-gomod-buffer
|
||||||
|
autocmd! * <buffer>
|
||||||
|
|
||||||
|
autocmd BufWritePre <buffer> call go#auto#modfmt_autosave()
|
||||||
|
augroup end
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
|
" vim: sw=2 ts=2 et
|
3
pack/acp/start/vim-go/ftplugin/gomod/commands.vim
Normal file
3
pack/acp/start/vim-go/ftplugin/gomod/commands.vim
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
command! -nargs=0 -range GoModFmt call go#mod#Format()
|
||||||
|
|
||||||
|
command! -nargs=0 GoModFmtAutoSaveToggle call go#mod#ToggleModFmtAutoSave()
|
1
pack/acp/start/vim-go/ftplugin/gomod/mappings.vim
Normal file
1
pack/acp/start/vim-go/ftplugin/gomod/mappings.vim
Normal file
|
@ -0,0 +1 @@
|
||||||
|
nnoremap <silent> <Plug>(go-mod-fmt) :<C-u>call go#mod#Format()<CR>
|
|
@ -169,7 +169,7 @@ endsnippet
|
||||||
# error multiple return
|
# error multiple return
|
||||||
snippet errn, "Error return with two return values" !b
|
snippet errn, "Error return with two return values" !b
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ${1:nil}, err
|
return ${1:nil}, ${2:err}
|
||||||
}
|
}
|
||||||
${0}
|
${0}
|
||||||
endsnippet
|
endsnippet
|
||||||
|
@ -363,6 +363,28 @@ func Test${1:Function}(t *testing.T) {
|
||||||
}
|
}
|
||||||
endsnippet
|
endsnippet
|
||||||
|
|
||||||
|
# test table snippet
|
||||||
|
snippet tt
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
expected string
|
||||||
|
given string
|
||||||
|
}{
|
||||||
|
{"${1}", "${2}", "${3}",},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T){
|
||||||
|
actual := ${0:${VISUAL}}(tt.given)
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Errorf("$0(%s): expected %s, actual %s", tt.given, tt.expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
endsnippet
|
||||||
|
|
||||||
|
|
||||||
snippet hf "http.HandlerFunc" !b
|
snippet hf "http.HandlerFunc" !b
|
||||||
func ${1:handler}(w http.ResponseWriter, r *http.Request) {
|
func ${1:handler}(w http.ResponseWriter, r *http.Request) {
|
||||||
${0:fmt.Fprintf(w, "hello world")}
|
${0:fmt.Fprintf(w, "hello world")}
|
||||||
|
|
17
pack/acp/start/vim-go/gosnippets/minisnip/_go_tt
Normal file
17
pack/acp/start/vim-go/gosnippets/minisnip/_go_tt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
expected string
|
||||||
|
given string
|
||||||
|
}{
|
||||||
|
{"", "", "",},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T){
|
||||||
|
actual := {{++}}(tt.given)
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Errorf("{{+~\~1+}}(%s): expected %s, actual %s", tt.given, tt.expected, actual)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
|
@ -315,6 +315,25 @@ abbr func TestXYZ(t *testing.T) { ... }
|
||||||
func Test${1:Function}(t *testing.T) {
|
func Test${1:Function}(t *testing.T) {
|
||||||
${0}
|
${0}
|
||||||
}
|
}
|
||||||
|
# test table snippet
|
||||||
|
snippet tt
|
||||||
|
abbr var test = {...}{...} for {t.Run(){...}}
|
||||||
|
var tests = []struct {
|
||||||
|
name string
|
||||||
|
expected string
|
||||||
|
given string
|
||||||
|
}{
|
||||||
|
{"${2}", "${3}", "${4}",},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
tt := tt
|
||||||
|
t.Run(tt.name, func(t *testing.T){
|
||||||
|
actual := ${1:Function}(tt.given)
|
||||||
|
if actual != tt.expected {
|
||||||
|
t.Errorf("given(%s): expected %s, actual %s", tt.given, tt.expected, actual)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
# test server
|
# test server
|
||||||
snippet tsrv
|
snippet tsrv
|
||||||
abbr ts := httptest.NewServer(...)
|
abbr ts := httptest.NewServer(...)
|
||||||
|
|
|
@ -24,18 +24,11 @@ if exists("*GoIndent")
|
||||||
finish
|
finish
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" use shiftwidth function only if it's available
|
" don't spam the user when Vim is started in Vi compatibility mode
|
||||||
if exists('*shiftwidth')
|
let s:cpo_save = &cpo
|
||||||
func s:sw()
|
set cpo&vim
|
||||||
return shiftwidth()
|
|
||||||
endfunc
|
|
||||||
else
|
|
||||||
func s:sw()
|
|
||||||
return &sw
|
|
||||||
endfunc
|
|
||||||
endif
|
|
||||||
|
|
||||||
function! GoIndent(lnum)
|
function! GoIndent(lnum) abort
|
||||||
let prevlnum = prevnonblank(a:lnum-1)
|
let prevlnum = prevnonblank(a:lnum-1)
|
||||||
if prevlnum == 0
|
if prevlnum == 0
|
||||||
" top of file
|
" top of file
|
||||||
|
@ -49,19 +42,30 @@ function! GoIndent(lnum)
|
||||||
|
|
||||||
let ind = previ
|
let ind = previ
|
||||||
|
|
||||||
|
for synid in synstack(a:lnum, 1)
|
||||||
|
if synIDattr(synid, 'name') == 'goRawString'
|
||||||
|
if prevl =~ '\%(\%(:\?=\)\|(\|,\)\s*`[^`]*$'
|
||||||
|
" previous line started a multi-line raw string
|
||||||
|
return 0
|
||||||
|
endif
|
||||||
|
" return -1 to keep the current indent.
|
||||||
|
return -1
|
||||||
|
endif
|
||||||
|
endfor
|
||||||
|
|
||||||
if prevl =~ '[({]\s*$'
|
if prevl =~ '[({]\s*$'
|
||||||
" previous line opened a block
|
" previous line opened a block
|
||||||
let ind += s:sw()
|
let ind += shiftwidth()
|
||||||
endif
|
endif
|
||||||
if prevl =~# '^\s*\(case .*\|default\):$'
|
if prevl =~# '^\s*\(case .*\|default\):$'
|
||||||
" previous line is part of a switch statement
|
" previous line is part of a switch statement
|
||||||
let ind += s:sw()
|
let ind += shiftwidth()
|
||||||
endif
|
endif
|
||||||
" TODO: handle if the previous line is a label.
|
" TODO: handle if the previous line is a label.
|
||||||
|
|
||||||
if thisl =~ '^\s*[)}]'
|
if thisl =~ '^\s*[)}]'
|
||||||
" this line closed a block
|
" this line closed a block
|
||||||
let ind -= s:sw()
|
let ind -= shiftwidth()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" Colons are tricky.
|
" Colons are tricky.
|
||||||
|
@ -69,10 +73,14 @@ function! GoIndent(lnum)
|
||||||
" We ignore trying to deal with jump labels because (a) they're rare, and
|
" 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.
|
" (b) they're hard to disambiguate from a composite literal key.
|
||||||
if thisl =~# '^\s*\(case .*\|default\):$'
|
if thisl =~# '^\s*\(case .*\|default\):$'
|
||||||
let ind -= s:sw()
|
let ind -= shiftwidth()
|
||||||
endif
|
endif
|
||||||
|
|
||||||
return ind
|
return ind
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
|
" restore Vi compatibility settings
|
||||||
|
let &cpo = s:cpo_save
|
||||||
|
unlet s:cpo_save
|
||||||
|
|
||||||
" vim: sw=2 ts=2 et
|
" vim: sw=2 ts=2 et
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue