Updated vim-go to v1.24

This commit is contained in:
Anthony Rose 2020-10-28 13:35:24 +00:00
parent 35a4e11c1a
commit 075a943af7
106 changed files with 6983 additions and 1637 deletions

View file

@ -4,3 +4,5 @@
.dlv/
.git/
.viminfo
issues/
autoload/go/**/pkg/

View file

@ -0,0 +1 @@
patreon: bhcleek

View file

@ -1,21 +1,47 @@
### What did you do? (required. The issue will be **closed** when not provided.)
<!--
Before filing an issue, please check if vim-go's help addresses your problem (see `:help go-troubleshooting`).
Consider executing `:GoReportGitHubIssue` to populate much of this information automatically.
-->
### What did you do? (required: The issue will be **closed** when not provided)
<!--
If possible, please provide clear steps for reproducing the problem.
-->
### What did you expect to happen?
### What happened instead?
### Configuration (**MUST** fill this out):
* vim-go 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):
#### `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`):
If this is not provided or is obviously incomplete, the issue may be unceremoniously closed.
-->
<!-- vimrc -->
<details><summary>vimrc</summary><br><pre>
* Go version (`go version`):
</pre></details>
* Go environment (`go env`):
#### Vim version (first three lines from `:version`):
<!-- :version -->
#### Go version (`go version`):
<!-- go version -->
#### Go environment
<details><summary><code>go env</code> Output:</summary><br><pre>
<!-- go env -->
</pre></details>
#### gopls version
<details><summary><code>gopls version</code> Output:</summary><br><pre>
<!-- gopls version -->
</pre></details>

View file

@ -0,0 +1,60 @@
name: test
on: [push, pull_request]
jobs:
lint:
name: lint
runs-on: ubuntu-18.04
steps:
- name: set up python
uses: actions/setup-python@v1.2.0
with:
python-version: 3.6
- name: install vim-vint
run: |
python -m pip install --upgrade pip
pip install vim-vint pathlib
- name: checkout
uses: actions/checkout@v2.1.0
- name: install vim
run: $GITHUB_WORKSPACE/scripts/install-vim vim-8.2
- name: install tools
run: $GITHUB_WORKSPACE/scripts/install-tools vim-8.2
- name: lint
run: $GITHUB_WORKSPACE/scripts/lint vim-8.2
test:
name: test
runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
go: ['1.14','1.15']
vim: ['vim-8.0', 'vim-8.2', 'nvim']
steps:
- name: setup Go
uses: actions/setup-go@v2.0.3
with:
go-version: ${{ matrix.go }}
- name: set up python
uses: actions/setup-python@v1.2.0
with:
python-version: 3.6
- name: install covimerage
run: |
python -m pip install --upgrade pip
pip install covimerage==0.2.1 codecov pathlib
- name: checkout
uses: actions/checkout@v2.1.0
- name: install vim
run: $GITHUB_WORKSPACE/scripts/install-vim ${{ matrix.vim }}
- name: install tools
run: $GITHUB_WORKSPACE/scripts/install-tools ${{ matrix.vim }}
- name: test
run: $GITHUB_WORKSPACE/scripts/test -c ${{ matrix.vim }}
- uses: codecov/codecov-action@v1
with:
# token is not required for public repos
#token: ${{ secrets.CODECOV_TOKEN }}
file: $GITHUB_WORKSPACE/coverage.xml
flags: unittests
name: vim-go
fail_ci_if_error: false

View file

@ -1,5 +1,11 @@
.DS_Store
/doc/tags
/.coverage.covimerage
/coverage.xml
*.pyc
.DS_Store
/.bash_history
/.cache
/.config
/.coverage.covimerage
/.local
/.viminfo
/coverage.xml
/doc/tags
/issues

View file

@ -1,23 +0,0 @@
language: go
go:
- 1.12.1
notifications:
email: false
matrix:
include:
- env: SCRIPT="test -c" VIM_VERSION=vim-7.4
- env: SCRIPT="test -c" VIM_VERSION=vim-8.0
- env: SCRIPT="test -c" VIM_VERSION=nvim
- env: ENV=vimlint SCRIPT=lint VIM_VERSION=vim-8.0
language: python
python: 3.6
install:
- ./scripts/install-vim $VIM_VERSION
- |
if [ "$ENV" = "vimlint" ]; then
pip install vim-vint covimerage codecov pathlib
else
pip install --user vim-vint covimerage codecov pathlib
fi
script:
- ./scripts/$SCRIPT $VIM_VERSION

View file

@ -1,5 +1,529 @@
## unplanned
## v1.24 - (September 15, 2020)
IMPROVEMENTS:
* Clarify how `g:go_imports_autosave` and `g:go_fmt_autosave` interact.
[[GH-2893]](https://github.com/fatih/vim-go/pull/2893)
* Document what the working directory will be for `:GoRun`.
[[GH-2898]](https://github.com/fatih/vim-go/pull/2898)
* Add Ultisnip snippet for wrapping errors.
[[GH-2883]](https://github.com/fatih/vim-go/pull/2883)
* Beautify the godoc pop up window border.
[[GH-2900]](https://github.com/fatih/vim-go/pull/2900)
* Default `g:go_doc_url` to https://pkg.go.dev.
[[GH-2884]](https://github.com/fatih/vim-go/pull/2884)
* Default `g:go_gopls_options` to `[-remote=auto]` to share gopls instances
with other plugins and multiple instances of Vim.
[[GH-2905]](https://github.com/fatih/vim-go/pull/2905)
* Use the module root as the working directory when renaming so that all
references to the symbol will be renamed when in module aware mode and
`g:go_rename_command` is set to `gopls`.
[[GH-2917]](https://github.com/fatih/vim-go/pull/2917)
* Change `g:go_rename_command`'s default to `gopls`.
[[GH-2922]](https://github.com/fatih/vim-go/pull/2922)
* Do not send unnecessary textDocument/didChange notifications to `gopls`.
[[GH-2902]](https://github.com/fatih/vim-go/pull/2902)
[[GH-2930]](https://github.com/fatih/vim-go/pull/2930)
* Stop the debugger when the process being debugged exits.
[[GH-2921]](https://github.com/fatih/vim-go/pull/2921)
* Use the module package cache as a source of packages candidates when trying
to complete package names.
[[GH-2936]](https://github.com/fatih/vim-go/pull/2936)
[[GH-2939]](https://github.com/fatih/vim-go/pull/2939)
* Allow interaction with Vim while waiting for a breakpoint to be hit while
debugging.
[[GH-2932]](https://github.com/fatih/vim-go/pull/2932)
* Refactor Vim signs used for debugging breakpoints to avoid id collision with
other plugins.
[[GH-2943]](https://github.com/fatih/vim-go/pull/2943)
* Refactor debugger's rpc response handling to be asynchronous so that Vim will
be responsive while the program being debugged is executing.
[[GH-2948]](https://github.com/fatih/vim-go/pull/2948)
[[GH-2952]](https://github.com/fatih/vim-go/pull/2952)
* Warn when the debugger breaks in a file that has changed since debugging started.
[[GH-2950]](https://github.com/fatih/vim-go/pull/2950)
* Enable `go-run` mappings that use the terminal to work with Vim in addition to Neovim.
[[GH-2956]](https://github.com/fatih/vim-go/pull/2956)
* Use existing diagnostics for the file when the file hasn't changed and
`g:go_metalinter_command` is `gopls`.
[[GH-2960]](https://github.com/fatih/vim-go/pull/2960)
* Add a new option, `g:go_code_completion_icase`, to allow ignoring case when
filtering completion results.
[[GH-2961]](https://github.com/fatih/vim-go/pull/2961)
* Make sure tools are not cross-compiled with `:GoInstallBinaries` and
`:GoUpdateBinaries`.
[[GH-2982]](https://github.com/fatih/vim-go/pull/2982)
[[GH-2988]](https://github.com/fatih/vim-go/pull/2988)
* Add `:GoDebugHalt` to allow a program being debugged to be paused before it
hits a breakpoint.
[[GH-2983]](https://github.com/fatih/vim-go/pull/2983)
* Clear highlighting of the current line when after resuming when debugging.
[[GH-2984]](https://github.com/fatih/vim-go/pull/2984)
* Add `:GoDebugAttach` to debug a running process.
[[GH-2989]](https://github.com/fatih/vim-go/pull/2989)
* Add `g:go_term_reuse` option to allow the reuse of a terminal window.
[[GH-2990]](https://github.com/fatih/vim-go/pull/2990)
* Add official support for using `gopls`' `gofumpt` workspace setting via
`g:go_gopls_gofumpt`.
[[GH-2994]](https://github.com/fatih/vim-go/pull/2994)
[[GH-3005]](https://github.com/fatih/vim-go/pull/3005)
* Add support for using `gopls`' workspace settings that are otherwise not yet
officially supported by vim-go.
[[GH-2994]](https://github.com/fatih/vim-go/pull/2994)
BUG FIXES:
* Fix call to non-existent function in terminal mode edge case.
[[GH-2895]](https://github.com/fatih/vim-go/pull/2895)
* Do not show errors when adding a text property for highlighting fails.
[[GH-2892]](https://github.com/fatih/vim-go/pull/2892)
* Include `errcheck` in `g:go_metalinter_enabled`'s default.
[[GH-2903]](https://github.com/fatih/vim-go/pull/2903)
* Fix display of completion selection information on command-line when
`g:go_echo_go_info` is enabled.
[[GH-2907]](https://github.com/fatih/vim-go/pull/2907)
* Prevent `:GoDebugBreakpoint` from causing delve to exit.
[[GH-2908]](https://github.com/fatih/vim-go/pull/2908)
* Use the resolved directory name for `gopls`' working directory when `go.mod`
is in a symlinked path.
[[GH-2913]](https://github.com/fatih/vim-go/pull/2913)
* Fix buffer reuse with `:GoDef`.
[[GH-2928]](https://github.com/fatih/vim-go/pull/2928)
* Handle breakpoints that are already set before calling `:GoDebugStart` or
`:GoDebugTest` in some locales that cause the `sign place` output to vary.
[[GH-2921]](https://github.com/fatih/vim-go/pull/2921)
* Handle diagnostic errors at the end of a .go file.
[[GH-2942]](https://github.com/fatih/vim-go/pull/2942)
* Fix the `go-implements` mapping to use respect `g:go_implements_mode`.
[[GH-2944]](https://github.com/fatih/vim-go/pull/2944)
* Handle null results from `gopls` when getting definitions or type definitions
from virtual files.
[[GH-2951]](https://github.com/fatih/vim-go/pull/2951)
* Fix warning when Neovim is older than v0.4.0.
[[GH-2959]](https://github.com/fatih/vim-go/pull/2959)
* Correct documentation that referred to `g:go_imports_command` to refer to
`g:go_imports_mode` instead.
[[GH-2969]](https://github.com/fatih/vim-go/pull/2969)
* Remove reference to gocode in error message when `g:go_info_mode` is set to
an unsupported value.
[[GH-2978]](https://github.com/fatih/vim-go/pull/2978)
* Make sure debugging commands are configured when debugging a second time
within a single Vim session.
[[GH-2985]](https://github.com/fatih/vim-go/pull/2985)
* Correct documentation in for `:GoModifyTags` when adding a specific tag
value.
[[GH-3001]](https://github.com/fatih/vim-go/pull/3001)
* Fix the path given to `gopls` when `let g:go_metalinter='gopls'` and
`:GoMetaLinter` is called without any arguments.
[[GH-2992]](https://github.com/fatih/vim-go/pull/2992)
* Do not override a user's configuration for `GoDebugBreakpoint` or
`GoDebugCurrent` highlight groups.
[[GH-2998]](https://github.com/fatih/vim-go/pull/2998)
* Apply `gopls` text edits correctly that insert solitary newlines.
[[GH-3000]](https://github.com/fatih/vim-go/pull/3000)
## v1.23 - (May 16, 2020)
BACKWARDS INCOMPATIBILITIES:
* Remove support for gocode.
[[GH-2686]](https://github.com/fatih/vim-go/pull/2686)
* Require at least Neovim >= 0.4.0
[[GH-2853]](https://github.com/fatih/vim-go/pull/2853)
[[GH-2856]](https://github.com/fatih/vim-go/pull/2856)
[[GH-2863]](https://github.com/fatih/vim-go/pull/2863)
IMPROVEMENTS:
* Make signs for breakpoints configurable.
[[GH-2676]](https://github.com/fatih/vim-go/pull/2676)
[[GH-2690]](https://github.com/fatih/vim-go/pull/2690)
* Enable g:go_gopls_complete_unimported by default to stay aligned with gopls' defaults.
[[GH-2695]](https://github.com/fatih/vim-go/pull/2695)
* Document mappings that were recently added.
[[GH-2699]](https://github.com/fatih/vim-go/pull/2699)
* Handle null arrays better in gopls responses.
[[GH-2703]](https://github.com/fatih/vim-go/pull/2703)
* Use `gopls` defaults by default when they're not otherwise specified in vim-go options.
[[GH-2696]](https://github.com/fatih/vim-go/pull/2696)
* Add support for `gomodifytags --skip-unexported`
[[GH-2660]](https://github.com/fatih/vim-go/pull/2660)
* Show problems that prevent golangci-lint from running linters.
[[GH-2706]](https://github.com/fatih/vim-go/pull/2706)
[[GH-2720]](https://github.com/fatih/vim-go/pull/2720)
* Support golangci-lint config file by not using `--disable-all` when
`g:go_metalinter_enable` or `g:go_metalinter_autosave_enabled` is set to an
empty array.
[[GH-2655]](https://github.com/fatih/vim-go/pull/2655)
[[GH-2715]](https://github.com/fatih/vim-go/pull/2715)
* Add support for Vim8 terminals.
[[GH-2639]](https://github.com/fatih/vim-go/pull/2639)
[[GH-2736]](https://github.com/fatih/vim-go/pull/2736)
* Replace `g:go_gopls_fuzzy_matching` with `g:go_gopls_matcher` in response to
`gopls` deprecation of its `fuzzyMatching` option.
[[GH-2728]](https://github.com/fatih/vim-go/pull/2728)
* Set statuses and progress messages consistently for code quality tools.
[[GH-2727]](https://github.com/fatih/vim-go/pull/2727)
* Add a new supported value to `g:go_fmt_command` to format with `gopls`.
[[GH-2729]](https://github.com/fatih/vim-go/pull/2729)
[[GH-2752]](https://github.com/fatih/vim-go/pull/2752)
[[GH-2852]](https://github.com/fatih/vim-go/pull/2852)
* Handle changes to `go test -v` output.
[[GH-2743]](https://github.com/fatih/vim-go/pull/2743)
* Add `g:go_gopls_mod_tempfile` to configure `gopls`' `tempModfile`
configuration.
[[GH-2747]](https://github.com/fatih/vim-go/pull/2747)
* Add `g:go_gopls_options` to configure `gopls`' commandline options.
[[GH-2747]](https://github.com/fatih/vim-go/pull/2747)
* Improve readability of gopls logs.
[[GH-2773]](https://github.com/fatih/vim-go/pull/2773)
* Introduce `g:go_implements_mode` to allow `:GoImplements` to be satisfied
with `gopls`.
[[GH-2741]](https://github.com/fatih/vim-go/pull/2741)
[[GH-2799]](https://github.com/fatih/vim-go/pull/2799)
* Introduce `g:go_imports_mode` to allow `:GoImports` to be satisfied with
`gopls`.
[[GH-2791]](https://github.com/fatih/vim-go/pull/2791)
[[GH-2794]](https://github.com/fatih/vim-go/pull/2794)
[[GH-2796]](https://github.com/fatih/vim-go/pull/2796)
[[GH-2848]](https://github.com/fatih/vim-go/pull/2848)
* Send LSP synchronization messages to `gopls` when the file does not yet exist
on disk as long as its directory exists.
[[GH-2805]](https://github.com/fatih/vim-go/pull/2805)
* Run `gogetdoc` in the buffer's directory so that it will work regardless of
the user's working directory in module-aware mode.
[[GH-2804]](https://github.com/fatih/vim-go/pull/2804)
* Add `g:go_gopls_analyses` to support `gopls`' analyses options.
[[GH-2820]](https://github.com/fatih/vim-go/pull/2820)
* Add `g:go_gopls_local` to support `gopls`' local option to control how third
party imports are organized.
[[GH-2821]](https://github.com/fatih/vim-go/pull/2821)
* Use gopls to get documentation and documentation links for identifiers under
the cursor.
[[GH-2822]](https://github.com/fatih/vim-go/pull/2822)
[[GH-2839]](https://github.com/fatih/vim-go/pull/2839)
* Clarify documentation for terminal options.
[[GH-2843]](https://github.com/fatih/vim-go/pull/2843)
BUG FIXES:
* Use the discovered full path for gopls when renaming.
[[GH-2692]](https://github.com/fatih/vim-go/pull/2692)
* Execute commands correctly on windows when `'shell'` is not cmd.exe
[[GH-2713]](https://github.com/fatih/vim-go/pull/2713)
[[GH-2724]](https://github.com/fatih/vim-go/pull/2724)
* Always execute `errcheck` in the current package's directory.
[[GH-2726]](https://github.com/fatih/vim-go/pull/2726)
* Fix errors when highlighting diagnostics after a `:GoImports`.
[[GH-2746]](https://github.com/fatih/vim-go/pull/2746)
* Preserve errors from formatting when both formatting and metalinting happen
on save.
[[GH-2733]](https://github.com/fatih/vim-go/pull/2733)
[[GH-2810]](https://github.com/fatih/vim-go/pull/2810)
* Preserve ordering of gopls messages in the log.
[[GH-2753]](https://github.com/fatih/vim-go/pull/2753)
* Fix `:GoDef` on windows when `g:go_def_mode` is set to `gopls`.
[[GH-2768]](https://github.com/fatih/vim-go/pull/2768)
* Handle null values from `gopls`.
[[GH-2778]](https://github.com/fatih/vim-go/pull/2778)
* Preserve diagnostics highlights after formatting.
[[GH-2779]](https://github.com/fatih/vim-go/pull/2779)
* Fix the decoding and encoding of multi-byte file paths received from and sent
to `gopls`.
[[GH-2784]](https://github.com/fatih/vim-go/pull/2784)
* Fix `:GoRun` so that it works as expected when the current working directory
is neither in GOPATH nor within a module.
[[GH-2782]](https://github.com/fatih/vim-go/pull/2782)
[[GH-2818]](https://github.com/fatih/vim-go/pull/2818)
[[GH-2842]](https://github.com/fatih/vim-go/pull/2842)
* Use absolute file paths for `:GoRun`'s arguments in terminal mode.
[[GH-2844]](https://github.com/fatih/vim-go/pull/2844)
* Show the command executed by `:GoRun` when `g:go_debug` includes `'shell-commands'`.
[[GH-2785]](https://github.com/fatih/vim-go/pull/2785)
[[GH-2817]](https://github.com/fatih/vim-go/pull/2817)
* Clear the list for formatting errors when `g:go_fmt_command` is `gopls`.
[[GH-2790]](https://github.com/fatih/vim-go/pull/2790)
* Handle text edits from gopls that are only line insertions.
[[GH-2802]](https://github.com/fatih/vim-go/pull/2802)
[[GH-2803]](https://github.com/fatih/vim-go/pull/2803)
* Add `g:go_imports_autosave` so that imports can be adjusted on save when
`g:go_imports_mode` is set to `gopls`.
[[GH-2800]](https://github.com/fatih/vim-go/pull/2800)
[[GH-2858]](https://github.com/fatih/vim-go/pull/2858)
* Correct vim-go's help to correctly identify `g:go_referrer_mode`'s default.
[[GH-2832]](https://github.com/fatih/vim-go/pull/2832)
* Clear the quickfix list when `:GoLint` succeeds.
[[GH-2833]](https://github.com/fatih/vim-go/pull/2833)
* Respect arguments to `:GoDocBrowser`.
[[GH-2822]](https://github.com/fatih/vim-go/pull/2822)
* Use the correct path to documentation for struct fields with `:GoDocBrowser`.
[[GH-2822]](https://github.com/fatih/vim-go/pull/2822)
* Do not try parsing errors from terminal jobs when the working directory has
been removed.
[[GH-2824]](https://github.com/fatih/vim-go/pull/2824)
* Document that `g:go_jump_to_error` apples to running the metalinter on save,
too.
[[GH-2854]](https://github.com/fatih/vim-go/pull/2854)
* Ignore commented out import statements when executing `:GoImport`.
[[GH-2862]](https://github.com/fatih/vim-go/pull/2862)
* Interpret file paths in `go vet` errors relative to the current buffer's
directory.
[[GH-2882]](https://github.com/fatih/vim-go/pull/2882)
## v1.22 - (January 30, 2020)
BACKWARDS INCOMPATIBILITIES:
* Drop support for Vim 7.4. The minimum required version of Vim is now 8.0.1453.
[[GH-2495]](https://github.com/fatih/vim-go/pull/2495)
[[GH-2497]](https://github.com/fatih/vim-go/pull/2497)
* Drop support for `gometalinter`
[[GH-2494]](https://github.com/fatih/vim-go/pull/2494)
IMPROVEMENTS:
* Highlight the `go` keyword in go.mod files.
[[GH-2473]](https://github.com/fatih/vim-go/pull/2473)
* Use echo functions consistently.
[[GH-2458]](https://github.com/fatih/vim-go/pull/2458)
* Add support for managing goroutines in debugger.
[[GH-2463]](https://github.com/fatih/vim-go/pull/2463)
[[GH-2527]](https://github.com/fatih/vim-go/pull/2527)
* Document `g:go_doc_popup_window`.
[[GH-2506]](https://github.com/fatih/vim-go/pull/2506)
* Make `g:go_doc_popup_window=1` work for Neovim, too.
[[GH-2451]](https://github.com/fatih/vim-go/pull/2451)
[[GH-2512]](https://github.com/fatih/vim-go/pull/2512)
* Handle errors jumping to a definition in a file open in another Vim process
better.
[[GH-2518]](https://github.com/fatih/vim-go/pull/2518)
* Improve the UX when the gopls binary is missing.
[[GH-2522]](https://github.com/fatih/vim-go/pull/2522)
* Use gopls instead of guru for `:GoSameIds`.
[[GH-2519]](https://github.com/fatih/vim-go/pull/2519)
* Use gopls instead of guru for `:GoReferrers`.
[[GH-2535]](https://github.com/fatih/vim-go/pull/2535)
* Update documentation for `g:go_addtags_transform`.
[[GH-2541]](https://github.com/fatih/vim-go/pull/2541)
* Install most helper tools in module aware mode.
[[GH-2545]](https://github.com/fatih/vim-go/pull/2545)
* Add a new option, `g:go_referrers_mode` to allow the user to choose whether
to use gopls or guru for finding references.
[[GH-2566]](https://github.com/fatih/vim-go/pull/2566)
* Add options to control how gopls responds to completion requests.
[[GH-2567]](https://github.com/fatih/vim-go/pull/2567)
[[GH-2568]](https://github.com/fatih/vim-go/pull/2568)
* Add syntax highlighting for binary literals.
[[GH-2557]](https://github.com/fatih/vim-go/pull/2557)
* Improve highlighting of invalid numeric literals.
[[GH-2571]](https://github.com/fatih/vim-go/pull/2571)
[[GH-2587]](https://github.com/fatih/vim-go/pull/2587)
[[GH-2589]](https://github.com/fatih/vim-go/pull/2589)
[[GH-2584]](https://github.com/fatih/vim-go/pull/2584)
[[GH-2597]](https://github.com/fatih/vim-go/pull/2597)
[[GH-2599]](https://github.com/fatih/vim-go/pull/2599)
* Add highlighting of sections reported by gopls diagnostics' errors
and warnings.
[[GH-2569]](https://github.com/fatih/vim-go/pull/2569)
[[GH-2643]](https://github.com/fatih/vim-go/pull/2643)
* Make the highlighting of fzf decls configurable.
[[GH-2572]](https://github.com/fatih/vim-go/pull/2572)
[[GH-2579]](https://github.com/fatih/vim-go/pull/2579)
* Support renaming with gopls.
[[GH-2577]](https://github.com/fatih/vim-go/pull/2577)
[[GH-2618]](https://github.com/fatih/vim-go/pull/2618)
* Add an option, `g:go_gopls_enabled`, to allow gopls integration to be
disabled.
[[GH-2605]](https://github.com/fatih/vim-go/pull/2605)
[[GH-2609]](https://github.com/fatih/vim-go/pull/2609)
[[GH-2638]](https://github.com/fatih/vim-go/pull/2638)
[[GH-2640]](https://github.com/fatih/vim-go/pull/2640)
* Add a buffer level option, `b:go_fmt_options`, to control formatting options
per buffer.
[[GH-2613]](https://github.com/fatih/vim-go/pull/2613)
* Use build tags when running `:GoVet`.
[[GH-2615]](https://github.com/fatih/vim-go/pull/2615)
* Add new snippets for UltiSnips.
[[GH-2623]](https://github.com/fatih/vim-go/pull/2623)
[[GH-2627]](https://github.com/fatih/vim-go/pull/2627)
* Expand completions as snippets when `g:go_gopls_use_placeholders` is set.
[[GH-2624]](https://github.com/fatih/vim-go/pull/2624)
* Add a new function, `:GoDiagnostics` and an associated mapping for seeing
`gopls` diagnostics. Because of the performance implications on large
projects, `g:go_diagnostics_enabled` controls whether all diagnostics are
processed or only the diagnostics for the current buffer.
[[GH-2612]](https://github.com/fatih/vim-go/pull/2612)
* Explain how to find and detect multiple copies of vim-go in the FAQ.
[[GH-2632]](https://github.com/fatih/vim-go/pull/2632)
* Update the issue template to ask for the gopls version and
`:GoReportGitHubIssue` to provide it.
[[GH-2630]](https://github.com/fatih/vim-go/pull/2630)
* Use text properties when possible for some highlighting cases.
[[GH-2652]](https://github.com/fatih/vim-go/pull/2652)
[[GH-2662]](https://github.com/fatih/vim-go/pull/2662)
[[GH-2663]](https://github.com/fatih/vim-go/pull/2663)
[[GH-2672]](https://github.com/fatih/vim-go/pull/2672)
[[GH-2678]](https://github.com/fatih/vim-go/pull/2678)
BUG FIXES:
* Fix removal of missing directories from gopls workspaces.
[[GH-2507]](https://github.com/fatih/vim-go/pull/2507)
* Change to original window before trying to change directories when term job
ends.
[[GH-2508]](https://github.com/fatih/vim-go/pull/2508)
* Swallow errors when the hover info cannot be determined.
[[GH-2515]](https://github.com/fatih/vim-go/pull/2515)
* Fix errors when trying to debug lsp and hover.
[[GH-2516]](https://github.com/fatih/vim-go/pull/2516)
* Reset environment variables on Vim <= 8.0.1831 .
[[GH-2523]](https://github.com/fatih/vim-go/pull/2523)
* Handle empty results from delve.
[[GH-2526]](https://github.com/fatih/vim-go/pull/2526)
* Do not overwrite `updatetime` when `g:go_auto_sameids` or
`g:go_auto_type_info` is set.
[[GH-2529]](https://github.com/fatih/vim-go/pull/2529)
* Fix example for `g:go_debug_log_output` in docs.
[[GH-2547]](https://github.com/fatih/vim-go/pull/2547)
* Use FileChangedShellPost instead of FileChangedShell so that reload messages
are not hidden.
[[GH-2549]](https://github.com/fatih/vim-go/pull/2549)
* Restore cwd after `:GoTest` when `g:go_term_enabled` is set.
[[GH-2556]](https://github.com/fatih/vim-go/pull/2556)
* Expand struct variable correctly in the variables debug window.
[[GH-2574]](https://github.com/fatih/vim-go/pull/2574)
* Show output from errcheck when there are failures other than problems it can
report.
[[GH-2667]](https://github.com/fatih/vim-go/pull/2667)
## v1.21 - (September 11, 2019)
BACKWARDS INCOMPATIBILITIES:
* `g:go_metalinter_disabled` has been removed.
[[GH-2375]](https://github.com/fatih/vim-go/pull/2375)
IMPROVEMENTS:
* Add a new option, `g:go_code_completion_enabled`, to control whether omnifunc
is set.
[[GH-2229]](https://github.com/fatih/vim-go/pull/2229)
* Use build tags with golangci-lint.
[[GH-2261]](https://github.com/fatih/vim-go/pull/2261)
* Allow debugging of packages outside of GOPATH without a go.mod file.
[[GH-2269]](https://github.com/fatih/vim-go/pull/2269)
* Show which example failed when Example tests fail
[[GH-2277]](https://github.com/fatih/vim-go/pull/2277)
* Show function signature and return types in preview window when autocompleting functions and methods.
[[GH-2289]](https://github.com/fatih/vim-go/pull/2289)
* Improve the user experience when using null modules.
[[GH-2300]](https://github.com/fatih/vim-go/pull/2300)
* Modify `:GoReportGitHubIssue` to include vim-go configuration values
[[GH-2323]](https://github.com/fatih/vim-go/pull/2323)
* Respect `g:go_info_mode='gopls'` in go#complete#GetInfo.
[[GH-2313]](https://github.com/fatih/vim-go/pull/2313)
* Allow `:GoLint`, `:GoErrCheck`, and `:GoDebug` to work in null modules.
[[GH-2335]](https://github.com/fatih/vim-go/pull/2335)
* Change default value for `g:go_info_mode` and `g:go_def_mode` to `'gopls'`.
[[GH-2329]](https://github.com/fatih/vim-go/pull/2329)
* Add a new option, `g:go_doc_popup_window` to optionally use a popup window
for godoc in Vim 8.1.1513 and later.
[[GH-2347]](https://github.com/fatih/vim-go/pull/2347)
* Add `:GoAddWorkspace` function to support multiple workspaces with gopls.
[[GH-2356]](https://github.com/fatih/vim-go/pull/2356)
* Install gopls from its stable package.
[[GH-2360]](https://github.com/fatih/vim-go/pull/2360)
* Disambiguate progress message when initializing gopls.
[[GH-2369]](https://github.com/fatih/vim-go/pull/2369)
* Calculate LSP position correctly when on a line that contains multi-byte
characters before the position.
[[GH-2389]](https://github.com/fatih/vim-go/pull/2389)
* Calculate Vim position correctly from LSP text position.
[[GH-2395]](https://github.com/fatih/vim-go/pull/2395)
* Use the statusline to display gopls initialization status messages and only
echo the statuses when `g:go_echo_command_info` is set.
[[GH-2422]](https://github.com/fatih/vim-go/pull/2422)
* Send configuration to gopls so that build tags will be considered and hover
content won't have documentation.
[[GH-2429]](https://github.com/fatih/vim-go/pull/2429)
* Add a new option, `g:go_term_close_on_exit`, to control whether jobs run in a
terminal window will close the terminal window when the job exits.
[[GH-2409]](https://github.com/fatih/vim-go/pull/2409)
* Allow `g:go_template_file` and `g:go_template_test_files` to reside outside
of vim-go's template directory.
[[GH-2434]](https://github.com/fatih/vim-go/pull/2434)
* Add a new command, `:GoLSPDebugBrowser`, to open a browser to gopls debugging
view.
[[GH-2436]](https://github.com/fatih/vim-go/pull/2436)
* Restart gopls automatically when it is updated via `:GoUpdateBinaries`.
[[GH-2453]](https://github.com/fatih/vim-go/pull/2453)
* Reset `'more'` while installing binaries to avoid unnecessary more prompts.
[[GH-2457]](https://github.com/fatih/vim-go/pull/2457)
* Highlight `%w` as a format specifier (for Go 1.13).
[[GH-2433]](https://github.com/fatih/vim-go/pull/2433)
* Handle changes to Go 1.13's go vet output that gometalinter isn't expecting.
[[GH-2475]](https://github.com/fatih/vim-go/pull/2475)
* Make `golangci-lint` the default value for `g:go_metalinter_command`.
[[GH-2478]](https://github.com/fatih/vim-go/pull/2478)
* Parse compiler errors from Go 1.13 `go vet` correctly.
[[GH-2485]](https://github.com/fatih/vim-go/pull/2485)
BUG FIXES:
* display info about function and function types whose parameters are
`interface{}` without truncating the function signature.
[[GH-2244]](https://github.com/fatih/vim-go/pull/2244)
* install tools that require GOPATH mode when in module mode.
[[GH-2253]](https://github.com/fatih/vim-go/pull/2253)
* Detect GOPATH when starting `gopls`
[[GH-2255]](https://github.com/fatih/vim-go/pull/2255)
* Handle `gopls` responses in the same window from which the respective request
originated.
[[GH-2266]](https://github.com/fatih/vim-go/pull/2266)
* Show completion matches from gocode.
[[GH-2267]](https://github.com/fatih/vim-go/pull/2267)
* Show the completion preview window.
[[GH-2268]](https://github.com/fatih/vim-go/pull/2268)
* Set the anchor for method documentation correctly.
[[GH-2276]](https://github.com/fatih/vim-go/pull/2276)
* Respect the LSP information for determining where candidate matches start.
[[GH-2291]](https://github.com/fatih/vim-go/pull/2291)
* Restore environment variables with backslashes correctly.
[[GH-2292]](https://github.com/fatih/vim-go/pull/2292)
* Modify handling of gopls output for `:GoInfo` to ensure the value will be
displayed.
[[GH-2311]](https://github.com/fatih/vim-go/pull/2311)
* Run `:GoLint` successfully in null modules.
[[GH-2318]](https://github.com/fatih/vim-go/pull/2318)
* Ensure actions on save work in new buffers that have not yet been persisted to disk.
[[GH-2319]](https://github.com/fatih/vim-go/pull/2319)
* Restore population of information in `:GoReportGitHubIssue`.
[[GH-2312]](https://github.com/fatih/vim-go/pull/2312)
* Do not jump back to the originating window when jumping to definitions with
`g:go_def_mode='gopls'`.
[[GH-2327]](https://github.com/fatih/vim-go/pull/2327)
* Fix getting information about a valid identifier for which gopls returns no
information (e.g. calling `:GoInfo` on a package identifier).
[[GH-2339]](https://github.com/fatih/vim-go/pull/2339)
* Fix tab completion of package names on the cmdline in null modules.
[[GH-2342]](https://github.com/fatih/vim-go/pull/2342)
* Display identifier info correctly when the identifier has no godoc.
[[GH-2373]](https://github.com/fatih/vim-go/pull/2373)
* Fix false positives when saving a buffer and `g:go_metalinter_command` is
`golangci-lint`.
[[GH-2367]](https://github.com/fatih/vim-go/pull/2367)
* Fix `:GoDebugRestart`.
[[GH-2390]](https://github.com/fatih/vim-go/pull/2390)
* Do not execute tests twice in terminal mode.
[[GH-2397]](https://github.com/fatih/vim-go/pull/2397)
* Do not open a new buffer in Neovim when there are compilation errors and
terminal mode is enabled.
[[GH-2401]](https://github.com/fatih/vim-go/pull/2401)
* Fix error due to typo in implementation of `:GoAddWorkspace`.
[[GH-2415]](https://github.com/fatih/vim-go/pull/2401)
* Do not format the file automatically when `g:go_format_autosave` is set and
the file being written is not the current file.
[[GH-2442]](https://github.com/fatih/vim-go/pull/2442)
* Fix `go-debug-stepout` mapping.
[[GH-2464]](https://github.com/fatih/vim-go/pull/2464)
* Handle paths with spaces correctly when executing jobs.
[[GH-2472]](https://github.com/fatih/vim-go/pull/2472)
* Remove a space in the default value for `g:go_debug_log_output`, so that
Delve will start on Windows.
[[GH-2480]](https://github.com/fatih/vim-go/pull/2480)
## 1.20 - (April 22, 2019)
FEATURES:
@ -13,7 +537,7 @@ FEATURES:
* New `:GoDefType` command to jump to a type definition from an instance of the
type.
BACKWARDS INCOMPATABILITIES:
BACKWARDS INCOMPATIBILITIES:
* `g:go_highlight_function_arguments` is renamed to `g:go_highlight_function_parameters`
[[GH-2117]](https://github.com/fatih/vim-go/pull/2117)

View file

@ -1,6 +1,6 @@
FROM golang:1.12.1
FROM golang:1.15.1
RUN apt-get update -y && \
RUN apt-get update -y --allow-insecure-repositories && \
apt-get install -y build-essential curl git libncurses5-dev python3-pip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
@ -10,11 +10,18 @@ RUN pip3 install vim-vint
RUN useradd -ms /bin/bash -d /vim-go vim-go
USER vim-go
COPY scripts/install-vim /vim-go/scripts/install-vim
WORKDIR /vim-go
RUN scripts/install-vim vim-8.0
RUN scripts/install-vim vim-8.2
RUN scripts/install-vim nvim
COPY . /vim-go/
WORKDIR /vim-go
RUN scripts/install-vim vim-7.4
RUN scripts/install-vim vim-8.0
RUN scripts/install-vim nvim
RUN scripts/install-tools vim-8.0
RUN scripts/install-tools vim-8.2
RUN scripts/install-tools nvim
ENTRYPOINT ["make"]

View file

@ -1,4 +1,4 @@
VIMS ?= vim-7.4 vim-8.0 nvim
VIMS ?= vim-8.0 vim-8.2 nvim
all: install lint test
@ -6,6 +6,7 @@ install:
@echo "==> Installing Vims: $(VIMS)"
@for vim in $(VIMS); do \
./scripts/install-vim $$vim; \
./scripts/install-tools $$vim; \
done
test:
@ -16,7 +17,7 @@ test:
lint:
@echo "==> Running linting tools"
@./scripts/lint vim-8.0
@./scripts/lint vim-8.2
docker:
@echo "==> Building/starting Docker container"

View file

@ -1,4 +1,6 @@
# vim-go [![Build Status](http://img.shields.io/travis/fatih/vim-go.svg?style=flat-square)](https://travis-ci.org/fatih/vim-go)
# vim-go [![Build Status](http://img.shields.io/travis/fatih/vim-go.svg?style=flat-square)](https://travis-ci.org/fatih/vim-go) [![GitHub Actions Status](https://github.com/fatih/vim-go/workflows/test/badge.svg)](https://github.com/fatih/vim-go/actions)
<p align="center">
<img style="float: right;" src="assets/vim-go.png" alt="Vim-go logo"/>
@ -13,15 +15,15 @@ This plugin adds Go language support for Vim, with the following main features:
* Quickly execute your current file(s) with `:GoRun`.
* Improved syntax highlighting and folding.
* Debug programs with integrated `delve` support with `:GoDebugStart`.
* Completion support via `gocode` and `gopls`.
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
* Completion and many other features support via `gopls`.
* formatting on save keeps the cursor position and undo history.
* Go to symbol/declaration with `:GoDef`.
* Look up documentation with `:GoDoc` or `:GoDocBrowser`.
* Easily import packages via `:GoImport`, remove them via `:GoDrop`.
* Precise type-safe renaming of identifiers with `:GoRename`.
* See which code is covered by tests with `:GoCoverage`.
* Add or remove tags on struct fields with `:GoAddTags` and `:GoRemoveTags`.
* Call `gometalinter` with `:GoMetaLinter` to invoke all possible linters
* Call `golangci-lint` with `:GoMetaLinter` to invoke all possible linters
(`golint`, `vet`, `errcheck`, `deadcode`, etc.) and put the result in the
quickfix or location list.
* Lint your code with `:GoLint`, run your code through `:GoVet` to catch static
@ -30,10 +32,12 @@ This plugin adds Go language support for Vim, with the following main features:
`:GoCallees`, and `:GoReferrers`.
* ... and many more! Please see [doc/vim-go.txt](doc/vim-go.txt) for more
information.
* The `gopls` instance can be shared with other Vim plugins.
* Vim-go's use of `gopls` can be disabled.
## Install
vim-go requires at least Vim 7.4.2009 or Neovim 0.3.1.
vim-go requires at least Vim 8.0.1453 or Neovim 0.4.0.
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,
@ -68,8 +72,28 @@ Depending on your installation method, you may have to generate the plugin's
[`help tags`](http://vimhelp.appspot.com/helphelp.txt.html#%3Ahelptags)
manually (e.g. `:helptags ALL`).
We also have an [official vim-go tutorial](https://github.com/fatih/vim-go-tutorial).
We also have an [official vim-go tutorial](https://github.com/fatih/vim-go/wiki).
## FAQ and troubleshooting
The FAQ and troubleshooting tips are in the documentation and can be quickly
accessed using `:help go-troubleshooting`. If you believe you've found a bug or
shortcoming in vim-go that is neither addressed by help nor in [existing
issues](https://github.com/fatih/vim-go/issues), please open an issue with
clear reproduction steps. `:GoReportGitHubIssue` can be used pre-populate a lot
of the information needed when creating a new issue.
## Contributing
All PRs are welcome. If you are planning to contribute a large patch or to
integrate a new tool, please create an issue first to get any upfront questions
or design decisions out of the way first.
You can run the tests locally by running `make`. It will lint the VimL for you,
lint the documentation, and run the tests against the minimum required version
of Vim, other versions of Vim that may be critical to support, and Neovim.
## License
The BSD 3-Clause License - see [`LICENSE`](LICENSE) for more details

View file

@ -121,10 +121,10 @@ function! s:source(mode,...) abort
\ decl.col
\)
call add(ret_decls, printf("%s\t%s %s\t%s",
\ s:color(decl.ident . space, "Function"),
\ s:color(decl.keyword, "Keyword"),
\ s:color(pos, "SpecialComment"),
\ s:color(decl.full, "Comment"),
\ s:color(decl.ident . space, "goDeclsFzfFunction"),
\ s:color(decl.keyword, "goDeclsFzfKeyword"),
\ s:color(pos, "goDeclsFzfSpecialComment"),
\ s:color(decl.full, "goDeclsFzfComment"),
\))
endfor

View file

@ -11,7 +11,43 @@ function! go#auto#template_autocreate()
call go#template#create()
endfunction
function! go#auto#echo_go_info()
function! go#auto#complete_done()
call s:echo_go_info()
call s:ExpandSnippet()
endfunction
function! s:ExpandSnippet() abort
if !exists('v:completed_item') || empty(v:completed_item) || !go#config#GoplsUsePlaceholders()
return
endif
let l:engine = go#config#SnippetEngine()
if l:engine is 'ultisnips'
if !get(g:, 'did_plugin_ultisnips', 0)
return
endif
" the snippet may have a '{\}' in it. For UltiSnips, that should be spelled
" \{}. fmt.Printf is such a snippet that can be used to demonstrate.
let l:snippet = substitute(v:completed_item.word, '{\\}', '\{}', 'g')
call UltiSnips#Anon(l:snippet, v:completed_item.word, '', 'i')
" elseif l:engine is 'neosnippet'
" " TODO(bc): make the anonymous expansion for neosnippet work
"
" if !get(g:, 'loaded_neosnippet') is 1
" return
" endif
"
" " neosnippet#anonymous doesn't need a trigger, so replace the
" " completed_item.word with an empty string before calling neosnippet#anonymous
" let l:snippet = substitute(v:completed_item.word, '{\\}', '\{\}', 'g')
" call setline('.', substitute(getline('.'), substitute(v:completed_item.word, '\', '\\', 'g'), '', ''))
" call neosnippet#anonymous(l:snippet)
endif
endfunction
function! s:echo_go_info()
if !go#config#EchoGoInfo()
return
endif
@ -21,46 +57,109 @@ function! go#auto#echo_go_info()
endif
let item = v:completed_item
if !has_key(item, "info")
if !has_key(item, "user_data")
return
endif
if empty(item.info)
if empty(item.user_data)
return
endif
redraws! | echo "vim-go: " | echohl Function | echon item.info | echohl None
redraws! | echo "vim-go: " | echohl Function | echon item.user_data | echohl None
endfunction
function! go#auto#auto_type_info()
if !go#config#AutoTypeInfo() || !filereadable(expand('%:p'))
let s:timer_id = 0
" go#auto#update_autocmd() will be called on BufEnter,CursorHold. This
" configures the augroup according to conditions below.
"
" | # | has_timer | should_enable | do |
" |---|-----------|---------------|------------------------------------|
" | 1 | false | false | return early |
" | 2 | true | true | return early |
" | 3 | true | false | clear the group and stop the timer |
" | 4 | false | true | configure the group |
function! go#auto#update_autocmd()
let has_timer = get(b:, 'has_timer')
let should_enable = go#config#AutoTypeInfo() || go#config#AutoSameids()
if (!has_timer && !should_enable) || (has_timer && should_enable)
return
endif
" GoInfo automatic update
call go#tool#Info(0)
if has_timer
augroup vim-go-buffer-auto
autocmd! * <buffer>
augroup END
let b:has_timer = 0
call s:timer_stop()
return
endif
augroup vim-go-buffer-auto
autocmd! * <buffer>
autocmd CursorMoved <buffer> call s:timer_restart()
autocmd BufLeave <buffer> call s:timer_stop()
augroup END
let b:has_timer = 1
call s:timer_start()
endfunction
function! go#auto#auto_sameids()
if !go#config#AutoSameids() || !filereadable(expand('%:p'))
return
function! s:timer_restart()
if isdirectory(expand('%:p:h'))
call s:timer_stop()
call s:timer_start()
endif
endfunction
" GoSameId automatic update
call go#guru#SameIds(0)
function! s:timer_stop()
if s:timer_id
call timer_stop(s:timer_id)
let s:timer_id = 0
endif
endfunction
function! s:timer_start()
let s:timer_id = timer_start(go#config#Updatetime(), function('s:handler'))
endfunction
function! s:handler(timer_id)
if go#config#AutoTypeInfo()
call go#tool#Info(0)
endif
if go#config#AutoSameids()
call go#guru#SameIds(0)
endif
let s:timer_id = 0
endfunction
function! go#auto#fmt_autosave()
if !go#config#FmtAutosave() || !filereadable(expand('%:p'))
if !(isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
return
endif
if !(go#config#FmtAutosave() || go#config#ImportsAutosave())
return
endif
if go#config#ImportsAutosave() && !(go#config#FmtAutosave() && go#config#FmtCommand() == 'goimports')
call go#fmt#Format(1)
" return early when the imports mode is goimports, because there's no need
" to format again when goimports was run
if go#config#ImportsMode() == 'goimports'
return
endif
endif
if !go#config#FmtAutosave()
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'))
if !go#config#MetalinterAutosave() || !isdirectory(expand('%:p:h'))
return
endif
@ -69,7 +168,7 @@ function! go#auto#metalinter_autosave()
endfunction
function! go#auto#modfmt_autosave()
if !go#config#ModFmtAutosave() || !filereadable(expand('%:p'))
if !(go#config#ModFmtAutosave() && isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
return
endif
@ -78,7 +177,7 @@ function! go#auto#modfmt_autosave()
endfunction
function! go#auto#asmfmt_autosave()
if !go#config#AsmfmtAutosave() || !filereadable(expand('%:p'))
if !(go#config#AsmfmtAutosave() && isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
return
endif

View file

@ -9,8 +9,8 @@ function! go#cmd#autowrite() abort
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).
" often immediately overwritten by the async messages (which also
" doesn't invoke the "hit ENTER" prompt).
call go#util#EchoWarning('[No write since last change]')
sleep 1
return
@ -32,7 +32,7 @@ function! go#cmd#Build(bang, ...) abort
\ map(copy(a:000), "expand(v:val)") +
\ [".", "errors"]
" Vim and Neovim async.
" Vim and Neovim async
if go#util#has_job()
call s:cmd_job({
\ 'cmd': ['go'] + args,
@ -41,8 +41,15 @@ function! go#cmd#Build(bang, ...) abort
\ 'statustype': 'build'
\})
" Vim 7.4 without async
" Vim without async
else
let l:status = {
\ 'desc': 'current status',
\ 'type': 'build',
\ 'state': "started",
\ }
call go#statusline#Update(expand('%:p:h'), l:status)
let default_makeprg = &makeprg
let &makeprg = "go " . join(go#util#Shelllist(args), ' ')
@ -68,10 +75,16 @@ function! go#cmd#Build(bang, ...) abort
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
let l:status.state = 'failed'
else
call go#util#EchoSuccess("[build] SUCCESS")
let l:status.state = 'success'
if go#config#EchoCommandInfo()
call go#util#EchoSuccess("[build] SUCCESS")
endif
endif
call go#statusline#Update(expand('%:p:h'), l:status)
endif
endfunction
@ -104,16 +117,15 @@ endfunction
" Run runs the current file (and their dependencies if any) in a new terminal.
function! go#cmd#RunTerm(bang, mode, files) abort
let cmd = "go run "
let tags = go#config#BuildTags()
if len(tags) > 0
let cmd .= "-tags " . go#util#Shellescape(tags) . " "
let cmd = ["go", "run"]
if len(go#config#BuildTags()) > 0
call extend(cmd, ["-tags", go#config#BuildTags()])
endif
if empty(a:files)
let cmd .= go#util#Shelljoin(go#tool#Files())
call extend(cmd, go#tool#Files())
else
let cmd .= go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1)
call extend(cmd, map(copy(a:files), funcref('s:expandRunArgs')))
endif
call go#term#newmode(a:bang, cmd, s:runerrorformat(), a:mode)
endfunction
@ -123,7 +135,7 @@ endfunction
" suitable for long running apps, because vim is blocking by default and
" calling long running apps will block the whole UI.
function! go#cmd#Run(bang, ...) abort
if has('nvim')
if go#config#TermEnabled()
call go#cmd#RunTerm(a:bang, '', a:000)
return
endif
@ -134,61 +146,101 @@ function! go#cmd#Run(bang, ...) abort
" anything. Once this is implemented we're going to make :GoRun async
endif
let cmd = "go run "
let tags = go#config#BuildTags()
if len(tags) > 0
let cmd .= "-tags " . go#util#Shellescape(tags) . " "
let l:status = {
\ 'desc': 'current status',
\ 'type': 'run',
\ 'state': "started",
\ }
call go#statusline#Update(expand('%:p:h'), l:status)
let l:cmd = ['go', 'run']
let l:tags = go#config#BuildTags()
if len(l:tags) > 0
let l:cmd = l:cmd + ['-tags', l:tags]
endif
if a:0 == 0
let l:files = go#tool#Files()
else
let l:files = map(copy(a:000), funcref('s:expandRunArgs'))
endif
let l:cmd = l:cmd + l:files
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let l:dir = getcwd()
if go#util#IsWin()
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
try
if go#util#HasDebug('shell-commands')
call go#util#EchoInfo(printf('shell command: %s', string(l:cmd)))
endif
execute l:cd . fnameescape(expand("%:p:h"))
exec printf('!%s', go#util#Shelljoin(l:cmd, 1))
finally
execute l:cd . fnameescape(l:dir)
endtry
let l:status.state = 'success'
if v:shell_error
redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None
let l:status.state = 'failed'
if go#config#EchoCommandInfo()
redraws!
call go#util#EchoError('[run] FAILED')
endif
else
redraws! | echon "vim-go: [run] " | echohl Function | echon "SUCCESS"| echohl None
if go#config#EchoCommandInfo()
redraws!
call go#util#EchoSuccess('[run] SUCCESS')
endif
endif
call go#statusline#Update(expand('%:p:h'), l:status)
return
endif
" :make expands '%' and '#' wildcards, so they must also be escaped
let default_makeprg = &makeprg
if a:0 == 0
let &makeprg = cmd . go#util#Shelljoin(go#tool#Files(), 1)
else
let &makeprg = cmd . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
endif
let l:default_makeprg = &makeprg
let &makeprg = go#util#Shelljoin(l:cmd, 1)
let l:listtype = go#list#Type("GoRun")
try
let l:status.state = 'success'
try
" backup user's errorformat, will be restored once we are finished
let l:old_errorformat = &errorformat
let &errorformat = s:runerrorformat()
if go#util#HasDebug('shell-commands')
call go#util#EchoInfo('shell command: ' . l:cmd)
endif
execute l:cd . fnameescape(expand("%:p:h"))
if l:listtype == "locationlist"
exe 'lmake!'
else
exe 'make!'
endif
finally
"restore errorformat
"restore the working directory, errformat, and makeprg
execute cd . fnameescape(l:dir)
let &errorformat = l:old_errorformat
let &makeprg = default_makeprg
let &makeprg = l:default_makeprg
endtry
let l:errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(l:errors))
if !empty(l:errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
if !empty(l:errors)
let l:status.state = 'failed'
if !a:bang
call go#list#JumpToFirst(l:listtype)
endif
endif
call go#statusline#Update(expand('%:p:h'), l:status)
endfunction
" Install installs the package by simple calling 'go install'. If any argument
@ -255,9 +307,18 @@ function! go#cmd#Generate(bang, ...) abort
let &makeprg = "go generate " . goargs . ' ' . gofiles
endif
let l:listtype = go#list#Type("GoGenerate")
let l:status = {
\ 'desc': 'current status',
\ 'type': 'generate',
\ 'state': "started",
\ }
call go#statusline#Update(expand('%:p:h'), l:status)
echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None
if go#config#EchoCommandInfo()
call go#util#EchoProgress('generating ...')
endif
let l:listtype = go#list#Type("GoGenerate")
try
if l:listtype == "locationlist"
@ -273,13 +334,18 @@ function! go#cmd#Generate(bang, ...) abort
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if !empty(errors)
let l:status.status = 'failed'
if !a:bang
call go#list#JumpToFirst(l:listtype)
endif
else
redraws! | echon "vim-go: " | echohl Function | echon "[generate] SUCCESS"| echohl None
let l:status.status = 'success'
if go#config#EchoCommandInfo()
redraws!
call go#util#EchoSuccess('[generate] SUCCESS')
endif
endif
call go#statusline#Update(expand(':%:p:h'), l:status)
endfunction
function! s:runerrorformat()
@ -288,6 +354,18 @@ function! s:runerrorformat()
return l:errorformat
endfunction
" s:expandRunArgs expands arguments for go#cmd#Run according to the
" documentation of :GoRun. When val is a readable file, it is expanded to the
" full path so that go run can be executed in the current buffer's directory.
" val is return unaltered otherwise to support non-file arguments to go run.
function! s:expandRunArgs(idx, val) abort
let l:val = expand(a:val)
if !filereadable(l:val)
return l:val
endif
return fnamemodify(l:val, ':p')")
endfunction
" ---------------------
" | Vim job callbacks |
" ---------------------

View file

@ -2,276 +2,48 @@
let s:cpo_save = &cpo
set cpo&vim
function! s:gocodeCommand(cmd, args) abort
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)
return []
endif
let socket_type = go#config#GocodeSocketType()
let cmd = [bin_path]
let cmd = extend(cmd, ['-sock', socket_type])
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:args)
return cmd
endfunction
function! s:sync_gocode(cmd, args, input) abort
" We might hit cache problems, as gocode doesn't handle different GOPATHs
" well. See: https://github.com/nsf/gocode/issues/239
let old_goroot = $GOROOT
let $GOROOT = go#util#env("goroot")
try
let cmd = s:gocodeCommand(a:cmd, a:args)
" gocode can sometimes be slow, so redraw now to avoid waiting for gocode
" to return before redrawing automatically.
redraw
let [l:result, l:err] = go#util#Exec(cmd, a:input)
finally
let $GOROOT = old_goroot
endtry
if l:err != 0
return "[0, []]"
endif
if &encoding != 'utf-8'
let l:result = iconv(l:result, 'utf-8', &encoding)
endif
return l:result
endfunction
function! s:gocodeAutocomplete() abort
" use the offset as is, because the cursor position is the position for
" which autocomplete candidates are needed.
return s:sync_gocode('autocomplete',
\ [expand('%:p'), go#util#OffsetCursor()],
\ go#util#GetLines())
endfunction
" go#complete#GoInfo returns the description of the identifier under the
" cursor.
function! go#complete#GetInfo() abort
return s:sync_info(0)
endfunction
function! go#complete#Info(showstatus) abort
if go#util#has_job(1)
return s:async_info(1, a:showstatus)
else
return s:sync_info(1)
endif
endfunction
function! s:async_info(echo, showstatus)
let state = {'echo': a:echo}
" explicitly bind complete to state so that within it, self will
" always refer to state. See :help Partial for more information.
let state.complete = function('s:complete', [], state)
" add 1 to the offset, so that the position at the cursor will be included
" in gocode's search
let offset = go#util#OffsetCursor()+1
" We might hit cache problems, as gocode doesn't handle different GOPATHs
" well. See: https://github.com/nsf/gocode/issues/239
let env = {
\ "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',
\ [expand('%:p'), offset])
" 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:
" s:gocodeFile()}, but unfortunately {in_io: 'buffer', in_buf: bufnr('%')}
" doesn't work.
call extend(opts, {
\ 'env': env,
\ 'in_io': 'file',
\ 'in_name': s:gocodeFile(),
\ })
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
function! s:gocodeFile()
let file = tempname()
call writefile(go#util#GetLines(), file)
return file
endfunction
function! s:sync_info(echo)
" add 1 to the offset, so that the position at the cursor will be included
" in gocode's search
let offset = go#util#OffsetCursor()+1
let result = s:sync_gocode('autocomplete',
\ [expand('%:p'), offset],
\ go#util#GetLines())
let result = s:info_filter(a:echo, result)
return s:info_complete(a:echo, result)
endfunction
function! s:info_filter(echo, result) abort
if empty(a:result)
return ""
endif
let l:result = eval(a:result)
if len(l:result) != 2
return ""
endif
let l:candidates = l:result[1]
if len(l:candidates) == 1
" When gocode panics in vim mode, it returns
" [0, [{'word': 'PANIC', 'abbr': 'PANIC PANIC PANIC', 'info': 'PANIC PANIC PANIC'}]]
if a:echo && l:candidates[0].info ==# "PANIC PANIC PANIC"
return ""
endif
return l:candidates[0].info
endif
let filtered = []
let wordMatch = '\<' . expand("<cword>") . '\>'
" escape single quotes in wordMatch before passing it to filter
let wordMatch = substitute(wordMatch, "'", "''", "g")
let filtered = filter(l:candidates, "v:val.info =~ '".wordMatch."'")
if len(l:filtered) == 0
return "no matches"
elseif len(l:filtered) > 1
return "ambiguous match"
endif
return l:filtered[0].info
endfunction
function! s:info_complete(echo, result) abort
if a:echo
call go#util#ShowInfo(a:result)
endif
return a:result
endfunction
function! s:trim_bracket(val) abort
let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '')
return a:val
endfunction
let s:completions = []
function! go#complete#GocodeComplete(findstart, base) abort
"findstart = 1 when we need to get the text length
if a:findstart == 1
let l:completions = []
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
else
let s = getline(".")[col('.') - 1]
if s =~ '[(){}\{\}]'
return map(copy(s:completions[1]), 's:trim_bracket(v:val)')
endif
return s:completions[1]
endif
return go#lsp#GetInfo()
endfunction
function! go#complete#Complete(findstart, base) abort
let l:state = {'done': 0, 'matches': []}
if !go#config#GoplsEnabled()
return -3
endif
function! s:handler(state, matches) abort dict
let l:state = {'done': 0, 'matches': [], 'start': -1}
function! s:handler(state, start, matches) abort dict
let a:state.start = a:start
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]))
let [l:line, l:col] = getpos('.')[1:2]
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
let l:completion = go#lsp#Completion(expand('%:p'), l:line, l:col, funcref('s:handler', [l:state]))
if l:completion
return -3
endif
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('.')
let s:completions = l:state.matches
return go#lsp#lsp#PositionOf(getline(l:line+1), l:state.start-1)
else "findstart = 0 when we need to return the list of completions
return s:completions
endif
@ -281,11 +53,11 @@ function! go#complete#ToggleAutoTypeInfo() abort
if go#config#AutoTypeInfo()
call go#config#SetAutoTypeInfo(0)
call go#util#EchoProgress("auto type info disabled")
return
end
call go#config#SetAutoTypeInfo(1)
call go#util#EchoProgress("auto type info enabled")
else
call go#config#SetAutoTypeInfo(1)
call go#util#EchoProgress("auto type info enabled")
endif
call go#auto#update_autocmd()
endfunction
" restore Vi compatibility settings

View file

@ -2,23 +2,24 @@
let s:cpo_save = &cpo
set cpo&vim
func! Test_GetInfo()
func! Test_GetInfo_gopls()
let g:go_info_mode = 'gopls'
call s:getinfo()
unlet g:go_info_mode
endfunction
func! s:getinfo()
let l:filename = 'complete/complete.go'
let l:tmp = gotest#load_fixture(l:filename)
try
call cursor(8, 3)
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
let expected = 'func Example(s string)'
let actual = go#complete#GetInfo()
call assert_equal(expected, actual)
finally
call delete(l:tmp, 'rf')
endtry
endfunction
" restore Vi compatibility settings

View file

@ -21,10 +21,12 @@ endfunction
function! go#config#SetBuildTags(value) abort
if a:value is ''
silent! unlet g:go_build_tags
call go#lsp#ResetWorkspaceDirectories()
return
endif
let g:go_build_tags = a:value
call go#lsp#ResetWorkspaceDirectories()
endfunction
function! go#config#TestTimeout() abort
@ -47,8 +49,23 @@ function! go#config#TermMode() abort
return get(g:, 'go_term_mode', 'vsplit')
endfunction
function! go#config#TermCloseOnExit() abort
return get(g:, 'go_term_close_on_exit', 1)
endfunction
function! go#config#TermReuse() abort
return get(g:, 'go_term_reuse', 0)
endfunction
function! go#config#SetTermCloseOnExit(value) abort
let g:go_term_close_on_exit = a:value
endfunction
function! go#config#TermEnabled() abort
return has('nvim') && get(g:, 'go_term_enabled', 0)
" nvim always support
" vim will support if terminal feature exists
let l:support = has('nvim') || has('terminal')
return support && get(g:, 'go_term_enabled', 0)
endfunction
function! go#config#SetTermEnabled(value) abort
@ -72,7 +89,18 @@ function! go#config#StatuslineDuration() abort
endfunction
function! go#config#SnippetEngine() abort
return get(g:, 'go_snippet_engine', 'automatic')
let l:engine = get(g:, 'go_snippet_engine', 'automatic')
if l:engine is? "automatic"
if get(g:, 'did_plugin_ultisnips') is 1
let l:engine = 'ultisnips'
elseif get(g:, 'loaded_neosnippet') is 1
let l:engine = 'neosnippet'
elseif get(g:, 'loaded_minisnip') is 1
let l:engine = 'minisnip'
endif
endif
return l:engine
endfunction
function! go#config#PlayBrowserCommand() abort
@ -114,7 +142,7 @@ function! go#config#ListAutoclose() abort
endfunction
function! go#config#InfoMode() abort
return get(g:, 'go_info_mode', 'gocode')
return get(g:, 'go_info_mode', 'gopls')
endfunction
function! go#config#GuruScope() abort
@ -139,30 +167,13 @@ function! go#config#SetGuruScope(scope) abort
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'
let godoc_url = get(g:, 'go_doc_url', 'https://pkg.go.dev')
if godoc_url isnot 'https://pkg.go.dev'
" strip last '/' character if available
let last_char = strlen(godoc_url) - 1
if godoc_url[last_char] == '/'
@ -174,12 +185,15 @@ function! go#config#DocUrl() abort
return godoc_url
endfunction
function! go#config#DocPopupWindow() abort
return get(g:, 'go_doc_popup_window', 0)
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')
return get(g:, 'go_def_mode', 'gopls')
endfunction
function! go#config#DeclsIncludes() abort
@ -192,9 +206,10 @@ endfunction
function! go#config#DebugWindows() abort
return get(g:, 'go_debug_windows', {
\ 'stack': 'leftabove 20vnew',
\ 'out': 'botright 10new',
\ 'vars': 'leftabove 30vnew',
\ 'stack': 'leftabove 20new',
\ 'goroutines': 'botright 10new',
\ 'out': 'botright 5new',
\ }
\ )
@ -211,7 +226,7 @@ function! go#config#DebugCommands() abort
endfunction
function! go#config#DebugLogOutput() abort
return get(g:, 'go_debug_log_output', 'debugger, rpc')
return get(g:, 'go_debug_log_output', 'debugger,rpc')
endfunction
function! go#config#LspLog() abort
@ -236,6 +251,10 @@ function! go#config#AddtagsTransform() abort
return get(g:, 'go_addtags_transform', "snakecase")
endfunction
function! go#config#AddtagsSkipUnexported() abort
return get(g:, 'go_addtags_skip_unexported', 0)
endfunction
function! go#config#TemplateAutocreate() abort
return get(g:, "go_template_autocreate", 1)
endfunction
@ -245,31 +264,15 @@ function! go#config#SetTemplateAutocreate(value) abort
endfunction
function! go#config#MetalinterCommand() abort
return get(g:, "go_metalinter_command", "gometalinter")
return get(g:, "go_metalinter_command", "golangci-lint")
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)
return get(g:, "go_metalinter_autosave_enabled", ["govet", "golint"])
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", [])
return get(g:, "go_metalinter_enabled", ["vet", "golint", "errcheck"])
endfunction
function! go#config#GolintBin() abort
@ -296,6 +299,10 @@ function! go#config#FmtAutosave() abort
return get(g:, "go_fmt_autosave", 1)
endfunction
function! go#config#ImportsAutosave() abort
return get(g:, 'go_imports_autosave', 0)
endfunction
function! go#config#SetFmtAutosave(value) abort
let g:go_fmt_autosave = a:value
endfunction
@ -340,8 +347,12 @@ function! go#config#FmtCommand() abort
return get(g:, "go_fmt_command", "gofmt")
endfunction
function! go#config#ImportsMode() abort
return get(g:, "go_imports_mode", "goimports")
endfunction
function! go#config#FmtOptions() abort
return get(g:, "go_fmt_options", {})
return get(b:, "go_fmt_options", get(g:, "go_fmt_options", {}))
endfunction
function! go#config#FmtFailSilently() abort
@ -356,8 +367,13 @@ function! go#config#PlayOpenBrowser() abort
return get(g:, "go_play_open_browser", 1)
endfunction
function! go#config#RenameCommand() abort
" delegate to go#config#GorenameBin for backwards compatability.
return get(g:, "go_rename_command", go#config#GorenameBin())
endfunction
function! go#config#GorenameBin() abort
return get(g:, "go_gorename_bin", "gorename")
return get(g:, "go_gorename_bin", "gopls")
endfunction
function! go#config#GorenamePrefill() abort
@ -451,10 +467,22 @@ function! go#config#HighlightVariableDeclarations() abort
return get(g:, 'go_highlight_variable_declarations', 0)
endfunction
function! go#config#HighlightDiagnosticErrors() abort
return get(g:, 'go_highlight_diagnostic_errors', 1)
endfunction
function! go#config#HighlightDiagnosticWarnings() abort
return get(g:, 'go_highlight_diagnostic_warnings', 1)
endfunction
function! go#config#HighlightDebug() abort
return get(g:, 'go_highlight_debug', 1)
endfunction
function! go#config#DebugBreakpointSignText() abort
return get(g:, 'go_debug_breakpoint_sign_text', '>')
endfunction
function! go#config#FoldEnable(...) abort
if a:0 > 0
return index(go#config#FoldEnable(), a:1) > -1
@ -466,6 +494,82 @@ function! go#config#EchoGoInfo() abort
return get(g:, "go_echo_go_info", 1)
endfunction
function! go#config#CodeCompletionEnabled() abort
return get(g:, "go_code_completion_enabled", 1)
endfunction
function! go#config#CodeCompletionIcase() abort
return get(g:, "go_code_completion_icase", 0)
endfunction
function! go#config#Updatetime() abort
let go_updatetime = get(g:, 'go_updatetime', 800)
return go_updatetime == 0 ? &updatetime : go_updatetime
endfunction
function! go#config#ReferrersMode() abort
return get(g:, 'go_referrers_mode', 'gopls')
endfunction
function! go#config#ImplementsMode() abort
return get(g:, 'go_implements_mode', 'guru')
endfunction
function! go#config#GoplsCompleteUnimported() abort
return get(g:, 'go_gopls_complete_unimported', v:null)
endfunction
function! go#config#GoplsDeepCompletion() abort
return get(g:, 'go_gopls_deep_completion', v:null)
endfunction
function! go#config#GoplsMatcher() abort
if !exists('g:go_gopls_matcher') && get(g:, 'g:go_gopls_fuzzy_matching', v:null) is 1
return 'fuzzy'
endif
return get(g:, 'go_gopls_matcher', v:null)
endfunction
function! go#config#GoplsStaticCheck() abort
return get(g:, 'go_gopls_staticcheck', v:null)
endfunction
function! go#config#GoplsUsePlaceholders() abort
return get(g:, 'go_gopls_use_placeholders', v:null)
endfunction
function! go#config#GoplsTempModfile() abort
return get(g:, 'go_gopls_temp_modfile', v:null)
endfunction
function! go#config#GoplsAnalyses() abort
return get(g:, 'go_gopls_analyses', v:null)
endfunction
function! go#config#GoplsLocal() abort
return get(g:, 'go_gopls_local', v:null)
endfunction
function! go#config#GoplsGofumpt() abort
return get(g:, 'go_gopls_gofumpt', v:null)
endfunction
function! go#config#GoplsSettings() abort
return get(g:, 'go_gopls_settings', v:null)
endfunction
function! go#config#GoplsEnabled() abort
return get(g:, 'go_gopls_enabled', 1)
endfunction
function! go#config#DiagnosticsEnabled() abort
return get(g:, 'go_diagnostics_enabled', 0)
endfunction
function! go#config#GoplsOptions() abort
return get(g:, 'go_gopls_options', ['-remote=auto'])
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

View file

@ -0,0 +1,107 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
func! Test_SetBuildTags() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:dir = 'test-fixtures/config/buildtags'
let l:jumpstart = [0, 4, 2, 0]
execute 'e ' . printf('%s/buildtags.go', l:dir)
let l:jumpstartbuf = bufnr('')
call setpos('.', [l:jumpstartbuf, l:jumpstart[1], l:jumpstart[2], 0])
let l:expectedfilename = printf('%s/foo.go', l:dir)
let l:expected = [0, 5, 1, 0]
call assert_notequal(l:expected, l:jumpstart)
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expectedfilename, bufname("%"))
call assert_equal(l:expected, getpos('.'))
execute 'e ' . printf('%s/buildtags.go', l:dir)
" prepare to wait for the workspace/configuration request
let g:go_debug=['lsp']
" set the build constraint
call go#config#SetBuildTags('constrained')
" wait for the workspace/configuration request
let l:lsplog = getbufline('__GOLSP_LOG__', 1, '$')
let l:start = reltime()
while match(l:lsplog, 'workspace/configuration') == -1 && reltimefloat(reltime(l:start)) < 10
sleep 50m
let l:lsplog = getbufline('__GOLSP_LOG__', 1, '$')
endwhile
unlet g:go_debug
" close the __GOLSP_LOG__ window
only
" verify the cursor position within buildtags.go
call setpos('.', [l:jumpstartbuf, l:jumpstart[1], l:jumpstart[2], 0])
call assert_equal(l:jumpstart, getpos('.'))
let l:expectedfilename = printf('%s/constrainedfoo.go', l:dir)
let l:expected = [0, 6, 1, 0]
call assert_notequal(l:expected, l:jumpstart)
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expectedfilename, bufname("%"))
call assert_equal(l:expected, getpos('.'))
let l:lsplog = getbufline('__GOLSP_LOG__', 1, '$')
finally
call go#config#SetBuildTags('')
unlet g:go_def_mode
endtry
endfunc
func! Test_GoplsEnabled_Clear() abort
if !go#util#has_job()
return
endif
try
let g:go_gopls_enabled = 0
let l:tmp = gotest#write_file('gopls_disabled.go', [
\ 'package example',
\ '',
\ 'func Example() {',
\ "\tprintln(" . '"hello, world!")',
\ '}',
\ ] )
finally
unlet g:go_gopls_enabled
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

View file

@ -25,10 +25,10 @@ endfunction
" the code. Calling it again reruns the tests and shows the last updated
" coverage.
function! go#coverage#Buffer(bang, ...) abort
" we use matchaddpos() which was introduce with 7.4.330, be sure we have
" it: http://ftp.vim.org/vim/patches/7.4/7.4.330
" check if the version of Vim being tested supports matchaddpos()
if !exists("*matchaddpos")
call go#util#EchoError("GoCoverage is supported with Vim version 7.4-330 or later")
call go#util#EchoError("GoCoverage is not supported by your version of Vim.")
return -1
endif

File diff suppressed because it is too large Load diff

View file

@ -10,6 +10,10 @@ function! Test_GoDebugStart_RelativePackage() abort
call s:debug('./debug/debugmain')
endfunction
function! Test_GoDebugStart_RelativePackage_NullModule() abort
call s:debug('./debug/debugmain', 1)
endfunction
function! Test_GoDebugStart_Package() abort
call s:debug('debug/debugmain')
endfunction
@ -20,20 +24,21 @@ function! Test_GoDebugStart_Errors() abort
endif
try
let l:tmp = gotest#load_fixture('debug/compilerror/main.go')
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': 6, 'bufnr': bufnr('%'), '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)
call go#debug#Start('debug')
let l:actual = getqflist()
let l:start = reltime()
@ -52,14 +57,22 @@ function! Test_GoDebugStart_Errors() abort
endtry
endfunction
" s:debug takes 2 optional arguments. The first is a package to debug. The
" second is a flag to indicate whether to reset GOPATH after
" gotest#load_fixture is called in order to test behavior outside of GOPATH.
function! s:debug(...) abort
if !go#util#has_job()
return
endif
try
let $oldgopath = $GOPATH
let l:tmp = gotest#load_fixture('debug/debugmain/debugmain.go')
if a:0 > 1 && a:2 == 1
let $GOPATH = $oldgopath
endif
call go#debug#Breakpoint(6)
call assert_false(exists(':GoDebugStop'))
@ -67,9 +80,9 @@ function! s:debug(...) abort
if a:0 == 0
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
execute l:cd . ' debug/debugmain'
let l:job = go#debug#Start(0)
let l:job = go#debug#Start('debug')
else
let l:job = go#debug#Start(0, a:1)
let l:job = go#debug#Start('debug', a:1)
endif
let l:start = reltime()

View file

@ -6,7 +6,7 @@ let s:go_stack = []
let s:go_stack_level = 0
function! go#def#Jump(mode, type) abort
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
let l:fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
" so guru right now is slow for some people. previously we were using
" godef which also has it's own quirks. But this issue come up so many
@ -66,7 +66,15 @@ function! go#def#Jump(mode, type) abort
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
endif
elseif bin_name == 'gopls'
let [l:line, l:col] = getpos('.')[1:2]
if !go#config#GoplsEnabled()
call go#util#EchoError("go_def_mode is 'gopls', but gopls is disabled")
return
endif
" reset l:fname when using gopls so that the filename will be converted to
" a URI correctly on windows.
let l:fname = expand('%')
let [l:line, l:col] = go#lsp#lsp#Position()
" delegate to gopls, with an empty job object and an exit status of 0
" (they're irrelevant for gopls).
if a:type
@ -162,9 +170,9 @@ function! go#def#jump_to_declaration(out, mode, bin_name) abort
if filename != fnamemodify(expand("%"), ':p:gs?\\?/?')
" 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
if go#config#DefReuseBuffer() && bufloaded(filename) != 0 && bufwinnr(filename) != -1
" jumpt to existing buffer if it exists
execute bufwinnr(filename) . 'wincmd w'
if go#config#DefReuseBuffer() && bufwinnr(filename) != -1
" jump to existing buffer if it exists
call win_gotoid(bufwinid(filename))
else
if &modified
let cmd = 'hide edit'
@ -186,7 +194,13 @@ function! go#def#jump_to_declaration(out, mode, bin_name) abort
endif
" open the file and jump to line and column
exec cmd fnameescape(fnamemodify(filename, ':.'))
try
exec cmd fnameescape(fnamemodify(filename, ':.'))
catch
if stridx(v:exception, ':E325:') < 0
call go#util#EchoError(v:exception)
endif
endtry
endif
endif
call cursor(line, col)

View file

@ -2,19 +2,21 @@
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
func! Test_jump_to_declaration_guru() abort
try
let l:filename = 'def/jump.go'
let lnum = 5
let col = 6
let l:lnum = 5
let l:col = 6
let l:tmp = gotest#load_fixture(l:filename)
let guru_out = printf("%s:%d:%d: defined here as func main", filename, lnum, col)
call go#def#jump_to_declaration(guru_out, "", 'guru')
let l:guru_out = printf("%s:%d:%d: defined here as func main", l:filename, l:lnum, l:col)
call go#def#jump_to_declaration(l:guru_out, "", 'guru')
call assert_equal(filename, bufname("%"))
call assert_equal(lnum, getcurpos()[1])
call assert_equal(col, getcurpos()[2])
call assert_equal(l:filename, bufname("%"))
call assert_equal(l:lnum, getcurpos()[1])
call assert_equal(l:col, getcurpos()[2])
finally
call delete(l:tmp, 'rf')
endtry
@ -22,17 +24,17 @@ endfunc
func! Test_jump_to_declaration_godef() abort
try
let filename = 'def/jump.go'
let lnum = 5
let col = 6
let l:filename = 'def/jump.go'
let l:lnum = 5
let l:col = 6
let l:tmp = gotest#load_fixture(l:filename)
let godef_out = printf("%s:%d:%d\ndefined here as func main", filename, lnum, col)
let l:godef_out = printf("%s:%d:%d\ndefined here as func main", l:filename, l:lnum, l:col)
call go#def#jump_to_declaration(godef_out, "", 'godef')
call assert_equal(filename, bufname("%"))
call assert_equal(lnum, getcurpos()[1])
call assert_equal(col, getcurpos()[2])
call assert_equal(l:filename, bufname("%"))
call assert_equal(l:lnum, getcurpos()[1])
call assert_equal(l:col, getcurpos()[2])
finally
call delete(l:tmp, 'rf')
endtry
@ -40,33 +42,180 @@ endfunc
func! Test_Jump_leaves_lists() abort
try
let filename = 'def/jump.go'
let l: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'}]
let l: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' )
call setloclist(0, copy(l:expected), 'r' )
call setqflist(copy(l:expected), 'r' )
let l:bufnr = bufnr('%')
call cursor(6, 7)
if !go#util#has_job()
let g:go_def_mode='godef'
endif
call go#def#Jump('', 0)
let start = reltime()
while bufnr('%') == l:bufnr && reltimefloat(reltime(start)) < 10
if !go#util#has_job()
unlet g:go_def_mode
endif
let l:start = reltime()
while bufnr('%') == l:bufnr && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
let actual = getloclist(winnr())
call gotest#assert_quickfix(actual, expected)
let l:actual = getloclist(0)
call gotest#assert_quickfix(l:actual, l:expected)
let actual = getqflist()
call gotest#assert_quickfix(actual, expected)
let l:actual = getqflist()
call gotest#assert_quickfix(l:actual, l:expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_DefJump_gopls_simple_first() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:tmp = gotest#write_file('simple/firstposition/firstposition.go', [
\ 'package firstposition',
\ '',
\ 'func Example() {',
\ "\tid := " . '"foo"',
\ "\tprintln(" . '"id:", id)',
\ '}',
\ ] )
let l:expected = [0, 4, 2, 0]
call assert_notequal(l:expected, getpos('.'))
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expected, getpos('.'))
finally
call delete(l:tmp, 'rf')
unlet g:go_def_mode
endtry
endfunc
func! Test_DefJump_gopls_simple_last() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:tmp = gotest#write_file('simple/lastposition/lastposition.go', [
\ 'package lastposition',
\ '',
\ 'func Example() {',
\ "\tid := " . '"foo"',
\ "\tprintln(" . '"id:", id)',
\ '}',
\ ] )
let l:expected = [0, 4, 2, 0]
call assert_notequal(l:expected, getpos('.'))
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expected, getpos('.'))
finally
call delete(l:tmp, 'rf')
unlet g:go_def_mode
endtry
endfunc
func! Test_DefJump_gopls_MultipleCodeUnit_first() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:tmp = gotest#write_file('multiplecodeunit/firstposition/firstposition.go', [
\ 'package firstposition',
\ '',
\ 'func Example() {',
\ "\t𐐀, id := " . '"foo", "bar"',
\ "\tprintln(" . '"(𐐀, id):", 𐐀, id)',
\ '}',
\ ] )
let l:expected = [0, 4, 8, 0]
call assert_notequal(l:expected, getpos('.'))
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expected, getpos('.'))
finally
call delete(l:tmp, 'rf')
unlet g:go_def_mode
endtry
endfunc
func! Test_DefJump_gopls_MultipleCodeUnit_last() abort
if !go#util#has_job()
return
endif
try
let g:go_def_mode = 'gopls'
let l:tmp = gotest#write_file('multiplecodeunit/lastposition/lastposition.go', [
\ 'package lastposition',
\ '',
\ 'func Example() {',
\ "\t𐐀, id := " . '"foo", "bar"',
\ "\tprintln(" . '"(𐐀, id):", 𐐀, id)',
\ '}',
\ ] )
let l:expected = [0, 4, 8, 0]
call assert_notequal(l:expected, getpos('.'))
call go#def#Jump('', 0)
let l:start = reltime()
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
sleep 100m
endwhile
call assert_equal(l:expected, getpos('.'))
finally
call delete(l:tmp, 'rf')
unlet g:go_def_mode
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -6,36 +6,25 @@
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
let s:buf_nr = -1
function! go#doc#OpenBrowser(...) abort
" check if we have gogetdoc as it gives us more and accurate information.
" Only supported if we have json_decode as it's not worth to parse the plain
" non-json output of gogetdoc
let bin_path = go#path#CheckBinPath('gogetdoc')
if !empty(bin_path) && exists('*json_decode')
let [l:json_out, l:err] = s:gogetdoc(1)
if len(a:000) == 0
let [l:out, l:err] = go#lsp#DocLink()
if l:err
call go#util#EchoError(json_out)
call go#util#EchoError(l:out)
return
endif
let out = json_decode(json_out)
if type(out) != type({})
call go#util#EchoError("gogetdoc output is malformed")
if len(l:out) == 0
call go#util#EchoWarning("could not path for doc URL")
endif
let import = out["import"]
let name = out["name"]
let decl = out["decl"]
let l:godoc_url = printf('%s/%s', go#config#DocUrl(), l:out)
let godoc_url = go#config#DocUrl()
let godoc_url .= "/" . import
if decl !~ "^package"
let godoc_url .= "#" . name
endif
call go#util#OpenBrowser(godoc_url)
call go#util#OpenBrowser(l:godoc_url)
return
endif
@ -48,7 +37,7 @@ function! go#doc#OpenBrowser(...) abort
let exported_name = pkgs[1]
" example url: https://godoc.org/github.com/fatih/set#Set
let godoc_url = go#config#DocUrl() . "/" . pkg . "#" . exported_name
let godoc_url = printf('%s/%s#%s', go#config#DocUrl(), pkg, exported_name)
call go#util#OpenBrowser(godoc_url)
endfunction
@ -56,11 +45,8 @@ function! go#doc#Open(newmode, mode, ...) abort
" With argument: run "godoc [arg]".
if len(a:000)
let [l:out, l:err] = go#util#Exec(['go', 'doc'] + a:000)
else " Without argument: run gogetdoc on cursor position.
let [l:out, l:err] = s:gogetdoc(0)
if out == -1
return
endif
else " Without argument: use gopls to get documentation
let [l:out, l:err] = go#lsp#Doc()
endif
if l:err
@ -68,10 +54,62 @@ function! go#doc#Open(newmode, mode, ...) abort
return
endif
call s:GodocView(a:newmode, a:mode, out)
call s:GodocView(a:newmode, a:mode, l:out)
endfunction
function! s:GodocView(newposition, position, content) abort
" popup window
if go#config#DocPopupWindow()
if exists('*popup_atcursor') && exists('*popup_clear')
call popup_clear()
let borderchars = ['-', '|', '-', '|', '+', '+', '+', '+']
if &encoding == "utf-8"
let borderchars = ['─', '│', '─', '│', '┌', '┐', '┘', '└']
endif
call popup_atcursor(split(a:content, '\n'), {
\ 'padding': [1, 1, 1, 1],
\ 'borderchars': borderchars,
\ 'border': [1, 1, 1, 1],
\ })
elseif has('nvim') && exists('*nvim_open_win')
let lines = split(a:content, '\n')
let height = 0
let width = 0
for line in lines
let lw = strdisplaywidth(line)
if lw > width
let width = lw
endif
let height += 1
endfor
let width += 1 " right margin
let max_height = go#config#DocMaxHeight()
if height > max_height
let height = max_height
endif
let buf = nvim_create_buf(v:false, v:true)
call nvim_buf_set_lines(buf, 0, -1, v:true, lines)
let opts = {
\ 'relative': 'cursor',
\ 'row': 1,
\ 'col': 0,
\ 'width': width,
\ 'height': height,
\ 'style': 'minimal',
\ }
call nvim_open_win(buf, v:true, opts)
setlocal nomodified nomodifiable filetype=godoc
" close easily with CR, Esc and q
noremap <buffer> <silent> <CR> :<C-U>close<CR>
noremap <buffer> <silent> <Esc> :<C-U>close<CR>
noremap <buffer> <silent> q :<C-U>close<CR>
endif
return
endif
" reuse existing buffer window if it exists otherwise create a new one
let is_visible = bufexists(s:buf_nr) && bufwinnr(s:buf_nr) != -1
if !bufexists(s:buf_nr)
@ -129,23 +167,6 @@ function! s:GodocView(newposition, position, content) abort
nnoremap <buffer> <silent> <Esc>[ <Esc>[
endfunction
function! s:gogetdoc(json) abort
let l:cmd = [
\ 'gogetdoc',
\ '-tags', go#config#BuildTags(),
\ '-pos', expand("%:p:gs!\\!/!") . ':#' . go#util#OffsetCursor()]
if a:json
let l:cmd += ['-json']
endif
if &modified
let l:cmd += ['-modified']
return go#util#Exec(l:cmd, go#util#archive())
endif
return go#util#Exec(l:cmd)
endfunction
" returns the package and exported name. exported name might be empty.
" ie: fmt and Println
" ie: github.com/fatih/set and New

View file

@ -62,7 +62,7 @@ func! Test_fillstruct_two_line() abort
\ '\tAddress: "",',
\ '}) }'])
finally
"call delete(l:tmp, 'rf')
call delete(l:tmp, 'rf')
endtry
endfunc

View file

@ -18,6 +18,30 @@ set cpo&vim
" this and have VimL experience, please look at the function for
" improvements, patches are welcome :)
function! go#fmt#Format(withGoimport) abort
let l:bin_name = go#config#FmtCommand()
if a:withGoimport == 1
let l:mode = go#config#ImportsMode()
if l:mode == 'gopls'
if !go#config#GoplsEnabled()
call go#util#EchoError("go_imports_mode is 'gopls', but gopls is disabled")
return
endif
call go#lsp#Imports()
return
endif
let l:bin_name = 'goimports'
endif
if l:bin_name == 'gopls'
if !go#config#GoplsEnabled()
call go#util#EchoError("go_def_mode is 'gopls', but gopls is disabled")
return
endif
call go#lsp#Format()
return
endif
if go#config#FmtExperimental()
" Using winsaveview to save/restore cursor state has the problem of
" closing folds on save:
@ -52,20 +76,16 @@ function! go#fmt#Format(withGoimport) abort
let l:tmpname = tr(l:tmpname, '\', '/')
endif
let bin_name = go#config#FmtCommand()
if a:withGoimport == 1
let bin_name = "goimports"
endif
let current_col = col('.')
let [l:out, l:err] = go#fmt#run(bin_name, l:tmpname, expand('%'))
let diff_offset = len(readfile(l:tmpname)) - line('$')
let [l:out, l:err] = go#fmt#run(l:bin_name, l:tmpname, expand('%'))
let line_offset = len(readfile(l:tmpname)) - line('$')
let l:orig_line = getline('.')
if l:err == 0
call go#fmt#update_file(l:tmpname, expand('%'))
elseif !go#config#FmtFailSilently()
let errors = s:parse_errors(expand('%'), out)
call s:show_errors(errors)
let l:errors = s:replace_filename(expand('%'), out)
call go#fmt#ShowErrors(l:errors)
endif
" We didn't use the temp file, so clean up
@ -87,8 +107,10 @@ function! go#fmt#Format(withGoimport) abort
call winrestview(l:curw)
endif
" be smart and jump to the line the new statement was added/removed
call cursor(line('.') + diff_offset, current_col)
" be smart and jump to the line the new statement was added/removed and
" adjust the column within the line
let l:lineno = line('.') + line_offset
call cursor(l:lineno, current_col + (len(getline(l:lineno)) - len(l:orig_line)))
" Syntax highlighting breaks less often.
syntax sync fromstart
@ -115,28 +137,12 @@ function! go#fmt#update_file(source, target)
" reload buffer to reflect latest changes
silent edit!
call go#lsp#DidChange(expand(a:target, ':p'))
let &fileformat = old_fileformat
let &syntax = &syntax
let l:listtype = go#list#Type("GoFmt")
" 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
call go#fmt#CleanErrors()
endfunction
" run runs the gofmt/goimport command for the given source file and returns
@ -169,39 +175,42 @@ function! s:fmt_cmd(bin_name, source, target)
return cmd
endfunction
" parse_errors parses the given errors and returns a list of parsed errors
function! s:parse_errors(filename, content) abort
let splitted = split(a:content, '\n')
" replace_filename replaces the filename on each line of content with
" a:filename.
function! s:replace_filename(filename, content) abort
let l:errors = split(a:content, '\n')
" list of errors to be put into location list
let errors = []
for line in splitted
let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)')
if !empty(tokens)
call add(errors,{
\"filename": a:filename,
\"lnum": tokens[2],
\"col": tokens[3],
\"text": tokens[4],
\ })
endif
endfor
return errors
let l:errors = map(l:errors, printf('substitute(v:val, ''^.\{-}:'', ''%s:'', '''')', a:filename))
return join(l:errors, "\n")
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
function! go#fmt#CleanErrors() abort
let l:listtype = go#list#Type("GoFmt")
if !empty(a:errors)
call go#list#Populate(l:listtype, a:errors, 'Format')
echohl Error | echomsg "Gofmt returned error" | echohl None
" 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
if has_key(l:list_title, 'title') && (l:list_title['title'] == 'Format' || l:list_title['title'] == 'GoMetaLinterAutoSave')
call go#list#Clean(l:listtype)
endif
endfunction
" show_errors opens a location list and shows the given errors. If errors is
" empty, it closes the the location list.
function! go#fmt#ShowErrors(errors) abort
let l:errorformat = '%f:%l:%c:\ %m'
let l:listtype = go#list#Type("GoFmt")
call go#list#ParseFormat(l:listtype, l:errorformat, a:errors, 'Format', 0)
let l:errors = go#list#Get(l:listtype)
" 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))
" it if there are any.
call go#list#Window(l:listtype, len(l:errors))
endfunction
function! go#fmt#ToggleFmtAutoSave() abort

View file

@ -35,7 +35,7 @@ func! Test_update_file() abort
endfunc
func! Test_goimports() abort
let $GOPATH = 'test-fixtures/fmt/'
let $GOPATH = printf('%s/%s', fnamemodify(getcwd(), ':p'), 'test-fixtures/fmt')
let actual_file = tempname()
call writefile(readfile("test-fixtures/fmt/src/imports/goimports.go"), actual_file)

View file

@ -232,11 +232,10 @@ function! go#guru#Describe(selected) abort
endfunction
function! go#guru#DescribeInfo(showstatus) abort
" json_encode() and friends are introduced with this patch (7.4.1304)
" vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ
" nvim: https://github.com/neovim/neovim/pull/4131
" check if the version of Vim being tested supports json_decode()
if !exists("*json_decode")
call go#util#EchoError("requires 'json_decode'. Update your Vim/Neovim version.")
call go#util#EchoError("GoDescribeInfo requires 'json_decode'. Update your Vim/Neovim version.")
return
endif
@ -406,43 +405,31 @@ endfunction
" Show all refs to entity denoted by selected identifier
function! go#guru#Referrers(selected) abort
let args = {
\ 'mode': 'referrers',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 0,
\ }
\ 'mode': 'referrers',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 0,
\ }
call s:run_guru(args)
endfunction
function! go#guru#SameIds(showstatus) abort
" we use matchaddpos() which was introduce with 7.4.330, be sure we have
" it: http://ftp.vim.org/vim/patches/7.4/7.4.330
" check if the version of Vim being tested supports matchaddpos()
if !exists("*matchaddpos")
call go#util#EchoError("GoSameIds requires 'matchaddpos'. Update your Vim/Neovim version.")
return
endif
" json_encode() and friends are introduced with this patch (7.4.1304)
" vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ
" nvim: https://github.com/neovim/neovim/pull/4131
" check if the version of Vim being tested supports json_decode()
if !exists("*json_decode")
call go#util#EchoError("GoSameIds requires 'json_decode'. Update your Vim/Neovim version.")
return
endif
let args = {
\ 'mode': 'what',
\ 'format': 'json',
\ 'selected': -1,
\ 'needs_scope': 0,
\ 'custom_parse': function('s:same_ids_highlight'),
\ }
if !a:showstatus
let args.disable_progress = 1
endif
call s:run_guru(args)
let [l:line, l:col] = getpos('.')[1:2]
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
call go#lsp#SameIDs(0, expand('%:p'), l:line, l:col, funcref('s:same_ids_highlight'))
endfunction
function! s:same_ids_highlight(exit_val, output, mode) abort
@ -482,18 +469,26 @@ function! s:same_ids_highlight(exit_val, output, mode) abort
endif
let same_ids = result['sameids']
" highlight the lines
let l:matches = []
for item in same_ids
let pos = split(item, ':')
call matchaddpos('goSameId', [[str2nr(pos[-2]), str2nr(pos[-1]), str2nr(poslen)]])
let l:matches = add(l:matches, [str2nr(pos[-2]), str2nr(pos[-1]), str2nr(poslen)])
endfor
call go#util#HighlightPositions('goSameId', l:matches)
if go#config#AutoSameids()
" re-apply SameIds at the current cursor position at the time the buffer
" is redisplayed: e.g. :edit, :GoRename, etc.
augroup vim-go-sameids
autocmd! * <buffer>
autocmd BufWinEnter <buffer> nested call go#guru#SameIds(0)
if has('textprop')
autocmd BufReadPost <buffer> nested call go#guru#SameIds(0)
else
autocmd BufWinEnter <buffer> nested call go#guru#SameIds(0)
endif
augroup end
endif
endfunction
@ -501,15 +496,7 @@ endfunction
" ClearSameIds returns 0 when it removes goSameId groups and non-zero if no
" goSameId groups are found.
function! go#guru#ClearSameIds() abort
let l:cleared = 0
let m = getmatches()
for item in m
if item['group'] == 'goSameId'
call matchdelete(item['id'])
let l:cleared = 1
endif
endfor
let l:cleared = go#util#ClearHighlights('goSameId')
if !l:cleared
return 1
@ -534,11 +521,11 @@ function! go#guru#AutoToggleSameIds() abort
call go#util#EchoProgress("sameids auto highlighting disabled")
call go#guru#ClearSameIds()
call go#config#SetAutoSameids(0)
return
else
call go#util#EchoSuccess("sameids auto highlighting enabled")
call go#config#SetAutoSameids(1)
endif
call go#util#EchoSuccess("sameids auto highlighting enabled")
call go#config#SetAutoSameids(1)
call go#auto#update_autocmd()
endfunction
@ -565,11 +552,11 @@ function! s:parse_guru_output(exit_val, output, title) abort
let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m"
let l:listtype = go#list#Type("_guru")
call go#list#ParseFormat(l:listtype, errformat, a:output, a:title)
call go#list#ParseFormat(l:listtype, errformat, a:output, a:title, 0)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
endfun
endfunction
function! go#guru#Scope(...) abort
if a:0
@ -602,11 +589,9 @@ function! go#guru#DescribeBalloon() abort
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
" check if the version of Vim being tested supports json_decode()
if !exists("*json_decode")
call go#util#EchoError("requires 'json_decode'. Update your Vim/Neovim version.")
call go#util#EchoError("GoDescribeBalloon requires 'json_decode'. Update your Vim/Neovim version.")
return
endif

View file

@ -98,6 +98,325 @@ function! Test_gomodVersion_incompatible_highlight() abort
endtry
endfunc
function! Test_numeric_literal_highlight() abort
syntax on
let tests = {
\ 'lone zero': {'group': 'goDecimalInt', 'value': '0'},
\ 'integer': {'group': 'goDecimalInt', 'value': '1234567890'},
\ 'integerGrouped': {'group': 'goDecimalInt', 'value': '1_234_567_890'},
\ 'integerErrorLeadingUnderscore': {'group': 'goDecimalError', 'value': '_1234_567_890'},
\ 'integerErrorTrailingUnderscore': {'group': 'goDecimalError', 'value': '1_234_567890_'},
\ 'integerErrorDoubleUnderscore': {'group': 'goDecimalError', 'value': '1_234__567_890'},
\ 'hexadecimal': {'group': 'goHexadecimalInt', 'value': '0x0123456789abdef'},
\ 'hexadecimalGrouped': {'group': 'goHexadecimalInt', 'value': '0x012_345_678_9ab_def'},
\ 'hexadecimalErrorLeading': {'group': 'goHexadecimalError', 'value': '0xg0123456789abdef'},
\ 'hexadecimalErrorTrailing': {'group': 'goHexadecimalError', 'value': '0x0123456789abdefg'},
\ 'hexadecimalErrorDoubleUnderscore': {'group': 'goHexadecimalError', 'value': '0x__0123456789abdef'},
\ 'hexadecimalErrorTrailingUnderscore': {'group': 'goHexadecimalError', 'value': '0x0123456789abdef_'},
\ 'heXadecimal': {'group': 'goHexadecimalInt', 'value': '0X0123456789abdef'},
\ 'heXadecimalErrorLeading': {'group': 'goHexadecimalError', 'value': '0Xg0123456789abdef'},
\ 'heXadecimalErrorTrailing': {'group': 'goHexadecimalError', 'value': '0X0123456789abdefg'},
\ 'octal': {'group': 'goOctalInt', 'value': '01234567'},
\ 'octalPrefix': {'group': 'goOctalInt', 'value': '0o1234567'},
\ 'octalGrouped': {'group': 'goOctalInt', 'value': '0o1_234_567'},
\ 'octalErrorLeading': {'group': 'goOctalError', 'value': '081234567'},
\ 'octalErrorTrailing': {'group': 'goOctalError', 'value': '012345678'},
\ 'octalErrorDoubleUnderscore': {'group': 'goOctalError', 'value': '0o__1234567'},
\ 'octalErrorTrailingUnderscore': {'group': 'goOctalError', 'value': '0o_123456_7_'},
\ 'octalErrorTrailingO': {'group': 'goOctalError', 'value': '0o_123456_7o'},
\ 'octalErrorTrailingX': {'group': 'goOctalError', 'value': '0o_123456_7x'},
\ 'octalErrorTrailingB': {'group': 'goOctalError', 'value': '0o_123456_7b'},
\ 'OctalPrefix': {'group': 'goOctalInt', 'value': '0O1234567'},
\ 'binaryInt': {'group': 'goBinaryInt', 'value': '0b0101'},
\ 'binaryIntGrouped': {'group': 'goBinaryInt', 'value': '0b_01_01'},
\ 'binaryErrorLeading': {'group': 'goBinaryError', 'value': '0b20101'},
\ 'binaryErrorTrailing': {'group': 'goBinaryError', 'value': '0b01012'},
\ 'binaryErrorDoubleUnderscore': {'group': 'goBinaryError', 'value': '0b_01__01'},
\ 'binaryOverrideOctal': {'group': 'goBinaryError', 'value': '0b1234567'},
\ 'binaryErrorTrailingUnderscore': {'group': 'goBinaryError', 'value': '0b_01_01_'},
\ 'BinaryInt': {'group': 'goBinaryInt', 'value': '0B0101'},
\ 'BinaryErrorLeading': {'group': 'goBinaryError', 'value': '0B20101'},
\ 'BinaryErrorTrailing': {'group': 'goBinaryError', 'value': '0B01012'},
\ }
for kv in items(tests)
let l:actual = s:numericHighlightGroupInAssignment(kv[0], kv[1].value)
call assert_equal(kv[1].group, l:actual, kv[0])
endfor
endfunction
function! Test_zero_as_index_element() abort
syntax on
let l:actual = s:numericHighlightGroupInSliceElement('zero-element', '0')
call assert_equal('goDecimalInt', l:actual)
let l:actual = s:numericHighlightGroupInMultidimensionalSliceElement('zero-element', '0')
call assert_equal('goDecimalInt', l:actual, 'multi-dimensional')
endfunction
function! Test_zero_as_slice_index() abort
syntax on
let l:actual = s:numericHighlightGroupInSliceIndex('zero-index', '0')
call assert_equal('goDecimalInt', l:actual)
let l:actual = s:numericHighlightGroupInMultidimensionalSliceIndex('zero-index', '0', '0')
call assert_equal('goDecimalInt', l:actual, 'multi-dimensional')
endfunction
function! Test_zero_as_start_slicing_slice() abort
syntax on
let l:actual = s:numericHighlightGroupInSliceSlicing('slice-slicing', '0', '1')
call assert_equal('goDecimalInt', l:actual)
endfunction
function! s:numericHighlightGroupInAssignment(testname, value)
let l:dir = gotest#write_file(printf('numeric/%s.go', a:testname), [
\ 'package numeric',
\ '',
\ printf("var v = %s\x1f", a:value),
\ ])
try
let l:pos = getcurpos()
let l:actual = synIDattr(synID(l:pos[1], l:pos[2], 1), 'name')
return l:actual
finally
call delete(l:dir, 'rf')
endtry
endfunction
function! s:numericHighlightGroupInSliceElement(testname, value)
let l:dir = gotest#write_file(printf('numeric/slice-element/%s.go', a:testname), [
\ 'package numeric',
\ '',
\ printf("v := []int{\x1f%s}", a:value),
\ ])
try
let l:pos = getcurpos()
let l:actual = synIDattr(synID(l:pos[1], l:pos[2], 1), 'name')
return l:actual
finally
call delete(l:dir, 'rf')
endtry
endfunction
function! s:numericHighlightGroupInMultidimensionalSliceElement(testname, value)
let l:dir = gotest#write_file(printf('numeric/slice-multidimensional-element/%s.go', a:testname), [
\ 'package numeric',
\ '',
\ printf("v := [][]int{{\x1f%s},{%s}}", a:value, a:value),
\ ])
try
let l:pos = getcurpos()
let l:actual = synIDattr(synID(l:pos[1], l:pos[2], 1), 'name')
return l:actual
finally
call delete(l:dir, 'rf')
endtry
endfunction
function! s:numericHighlightGroupInSliceIndex(testname, value)
let l:dir = gotest#write_file(printf('numeric/slice-index/%s.go', a:testname), [
\ 'package numeric',
\ '',
\ 'var sl []int',
\ printf("println(sl[\x1f%s])", a:value),
\ ])
try
let l:pos = getcurpos()
let l:actual = synIDattr(synID(l:pos[1], l:pos[2], 1), 'name')
return l:actual
finally
call delete(l:dir, 'rf')
endtry
endfunction
function! s:numericHighlightGroupInMultidimensionalSliceIndex(testname, first, second)
let l:dir = gotest#write_file(printf('numeric/slice-multidimensional-index/%s.go', a:testname), [
\ 'package numeric',
\ '',
\ 'var sl [][]int',
\ printf("println(sl[\x1f%s][%s])", a:first, a:second),
\ ])
try
let l:pos = getcurpos()
let l:actual = synIDattr(synID(l:pos[1], l:pos[2], 1), 'name')
return l:actual
finally
call delete(l:dir, 'rf')
endtry
endfunction
function! s:numericHighlightGroupInSliceSlicing(testname, from, to)
let l:dir = gotest#write_file(printf('numeric/slice-slicing/%s.go', a:testname), [
\ 'package numeric',
\ '',
\ 'var sl = []int{1,2}',
\ printf("println(sl[\x1f%s:%s])", a:from, a:to),
\ ])
try
let l:pos = getcurpos()
let l:actual = synIDattr(synID(l:pos[1], l:pos[2], 1), 'name')
return l:actual
finally
call delete(l:dir, 'rf')
endtry
endfunction
function! Test_diagnostic_after_fmt() abort
let g:go_fmt_command = 'gofmt'
try
call s:diagnostic_after_write( [
\ 'package main',
\ 'import "fmt"',
\ '',
\ 'func main() {',
\ '',
\ "\tfmt.Println(\x1fhello)",
\ '}',
\ ], [])
finally
unlet g:go_fmt_command
endtry
endfunction
function! Test_diagnostic_after_fmt_change() abort
" craft a file that will be changed when its written (gofmt will change it).
let g:go_fmt_command = 'gofmt'
try
call s:diagnostic_after_write( [
\ 'package main',
\ 'import "fmt"',
\ '',
\ 'func main() {',
\ '',
\ "fmt.Println(\x1fhello)",
\ '}',
\ ], [])
finally
unlet g:go_fmt_command
endtry
endfunction
function! Test_diagnostic_after_fmt_cleared() abort
" craft a file that will be fixed when it is written.
let g:go_fmt_command = 'gofmt'
try
call s:diagnostic_after_write( [
\ 'package main',
\ 'import "fmt"',
\ '',
\ 'func main() {',
\ '',
\ "fmt.Println(\x1fhello)",
\ '}',
\ ], ['hello := "hello, vim-go"'])
finally
unlet g:go_fmt_command
endtry
endfunction
function! Test_diagnostic_after_reload() abort
let l:dir = gotest#write_file('diagnostic/after-reload.go', [
\ 'package main',
\ 'import "fmt"',
\ '',
\ 'func main() {',
\ '',
\ "\tfmt.Println(\x1fhello)",
\ '}',
\ ])
try
call s:check_diagnostics('', 'goDiagnosticError', 'initial')
let l:pos = getcurpos()
edit
call setpos('.', l:pos)
call s:check_diagnostics('', 'goDiagnosticError', 'after-reload')
finally
call delete(l:dir, 'rf')
endtry
endfunction
function! s:diagnostic_after_write(contents, changes) abort
syntax on
let l:dir = gotest#write_file('diagnostic/after-write.go', a:contents)
try
let l:pos = getcurpos()
call s:check_diagnostics('', 'goDiagnosticError', 'initial')
" write a:changes to the previous line and make sure l:actual and
" l:expected are set so that they won't accidentally match on the next
" check.
if len(a:changes) > 0
call append(l:pos[1]-1, a:changes)
let l:actual = 'goDiagnosticError'
let l:expected = ''
else
let l:actual = ''
let l:expected = 'goDiagnosticError'
endif
write
call s:check_diagnostics(l:actual, l:expected, 'after-write')
finally
call delete(l:dir, 'rf')
endtry
endfunction
function! s:check_diagnostics(actual, expected, when)
let l:actual = a:actual
let l:start = reltime()
while l:actual != a:expected && reltimefloat(reltime(l:start)) < 10
" Get the cursor position on each iteration, because the cursor postion
" may change between iterations when go#fmt#GoFmt formats, reloads the
" file, and moves the cursor to try to keep it where the user expects it
" to be when gofmt modifies the files.
let l:pos = getcurpos()
if !has('textprop')
let l:matches = getmatches()
if len(l:matches) == 0
let l:actual = ''
endif
for l:m in l:matches
let l:matchline = l:m.pos1[0]
if len(l:m.pos1) < 2
continue
endif
let l:matchcol = get(l:m.pos1, 1, 1)
if l:pos[1] == l:matchline && l:pos[2] >= l:matchcol && l:pos[2] <= l:matchcol + l:m.pos1[2]
" Ideally, we'd check that the cursor is within the match, but when a
" tab is added on the current line, the cursor position within the
" line will stay constant while the line itself is shifted over by a
" column, so just check the line itself instead of checking a precise
" cursor location.
" if l:pos[1] == l:matchline
let l:actual = l:m.group
break
endif
endfor
sleep 100m
continue
endif
let l:actual = get(prop_list(l:pos[1]), 0, {'type': ''}).type
sleep 100m
endwhile
call assert_equal(a:expected, l:actual, a:when)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -12,7 +12,7 @@ func! Test_impl() abort
call go#impl#Impl('r', 'reader', 'io.Reader')
call gotest#assert_buffer(1, [
\ 'func (r reader) Read(p []byte) (n int, err error) {',
\ ' panic("not implemented")',
\ ' panic("not implemented") // TODO: Implement',
\ '}'])
finally
call delete(l:tmp, 'rf')
@ -33,7 +33,7 @@ func! Test_impl_get() abort
\ 'type reader struct {}',
\ '',
\ 'func (r *reader) Read(p []byte) (n int, err error) {',
\ ' panic("not implemented")',
\ ' panic("not implemented") // TODO: Implement',
\ '}'])
finally
call delete(l:tmp, 'rf')

View file

@ -0,0 +1,44 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#implements#Implements(selected) abort
let l:mode = go#config#ImplementsMode()
if l:mode == 'guru'
call go#guru#Implements(a:selected)
return
elseif l:mode == 'gopls'
if !go#config#GoplsEnabled()
call go#util#EchoError("go_implements_mode is 'gopls', but gopls is disabled")
endif
let [l:line, l:col] = getpos('.')[1:2]
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
let l:fname = expand('%:p')
call go#lsp#Implements(l:fname, l:line, l:col, funcref('s:parse_output'))
return
else
call go#util#EchoWarning('unknown value for g:go_implements_mode')
endif
endfunction
" This uses Vim's errorformat to parse the output and put it into a quickfix
" or locationlist.
function! s:parse_output(exit_val, output, title) abort
if a:exit_val
call go#util#EchoError(a:output)
return
endif
let errformat = ",%f:%l:%c:\ %m"
let l:listtype = go#list#Type("GoImplements")
call go#list#ParseFormat(l:listtype, errformat, a:output, a:title, 0)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View file

@ -82,7 +82,7 @@ function! go#import#SwitchImport(enabled, localname, path, bang) abort
while line <= line("$")
let line = line + 1
let linestr = getline(line)
let m = matchlist(getline(line), '^\()\|\(\s\+\)\(\S*\s*\)"\(.\+\)"\)')
let m = matchlist(getline(line), '^\()\|\(\s\+\)\(\w\+\s\+\)\="\(.\+\)"\)')
if empty(m)
if siteprefix == "" && a:enabled
" must be in the first group

View file

@ -0,0 +1,35 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_SwitchImportAddIgnoresCommented()
try
let l:tmp = gotest#write_file('import/import.go', [
\ 'package import',
\ '',
\ 'import (',
\ "\t" . '// "fmt"',
\ "\t" . '"io"',
\ "\t" . '"ioutil"',
\ "\t" . '"os"',
\ ')',
\ '',
\ 'func main() {',
\ ' io.Copy(ioutil.Discard, os.Stdin)',
\ ' fmt.Println("import the package")',
\ '}',
\ ])
call go#import#SwitchImport(1, '', 'fmt', 0)
let l:actual = getline(4)
call assert_equal("\t" . '"fmt"', l:actual)
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

View file

@ -18,20 +18,34 @@ function! s:issuebody() abort
for l in lines
let body = add(body, l)
if l =~ '^\* Vim version'
redir => out
silent version
redir END
if l =~ '^<!-- :version'
let out = execute('version')
let body = extend(body, split(out, "\n")[0:2])
elseif l =~ '^\* Go version'
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'])
elseif l =~ '^<!-- go env -->'
let [out, err] = go#util#ExecInDir(['go', 'env'])
let body = add(body, substitute(l:out, rtrimpat, '', ''))
elseif l=~ '^<!-- gopls version -->'
let [out, err] = go#util#Exec(['gopls', 'version'])
let body = add(body, substitute(l:out, rtrimpat, '', ''))
endif
endfor
let body = add(body, "\n#### vim-go configuration:\n<details><summary>vim-go configuration</summary><br><pre>")
for k in keys(g:)
if k =~ '^go_'
let body = add(body, 'g:' . k . ' = ' . string(get(g:, k)))
endif
endfor
let body = add(body, '</pre></details>')
let body = add(body, printf("\n#### filetype detection configuration:\n<details><summary>filetype detection</summary><br><pre>%s", execute('filetype')))
let body = add(body, '</pre></details>')
return join(body, "\n")
endfunction

View file

@ -41,7 +41,12 @@ endfunction
" 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.
" 'preserveerrors':
" A function that will be passed one value, the list type. It should
" return a boolean value that indicates whether any errors encountered
" should be consider additive to the existing set of errors. This is
" mostly useful for a set of commands that are run via autocmds.
"
" The return value is a dictionary with these keys:
" 'callback':
" A function suitable to be passed as a job callback handler. See
@ -61,7 +66,7 @@ function! go#job#Options(args)
let state = {
\ 'winid': win_getid(winnr()),
\ 'dir': getcwd(),
\ 'jobdir': fnameescape(expand("%:p:h")),
\ 'jobdir': expand("%:p:h"),
\ 'messages': [],
\ 'bang': 0,
\ 'for': "_job",
@ -69,12 +74,10 @@ function! go#job#Options(args)
\ 'exit_status': 0,
\ 'closed': 0,
\ 'errorformat': &errorformat,
\ 'statustype' : ''
\ 'statustype' : '',
\ }
if has("patch-8.0.0902") || has('nvim')
let cbs.cwd = state.jobdir
endif
let cbs.cwd = state.jobdir
if has_key(a:args, 'bang')
let state.bang = a:args.bang
@ -92,6 +95,10 @@ function! go#job#Options(args)
let state.errorformat = a:args.errorformat
endif
if has_key(a:args, 'preserveerrors')
let state.preserveerrors = a:args.preserveerrors
endif
function state.complete(job, exit_status, data)
if has_key(self, 'custom_complete')
let l:winid = win_getid(winnr())
@ -177,16 +184,26 @@ function! go#job#Options(args)
call win_gotoid(self.winid)
let l:listtype = go#list#Type(self.for)
let l:preserveerrors = 0
if has_key(self, 'preserveerrors')
let l:preserveerrors = self.preserveerrors(l:listtype)
endif
if a:exit_status == 0
call go#list#Clean(l:listtype)
call win_gotoid(l:winid)
if !l:preserveerrors
call go#list#Clean(l:listtype)
call win_gotoid(l:winid)
endif
return
endif
let l:listtype = go#list#Type(self.for)
if len(a:data) == 0
call go#list#Clean(l:listtype)
call win_gotoid(l:winid)
if !l:preserveerrors
call go#list#Clean(l:listtype)
call win_gotoid(l:winid)
endif
return
endif
@ -195,8 +212,8 @@ function! go#job#Options(args)
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
try
" parse the errors relative to self.jobdir
execute l:cd self.jobdir
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
execute l:cd fnameescape(self.jobdir)
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for, l:preserveerrors)
let errors = go#list#Get(l:listtype)
finally
execute l:cd fnameescape(self.dir)
@ -295,11 +312,6 @@ function! go#job#Start(cmd, options)
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')

View file

@ -0,0 +1,53 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_JobDirWithSpaces()
if !go#util#has_job()
return
endif
try
let l:filename = 'job/dir has spaces/main.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . fnameescape(l:tmp . '/src/job/dir has spaces')
" set the compiler type so that the errorformat option will be set
" correctly.
compiler go
let expected = [{'lnum': 4, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'undefined: notafunc'}]
" clear the quickfix lists
call setqflist([], 'r')
" go build discards any results when it compiles multiple packages. So we
" pass the `errors` package just as a placeholder with the current folder
" (indicated with '.').
let l:cmd = ['go', 'build', '.', 'errors']
let l:complete = go#promise#New(function('s:complete'), 10000, '')
call go#job#Spawn(l:cmd, {
\ 'for': 'GoBuild',
\ 'complete': l:complete.wrapper,
\ 'statustype': 'build'
\})
let l:out = l:complete.await()
let actual = getqflist()
call gotest#assert_quickfix(actual, l:expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! s:complete(job, exit_code, messages)
return a:messages
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View file

@ -3,30 +3,36 @@ let s:cpo_save = &cpo
set cpo&vim
function! go#lint#Gometa(bang, autosave, ...) abort
if a:0 == 0
let goargs = [expand('%:p:h')]
else
let goargs = a:000
endif
let l:metalinter = go#config#MetalinterCommand()
if l:metalinter == 'gometalinter' || l:metalinter == 'golangci-lint'
let cmd = s:metalintercmd(l:metalinter)
if a:0 == 0
let l:goargs = [expand('%:p:h')]
if l:metalinter == 'gopls'
let l:pkg = go#package#ImportPath()
if l:pkg == -1
call go#util#EchoError('could not determine package name')
return
endif
let l:goargs = [l:pkg]
endif
else
let l:goargs = a:000
endif
let cmd = []
if l:metalinter == 'golangci-lint'
let linters = a:autosave ? go#config#MetalinterAutosaveEnabled() : go#config#MetalinterEnabled()
let cmd = s:metalintercmd(l:metalinter, len(linters) != 0)
if empty(cmd)
return
endif
" linters
let linters = a:autosave ? go#config#MetalinterAutosaveEnabled() : go#config#MetalinterEnabled()
" add linters to cmd
for linter in linters
let cmd += ["--enable=".linter]
endfor
for linter in go#config#MetalinterDisabled()
let cmd += ["--disable=".linter]
endfor
else
elseif l:metalinter != 'gopls'
" the user wants something else, let us use it.
let cmd = split(go#config#MetalinterCommand(), " ")
endif
@ -36,15 +42,8 @@ function! go#lint#Gometa(bang, autosave, ...) abort
" will be cleared
redraw
if l:metalinter == "gometalinter"
" Include only messages for the active buffer for autosave.
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')
if l:metalinter == "golangci-lint"
let l:goargs[0] = expand('%:p:h')
endif
endif
@ -54,139 +53,288 @@ function! go#lint#Gometa(bang, autosave, ...) abort
let cmd += ["--deadline=" . deadline]
endif
let cmd += goargs
let cmd += l:goargs
if l:metalinter == "gometalinter"
" 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"
let errformat = s:errorformat(l:metalinter)
if l:metalinter == 'gopls'
if a:autosave
let l:messages = go#lsp#AnalyzeFile(expand('%:p'))
else
let l:import_paths = l:goargs
let l:messages = call('go#lsp#Diagnostics', l:import_paths)
endif
let l:err = len(l:messages)
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
if go#util#has_job()
if a:autosave
let l:for = 'GoMetaLinterAutoSave'
else
let l:for = 'GoMetaLinter'
endif
if go#util#has_job()
call s:lint_job({'cmd': cmd, 'statustype': l:metalinter, 'errformat': errformat}, a:bang, a:autosave)
return
endif
call s:lint_job(l:metalinter, {'cmd': cmd, 'statustype': l:metalinter, 'errformat': errformat, 'for': l:for}, a:bang, a:autosave)
return
endif
let [l:out, l:err] = go#util#Exec(cmd)
let [l:out, l:err] = go#util#Exec(cmd)
let l:messages = split(out, "\n")
endif
if a:autosave
let l:listtype = go#list#Type("GoMetaLinterAutoSave")
let l:listtype = go#list#Type('GoMetaLinterAutoSave')
let l:for = 'GoMetaLinterAutoSave'
else
let l:listtype = go#list#Type("GoMetaLinter")
let l:listtype = go#list#Type('GoMetaLinter')
let l:for = 'GoMetaLinterAuto'
endif
if l:err == 0
call go#list#Clean(l:listtype)
echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
if !s:preserveerrors(a:autosave, l:listtype)
call go#list#Clean(l:listtype)
endif
call go#util#EchoSuccess('[metalinter] PASS')
else
let l:winid = win_getid(winnr())
" Parse and populate our location list
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter')
if a:autosave
call s:metalinterautosavecomplete(l:metalinter, fnamemodify(expand('%:p'), ":."), 0, 1, l:messages)
endif
call go#list#ParseFormat(l:listtype, errformat, l:messages, l:for, s:preserveerrors(a:autosave, l:listtype))
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if a:autosave || a:bang
call win_gotoid(l:winid)
else
call go#list#JumpToFirst(l:listtype)
endif
endif
endfunction
function! go#lint#Diagnostics(bang, ...) abort
if a:0 == 0
let l:pkg = go#package#ImportPath()
if l:pkg == -1
call go#util#EchoError('could not determine package name')
return
endif
call go#list#JumpToFirst(l:listtype)
let l:import_paths = [l:pkg]
else
let l:import_paths = a:000
endif
let errformat = s:errorformat('gopls')
let l:messages = call('go#lsp#Diagnostics', l:import_paths)
let l:listtype = go#list#Type("GoDiagnostics")
if len(l:messages) == 0
call go#list#Clean(l:listtype)
call go#util#EchoSuccess('[diagnostics] PASS')
else
" Parse and populate the quickfix list
let l:winid = win_getid(winnr())
call go#list#ParseFormat(l:listtype, errformat, l:messages, 'GoDiagnostics', 0)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if a:bang
call win_gotoid(l:winid)
else
call go#list#JumpToFirst(l:listtype)
endif
endif
endfunction
" Golint calls 'golint' on the current directory. Any warnings are populated in
" the location list
function! go#lint#Golint(bang, ...) abort
call go#cmd#autowrite()
let l:type = 'golint'
let l:status = {
\ 'desc': 'current status',
\ 'type': l:type,
\ 'state': "started",
\ }
if go#config#EchoCommandInfo()
call go#util#EchoProgress(printf('[%s] analyzing...', l:type))
endif
call go#statusline#Update(expand('%:p:h'), l:status)
if a:0 == 0
let [l:out, l:err] = go#util#Exec([go#config#GolintBin(), go#package#ImportPath()])
let [l:out, l:err] = go#util#Exec([go#config#GolintBin(), expand('%:p:h')])
else
let [l:out, l:err] = go#util#Exec([go#config#GolintBin()] + a:000)
endif
if empty(l:out)
call go#util#EchoSuccess('[lint] PASS')
return
endif
let l:winid = win_getid(winnr())
let l:status.state = 'success'
let l:state = 'PASS'
let l:listtype = go#list#Type("GoLint")
call go#list#Parse(l:listtype, l:out, "GoLint")
let l:errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(l:errors))
if !empty(l:out)
let l:status.state = 'failed'
let l:state = 'FAIL'
if a:bang
call win_gotoid(l:winid)
return
let l:winid = win_getid(winnr())
call go#list#Parse(l:listtype, l:out, "GoLint", 0)
let l:errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(l:errors))
if a:bang
call win_gotoid(l:winid)
else
call go#list#JumpToFirst(l:listtype)
endif
if go#config#EchoCommandInfo()
call go#util#EchoError(printf('[%s] %s', l:type, l:state))
endif
else
call go#list#Clean(l:listtype)
if go#config#EchoCommandInfo()
call go#util#EchoSuccess(printf('[%s] %s', l:type, l:state))
endif
endif
call go#list#JumpToFirst(l:listtype)
call go#statusline#Update(expand('%:p:h'), l:status)
endfunction
" Vet calls 'go vet' on the current directory. Any warnings are populated in
" the location list
" Vet calls 'go vet' on the current buffer's directory. Any warnings are
" populated in the location list
function! go#lint#Vet(bang, ...) abort
call go#cmd#autowrite()
if go#config#EchoCommandInfo()
call go#util#EchoProgress('calling vet...')
let l:cmd = ['go', 'vet']
let buildtags = go#config#BuildTags()
if buildtags isnot ''
let l:cmd += ['-tags', buildtags]
endif
if a:0 == 0
let [l:out, l:err] = go#util#Exec(['go', 'vet', go#package#ImportPath()])
let l:import_path = go#package#ImportPath()
if l:import_path == -1
call go#util#EchoError('could not determine package')
return
endif
let l:cmd = add(l:cmd, l:import_path)
else
let [l:out, l:err] = go#util#Exec(['go', 'tool', 'vet'] + a:000)
let l:cmd = extend(l:cmd, a:000)
endif
let l:type = 'go vet'
if go#config#EchoCommandInfo()
call go#util#EchoProgress(printf('[%s] analyzing...', l:type))
endif
let l:status = {
\ 'desc': 'current status',
\ 'type': l:type,
\ 'state': "started",
\ }
call go#statusline#Update(expand('%:p:h'), l:status)
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
let l:status.state = 'success'
let l:state = 'PASS'
let l:listtype = go#list#Type("GoVet")
if l:err != 0
let l:status.state = 'failed'
let l:state = 'FAIL'
let l:winid = win_getid(winnr())
let errorformat = "%-Gexit status %\\d%\\+," . &errorformat
call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet")
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
let l:errorformat = "%-Gexit status %\\d%\\+," . &errorformat
let l:dir = go#util#Chdir(expand('%:p:h'))
try
call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet", 0)
finally
call go#util#Chdir(l:dir)
endtry
let l:errors = go#list#Get(l:listtype)
if empty(l:errors)
call go#util#EchoError(l:out)
return
endif
call go#list#Window(l:listtype, len(l:errors))
if !empty(l:errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
else
call win_gotoid(l:winid)
endif
if go#config#EchoCommandInfo()
call go#util#EchoError(printf('[%s] %s', l:type, l:state))
endif
else
call go#list#Clean(l:listtype)
call go#util#EchoSuccess('[vet] PASS')
if go#config#EchoCommandInfo()
call go#util#EchoSuccess(printf('[%s] %s', l:type, l:state))
endif
endif
call go#statusline#Update(expand('%:p:h'), l:status)
endfunction
" ErrCheck calls 'errcheck' for the given packages. Any warnings are populated in
" the location list
function! go#lint#Errcheck(bang, ...) abort
if a:0 == 0
let l:import_path = go#package#ImportPath()
if import_path == -1
call go#util#EchoError('package is not inside GOPATH src')
return
endif
else
let l:import_path = join(a:000, ' ')
call go#cmd#autowrite()
let l:cmd = [go#config#ErrcheckBin(), '-abspath']
let buildtags = go#config#BuildTags()
if buildtags isnot ''
let l:cmd += ['-tags', buildtags]
endif
call go#util#EchoProgress('[errcheck] analysing ...')
if a:0 == 0
let l:import_path = go#package#ImportPath()
if l:import_path == -1
call go#util#EchoError('could not determine package')
return
endif
let l:cmd = add(l:cmd, l:import_path)
else
let l:cmd = extend(l:cmd, a:000)
endif
let l:type = 'errcheck'
if go#config#EchoCommandInfo()
call go#util#EchoProgress(printf('[%s] analyzing...', l:type))
endif
let l:status = {
\ 'desc': 'current status',
\ 'type': l:type,
\ 'state': "started",
\ }
redraw
let [l:out, l:err] = go#util#Exec([go#config#ErrcheckBin(), '-abspath', l:import_path])
call go#statusline#Update(expand('%:p:h'), l:status)
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
let l:status.state = 'success'
let l:state = 'PASS'
let l:listtype = go#list#Type("GoErrCheck")
if l:err != 0
let l:winid = win_getid(winnr())
let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m"
let l:status.state = 'failed'
let l:state = 'FAIL'
" Parse and populate our location list
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'Errcheck')
let l:winid = win_getid(winnr())
if l:err == 1
let l:errformat = "%f:%l:%c:\ %m,%f:%l:%c\ %#%m"
" Parse and populate our location list
call go#list#ParseFormat(l:listtype, l:errformat, split(out, "\n"), 'Errcheck', 0)
endif
let l:errors = go#list#Get(l:listtype)
if empty(l:errors)
@ -195,18 +343,24 @@ function! go#lint#Errcheck(bang, ...) abort
endif
if !empty(errors)
call go#list#Populate(l:listtype, errors, 'Errcheck')
call go#list#Window(l:listtype, len(errors))
call go#list#Populate(l:listtype, l:errors, 'Errcheck')
call go#list#Window(l:listtype, len(l:errors))
if !a:bang
call go#list#JumpToFirst(l:listtype)
else
call win_gotoid(l:winid)
endif
endif
if go#config#EchoCommandInfo()
call go#util#EchoError(printf('[%s] %s', l:type, l:state))
endif
else
call go#list#Clean(l:listtype)
call go#util#EchoSuccess('[errcheck] PASS')
if go#config#EchoCommandInfo()
call go#util#EchoSuccess(printf('[%s] %s', l:type, l:state))
endif
endif
call go#statusline#Update(expand('%:p:h'), l:status)
endfunction
function! go#lint#ToggleMetaLinterAutoSave() abort
@ -220,16 +374,19 @@ function! go#lint#ToggleMetaLinterAutoSave() abort
call go#util#EchoProgress("auto metalinter enabled")
endfunction
function! s:lint_job(args, bang, autosave)
function! s:lint_job(metalinter, args, bang, autosave)
let l:opts = {
\ 'statustype': a:args.statustype,
\ 'errorformat': a:args.errformat,
\ 'for': "GoMetaLinter",
\ 'for': 'GoMetaLinter',
\ 'bang': a:bang,
\ }
\ }
if a:autosave
let l:opts.for = "GoMetaLinterAutoSave"
let l:opts.for = 'GoMetaLinterAutoSave'
" s:metalinterautosavecomplete is really only needed for golangci-lint
let l:opts.complete = funcref('s:metalinterautosavecomplete', [a:metalinter, expand('%:p:t')])
let l:opts.preserveerrors = funcref('s:preserveerrors', [a:autosave])
endif
" autowrite is not enabled for jobs
@ -238,45 +395,72 @@ function! s:lint_job(args, bang, autosave)
call go#job#Spawn(a:args.cmd, l:opts)
endfunction
function! s:metalintercmd(metalinter)
function! s:metalintercmd(metalinter, haslinter)
let l:cmd = []
let bin_path = go#path#CheckBinPath(a:metalinter)
if !empty(bin_path)
if a:metalinter == "gometalinter"
let l:cmd = s:gometalintercmd(bin_path)
elseif a:metalinter == "golangci-lint"
let l:cmd = s:golangcilintcmd(bin_path)
if a:metalinter == "golangci-lint"
let l:cmd = s:golangcilintcmd(bin_path, a:haslinter)
endif
endif
return cmd
endfunction
function! s:gometalintercmd(bin_path)
let cmd = [a:bin_path]
let cmd += ["--disable-all"]
" 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"]
return cmd
endfunction
function! s:golangcilintcmd(bin_path)
function! s:golangcilintcmd(bin_path, haslinter)
let cmd = [a:bin_path]
let cmd += ["run"]
let cmd += ["--print-issued-lines=false"]
let cmd += ["--disable-all"]
let cmd += ['--build-tags', go#config#BuildTags()]
" do not use the default exclude patterns, because doing so causes golint
" problems about missing doc strings to be ignored and other things that
" golint identifies.
let cmd += ["--exclude-use-default=false"]
if a:haslinter
let cmd += ["--disable-all"]
endif
return cmd
endfunction
function! s:metalinterautosavecomplete(metalinter, filepath, job, exit_code, messages)
if a:metalinter != 'golangci-lint'
return
endif
if len(a:messages) == 0
return
endif
let l:idx = len(a:messages) - 1
while l:idx >= 0
" leave in any messages that report errors about a:filepath or that report
" more general problems that prevent golangci-lint from linting
" a:filepath.
if a:messages[l:idx] !~# '^' . a:filepath . ':' && a:messages[l:idx] !~# '^level='
call remove(a:messages, l:idx)
endif
let l:idx -= 1
endwhile
endfunction
function! s:errorformat(metalinter) abort
if a:metalinter == 'golangci-lint'
" Golangci-lint can output the following:
" <file>:<line>:<column>: <message> (<linter>)
" This can be defined by the following errorformat:
return 'level=%tarning\ msg="%m:\ [%f:%l:%c:\ %.%#]",level=%tarning\ msg="%m",level=%trror\ msg="%m:\ [%f:%l:%c:\ %.%#]",level=%trror\ msg="%m",%f:%l:%c:\ %m,%f:%l\ %m'
elseif a:metalinter == 'gopls'
return '%f:%l:%c:%t:\ %m,%f:%l:%c::\ %m,%f:%l::%t:\ %m'
endif
endfunction
function! s:preserveerrors(autosave, listtype) abort
return a:autosave && a:listtype == go#list#Type("GoFmt") && go#config#FmtAutosave() && isdirectory(expand('%:p:h'))
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -2,23 +2,24 @@
let s:cpo_save = &cpo
set cpo&vim
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 RestoreGOPATH = go#util#SetEnv('GOPATH', fnamemodify(getcwd(), ':p') . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
try
let g:go_metalinter_comand = a:metalinter
let g:go_metalinter_command = a:metalinter
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)'}
\ ]
if a:metalinter == 'golangci-lint'
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%')+5, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function `MissingFooDoc` should have comment or be unexported (golint)'}
\ ]
endif
" clear the quickfix lists
call setqflist([], 'r')
@ -36,34 +37,32 @@ func! s:gometa(metalinter) abort
call gotest#assert_quickfix(actual, expected)
finally
call call(RestoreGOPATH, [])
unlet g:go_metalinter_enabled
endtry
endfunc
func! Test_GometaWithDisabled() abort
call s:gometawithdisabled('gometalinter')
func! Test_GometaGolangciLint_shadow() abort
call s:gometa_shadow('golangci-lint')
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'
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
func! s:gometa_shadow(metalinter) abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnamemodify(getcwd(), ':p') . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/golangci-lint/problems/shadow/problems.go'
try
let g:go_metalinter_comand = a:metalinter
let g:go_metalinter_command = a:metalinter
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': 4, 'bufnr': bufnr('%'), 'col': 7, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'module': '', 'text': '[runner] Can''t run linter golint: golint: analysis skipped: errors in package'},
\ {'lnum': 4, 'bufnr': bufnr('%'), 'col': 7, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'e', 'module': '', 'text': 'Running error: golint: analysis skipped: errors in package'}
\ ]
" clear the quickfix lists
call setqflist([], 'r')
let g:go_metalinter_disabled = ['vet']
let g:go_metalinter_enabled = ['golint']
call go#lint#Gometa(0, 0, $GOPATH . '/src/foo')
call go#lint#Gometa(0, 0)
let actual = getqflist()
let start = reltime()
@ -74,58 +73,305 @@ func! s:gometawithdisabled(metalinter) abort
call gotest#assert_quickfix(actual, expected)
finally
unlet g:go_metalinter_disabled
call call(RestoreGOPATH, [])
unlet g:go_metalinter_enabled
endtry
endfunc
func! Test_GometaAutoSave() abort
call s:gometaautosave('gometalinter')
endfunc
func! Test_GometaAutoSaveGolangciLint() abort
call s:gometaautosave('golangci-lint')
call s:gometaautosave('golangci-lint', 0)
endfunc
func! s:gometaautosave(metalinter) abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
func! Test_GometaAutoSaveKeepsErrors() abort
call s:gometaautosave('golangci-lint', 1)
endfunc
func! s:gometaautosave(metalinter, withList) abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
try
let g:go_metalinter_comand = a:metalinter
let expected = [
let g:go_metalinter_command = a:metalinter
let l: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)'}
\ ]
if a:metalinter == 'golangci-lint'
let l:expected = [
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function `MissingDoc` should have comment or be unexported (golint)'}
\ ]
endif
let winnr = winnr()
let l:list = []
if a:withList
let l:list = [
\ {'lnum': 1, 'bufnr': 1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'before metalinter'}
\ ]
let l:expected = extend(l:list, l:expected)
endif
" clear the location lists
call setloclist(l:winnr, [], 'r')
" set the location lists
call setloclist(0, l:list, 'r')
let g:go_metalinter_autosave_enabled = ['golint']
call go#lint#Gometa(0, 1)
let actual = getloclist(l:winnr)
let l:actual = getloclist(0)
let l:start = reltime()
while len(l:actual) != len(l:expected) && reltimefloat(reltime(l:start)) < 10
sleep 100m
let l:actual = getloclist(0)
endwhile
call gotest#assert_quickfix(l:actual, l:expected)
finally
call call(RestoreGOPATH, [])
unlet g:go_metalinter_autosave_enabled
endtry
endfunc
func! Test_GometaGolangciLint_importabs() abort
call s:gometa_importabs('golangci-lint')
endfunc
func! s:gometa_importabs(metalinter) abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnamemodify(getcwd(), ':p') . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/golangci-lint/problems/importabs/problems.go'
try
let g:go_metalinter_command = a:metalinter
let expected = [
\ {'lnum': 3, 'bufnr': bufnr('%'), 'col': 8, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'module': '', 'text': '[runner] Can''t run linter golint: golint: analysis skipped: errors in package'},
\ {'lnum': 3, 'bufnr': bufnr('%'), 'col': 8, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'e', 'module': '', 'text': 'Running error: golint: analysis skipped: errors in package'},
\ ]
" clear the quickfix lists
call setqflist([], 'r')
let g:go_metalinter_enabled = ['golint']
call go#lint#Gometa(0, 0)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getloclist(l:winnr)
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call call(RestoreGOPATH, [])
unlet g:go_metalinter_enabled
endtry
endfunc
func! Test_GometaAutoSaveGolangciLint_importabs() abort
call s:gometaautosave_importabs('golangci-lint')
endfunc
func! s:gometaautosave_importabs(metalinter) abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/golangci-lint/problems/importabs/ok.go'
try
let g:go_metalinter_command = a:metalinter
let expected = [
\ {'lnum': 3, 'bufnr': bufnr('%')+1, 'col': 8, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'module': '', 'text': '[runner] Can''t run linter golint: golint: analysis skipped: errors in package'},
\ {'lnum': 3, 'bufnr': bufnr('%')+1, 'col': 8, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'e', 'module': '', 'text': 'Running error: golint: analysis skipped: errors in package'}
\ ]
" clear the location lists
call setloclist(0, [], 'r')
let g:go_metalinter_autosave_enabled = ['golint']
call go#lint#Gometa(0, 1)
let actual = getloclist(0)
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getloclist(0)
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call call(RestoreGOPATH, [])
unlet g:go_metalinter_autosave_enabled
endtry
endfunc
func! Test_GometaGolangciLint_multiple() abort
call s:gometa_multiple('golangci-lint')
endfunc
func! s:gometa_multiple(metalinter) abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnamemodify(getcwd(), ':p') . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/golangci-lint/problems/multiple/problems.go'
try
let g:go_metalinter_command = a:metalinter
let expected = [
\ {'lnum': 8, 'bufnr': bufnr('%'), 'col': 7, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'module': '', 'text': '[runner] Can''t run linter golint: golint: analysis skipped: errors in package'},
\ {'lnum': 8, 'bufnr': bufnr('%'), 'col': 7, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'e', 'module': '', 'text': 'Running error: golint: analysis skipped: errors in package'},
\ ]
" clear the quickfix lists
call setqflist([], 'r')
let g:go_metalinter_enabled = ['golint']
call go#lint#Gometa(0, 0)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call call(RestoreGOPATH, [])
unlet g:go_metalinter_enabled
endtry
endfunc
func! Test_GometaAutoSaveGolangciLint_multiple() abort
call s:gometaautosave_multiple('golangci-lint')
endfunc
func! s:gometaautosave_multiple(metalinter) abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/golangci-lint/problems/multiple/problems.go'
try
let g:go_metalinter_command = a:metalinter
let expected = [
\ {'lnum': 8, 'bufnr': bufnr('%'), 'col': 7, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'module': '', 'text': '[runner] Can''t run linter golint: golint: analysis skipped: errors in package'},
\ {'lnum': 8, 'bufnr': bufnr('%'), 'col': 7, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'e', 'module': '', 'text': 'Running error: golint: analysis skipped: errors in package'},
\ ]
" clear the location lists
call setloclist(0, [], 'r')
let g:go_metalinter_autosave_enabled = ['golint']
call go#lint#Gometa(0, 1)
let actual = getloclist(0)
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getloclist(0)
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call call(RestoreGOPATH, [])
unlet g:go_metalinter_autosave_enabled
endtry
endfunc
func! Test_Vet() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/vet/vet.go'
let l:tmp = gotest#load_fixture('lint/src/vet/vet.go')
try
let expected = [
\ {'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()
" clear the location lists
call setqflist([], 'r')
call go#lint#Vet(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_Vet_subdir() abort
let l:tmp = gotest#load_fixture('lint/src/vet/vet.go')
" go up one directory to easily test that go vet's file paths are handled
" correctly when the working directory is not the directory that contains
" the file being vetted.
call go#util#Chdir('..')
try
let expected = [
\ {'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()
" clear the location lists
call setqflist([], 'r')
call go#lint#Vet(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_Vet_compilererror() abort
let l:tmp = gotest#load_fixture('lint/src/vet/compilererror/compilererror.go')
try
let expected = [
\ {'lnum': 6, 'bufnr': bufnr('%'), 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': "missing ',' before newline in argument list (and 1 more errors)"}
\ ]
let winnr = winnr()
" clear the location lists
call setqflist([], 'r')
call go#lint#Vet(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_Lint_GOPATH() abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
compiler go
let expected = [
\ {'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'}
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported'},
\ {'lnum': 5, 'bufnr': bufnr('%')+7, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function AlsoMissingDoc should have comment or be unexported'}
\ ]
let winnr = winnr()
@ -133,7 +379,35 @@ func! Test_Vet() abort
" clear the location lists
call setqflist([], 'r')
call go#lint#Vet(1)
call go#lint#Golint(1)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
call gotest#assert_quickfix(actual, expected)
call call(RestoreGOPATH, [])
endfunc
func! Test_Lint_NullModule() abort
silent exe 'e ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint/src/lint/lint.go'
compiler go
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported'},
\ {'lnum': 5, 'bufnr': bufnr('%')+7, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function AlsoMissingDoc should have comment or be unexported'}
\ ]
let winnr = winnr()
" clear the location lists
call setqflist([], 'r')
call go#lint#Golint(1)
let actual = getqflist()
let start = reltime()
@ -145,6 +419,70 @@ func! Test_Vet() abort
call gotest#assert_quickfix(actual, expected)
endfunc
func! Test_Errcheck() abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnamemodify(getcwd(), ':p') . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/errcheck/errcheck.go'
try
let l:bufnr = bufnr('')
let expected = [
\ {'lnum': 9, 'bufnr': bufnr(''), 'col': 9, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': ":\tio.Copy(os.Stdout, os.Stdin)"},
\ {'lnum': 10, 'bufnr': bufnr('')+1, 'col': 9, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': ":\tio.Copy(os.Stdout, os.Stdin)"},
\ ]
" clear the location lists
call setqflist([], 'r')
call go#lint#Errcheck(1)
call gotest#assert_quickfix(getqflist(), expected)
call assert_equal(l:bufnr, bufnr(''))
finally
call call(RestoreGOPATH, [])
endtry
endfunc
func! Test_Errcheck_options() abort
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnamemodify(getcwd(), ':p') . 'test-fixtures/lint')
silent exe 'e ' . $GOPATH . '/src/errcheck/errcheck.go'
try
let l:bufnr = bufnr('')
let expected = [
\ {'lnum': 9, 'bufnr': bufnr(''), 'col': 9, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'module': '', 'text': ":\tio.Copy(os.Stdout, os.Stdin)"},
\ ]
" clear the location lists
call setqflist([], 'r')
call go#lint#Errcheck(1, '-ignoretests')
call gotest#assert_quickfix(getqflist(), expected)
call assert_equal(l:bufnr, bufnr(''))
finally
call call(RestoreGOPATH, [])
endtry
endfunc
func! Test_Errcheck_compilererror() abort
let l:tmp = gotest#load_fixture('lint/src/errcheck/compilererror/compilererror.go')
try
let l:bufnr = bufnr('')
let expected = []
" clear the location lists
call setqflist([], 'r')
call go#lint#Errcheck(1)
call gotest#assert_quickfix(getqflist(), expected)
call assert_equal(l:bufnr, bufnr(''))
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -49,26 +49,23 @@ endfunction
function! go#list#Populate(listtype, items, title) abort
if a:listtype == "locationlist"
call setloclist(0, a:items, 'r')
" The last argument ({what}) is introduced with 7.4.2200:
" https://github.com/vim/vim/commit/d823fa910cca43fec3c31c030ee908a14c272640
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
call setloclist(0, [], 'a', {'title': a:title})
else
call setqflist(a:items, 'r')
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
call setqflist([], 'a', {'title': a:title})
endif
endfunction
" Parse parses the given items based on the specified errorformat and
" populates the list.
function! go#list#ParseFormat(listtype, errformat, items, title) abort
function! go#list#ParseFormat(listtype, errformat, items, title, add) abort
" backup users errorformat, will be restored once we are finished
let old_errorformat = &errorformat
" parse and populate the location list
let &errorformat = a:errformat
try
call go#list#Parse(a:listtype, a:items, a:title)
call go#list#Parse(a:listtype, a:items, a:title, a:add)
finally
"restore back
let &errorformat = old_errorformat
@ -77,13 +74,26 @@ endfunction
" Parse parses the given items based on the global errorformat and
" populates the list.
function! go#list#Parse(listtype, items, title) abort
function! go#list#Parse(listtype, items, title, add) abort
let l:list = []
if a:add
let l:list = go#list#Get(a:listtype)
endif
if a:listtype == "locationlist"
lgetexpr a:items
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
if a:add
laddexpr a:items
else
lgetexpr a:items
endif
call setloclist(0, [], 'a', {'title': a:title})
else
cgetexpr a:items
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
if a:add
caddexpr a:items
else
cgetexpr a:items
endif
call setqflist([], 'a', {'title': a:title})
endif
endfunction
@ -138,6 +148,7 @@ endfunction
" in g:go_list_type_commands.
let s:default_list_type_commands = {
\ "GoBuild": "quickfix",
\ "GoDiagnostics": "quickfix",
\ "GoDebug": "quickfix",
\ "GoErrCheck": "quickfix",
\ "GoFmt": "locationlist",
@ -152,6 +163,8 @@ let s:default_list_type_commands = {
\ "GoRun": "quickfix",
\ "GoTest": "quickfix",
\ "GoVet": "quickfix",
\ "GoReferrers": "locationlist",
\ "GoImplements": "locationlist",
\ "_guru": "locationlist",
\ "_term": "locationlist",
\ "_job": "locationlist",

File diff suppressed because it is too large Load diff

View file

@ -28,7 +28,7 @@ let s:Event = 23
let s:Operator = 24
let s:TypeParameter = 25
function! go#lsp#completionitemkind#Vim(kind)
function! go#lsp#completionitemkind#Vim(kind) abort
if a:kind == s:Method || a:kind == s:Function || a:kind == s:Constructor
return 'f'
elseif a:kind == s:Variable || a:kind == s:Constant
@ -40,6 +40,22 @@ function! go#lsp#completionitemkind#Vim(kind)
endif
endfunction
function! go#lsp#completionitemkind#IsFunction(kind) abort
if a:kind == s:Function
return 1
endif
return 0
endfunction
function! go#lsp#completionitemkind#IsMethod(kind) abort
if a:kind == s:Method
return 1
endif
return 0
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View 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
" go#lsp#lsp#Position returns the LSP text position. If no arguments are
" provided, the cursor position is assumed. Otherwise, there should be two
" arguments: the line and the column.
function! go#lsp#lsp#Position(...)
if a:0 < 2
let [l:line, l:col] = getpos('.')[1:2]
else
let l:line = a:1
let l:col = a:2
endif
let l:content = getline(l:line)
" LSP uses 0-based lines.
return [l:line - 1, s:character(l:line, l:col-1)]
endfunction
function! s:strlen(str) abort
let l:runes = split(a:str, '\zs')
return len(l:runes) + len(filter(l:runes, 'char2nr(v:val)>=0x10000'))
endfunction
function! s:character(line, col) abort
return s:strlen(getline(a:line)[:col([a:line, a:col - 1])])
endfunction
" go#lsp#PositionOf returns len(content[0:units]) where units is utf-16 code
" units. This is mostly useful for converting LSP text position to vim
" position.
function! go#lsp#lsp#PositionOf(content, units, ...) abort
if a:units == 0
return 1
endif
let l:remaining = a:units
let l:str = ''
for l:rune in split(a:content, '\zs')
if l:remaining < 0
break
endif
let l:remaining -= 1
if char2nr(l:rune) >= 0x10000
let l:remaining -= 1
endif
let l:str = l:str . l:rune
endfor
return len(l:str)
endfunction
function! go#lsp#lsp#SeverityToErrorType(severity) abort
if a:severity == 1
return 'E'
elseif a:severity == 2
return 'W'
elseif a:severity == 3
return 'I'
elseif a:severity == 4
return 'I'
endif
return ''
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View file

@ -0,0 +1,32 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
function! Test_PositionOf_Simple()
let l:actual = go#lsp#lsp#PositionOf("just ascii", 3)
call assert_equal(4, l:actual)
endfunc
function! Test_PositionOf_MultiByte()
" ⌘ is U+2318, which encodes to three bytes in utf-8 and 1 code unit in
" utf-16.
let l:actual = go#lsp#lsp#PositionOf("⌘⌘ foo", 3)
call assert_equal(8, l:actual)
endfunc
function! Test_PositionOf_MultipleCodeUnit()
" 𐐀 is U+10400, which encodes to 4 bytes in utf-8 and 2 code units in
" utf-16.
let l:actual = go#lsp#lsp#PositionOf("𐐀 bar", 3)
call assert_equal(6, l:actual)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View file

@ -10,17 +10,100 @@ function! go#lsp#message#Initialize(wd) abort
\ 'processId': getpid(),
\ 'rootUri': go#path#ToURI(a:wd),
\ 'capabilities': {
\ 'workspace': {},
\ 'workspace': {
\ 'workspaceFolders': v:true,
\ 'didChangeConfiguration': {
\ 'dynamicRegistration': v:true,
\ },
\ 'configuration': v:true,
\ },
\ 'textDocument': {
\ 'hover': {
\ 'contentFormat': ['plaintext'],
\ },
\ 'completion': {
\ 'completionItem': {
\ 'snippetSupport': go#config#GoplsUsePlaceholders() ? v:true : v:false,
\ },
\ },
\ 'codeAction': {
\ 'codeActionLiteralSupport': {
\ 'codeActionKind': {
\ 'valueSet': ['source.organizeImports'],
\ },
\ },
\ },
\ }
\ }
\ },
\ 'workspaceFolders': [s:workspaceFolder(0, a:wd)],
\ }
\ }
endfunction
function! go#lsp#message#Initialized() abort
return {
\ 'notification': 1,
\ 'method': 'initialized',
\ 'params': {},
\ }
endfunction
function! go#lsp#message#Shutdown() abort
return {
\ 'notification': 0,
\ 'method': 'shutdown',
\ }
endfunction
function! go#lsp#message#Format(file) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/formatting',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'options': {
\ 'insertSpaces': v:false,
\ },
\ }
\ }
endfunction
function! go#lsp#message#CodeActionImports(file) abort
return s:codeAction('source.organizeImports', a:file)
endfunction
function! s:codeAction(name, file) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/codeAction',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'range': {
\ 'start': {'line': 0, 'character': 0},
\ 'end': {'line': line('$'), 'character': 0},
\ },
\ 'context': {
\ 'only': [a:name],
\ },
\ }
\ }
endfunction
function! go#lsp#message#Exit() abort
return {
\ 'notification': 1,
\ 'method': 'exit',
\ }
endfunction
function! go#lsp#message#WorkspaceFoldersResult(dirs) abort
return map(copy(a:dirs), function('s:workspaceFolder', []))
endfunction
function! go#lsp#message#Definition(file, line, col) abort
return {
\ 'notification': 0,
@ -47,7 +130,20 @@ function! go#lsp#message#TypeDefinition(file, line, col) abort
\ }
endfunction
function! go#lsp#message#DidOpen(file, content) abort
function! go#lsp#message#Implementation(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/implementation',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'position': s:position(a:line, a:col)
\ }
\ }
endfunction
function! go#lsp#message#DidOpen(file, content, version) abort
return {
\ 'notification': 1,
\ 'method': 'textDocument/didOpen',
@ -56,18 +152,20 @@ function! go#lsp#message#DidOpen(file, content) abort
\ 'uri': go#path#ToURI(a:file),
\ 'languageId': 'go',
\ 'text': a:content,
\ 'version': a:version,
\ }
\ }
\ }
endfunction
function! go#lsp#message#DidChange(file, content) abort
function! go#lsp#message#DidChange(file, content, version) abort
return {
\ 'notification': 1,
\ 'method': 'textDocument/didChange',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file),
\ 'version': a:version,
\ },
\ 'contentChanges': [
\ {
@ -103,6 +201,22 @@ function! go#lsp#message#Completion(file, line, col) abort
\ }
endfunction
function! go#lsp#message#References(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/references',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'position': s:position(a:line, a:col),
\ 'context': {
\ 'includeDeclaration': v:true,
\ },
\ }
\ }
endfunction
function! go#lsp#message#Hover(file, line, col) abort
return {
\ 'notification': 0,
@ -116,8 +230,120 @@ function! go#lsp#message#Hover(file, line, col) abort
\ }
endfunction
function! go#lsp#message#ChangeWorkspaceFolders(add, remove) abort
let l:addDirs = map(copy(a:add), function('s:workspaceFolder', []))
let l:removeDirs = map(copy(a:remove), function('s:workspaceFolder', []))
return {
\ 'notification': 1,
\ 'method': 'workspace/didChangeWorkspaceFolders',
\ 'params': {
\ 'event': {
\ 'removed': l:removeDirs,
\ 'added': l:addDirs,
\ },
\ }
\ }
endfunction
function! go#lsp#message#ConfigurationResult(items) abort
let l:result = []
" results must be in the same order as the items
for l:item in a:items
let l:config = {
\ 'buildFlags': [],
\ 'hoverKind': 'Structured',
\ }
let l:buildtags = go#config#BuildTags()
if buildtags isnot ''
let l:config.buildFlags = extend(l:config.buildFlags, ['-tags', go#config#BuildTags()])
endif
let l:deepCompletion = go#config#GoplsDeepCompletion()
let l:matcher = go#config#GoplsMatcher()
let l:completeUnimported = go#config#GoplsCompleteUnimported()
let l:staticcheck = go#config#GoplsStaticCheck()
let l:usePlaceholder = go#config#GoplsUsePlaceholders()
let l:tempModfile = go#config#GoplsTempModfile()
let l:analyses = go#config#GoplsAnalyses()
let l:local = go#config#GoplsLocal()
let l:gofumpt = go#config#GoplsGofumpt()
let l:settings = go#config#GoplsSettings()
if l:deepCompletion isnot v:null
if l:deepCompletion
let l:config.deepCompletion = v:true
else
let l:config.deepCompletion = v:false
endif
endif
if l:matcher isnot v:null
let l:config.matcher = l:matcher
endif
if l:completeUnimported isnot v:null
if l:completeUnimported
let l:config.completeUnimported = v:true
else
let l:config.completeUnimported = v:false
endif
endif
if l:staticcheck isnot v:null
if l:staticcheck
let l:config.staticcheck = v:true
else
let l:config.staticcheck = v:false
endif
endif
if l:usePlaceholder isnot v:null
if l:usePlaceholder
let l:config.usePlaceholders = v:true
else
let l:config.usePlaceholders = v:false
endif
endif
if l:tempModfile isnot v:null
if l:tempModfile
let l:config.tempModfile = v:true
else
let l:config.tempModfile = v:false
endif
endif
if l:analyses isnot v:null
let l:config.analyses = l:analyses
endif
if l:local isnot v:null
let l:config.local = l:local
endif
if l:gofumpt isnot v:null
let l:config.gofumpt = l:gofumpt
endif
if l:settings isnot v:null
let l:config = extend(l:config, l:settings, 'keep')
endif
let l:result = add(l:result, l:config)
endfor
return l:result
endfunction
function s:workspaceFolder(key, val) abort
return {'uri': go#path#ToURI(a:val), 'name': a:val}
endfunction
function! s:position(line, col) abort
return {'line': a:line - 1, 'character': a:col-1}
return {'line': a:line, 'character': a:col}
endfunction
" restore Vi compatibility settings

View file

@ -0,0 +1,97 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
function! Test_GetSimpleTextPosition()
call s:getinfo('lsp text position should align with cursor position after ascii only', 'ascii')
endfunction
function! Test_GetMultiByteTextPosition()
call s:getinfo('lsp text position should align with cursor position after two place of interest symbols ⌘⌘', 'multi-byte')
endfunction
function! Test_GetMultipleCodeUnitTextPosition()
call s:getinfo('lsp text position should align with cursor position after Deseret Capital Letter Long I 𐐀', 'multi-code-units')
endfunction
function! s:getinfo(str, name)
if !go#util#has_job()
return
endif
try
let g:go_info_mode = 'gopls'
let l:tmp = gotest#write_file(a:name . '/position/position.go', [
\ 'package position',
\ '',
\ 'func Example() {',
\ "\tid := " . '"foo"',
\ "\tprintln(" .'"' . a:str . '", id)',
\ '}',
\ ] )
let l:expected = 'var id string'
let l:actual = go#lsp#GetInfo()
call assert_equal(l:expected, l:actual)
finally
call delete(l:tmp, 'rf')
unlet g:go_info_mode
endtry
endfunction
func! Test_Format() abort
try
let expected = join(readfile("test-fixtures/lsp/fmt/format_golden.go"), "\n")
let l:tmp = gotest#load_fixture('lsp/fmt/format.go')
call go#lsp#Format()
" this should now contain the formatted code
let actual = join(go#util#GetLines(), "\n")
call assert_equal(expected, actual)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_Format_SingleNewline() abort
try
let expected = join(readfile("test-fixtures/lsp/fmt/format_golden.go"), "\n")
let l:tmp = gotest#load_fixture('lsp/fmt/newline.go')
call go#lsp#Format()
" this should now contain the formatted code
let actual = join(go#util#GetLines(), "\n")
call assert_equal(expected, actual)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_Imports() abort
try
let expected = join(readfile("test-fixtures/lsp/imports/imports_golden.go"), "\n")
let l:tmp = gotest#load_fixture('lsp/imports/imports.go')
call go#lsp#Imports()
" this should now contain the expected imports code
let actual = join(go#util#GetLines(), "\n")
call assert_equal(expected, actual)
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

View file

@ -7,11 +7,15 @@ 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])
let tokens = matchlist(go#util#Exec(['go', 'version']), '\d\+.\(\d\+\)\(\.\d\+\)\? ')
if len(tokens) > 0
let s:go_major_version = str2nr(tokens[1])
else
let s:go_major_version = ""
endif
endif
if s:go_major_version < "11"
if !empty(s:go_major_version) && s:go_major_version < "11"
call go#util#EchoError("Go v1.11 is required to format go.mod file")
return
endif
@ -79,18 +83,11 @@ function! go#mod#update_file(source, target)
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
" clean up previous list
if l:listtype == "quickfix"
let l:list_title = getqflist({'title': 1})
else
" can't check the title, so assume that the list was for go fmt.
let l:list_title = {'title': 'Format'}
let l:list_title = getloclist(0, {'title': 1})
endif
if has_key(l:list_title, "title") && l:list_title['title'] == "Format"

View file

@ -39,7 +39,7 @@ function! s:paths() abort
if executable('go')
let s:goroot = go#util#env("goroot")
if go#util#ShellError() != 0
echomsg '''go env GOROOT'' failed'
call go#util#EchoError('`go env GOROOT` failed')
endif
else
let s:goroot = $GOROOT
@ -82,6 +82,10 @@ function! s:vendordirs() abort
if l:err != 0
return []
endif
if empty(l:root)
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}}'])
@ -111,55 +115,77 @@ function! s:vendordirs() abort
endfunction
let s:import_paths = {}
" ImportPath returns the import path of the package for current buffer.
" ImportPath returns the import path of the package for current buffer. It
" returns -1 if the import path cannot be determined.
function! go#package#ImportPath() abort
let dir = expand("%:p:h")
let l:dir = expand("%:p:h")
if has_key(s:import_paths, dir)
return s:import_paths[dir]
return s:import_paths[l:dir]
endif
let [l:out, l:err] = go#util#ExecInDir(['go', 'list'])
if l:err != 0
let l:importpath = go#package#FromPath(l:dir)
if type(l:importpath) == type(0)
return -1
endif
let l:importpath = split(out, '\n')[0]
" go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH.
" Check it and retun an error if that is the case
if l:importpath[0] ==# '_'
return -1
endif
let s:import_paths[dir] = l:importpath
let s:import_paths[l:dir] = l:importpath
return l:importpath
endfunction
" FromPath returns the import path of arg.
" go#package#FromPath returns the import path of arg. -1 is returned when arg
" does not specify a package. -2 is returned when arg is a relative path
" outside of GOPATH, not in a module, and not below the current working
" directory. A relative path is returned when in a null module at or below the
" current working directory..
function! go#package#FromPath(arg) abort
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let l:dir = getcwd()
let l:path = a:arg
let l:path = fnamemodify(a:arg, ':p')
if !isdirectory(l:path)
let l:path = fnamemodify(l:path, ':h')
endif
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
endif
try
if glob("*.go") == ""
" There's no Go code in this directory. We might be in a module directory
" which doesn't have any code at this level. To avoid `go list` making a
" bunch of HTTP requests to fetch dependencies, short-circuit `go list`
" and return -1 immediately.
if !empty(s:module())
return -1
endif
endif
let [l:out, l:err] = go#util#Exec(['go', 'list'])
if l:err != 0
return -1
endif
let l:importpath = split(l:out, '\n')[0]
let l:importpath = split(l:out, '\n')[0]
finally
execute l:cd fnameescape(l:dir)
endtry
" go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH.
" Check it and retun an error if that is the case
" go list returns '_CURRENTDIRECTORY' if the directory is in a null module
" (i.e. neither in GOPATH nor in a module). Return a relative import path
" if possible or an error if that is the case.
if l:importpath[0] ==# '_'
return -1
let l:relativeimportpath = fnamemodify(l:importpath[1:], ':.')
if go#util#IsWin()
let l:relativeimportpath = substitute(l:relativeimportpath, '\\', '/', 'g')
endif
if l:relativeimportpath == l:importpath[1:]
return '.'
endif
if l:relativeimportpath[0] == '/'
return -2
endif
let l:importpath= printf('./%s', l:relativeimportpath)
endif
return l:importpath
@ -206,10 +232,17 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
let vendordirs = s:vendordirs()
let l:modcache = go#util#env('gomodcache')
let ret = {}
for dir in dirs
" this may expand to multiple lines
let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
if l:modcache != ''
let root = add(root, l:modcache)
else
let root = add(root, expand(dir . '/pkg/mod'))
endif
let root = add(root, expand(dir . '/src'), )
let root = extend(root, vendordirs)
let root = add(root, module)
@ -234,9 +267,8 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
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.
" use the module directory when module.path begins wih a:ArgLead and
" module.path does not have any path segments after a:ArgLead.
let glob = module.dir
else
continue
@ -251,6 +283,11 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
if fnamemodify(candidate, ':t') == 'vendor'
continue
endif
" if path contains version info, strip it out
let vidx = strridx(candidate, '@')
if vidx >= 0
let candidate = strpart(candidate, 0, vidx)
endif
let candidate .= '/'
elseif candidate !~ '\.a$'
continue

View file

@ -28,11 +28,11 @@ function! go#path#GoPath(...) abort
let s:initial_go_path = ""
endif
echon "vim-go: " | echohl Function | echon "GOPATH restored to ". $GOPATH | echohl None
call go#util#EchoInfo("GOPATH restored to ". $GOPATH)
return
endif
echon "vim-go: " | echohl Function | echon "GOPATH changed to ". a:1 | echohl None
call go#util#EchoInfo("GOPATH changed to ". a:1)
let s:initial_go_path = $GOPATH
let $GOPATH = a:1
endfunction

View file

@ -4,7 +4,7 @@ set cpo&vim
function! go#play#Share(count, line1, line2) abort
if !executable('curl')
echohl ErrorMsg | echomsg "vim-go: require 'curl' command" | echohl None
call go#util#EchoError('cannot share: curl cannot be found')
return
endif
@ -20,8 +20,7 @@ function! go#play#Share(count, line1, line2) abort
call delete(share_file)
if l:err != 0
echom 'A error has occurred. Run this command to see what the problem is:'
echom go#util#Shelljoin(l:cmd)
call go#util#EchoError(['A error has occurred. Run this command to see what the problem is:', go#util#Shelljoin(l:cmd)])
return
endif
@ -38,7 +37,7 @@ function! go#play#Share(count, line1, line2) abort
call go#util#OpenBrowser(url)
endif
echo "vim-go: snippet uploaded: ".url
call go#util#EchoInfo('snippet uploaded: ' . url)
endfunction

View 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
scriptencoding utf-8
" New returns a promise. A promise's primary purpose is to make async jobs
" synchronous by awaiting fn.
"
" A promise is a dictionary with two keys:
" 'wrapper':
" A function that wraps fn. It can be used in place of fn.
" 'await':
" A function that waits for wrapper to be called and returns the value
" returned by fn. Returns default if timeout expires.
function! go#promise#New(fn, timeout, default) abort
let l:state = {}
" explicitly bind to state so that within l:promise's methods, self will
" always refer to state. See :help Partial for more information.
return {
\ 'wrapper': function('s:wrapper', [a:fn, a:default], l:state),
\ 'await': function('s:await', [a:timeout, a:default], l:state),
\ }
endfunction
function! s:wrapper(fn, default, ...) dict
try
let self.retval = call(a:fn, a:000)
catch
let self.retval = substitute(v:exception, '^Vim', '', '')
let self.exception = 1
endtry
return self.retval
endfunction
function! s:await(timeout, default) dict
let l:timer = timer_start(a:timeout, function('s:setretval', [a:default], self))
while !has_key(self, 'retval')
sleep 50m
endwhile
call timer_stop(l:timer)
if get(self, 'exception', 0)
throw self.retval
endif
return self.retval
endfunction
function! s:setretval(val, timer) dict
let self.retval = a:val
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View file

@ -0,0 +1,41 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
func! Test_PromiseNew() abort
let l:sut = go#promise#New(function('s:work', []), 100, -1)
call assert_true(has_key(l:sut, 'wrapper'))
call assert_true(has_key(l:sut, 'await'))
endfunc
func! Test_PromiseAwait() abort
let l:expected = 1
let l:default = -1
let l:sut = go#promise#New(function('s:work', [l:expected]), 100, l:default)
call timer_start(10, l:sut.wrapper)
let l:actual = call(l:sut.await, [])
call assert_equal(l:expected, l:actual)
endfunc
func! Test_PromiseAwait_Timeout() abort
let l:desired = 1
let l:expected = -1
let l:sut = go#promise#New(function('s:work', [l:desired]), 10, l:expected)
call timer_start(100, l:sut.wrapper)
let l:actual = call(l:sut.await, [])
call assert_equal(l:expected, l:actual)
endfunc
func! s:work(val, timer)
return a:val
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View file

@ -0,0 +1,43 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
function! go#referrers#Referrers(selected) abort
let l:mode = go#config#ReferrersMode()
if l:mode == 'guru'
call go#guru#Referrers(a:selected)
return
elseif l:mode == 'gopls'
if !go#config#GoplsEnabled()
call go#util#EchoError("go_referrers_mode is 'gopls', but gopls is disabled")
endif
let [l:line, l:col] = getpos('.')[1:2]
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
let l:fname = expand('%:p')
call go#lsp#Referrers(l:fname, l:line, l:col, funcref('s:parse_output'))
return
else
call go#util#EchoWarning('unknown value for g:go_referrers_mode')
endif
endfunction
" This uses Vim's errorformat to parse the output and put it into a quickfix
" or locationlist.
function! s:parse_output(exit_val, output, title) abort
if a:exit_val
call go#util#EchoError(a:output)
return
endif
let errformat = ",%f:%l:%c:\ %m"
let l:listtype = go#list#Type("GoReferrers")
call go#list#ParseFormat(l:listtype, errformat, a:output, a:title, 0)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View file

@ -20,8 +20,10 @@ function! go#rename#Rename(bang, ...) abort
let to_identifier = a:1
endif
let l:bin = go#config#RenameCommand()
" return with a warning if the bin doesn't exist
let bin_path = go#path#CheckBinPath(go#config#GorenameBin())
let bin_path = go#path#CheckBinPath(l:bin)
if empty(bin_path)
return
endif
@ -29,7 +31,18 @@ function! go#rename#Rename(bang, ...) abort
let fname = expand('%:p')
let pos = go#util#OffsetCursor()
let offset = printf('%s:#%d', fname, pos)
let cmd = [bin_path, "-offset", offset, "-to", to_identifier, '-tags', go#config#BuildTags()]
let args = []
if l:bin == 'gorename'
let l:args = extend(l:args, ['-tags', go#config#BuildTags(), '-offset', offset, '-to', to_identifier])
elseif l:bin == 'gopls'
" TODO(bc): use -tags when gopls supports it
let l:args = extend(l:args, ['rename', '-w', l:offset, to_identifier])
else
call go#util#EchoWarning('unexpected rename command')
endif
let l:cmd = extend([l:bin_path], l:args)
if go#util#has_job()
call s:rename_job({
@ -39,7 +52,12 @@ function! go#rename#Rename(bang, ...) abort
return
endif
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
let l:wd = go#util#ModuleRoot()
if l:wd == -1
let l:wd = expand("%:p:h")
endif
let [l:out, l:err] = go#util#ExecInWorkDir(l:cmd, l:wd)
call s:parse_errors(l:err, a:bang, split(l:out, '\n'))
endfunction
@ -54,6 +72,11 @@ function s:rename_job(args)
call go#cmd#autowrite()
let l:cbs = go#job#Options(l:job_opts)
let l:wd = go#util#ModuleRoot()
if l:wd != -1
let l:cbs.cwd = l:wd
endif
" wrap l:cbs.exit_cb in s:exit_cb.
let l:cbs.exit_cb = funcref('s:exit_cb', [l:cbs.exit_cb])

View file

@ -27,9 +27,8 @@ let s:last_status = ""
" if not it returns an empty string. This function should be plugged directly
" into the statusline.
function! go#statusline#Show() abort
" lazy initialiation of the cleaner
" lazy initialization of the cleaner
if !s:timer_id
" clean every 60 seconds all statuses
let interval = go#config#StatuslineDuration()
let s:timer_id = timer_start(interval, function('go#statusline#Clear'), {'repeat': -1})
endif
@ -57,9 +56,9 @@ function! go#statusline#Show() abort
" only update highlight if status has changed.
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" || status.state =~ 'initialized'
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" || status.state =~ 'initializing'
hi goStatusLineColor cterm=bold ctermbg=208 ctermfg=88 guibg=#ff8700 guifg=#870000
elseif status.state =~ "failed"
hi goStatusLineColor cterm=bold ctermbg=196 ctermfg=52 guibg=#ff0000 guifg=#5f0000
@ -83,10 +82,11 @@ function! go#statusline#Update(status_dir, status) abort
" before we stop the timer, check if we have any previous jobs to be cleaned
" up. Otherwise every job will reset the timer when this function is called
" and thus old jobs will never be cleaned
call go#statusline#Clear(0)
call s:clear()
" also reset the timer, so the user has time to see it in the statusline.
" Setting the timer_id to 0 will trigger a new cleaner routine.
" Setting the timer_id to 0 will cause a new timer to be created the next
" time the go#statusline#Show() is called.
call timer_stop(s:timer_id)
let s:timer_id = 0
endfunction
@ -94,6 +94,10 @@ endfunction
" Clear clears all currently stored statusline data. The timer_id argument is
" just a placeholder so we can pass it to a timer_start() function if needed.
function! go#statusline#Clear(timer_id) abort
call s:clear()
endfunction
function! s:clear()
for [status_dir, status] in items(s:statuses)
let elapsed_time = reltimestr(reltime(status.created_at))
" strip whitespace

View file

@ -98,7 +98,7 @@ func s:write_out(out) abort
if has_key(result, 'errors')
let l:winnr = winnr()
let l:listtype = go#list#Type("GoModifyTags")
call go#list#ParseFormat(l:listtype, "%f:%l:%c:%m", result['errors'], "gomodifytags")
call go#list#ParseFormat(l:listtype, "%f:%l:%c:%m", result['errors'], "gomodifytags", 0)
call go#list#Window(l:listtype, len(result['errors']))
"prevent jumping to quickfix list
@ -124,6 +124,7 @@ func s:create_cmd(args) abort
let l:mode = a:args.mode
let l:cmd_args = a:args.cmd_args
let l:modifytags_transform = go#config#AddtagsTransform()
let l:modifytags_skip_unexported = go#config#AddtagsSkipUnexported()
" start constructing the command
let cmd = [bin_path]
@ -131,6 +132,10 @@ func s:create_cmd(args) abort
call extend(cmd, ["-file", a:args.fname])
call extend(cmd, ["-transform", l:modifytags_transform])
if l:modifytags_skip_unexported
call extend(cmd, ["-skip-unexported"])
endif
if has_key(a:args, "modified")
call add(cmd, "-modified")
endif

View file

@ -24,8 +24,13 @@ function! go#template#create() abort
else
let l:template_file = go#config#TemplateFile()
endif
let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file)
silent exe 'keepalt 0r ' . fnameescape(l:template_path)
" If template_file is an absolute path, use it as-is. This is to support
" overrides pointing to templates outside of the vim-go plugin dir
if fnamemodify(l:template_file, ':p') != l:template_file
let l:template_file = go#util#Join(l:root_dir, "templates", l:template_file)
endif
silent exe 'keepalt 0r ' . fnameescape(l:template_file)
endif
else
let l:content = printf("package %s", l:package_name)

View file

@ -2,6 +2,8 @@
let s:cpo_save = &cpo
set cpo&vim
let s:bufnameprefix = 'goterm://'
" new creates a new terminal with the given command. Mode is set based on the
" global variable g:go_term_mode, which is by default set to :vsplit
function! go#term#new(bang, cmd, errorformat) abort
@ -15,6 +17,10 @@ function! go#term#newmode(bang, cmd, errorformat, mode) abort
let l:mode = go#config#TermMode()
endif
if go#config#TermReuse()
call s:closeterm()
endif
let l:state = {
\ 'cmd': a:cmd,
\ 'bang' : a:bang,
@ -24,55 +30,96 @@ function! go#term#newmode(bang, cmd, errorformat, mode) abort
\ 'errorformat': a:errorformat,
\ }
" execute go build in the files directory
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let l:dir = getcwd()
execute l:cd . fnameescape(expand("%:p:h"))
" execute the command in the current file's directory
let l:dir = go#util#Chdir(expand('%:p:h'))
execute l:mode . ' __go_term__'
setlocal filetype=goterm
setlocal bufhidden=delete
setlocal winfixheight
" TODO(bc)?: setlocal winfixwidth
setlocal noswapfile
setlocal nobuflisted
" explicitly bind callbacks to state so that within them, self will always
" refer to state. See :help Partial for more information.
"
" 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
let l:job = {
\ 'on_stdout': function('s:on_stdout', [], state),
\ 'on_exit' : function('s:on_exit', [], state),
\ }
" setup job for nvim
if has('nvim')
" explicitly bind callbacks to state so that within them, self will always
" refer to state. See :help Partial for more information.
"
" 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
let l:job = {
\ 'on_stdout': function('s:on_stdout', [], state),
\ 'on_exit' : function('s:on_exit', [], state),
\ }
let l:state.id = termopen(a:cmd, l:job)
let l:state.termwinid = win_getid(winnr())
let s:lasttermwinid = l:state.termwinid
call go#util#Chdir(l:dir)
let l:state.id = termopen(a:cmd, l:job)
let l:state.termwinid = win_getid(winnr())
" resize new term if needed.
let l:height = go#config#TermHeight()
let l:width = go#config#TermWidth()
execute l:cd . fnameescape(l:dir)
" Adjust the window width or height depending on whether it's a vertical or
" horizontal split.
if l:mode =~ "vertical" || l:mode =~ "vsplit" || l:mode =~ "vnew"
exe 'vertical resize ' . l:width
elseif mode =~ "split" || mode =~ "new"
exe 'resize ' . l:height
endif
" we also need to resize the pty, so there you go...
call jobresize(l:state.id, l:width, l:height)
" resize new term if needed.
let l:height = go#config#TermHeight()
let l:width = go#config#TermWidth()
" setup term for vim8
elseif has('terminal')
" Not great randomness, but "good enough" for our purpose here.
let l:rnd = sha256(printf('%s%s', reltimestr(reltime()), fnamemodify(bufname(''), ":p")))
let l:termname = printf("%s%s", s:bufnameprefix, l:rnd)
" Adjust the window width or height depending on whether it's a vertical or
" horizontal split.
if l:mode =~ "vertical" || l:mode =~ "vsplit" || l:mode =~ "vnew"
exe 'vertical resize ' . l:width
elseif mode =~ "split" || mode =~ "new"
exe 'resize ' . l:height
let l:term = {
\ 'out_cb': function('s:out_cb', [], state),
\ 'exit_cb' : function('s:exit_cb', [], state),
\ 'curwin': 1,
\ 'term_name': l:termname,
\ }
if l:mode =~ "vertical" || l:mode =~ "vsplit" || l:mode =~ "vnew"
let l:term["vertical"] = l:mode
endif
let l:state.id = term_start(a:cmd, l:term)
let l:state.termwinid = win_getid(bufwinnr(l:state.id))
let s:lasttermwinid = l:state.termwinid
call go#util#Chdir(l:dir)
" resize new term if needed.
let l:height = go#config#TermHeight()
let l:width = go#config#TermWidth()
" Adjust the window width or height depending on whether it's a vertical or
" horizontal split.
if l:mode =~ "vertical" || l:mode =~ "vsplit" || l:mode =~ "vnew"
exe 'vertical resize ' . l:width
elseif mode =~ "split" || mode =~ "new"
exe 'resize ' . l:height
endif
"if exists(*term_setsize)
"call term_setsize(l:state.id, l:height, l:width)
"endif
endif
" we also need to resize the pty, so there you go...
call jobresize(l:state.id, l:width, l:height)
call win_gotoid(l:state.winid)
return l:state.id
endfunction
" out_cb continually concat's the self.stdout_buf on recv of stdout
" and sets self.stdout to the new-lined split content in self.stdout_buf
func! s:out_cb(channel, msg) dict abort
let self.stdout_buf = self.stdout_buf . a:msg
let self.stdout = split(self.stdout_buf, '\n')
endfunction
function! s:on_stdout(job_id, data, event) dict abort
" 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
@ -95,9 +142,21 @@ function! s:on_stdout(job_id, data, event) dict abort
endif
endfunction
" vim8 exit callback
function! s:exit_cb(job_id, exit_status) dict abort
call s:handle_exit(a:job_id, a:exit_status, self)
endfunction
" nvim exit callback
function! s:on_exit(job_id, exit_status, event) dict abort
call s:handle_exit(a:job_id, a:exit_status, self)
endfunction
" handle_exit implements both vim8 and nvim exit callbacks
func s:handle_exit(job_id, exit_status, state) abort
let l:winid = win_getid(winnr())
call win_gotoid(self.winid)
call win_gotoid(a:state.winid)
let l:listtype = go#list#Type("_term")
if a:exit_status == 0
@ -106,40 +165,106 @@ function! s:on_exit(job_id, exit_status, event) dict abort
return
endif
call win_gotoid(self.winid)
let l:bufdir = expand('%:p:h')
if !isdirectory(l:bufdir)
call go#util#EchoWarning('terminal job failure not processed, because the job''s working directory no longer exists')
call win_gotoid(l:winid)
return
endif
let l:title = self.cmd
" change to directory where the command was run. If we do not do this the
" quickfix items will have the incorrect paths.
" see: https://github.com/fatih/vim-go/issues/2400
let l:dir = go#util#Chdir(l:bufdir)
let l:title = a:state.cmd
if type(l:title) == v:t_list
let l:title = join(self.cmd)
let l:title = join(a:state.cmd)
endif
let l:i = 0
while l:i < len(self.stdout)
let self.stdout[l:i] = substitute(self.stdout[l:i], "\r$", '', 'g')
while l:i < len(a:state.stdout)
let a:state.stdout[l:i] = substitute(a:state.stdout[l:i], "\r$", '', 'g')
let l:i += 1
endwhile
call go#list#ParseFormat(l:listtype, self.errorformat, self.stdout, l:title)
call go#list#ParseFormat(l:listtype, a:state.errorformat, a:state.stdout, l:title, 0)
let l:errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(l:errors))
" close terminal; we don't need it anymore
if go#config#TermCloseOnExit()
call win_gotoid(a:state.termwinid)
close!
endif
if empty(l:errors)
call go#util#EchoError( '[' . l:title . '] ' . "FAIL")
call go#util#Chdir(l:dir)
call win_gotoid(l:winid)
return
endif
" close terminal; we don't need it anymore
call win_gotoid(self.termwinid)
close!
if self.bang
if a:state.bang
call go#util#Chdir(l:dir)
call win_gotoid(l:winid)
return
endif
call win_gotoid(self.winid)
call win_gotoid(a:state.winid)
call go#list#JumpToFirst(l:listtype)
" change back to original working directory
call go#util#Chdir(l:dir)
endfunction
function! go#term#ToggleCloseOnExit() abort
if go#config#TermCloseOnExit()
call go#config#SetTermCloseOnExit(0)
call go#util#EchoProgress("term close on exit disabled")
return
endif
call go#config#SetTermCloseOnExit(1)
call go#util#EchoProgress("term close on exit enabled")
return
endfunction
function! s:closeterm()
if !exists('s:lasttermwinid')
return
endif
try
let l:termwinid = s:lasttermwinid
unlet s:lasttermwinid
let l:info = getwininfo(l:termwinid)
if empty(l:info)
return
endif
let l:info = l:info[0]
if !get(l:info, 'terminal', 0) is 1
return
endif
if has('nvim')
if 'goterm' == nvim_buf_get_option(nvim_win_get_buf(l:termwinid), 'filetype')
call nvim_win_close(l:termwinid, v:true)
endif
return
endif
if stridx(bufname(winbufnr(l:termwinid)), s:bufnameprefix, 0) == 0
let l:winid = win_getid()
call win_gotoid(l:termwinid)
close!
call win_gotoid(l:winid)
endif
catch
call go#util#EchoError(printf("vim-go: %s", v:exception))
endtry
endfunction
" restore Vi compatibility settings

View file

@ -3,7 +3,7 @@ let s:cpo_save = &cpo
set cpo&vim
func! Test_GoTermNewMode()
if !has('nvim')
if !(has('nvim') || has('terminal'))
return
endif
@ -22,12 +22,13 @@ func! Test_GoTermNewMode()
call assert_equal(actual, l:expected)
finally
sleep 50m
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_GoTermNewMode_SplitRight()
if !has('nvim')
if !(has('nvim') || has('terminal'))
return
endif
@ -46,11 +47,46 @@ func! Test_GoTermNewMode_SplitRight()
call assert_equal(actual, l:expected)
finally
sleep 50m
call delete(l:tmp, 'rf')
set nosplitright
endtry
endfunc
func! Test_GoTermReuse()
if !(has('nvim') || has('terminal'))
return
endif
try
let l:filename = 'term/term.go'
let l:tmp = gotest#load_fixture(l:filename)
exe 'cd ' . l:tmp . '/src/term'
let expected = expand('%:p')
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
set nosplitright
let g:go_term_reuse = 1
call go#term#new(0, cmd, &errorformat)
let actual = expand('%:p')
call assert_equal(actual, l:expected)
call assert_equal(3, len(getwininfo()))
call go#term#new(0, cmd, &errorformat)
let actual = expand('%:p')
call assert_equal(actual, l:expected)
call assert_equal(3, len(getwininfo()))
finally
sleep 50m
unlet g:go_term_reuse
call delete(l:tmp, 'rf')
endtry
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -0,0 +1,5 @@
package config
func Example() {
foo()
}

View file

@ -0,0 +1,8 @@
// +build constrained
package config
// foo is constrained and this comment exists to make the line numbers different than foo.go
func foo() {
println("foo")
}

View file

@ -0,0 +1,7 @@
// +build !constrained
package config
func foo() {
println("foo")
}

View file

@ -0,0 +1,3 @@
module config
go 1.13

View file

@ -1,13 +0,0 @@
package main
import (
"fmt"
)
func Foo(log *logging.TestLogger) {
log.Debug("vim-go")
}
func main() {
fmt.Println("vim-go")
}

View file

@ -1,15 +0,0 @@
package main
import (
"fmt"
logging "gh.com/gi/foo-logging"
)
func Foo(log *logging.TestLogger) {
log.Debug("vim-go")
}
func main() {
fmt.Println("vim-go")
}

View file

@ -1,12 +0,0 @@
package logging
import "fmt"
type TestLogger struct {
Value string
}
func (l *TestLogger) Debug(msg string) {
fmt.Println(msg)
fmt.Println(l.Value)
}

View file

@ -0,0 +1,6 @@
package main
func main() {
notafunc()
println("vim-go")
}

View file

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("vim-go"
}

View file

@ -0,0 +1,10 @@
package errcheck
import (
"io"
"os"
)
func foo() {
io.Copy(os.Stdout, os.Stdin)
}

View file

@ -0,0 +1,11 @@
package errcheck
import (
"io"
"os"
"testing"
)
func TestFoo(t *testing.T) {
io.Copy(os.Stdout, os.Stdin)
}

View file

@ -0,0 +1,3 @@
package lint
func baz() {}

View file

@ -0,0 +1,3 @@
package problems
func bar() {}

View file

@ -0,0 +1,5 @@
package problems
import "/quux"
func baz() {}

View file

@ -0,0 +1,9 @@
package problems
import (
"time"
)
func mySleep(time int) {
time.Sleep(500 * time.Millisecond)
}

View file

@ -0,0 +1,5 @@
package problems
func mySleep(time int) {
time.Sleep(500 * time.Millisecond)
}

View file

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("vim-go"
}

View file

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("vim-go")
}

View file

@ -0,0 +1,7 @@
package main
import "fmt"
func main() {
fmt.Println("vim-go")
}

View file

@ -0,0 +1,6 @@
package main
import "fmt"
func main() {
fmt.Println("vim-go")
}

View file

@ -0,0 +1,10 @@
package main
import (
"fmt"
)
func main() {
io.Copy(ioutil.Discard, os.Stdin)
fmt.Println("vim-go")
}

View file

@ -0,0 +1,13 @@
package main
import (
"fmt"
"io"
"io/ioutil"
"os"
)
func main() {
io.Copy(ioutil.Discard, os.Stdin)
fmt.Println("vim-go")
}

View file

@ -0,0 +1,10 @@
package main
import (
"fmt"
)
func ExampleHelloWorld() {
fmt.Println("Hello, World")
// Output: What's shakin
}

View file

@ -6,7 +6,10 @@ set cpo&vim
" compile the tests instead of running them (useful to catch errors in the
" test files). Any other argument is appended to the final `go test` command.
function! go#test#Test(bang, compile, ...) abort
let args = ["test", '-tags', go#config#BuildTags()]
let args = ["test"]
if len(go#config#BuildTags()) > 0
call extend(args, ["-tags", go#config#BuildTags()])
endif
" don't run the test, only compile it. Useful to capture and fix errors.
if a:compile
@ -32,6 +35,7 @@ function! go#test#Test(bang, compile, ...) abort
if go#config#TermEnabled()
call go#term#new(a:bang, ["go"] + args, s:errorformat())
return
endif
if go#util#has_job()
@ -76,7 +80,7 @@ function! go#test#Test(bang, compile, ...) abort
if l:err != 0
let l:winid = win_getid(winnr())
call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), l:cmd)
call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), l:cmd, 0)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if empty(errors)
@ -166,9 +170,17 @@ function! s:errorformat() abort
let format .= ",%-G" . indent . "%#--- PASS: %.%#"
" Match failure lines.
"
" Example failures start with '--- FAIL: ', followed by the example name
" followed by a space , followed by the duration of the example in
" parantheses. They aren't nested, though, so don't check for indentation.
" The errors from them also aren't indented and don't report file location
" or line numbers, so those won't show up. This will at least let the user
" know which example failed, though.
let format .= ',%G--- FAIL: %\\%(Example%\\)%\\@=%m (%.%#)'
" Test failures start with '--- FAIL: ', followed by the test name followed
" by a space the duration of the test in parentheses
" by a space, followed by the duration of the test in parentheses.
"
" e.g.:
" '--- FAIL: TestSomething (0.00s)'
@ -207,6 +219,14 @@ function! s:errorformat() abort
let format .= ",%G" . indent . "%#%\\t%\\{2}%m"
" }}}1
" Go 1.14 test verbose output {{{1
" Match test output lines similarly to Go 1.11 test output lines, but they
" have the test name followed by a colon before the filename when run with
" the -v flag.
let format .= ",%A" . indent . "%\\+%[%^:]%\\+: %f:%l: %m"
let format .= ",%A" . indent . "%\\+%[%^:]%\\+: %f:%l: "
" }}}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

View file

@ -66,9 +66,9 @@ endfunc
func! Test_GoTestShowName() abort
let expected = [
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld'},
\ {'lnum': 6, 'bufnr': 7, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'so long'},
\ {'lnum': 6, 'bufnr': 8, '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': 9, 'bufnr': 7, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
\ {'lnum': 9, 'bufnr': 8, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
\ ]
let g:go_test_show_name=1
@ -78,20 +78,27 @@ 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'},
\ {'lnum': 6, 'bufnr': 11, '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': 10, 'bufnr': 9, '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
func! Test_GoTestExample() abort
let expected = [
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'ExampleHelloWorld'}
\ ]
call s:test('example/example_test.go', expected)
endfunc
func! s:test(file, expected, ...) abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test'
silent exe 'e ' . $GOPATH . '/src/' . a:file

View file

@ -82,14 +82,16 @@ endfunction
function! go#tool#Info(showstatus) abort
let l:mode = go#config#InfoMode()
if l:mode == 'gocode'
call go#complete#Info(a:showstatus)
elseif l:mode == 'guru'
if l:mode == 'guru'
call go#guru#DescribeInfo(a:showstatus)
elseif l:mode == 'gopls'
if !go#config#GoplsEnabled()
call go#util#EchoError("go_info_mode is 'gopls', but gopls is disabled")
return
endif
call go#lsp#Info(a:showstatus)
else
call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [gocode, guru, gopls]')
call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [guru, gopls]')
endif
endfunction
@ -115,7 +117,15 @@ endfunction
function! go#tool#DescribeBalloon()
let l:fname = fnamemodify(bufname(v:beval_bufnr), ':p')
call go#lsp#Hover(l:fname, v:beval_lnum, v:beval_col, funcref('s:balloon', []))
let l:winid = win_getid()
call win_gotoid(bufwinid(v:beval_bufnr))
let [l:line, l:col] = go#lsp#lsp#Position(v:beval_lnum, v:beval_col)
call go#lsp#Hover(l:fname, l:line, l:col, funcref('s:balloon', []))
call win_gotoid(l:winid)
return ''
endfunction

View file

@ -18,7 +18,7 @@ function! s:encode(value, unreserved)
return substitute(
\ a:value,
\ a:unreserved,
\ '\="%".printf(''%02X'', char2nr(submatch(0)))',
\ '\=s:encodechar(submatch(0))',
\ 'g'
\)
endfunction
@ -27,10 +27,27 @@ function! go#uri#Decode(value) abort
return substitute(
\ a:value,
\ '%\(\x\x\)',
\ '\=nr2char(''0X'' . submatch(1))',
\ '\=s:decodehex(submatch(1))',
\ 'g'
\)
endfunction
function! s:encodechar(value)
let l:idx = 0
let l:encoded = ''
while l:idx < strlen(a:value)
let l:byte = strpart(a:value, l:idx, 1)
let l:encoded = printf('%s%%%02X', l:encoded, char2nr(l:byte))
let l:idx += 1
endwhile
return l:encoded
endfunction
function! s:decodehex(value)
return eval(printf('"\x%s"', a:value))
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -0,0 +1,56 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim
scriptencoding utf-8
func! Test_EncodePath_simple() abort
let l:uri = '/simple/foo'
let l:expected = '/simple/foo'
let l:actual = go#uri#EncodePath(l:uri)
call assert_equal(l:expected, l:actual)
endfunc
func! Test_EncodePath_multibyte() abort
let l:uri = '/multi-byte/⌘⌘'
let l:expected = '/multi-byte/%E2%8C%98%E2%8C%98'
let l:actual = go#uri#EncodePath(l:uri)
call assert_equal(l:expected, l:actual)
endfunc
func! Test_Decode_simple() abort
let l:uri = '/simple/foo'
let l:expected = '/simple/foo'
let l:actual = go#uri#Decode(l:uri)
call assert_equal(l:expected, l:actual)
endfunc
func! Test_Decode_multibyte() abort
let l:uri = '/multi-byte/%E2%8C%98%E2%8C%98'
let l:expected = '/multi-byte/⌘⌘'
let l:actual = go#uri#Decode(l:uri)
call assert_equal(l:expected, l:actual)
endfunc
func! Test_Roundtrip_simple() abort
let l:expected = '/simple/foo'
let l:actual = go#uri#Decode(go#uri#EncodePath(l:expected))
call assert_equal(l:expected, l:actual)
endfunc
func! Test_Roundtrip_multibyte() abort
let l:expected = '/multi-byte/⌘⌘'
let l:actual = go#uri#Decode(go#uri#EncodePath(l:expected))
call assert_equal(l:expected, l:actual)
endfunc
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
" vim: sw=2 ts=2 et

View file

@ -36,15 +36,9 @@ function! go#util#Join(...) abort
endfunction
" IsWin returns 1 if current OS is Windows or 0 otherwise
" Note that has('win32') is always 1 when has('win64') is 1, so has('win32') is enough.
function! go#util#IsWin() abort
let win = ['win16', 'win32', 'win64', 'win95']
for w in win
if (has(w))
return 1
endif
endfor
return 0
return has('win32')
endfunction
" IsMac returns 1 if current OS is macOS or 0 otherwise.
@ -68,18 +62,7 @@ endfunction
" 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
" 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('nvim')
endfunction
let s:env_cache = {}
@ -137,9 +120,39 @@ function! go#util#gomod() abort
return substitute(s:exec(['go', 'env', 'GOMOD'])[0], '\n', '', 'g')
endfunction
" gomodcache returns 'go env GOMODCACHE'. Instead use 'go#util#env("gomodcache")'
function! go#util#gomodcache() abort
return substitute(s:exec(['go', 'env', 'GOMODCACHE'])[0], '\n', '', 'g')
endfunction
function! go#util#osarch() abort
return go#util#env("goos") . '_' . go#util#env("goarch")
" hostosarch returns the OS and ARCH values that the go binary is intended for.
function! go#util#hostosarch() abort
let [l:hostos, l:err] = s:exec(['go', 'env', 'GOHOSTOS'])
let [l:hostarch, l:err] = s:exec(['go', 'env', 'GOHOSTARCH'])
return [substitute(l:hostos, '\n', '', 'g'), substitute(l:hostarch, '\n', '', 'g')]
endfunction
" go#util#ModuleRoot returns the root directory of the module of the current
" buffer.
function! go#util#ModuleRoot() abort
let [l:out, l:err] = go#util#ExecInDir(['go', 'env', 'GOMOD'])
if l:err != 0
return -1
endif
let l:module = split(l:out, '\n', 1)[0]
" When run with `GO111MODULE=on and not in a module directory, the module will be reported as /dev/null.
let l:fakeModule = '/dev/null'
if go#util#IsWin()
let l:fakeModule = 'NUL'
endif
if l:fakeModule == l:module
return expand('%:p:h')
endif
return resolve(fnamemodify(l:module, ':p:h'))
endfunction
" Run a shell command.
@ -157,6 +170,13 @@ function! s:system(cmd, ...) abort
set shell=/bin/sh shellredir=>%s\ 2>&1 shellcmdflag=-c
endif
if go#util#IsWin()
if executable($COMSPEC)
let &shell = $COMSPEC
set shellcmdflag=/C
endif
endif
try
return call('system', [a:cmd] + a:000)
finally
@ -196,18 +216,25 @@ function! go#util#Exec(cmd, ...) abort
return call('s:exec', [[l:bin] + a:cmd[1:]] + a:000)
endfunction
" ExecInDir will execute cmd with the working directory set to the current
" buffer's directory.
function! go#util#ExecInDir(cmd, ...) abort
if !isdirectory(expand("%:p:h"))
let l:wd = expand('%:p:h')
return call('go#util#ExecInWorkDir', [a:cmd, l:wd] + a:000)
endfunction
" ExecInWorkDir will execute cmd with the working diretory set to wd. Additional arguments will be passed
" to cmd.
function! go#util#ExecInWorkDir(cmd, wd, ...) abort
if !isdirectory(a:wd)
return ['', 1]
endif
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
let l:dir = go#util#Chdir(a:wd)
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)
call go#util#Chdir(l:dir)
endtry
return [l:out, l:err]
endfunction
@ -446,7 +473,7 @@ function! go#util#tempdir(prefix) abort
endif
" Not great randomness, but "good enough" for our purpose here.
let l:rnd = sha256(printf('%s%s', localtime(), fnamemodify(bufname(''), ":p")))
let l:rnd = sha256(printf('%s%s', reltimestr(reltime()), fnamemodify(bufname(''), ":p")))
let l:tmp = printf("%s/%s%s", l:dir, a:prefix, l:rnd)
call mkdir(l:tmp, 'p', 0700)
return l:tmp
@ -519,6 +546,170 @@ function! go#util#ShowInfo(info)
echo "vim-go: " | echohl Function | echon a:info | echohl None
endfunction
" go#util#SetEnv takes the name of an environment variable and what its value
" should be and returns a function that will restore it to its original value.
function! go#util#SetEnv(name, value) abort
let l:state = {}
if len(a:name) == 0
return function('s:noop', [], l:state)
endif
let l:remove = 0
if exists('$' . a:name)
let l:oldvalue = eval('$' . a:name)
else
let l:remove = 1
endif
" wrap the value in single quotes so that it will work on windows when there
" are backslashes present in the value (e.g. $PATH).
call execute('let $' . a:name . " = '" . a:value . "'")
if l:remove
return function('s:unset', [a:name], l:state)
endif
return function('go#util#SetEnv', [a:name, l:oldvalue], l:state)
endfunction
function! go#util#ClearHighlights(group) abort
if has('textprop')
" the property type may not exist when syntax highlighting is not enabled.
if empty(prop_type_get(a:group))
return
endif
if !has('patch-8.1.1035')
return prop_remove({'type': a:group, 'all': 1}, 1, line('$'))
endif
return prop_remove({'type': a:group, 'all': 1})
endif
if exists("*matchaddpos")
return s:clear_group_from_matches(a:group)
endif
endfunction
function! s:clear_group_from_matches(group) abort
let l:cleared = 0
let m = getmatches()
for item in m
if item['group'] == a:group
call matchdelete(item['id'])
let l:cleared = 1
endif
endfor
return l:cleared
endfunction
function! s:unset(name) abort
try
" unlet $VAR was introducted in Vim 8.0.1832, which is newer than the
" minimal version that vim-go supports. Set the environment variable to
" the empty string in that case. It's not perfect, but it will work fine
" for most things, and is really the best alternative that's available.
if !has('patch-8.0.1832')
call go#util#SetEnv(a:name, '')
return
endif
call execute('unlet $' . a:name)
catch
call go#util#EchoError(printf('could not unset $%s: %s', a:name, v:exception))
endtry
endfunction
function! s:noop(...) abort dict
endfunction
" go#util#HighlightPositions highlights using text properties if possible and
" falls back to matchaddpos() if necessary. It works around matchaddpos()'s
" limit of only 8 positions per call by calling matchaddpos() with no more
" than 8 positions per call.
"
" pos should be a list of 3 element lists. The lists should be [line, col,
" length] as used by matchaddpos().
function! go#util#HighlightPositions(group, pos) abort
if has('textprop')
for l:pos in a:pos
" use a single line prop by default
let l:prop = {'type': a:group, 'length': l:pos[2]}
let l:line = getline(l:pos[0])
" l:max is the 1-based index within the buffer of the first character after l:pos.
let l:max = line2byte(l:pos[0]) + l:pos[1] + l:pos[2] - 1
if has('patch-8.2.115')
" Use byte2line as long as 8.2.115 (which resolved
" https://github.com/vim/vim/issues/5334) is available.
let l:end_lnum = byte2line(l:max)
" specify end line and column if needed.
if l:pos[0] != l:end_lnum
let l:end_col = l:max - line2byte(l:end_lnum)
let l:prop = {'type': a:group, 'end_lnum': l:end_lnum, 'end_col': l:end_col}
endif
elseif l:pos[1] + l:pos[2] - 1 > len(l:line)
let l:end_lnum = l:pos[0]
while line2byte(l:end_lnum+1) < l:max
let l:end_lnum += 1
endwhile
" l:end_col is the full length - the byte position of l:end_lnum +
" the number of newlines (number of newlines is l:end_lnum -
" l:pos[0].
let l:end_col = l:max - line2byte(l:end_lnum) + l:end_lnum - l:pos[0]
let l:prop = {'type': a:group, 'end_lnum': l:end_lnum, 'end_col': l:end_col}
endif
try
call prop_add(l:pos[0], l:pos[1], l:prop)
catch
" Swallow any exceptions encountered while trying to add the property
" Due to the asynchronous nature, it's possible that the buffer has
" changed since the buffer was analyzed and that the specified
" position is no longer valid.
endtry
endfor
return
endif
if exists('*matchaddpos')
return s:matchaddpos(a:group, a:pos)
endif
endfunction
" s:matchaddpos works around matchaddpos()'s limit of only 8 positions per
" call by calling matchaddpos() with no more than 8 positions per call.
function! s:matchaddpos(group, pos) abort
let l:partitions = []
let l:partitionsIdx = 0
let l:posIdx = 0
for l:pos in a:pos
if l:posIdx % 8 == 0
let l:partitions = add(l:partitions, [])
let l:partitionsIdx = len(l:partitions) - 1
endif
let l:partitions[l:partitionsIdx] = add(l:partitions[l:partitionsIdx], l:pos)
let l:posIdx = l:posIdx + 1
endfor
for l:positions in l:partitions
call matchaddpos(a:group, l:positions)
endfor
endfunction
function! go#util#Chdir(dir) abort
if !exists('*chdir')
let l:olddir = getcwd()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
execute cd . fnameescape(a:dir)
return l:olddir
endif
return chdir(a:dir)
endfunction
" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

View file

@ -18,16 +18,23 @@ fun! gotest#write_file(path, contents) abort
call mkdir(fnamemodify(l:full_path, ':h'), 'p')
call writefile(a:contents, l:full_path)
exe 'cd ' . l:dir . '/src'
call go#util#Chdir(l:dir . '/src')
silent exe 'e! ' . a:path
" Set cursor.
let l:lnum = 1
for l:line in a:contents
let l:m = match(l:line, "\x1f")
let l:m = stridx(l:line, "\x1f")
if l:m > -1
call setpos('.', [0, l:lnum, l:m, 0])
let l:byte = line2byte(l:lnum) + l:m
exe 'goto '. l:byte
call setline('.', substitute(getline('.'), "\x1f", '', ''))
silent noautocmd w!
call go#lsp#DidClose(expand('%:p'))
call go#lsp#DidOpen(expand('%:p'))
break
endif
@ -42,15 +49,21 @@ endfun
" The file will be copied to a new GOPATH-compliant temporary directory and
" loaded as the current buffer.
fun! gotest#load_fixture(path) abort
if go#util#has_job()
call go#lsp#CleanWorkspaces()
endif
let l:dir = go#util#tempdir("vim-go-test/testrun/")
let $GOPATH .= ':' . l:dir
let l:full_path = l:dir . '/src/' . a:path
call mkdir(fnamemodify(l:full_path, ':h'), 'p')
exe 'cd ' . l:dir . '/src'
silent exe 'noautocmd e ' . a:path
call go#util#Chdir(l:dir . '/src')
silent exe 'noautocmd e! ' . a:path
silent exe printf('read %s/test-fixtures/%s', g:vim_go_root, a:path)
silent noautocmd w!
if go#util#has_job()
call go#lsp#AddWorkspaceDirectory(fnamemodify(l:full_path, ':p:h'))
endif
return l:dir
endfun
@ -109,26 +122,29 @@ endfun
func! gotest#assert_quickfix(got, want) abort
call assert_equal(len(a:want), len(a:got), "number of errors")
if len(a:want) != len(a:got)
call assert_equal(a:want, a:got)
return
return assert_equal(a:want, a:got)
endif
let l:retval = 0
let i = 0
while i < len(a:want)
let want_item = a:want[i]
let got_item = a:got[i]
let i += 1
call assert_equal(want_item.bufnr, got_item.bufnr, "bufnr")
call assert_equal(want_item.lnum, got_item.lnum, "lnum")
call assert_equal(want_item.col, got_item.col, "col")
call assert_equal(want_item.vcol, got_item.vcol, "vcol")
call assert_equal(want_item.nr, got_item.nr, "nr")
call assert_equal(want_item.pattern, got_item.pattern, "pattern")
call assert_equal(want_item.text, got_item.text, "text")
call assert_equal(want_item.type, got_item.type, "type")
call assert_equal(want_item.valid, got_item.valid, "valid")
let l:retval = assert_equal(want_item.bufnr, got_item.bufnr, "bufnr") || l:retval
let l:retval = assert_equal(want_item.lnum, got_item.lnum, "lnum") || l:retval
let l:retval = assert_equal(want_item.col, got_item.col, "col") || l:retval
let l:retval = assert_equal(want_item.vcol, got_item.vcol, "vcol") || l:retval
let l:retval = assert_equal(want_item.nr, got_item.nr, "nr") || l:retval
let l:retval = assert_equal(want_item.pattern, got_item.pattern, "pattern") || l:retval
let l:retval = assert_equal(want_item.text, got_item.text, "text") || l:retval
let l:retval = assert_equal(want_item.type, got_item.type, "type") || l:retval
let l:retval = assert_equal(want_item.valid, got_item.valid, "valid") || l:retval
endwhile
return l:retval
endfunc
" restore Vi compatibility settings

View file

@ -31,14 +31,13 @@ endif
" use a different output, for those we define them directly and modify the
" errorformat ourselves. More information at:
" http://vimdoc.sourceforge.net/htmldoc/quickfix.html#errorformat
CompilerSet errorformat =%-G#\ %.%# " Ignore lines beginning with '#' ('# command-line-arguments' line sometimes appears?)
CompilerSet errorformat+=%-G%.%#panic:\ %m " Ignore lines containing 'panic: message'
CompilerSet errorformat+=%Ecan\'t\ load\ package:\ %m " Start of multiline error string is 'can\'t load package'
CompilerSet errorformat+=%A%f:%l:%c:\ %m " Start of multiline unspecified string is 'filename:linenumber:columnnumber:'
CompilerSet errorformat+=%A%f:%l:\ %m " Start of multiline unspecified string is 'filename:linenumber:'
CompilerSet errorformat+=%C%*\\s%m " Continuation of multiline error message is indented
CompilerSet errorformat+=%-G%.%# " All lines not matching any of the above patterns are ignored
CompilerSet errorformat =%-G#\ %.%# " Ignore lines beginning with '#' ('# command-line-arguments' line sometimes appears?)
CompilerSet errorformat+=%-G%.%#panic:\ %m " Ignore lines containing 'panic: message'
CompilerSet errorformat+=%Ecan\'t\ load\ package:\ %m " Start of multiline error string is 'can\'t load package'
CompilerSet errorformat+=%A%\\%%(%[%^:]%\\+:\ %\\)%\\?%f:%l:%c:\ %m " Start of multiline unspecified string is 'filename:linenumber:columnnumber:'
CompilerSet errorformat+=%A%\\%%(%[%^:]%\\+:\ %\\)%\\?%f:%l:\ %m " Start of multiline unspecified string is 'filename:linenumber:'
CompilerSet errorformat+=%C%*\\s%m " Continuation of multiline error message is indented
CompilerSet errorformat+=%-G%.%# " All lines not matching any of the above patterns are ignored
let &cpo = s:save_cpo
unlet s:save_cpo

File diff suppressed because it is too large Load diff

View file

@ -13,8 +13,8 @@ let b:did_ftplugin = 1
let s:cpo_save = &cpo
set cpo&vim
let b:undo_ftplugin = "setl fo< com< cms<
\ | exe 'au! vim-go-buffer * <buffer>'"
let b:undo_ftplugin = "setl fo< com< cms<"
\ . "| exe 'au! vim-go-buffer * <buffer>'"
setlocal formatoptions-=t
@ -25,10 +25,9 @@ setlocal noexpandtab
compiler go
" Set autocompletion
setlocal omnifunc=go#complete#Complete
if !go#util#has_job()
setlocal omnifunc=go#complete#GocodeComplete
if go#config#CodeCompletionEnabled()
" Set autocompletion
setlocal omnifunc=go#complete#Complete
endif
if get(g:, "go_doc_keywordprg_enabled", 1)
@ -72,43 +71,59 @@ if get(g:, "go_textobj_enabled", 1)
xnoremap <buffer> <silent> [[ :<c-u>call go#textobj#FunctionJump('v', 'prev')<cr>
endif
if go#config#AutoTypeInfo() || go#config#AutoSameids()
let &l:updatetime= get(g:, "go_updatetime", 800)
endif
" Autocommands
" ============================================================================
"
augroup vim-go-buffer
autocmd! * <buffer>
" The file is registered (textDocument/DidOpen) with gopls in in
" plugin/go.vim on the FileType event.
" TODO(bc): handle all the other events that may be of interest to gopls,
" too (e.g. BufFilePost , CursorHold , CursorHoldI, FileReadPost,
" StdinReadPre, BufWritePost, TextChange, TextChangedI)
" The file is registered (textDocument/DidOpen) with gopls in plugin/go.vim
" on the FileType event.
if go#util#has_job()
autocmd BufWritePost <buffer> call go#lsp#DidChange(expand('<afile>:p'))
autocmd FileChangedShell <buffer> call go#lsp#DidChange(expand('<afile>:p'))
autocmd BufWritePost,FileChangedShellPost <buffer> call go#lsp#DidChange(expand('<afile>:p'))
autocmd BufDelete <buffer> call go#lsp#DidClose(expand('<afile>:p'))
endif
autocmd CursorHold <buffer> call go#auto#auto_type_info()
autocmd CursorHold <buffer> call go#auto#auto_sameids()
" send the textDocument/didChange notification when idle. go#lsp#DidChange
" will not send an event if the buffer hasn't changed since the last
" notification.
autocmd CursorHold,CursorHoldI <buffer> call go#lsp#DidChange(expand('<afile>:p'))
autocmd BufEnter,CursorHold <buffer> call go#auto#update_autocmd()
" Echo the identifier information when completion is done. Useful to see
" the signature of a function, etc...
if exists('##CompleteDone')
autocmd CompleteDone <buffer> call go#auto#echo_go_info()
autocmd CompleteDone <buffer> call go#auto#complete_done()
endif
autocmd BufWritePre <buffer> call go#auto#fmt_autosave()
autocmd BufWritePost <buffer> call go#auto#metalinter_autosave()
" 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 !has('textprop')
"TODO(bc): how to clear sameids and diagnostics when a non-go buffer is
" loaded into a window and the previously loaded buffer is still loaded in
" another window?
" TODO(bc): only clear when the new buffer isn't the old buffer
" clear SameIds when the buffer is unloaded from its last window so that
" loading another buffer (especially of a different filetype) in the same
" window doesn't highlight the most recently matched identifier's positions.
autocmd BufWinLeave <buffer> call go#guru#ClearSameIds()
" clear SameIds when a new buffer is loaded in the window so that the
" previous buffer's highlighting isn't used.
autocmd BufWinEnter <buffer> call go#guru#ClearSameIds()
" clear diagnostics when the buffer is unloaded from its last window so that
" loading another buffer (especially of a different filetype) in the same
" window doesn't highlight the previously loaded buffer's diagnostics.
autocmd BufWinLeave <buffer> call go#lsp#ClearDiagnosticHighlights()
" clear diagnostics when a new buffer is loaded in the window so that the
" previous buffer's diagnostics aren't used.
"autocmd BufWinEnter <buffer> call go#lsp#ClearDiagnosticHighlights()
endif
autocmd BufEnter <buffer>
\ if go#config#AutodetectGopath() && !exists('b:old_gopath')

View file

@ -3,7 +3,7 @@ command! -nargs=? -complete=customlist,go#rename#Complete GoRename call go#renam
" -- guru
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#implements#Implements(<count>)
command! -range=% GoPointsTo call go#guru#PointsTo(<count>)
command! -range=% GoWhicherrs call go#guru#Whicherrs(<count>)
command! -range=% GoCallees call go#guru#Callees(<count>)
@ -12,7 +12,7 @@ command! -range=% GoCallers call go#guru#Callers(<count>)
command! -range=% GoCallstack call go#guru#Callstack(<count>)
command! -range=% GoFreevars call go#guru#Freevars(<count>)
command! -range=% GoChannelPeers call go#guru#ChannelPeers(<count>)
command! -range=% GoReferrers call go#guru#Referrers(<count>)
command! -range=% GoReferrers call go#referrers#Referrers(<count>)
command! -range=0 GoSameIds call go#guru#SameIds(1)
command! -range=0 GoSameIdsClear call go#guru#ClearSameIds()
@ -105,8 +105,9 @@ command! -nargs=0 GoFillStruct call go#fillstruct#FillStruct()
" -- debug
if !exists(':GoDebugStart')
command! -nargs=* -complete=customlist,go#package#Complete GoDebugStart call go#debug#Start(0, <f-args>)
command! -nargs=* -complete=customlist,go#package#Complete GoDebugTest call go#debug#Start(1, <f-args>)
command! -nargs=* -complete=customlist,go#package#Complete GoDebugStart call go#debug#Start('debug', <f-args>)
command! -nargs=* -complete=customlist,go#package#Complete GoDebugTest call go#debug#Start('test', <f-args>)
command! -nargs=1 GoDebugAttach call go#debug#Start('attach', <f-args>)
command! -nargs=? GoDebugBreakpoint call go#debug#Breakpoint(<f-args>)
endif
@ -116,4 +117,12 @@ command! -nargs=0 GoReportGitHubIssue call go#issue#New()
" -- iferr
command! -nargs=0 GoIfErr call go#iferr#Generate()
" -- lsp
command! -nargs=+ -complete=dir GoAddWorkspace call go#lsp#AddWorkspaceDirectory(<f-args>)
command! -nargs=0 GoLSPDebugBrowser call go#lsp#DebugBrowser()
command! -nargs=* -bang GoDiagnostics call go#lint#Diagnostics(<bang>0, <f-args>)
" -- term
command! GoToggleTermCloseOnExit call go#term#ToggleCloseOnExit()
" vim: sw=2 ts=2 et

View file

@ -11,7 +11,7 @@ endif
" Some handy plug mappings
nnoremap <silent> <Plug>(go-run) :<C-u>call go#cmd#Run(!g:go_jump_to_error)<CR>
if has("nvim")
if has("nvim") || has("terminal")
nnoremap <silent> <Plug>(go-run-vertical) :<C-u>call go#cmd#RunTerm(!g:go_jump_to_error, 'vsplit', [])<CR>
nnoremap <silent> <Plug>(go-run-split) :<C-u>call go#cmd#RunTerm(!g:go_jump_to_error, 'split', [])<CR>
nnoremap <silent> <Plug>(go-run-tab) :<C-u>call go#cmd#RunTerm(!g:go_jump_to_error, 'tabe', [])<CR>
@ -35,14 +35,14 @@ 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-imports) :<C-u>call go#fmt#Format(1)<CR>
nnoremap <silent> <Plug>(go-implements) :<C-u>call go#guru#Implements(-1)<CR>
nnoremap <silent> <Plug>(go-implements) :<C-u>call go#implements#Implements(-1)<CR>
nnoremap <silent> <Plug>(go-callees) :<C-u>call go#guru#Callees(-1)<CR>
nnoremap <silent> <Plug>(go-callers) :<C-u>call go#guru#Callers(-1)<CR>
nnoremap <silent> <Plug>(go-describe) :<C-u>call go#guru#Describe(-1)<CR>
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>
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#referrers#Referrers(-1)<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>
@ -83,4 +83,6 @@ nnoremap <silent> <Plug>(go-alternate-split) :<C-u>call go#alternate#Switch(0, "
nnoremap <silent> <Plug>(go-iferr) :<C-u>call go#iferr#Generate()<CR>
nnoremap <silent> <Plug>(go-diagnostics) :<C-u>call go#lint#Diagnostics(!g:go_jump_to_error)<CR>
" vim: sw=2 ts=2 et

View file

@ -52,19 +52,11 @@ endfunction
let s:engine = go#config#SnippetEngine()
if s:engine is? "automatic"
if get(g:, 'did_plugin_ultisnips') is 1
call s:GoUltiSnips()
elseif get(g:, 'loaded_neosnippet') is 1
call s:GoNeosnippet()
elseif get(g:, 'loaded_minisnip') is 1
call s:GoMinisnip()
endif
elseif s:engine is? "ultisnips"
if s:engine is? 'ultisnips'
call s:GoUltiSnips()
elseif s:engine is? "neosnippet"
elseif s:engine is? 'neosnippet'
call s:GoNeosnippet()
elseif s:engine is? "minisnip"
elseif s:engine is? 'minisnip'
call s:GoMinisnip()
endif

View file

@ -144,20 +144,42 @@ else {
endsnippet
# if inline error
snippet ife "If with inline erro"
snippet ife "If with inline error"
if err := ${1:condition}; err != nil {
${0:${VISUAL}}
}
endsnippet
snippet ew "errors.Wrap"
errors.Wrap(${1:err}, "${2:message}")
endsnippet
snippet ewf "errors.Wrapf"
errors.Wrapf(${1:err}, "${2:message %v}", ${3:args...})
endsnippet
# error snippet
snippet errn "Error return " !b
snippet errn "Error return" !b
if err != nil {
return err
}
${0}
endsnippet
snippet errnw "Error return wrap" !b
if err != nil {
return errors.Wrap(err, "${1:message}")
}
${0}
endsnippet
snippet errnwf "Error return wrapf" !b
if err != nil {
return errors.Wrapf(err, "${1:message %v}", ${2:args...})
}
${0}
endsnippet
# error log snippet
snippet errl "Error with log.Fatal(err)" !b
if err != nil {
@ -174,6 +196,20 @@ if err != nil {
${0}
endsnippet
snippet errn,w "Error return wrap with two return values" !b
if err != nil {
return nil, errors.Wrap(err, "${1:message}")
}
${0}
endsnippet
snippet errn,wf "Error return wrapf with two return values" !b
if err != nil {
return nil, errors.Wrapf(err, "${1:message %v}", ${2:args...})
}
${0}
endsnippet
# error panic
snippet errp "Error panic" !b
if err != nil {
@ -235,6 +271,20 @@ for ${2:k}, ${3:v} := range ${1} {
}
endsnippet
snippet forsel "for select"
for {
select {
case ${2:${1:result} := }<- ${3:channel}:
${0}
}
}
endsnippet
snippet selc "select case" !b
case ${1:${2:var} := }<-${3:channel}:
${0}
endsnippet
# function
snippet func "func Function(...) [error] { ... }"
func ${1:name}(${2:params})${3/(.+)/ /}`!p opening_par(snip, 3)`$3`!p closing_par(snip, 3)` {
@ -244,7 +294,12 @@ endsnippet
# Fmt Printf debug
snippet ff "fmt.Printf(...)"
fmt.Printf("${1:${VISUAL}} = %+v\n", $1)
fmt.Printf("$1 = %+v\n", ${1:${VISUAL}})
endsnippet
# Fmt Printf debug with hash
snippet ffh "fmt.Printf(#...) hash"
fmt.Printf("$1 = %#v\n", ${1:${VISUAL}})
endsnippet
# Fmt Println debug
@ -257,6 +312,18 @@ snippet fe "fmt.Errorf(...)"
fmt.Errorf("${1:${VISUAL}}")
endsnippet
# Fmt Errorf wrap
snippet few "fmt.Errorf(%w, err)"
fmt.Errorf("${1:message}: %w", ${2:${VISUAL:err}})
endsnippet
# Fmt Errorf wrap and return
snippet errnfw "Error return fmt.Errorf(%w, err)" !b
if ${1:${VISUAL:err}} != nil {
return fmt.Errorf("${2:message}: %w", $1)
}
endsnippet
# log printf
snippet lf "log.Printf(...)"
log.Printf("${1:${VISUAL}} = %+v\n", $1)
@ -326,7 +393,7 @@ endsnippet
# struct
snippet st "type T struct { ... }"
type ${1:Type} struct {
${0}
${0}
}
endsnippet
@ -338,6 +405,12 @@ case ${2:value1}:
}
endsnippet
snippet tswitch "type switch x { ... }"
switch ${2:$1 := }${1:v}.(type) {
${0}
}
endsnippet
# sprintf
snippet sp "fmt.Sprintf(...)"
fmt.Sprintf("%${1:s}", ${2:var})
@ -385,7 +458,7 @@ for _, tt := range tests {
endsnippet
snippet hf "http.HandlerFunc" !b
snippet hf "http.HandlerFunc"
func ${1:handler}(w http.ResponseWriter, r *http.Request) {
${0:fmt.Fprintf(w, "hello world")}
}

View file

@ -9,30 +9,22 @@ let s:cpo_save = &cpo
set cpo&vim
function! s:checkVersion() abort
" Not using the has('patch-7.4.2009') syntax because that wasn't added until
" 7.4.237, and we want to be sure this works for everyone (this is also why
" we're not using utils#EchoError()).
"
" Version 7.4.2009 was chosen because that's greater than what the most recent Ubuntu LTS
" release (16.04) uses and has a couple of features we need (e.g. execute()
" and :message clear).
let l:unsupported = 0
if go#config#VersionWarning() != 0
if has('nvim')
let l:unsupported = !has('nvim-0.3.1')
let l:unsupported = !has('nvim-0.4.0')
else
let l:unsupported = (v:version < 704 || (v:version == 704 && !has('patch2009')))
let l:unsupported = !has('patch-8.0.1453')
endif
if l:unsupported == 1
echohl Error
echom "vim-go requires Vim 7.4.2009 or Neovim 0.3.1, but you're using an older version."
echom "vim-go requires at least Vim 8.0.1453 or Neovim 0.4.0, but you're using an older version."
echom "Please update your Vim for the best vim-go experience."
echom "If you really want to continue you can set this to make the error go away:"
echom " let g:go_version_warning = 0"
echom "Note that some features may error out or behave incorrectly."
echom "Please do not report bugs unless you're using Vim 7.4.2009 or newer or Neovim 0.3.1."
echom "Please do not report bugs unless you're using at least Vim 8.0.1453 or Neovim 0.4.0."
echohl None
" Make sure people see this.
@ -45,28 +37,26 @@ call s:checkVersion()
" these packages are used by vim-go and can be automatically installed if
" needed by the user with GoInstallBinaries.
" NOTE(bc): varying the binary name and the tail of the import path does not yet work in module aware mode.
let s:packages = {
\ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt'],
\ 'dlv': ['github.com/go-delve/delve/cmd/dlv'],
\ 'errcheck': ['github.com/kisielk/errcheck'],
\ 'fillstruct': ['github.com/davidrjenni/reftools/cmd/fillstruct'],
\ 'gocode': ['github.com/mdempsky/gocode', {'windows': ['-ldflags', '-H=windowsgui']}],
\ 'gocode-gomod': ['github.com/stamblerre/gocode'],
\ 'godef': ['github.com/rogpeppe/godef'],
\ 'gogetdoc': ['github.com/zmb3/gogetdoc'],
\ 'goimports': ['golang.org/x/tools/cmd/goimports'],
\ 'golint': ['golang.org/x/lint/golint'],
\ 'gopls': ['golang.org/x/tools/cmd/gopls'],
\ 'gometalinter': ['github.com/alecthomas/gometalinter'],
\ 'golangci-lint': ['github.com/golangci/golangci-lint/cmd/golangci-lint'],
\ 'gomodifytags': ['github.com/fatih/gomodifytags'],
\ 'gorename': ['golang.org/x/tools/cmd/gorename'],
\ 'gotags': ['github.com/jstemmer/gotags'],
\ 'guru': ['golang.org/x/tools/cmd/guru'],
\ 'impl': ['github.com/josharian/impl'],
\ 'keyify': ['honnef.co/go/tools/cmd/keyify'],
\ 'motion': ['github.com/fatih/motion'],
\ 'iferr': ['github.com/koron/iferr'],
\ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt@master'],
\ 'dlv': ['github.com/go-delve/delve/cmd/dlv@master'],
\ 'errcheck': ['github.com/kisielk/errcheck@master'],
\ 'fillstruct': ['github.com/davidrjenni/reftools/cmd/fillstruct@master'],
\ 'godef': ['github.com/rogpeppe/godef@master'],
\ 'goimports': ['golang.org/x/tools/cmd/goimports@master'],
\ 'golint': ['golang.org/x/lint/golint@master'],
\ 'gopls': ['golang.org/x/tools/gopls@latest', {}, {'after': function('go#lsp#Restart', [])}],
\ 'golangci-lint': ['github.com/golangci/golangci-lint/cmd/golangci-lint@master'],
\ 'gomodifytags': ['github.com/fatih/gomodifytags@master'],
\ 'gorename': ['golang.org/x/tools/cmd/gorename@master'],
\ 'gotags': ['github.com/jstemmer/gotags@master'],
\ 'guru': ['golang.org/x/tools/cmd/guru@master'],
\ 'impl': ['github.com/josharian/impl@master'],
\ 'keyify': ['honnef.co/go/tools/cmd/keyify@master'],
\ 'motion': ['github.com/fatih/motion@master'],
\ 'iferr': ['github.com/koron/iferr@master'],
\ }
" These commands are available on any filetypes
@ -90,22 +80,21 @@ function! s:GoInstallBinaries(updateBinaries, ...)
endif
if go#path#Default() == ""
echohl Error
echomsg "vim.go: $GOPATH is not set and 'go env GOPATH' returns empty"
echohl None
call go#util#EchoError('$GOPATH is not set and `go env GOPATH` returns empty')
return
endif
let go_bin_path = go#path#BinPath()
" change $GOBIN so go get can automatically install to it
let $GOBIN = go_bin_path
let [l:goos, l:goarch] = go#util#hostosarch()
let Restore_goos = go#util#SetEnv('GOOS', l:goos)
let Restore_goarch = go#util#SetEnv('GOARCH', l:goarch)
" old_path is used to restore users own path
let old_path = $PATH
" change $GOBIN so go get can automatically install to it
let Restore_gobin = go#util#SetEnv('GOBIN', go_bin_path)
" vim's executable path is looking in PATH so add our go_bin path to it
let $PATH = go_bin_path . go#util#PathListSep() . $PATH
let Restore_path = go#util#SetEnv('PATH', go_bin_path . go#util#PathListSep() . $PATH)
" when shellslash is set on MS-* systems, shellescape puts single quotes
" around the output string. cmd on Windows does not handle single quotes
@ -117,10 +106,7 @@ function! s:GoInstallBinaries(updateBinaries, ...)
set noshellslash
endif
let l:dl_cmd = ['go', 'get', '-v', '-d']
if get(g:, "go_get_update", 1) != 0
let l:dl_cmd += ['-u']
endif
let l:get_base_cmd = ['go', 'get', '-v']
" Filter packages from arguments (if any).
let l:packages = {}
@ -142,50 +128,103 @@ function! s:GoInstallBinaries(updateBinaries, ...)
let l:platform = 'windows'
endif
for [binary, pkg] in items(l:packages)
let l:importPath = pkg[0]
let l:oldmore = &more
let &more = 0
let l:run_cmd = copy(l:dl_cmd)
if len(l:pkg) > 1 && get(l:pkg[1], l:platform, '') isnot ''
let l:run_cmd += get(l:pkg[1], l:platform, '')
endif
for [l:binary, l:pkg] in items(l:packages)
let l:importPath = l:pkg[0]
let bin_setting_name = "go_" . binary . "_bin"
" TODO(bc): how to support this with modules? Do we have to clone and then
" install manually? Probably not. I suspect that we can just use GOPATH
" mode and then do the legacy method.
let bin_setting_name = "go_" . l:binary . "_bin"
if exists("g:{bin_setting_name}")
let bin = g:{bin_setting_name}
else
if go#util#IsWin()
let bin = binary . '.exe'
let bin = l:binary . '.exe'
else
let bin = binary
let bin = l:binary
endif
endif
if !executable(bin) || a:updateBinaries == 1
if a:updateBinaries == 1
echo "vim-go: Updating " . binary . ". Reinstalling ". importPath . " to folder " . go_bin_path
echo "vim-go: Updating " . l:binary . ". Reinstalling ". importPath . " to folder " . go_bin_path
else
echo "vim-go: ". binary ." not found. Installing ". importPath . " to folder " . go_bin_path
echo "vim-go: ". l:binary ." not found. Installing ". importPath . " to folder " . go_bin_path
endif
" first download the binary
let [l:out, l:err] = go#util#Exec(l:run_cmd + [l:importPath])
if l:err
echom "Error downloading " . l:importPath . ": " . l:out
if l:importPath =~ "@"
let Restore_modules = go#util#SetEnv('GO111MODULE', 'on')
let l:tmpdir = go#util#tempdir('vim-go')
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let l:dir = getcwd()
try
execute l:cd . fnameescape(l:tmpdir)
let l:get_cmd = copy(l:get_base_cmd)
if len(l:pkg) > 1 && get(l:pkg[1], l:platform, []) isnot []
let l:get_cmd += get(l:pkg[1], l:platform, [])
endif
" TODO(bc): how to install the bin to a different name than the
" binary path? go get does not support -o
" let l:get_cmd += ['-o', printf('%s%s%s', go_bin_path, go#util#PathSep(), bin)]
let [l:out, l:err] = go#util#Exec(l:get_cmd + [l:importPath])
if l:err
call go#util#EchoError(printf('Error installing %s: %s', l:importPath, l:out))
endif
call call(Restore_modules, [])
finally
execute l:cd . fnameescape(l:dir)
endtry
call call(Restore_modules, [])
else
let l:get_cmd = copy(l:get_base_cmd)
let l:get_cmd += ['-d']
if get(g:, "go_get_update", 1) != 0
let l:get_cmd += ['-u']
endif
let Restore_modules = go#util#SetEnv('GO111MODULE', 'off')
" first download the binary
let [l:out, l:err] = go#util#Exec(l:get_cmd + [l:importPath])
if l:err
call go#util#EchoError(printf('Error downloading %s: %s', l:importPath, l:out))
endif
" and then build and install it
let l:build_cmd = ['go', 'build']
if len(l:pkg) > 1 && get(l:pkg[1], l:platform, []) isnot []
let l:build_cmd += get(l:pkg[1], l:platform, [])
endif
let l:build_cmd += ['-o', printf('%s%s%s', go_bin_path, go#util#PathSep(), bin), l:importPath]
let [l:out, l:err] = go#util#Exec(l:build_cmd)
if l:err
call go#util#EchoError(printf('Error installing %s: %s', l:importPath, l:out))
endif
call call(Restore_modules, [])
endif
" and then build and install it
let l:build_cmd = ['go', 'build', '-o', go_bin_path . go#util#PathSep() . bin, l:importPath]
let [l:out, l:err] = go#util#Exec(l:build_cmd)
if l:err
echom "Error installing " . l:importPath . ": " . l:out
if len(l:pkg) > 2
call call(get(l:pkg[2], 'after', function('s:noop', [])), [])
endif
endif
endfor
" restore back!
let $PATH = old_path
call call(Restore_path, [])
call call(Restore_gobin, [])
call call(Restore_goarch, [])
call call(Restore_goos, [])
if resetshellslash
set shellslash
endif
@ -195,18 +234,20 @@ function! s:GoInstallBinaries(updateBinaries, ...)
else
call go#util#EchoInfo('installing finished!')
endif
let &more = l:oldmore
endfunction
" CheckBinaries checks if the necessary binaries to install the Go tool
" commands are available.
function! s:CheckBinaries()
if !executable('go')
echohl Error | echomsg "vim-go: go executable not found." | echohl None
call go#util#EchoError('go executable not found.')
return -1
endif
if !executable('git')
echohl Error | echomsg "vim-go: git executable not found." | echohl None
call go#util#EchoError('git executable not found.')
return -1
endif
endfunction
@ -239,7 +280,15 @@ function! s:register()
return
endif
let l:RestoreGopath = function('s:noop')
if go#config#AutodetectGopath()
let l:RestoreGopath = go#util#SetEnv('GOPATH', go#path#Detect())
endif
call go#lsp#DidOpen(expand('<afile>:p'))
call call(l:RestoreGopath, [])
endfunction
function! s:noop(...) abort
endfunction
augroup vim-go

2
pack/acp/start/vim-go/scripts/bench-syntax Normal file → Executable file
View file

@ -13,7 +13,7 @@ cd "$vimgodir"
if [ -z "${1:-}" ]; then
echo "unknown version: '${1:-}'"
echo "First argument must be 'vim-7.4', 'vim-8.0', or 'nvim'."
echo "First argument must be 'vim-8.0' or 'nvim'."
exit 1
fi

2
pack/acp/start/vim-go/scripts/docker-test Normal file → Executable file
View file

@ -10,6 +10,6 @@ cd "$vimgodir"
docker build --tag vim-go-test .
# seccomp=confined is required for dlv to run in a container, hence it's
# required for vim-go's debug tests.
docker run --rm --security-opt="seccomp=unconfined" vim-go-test
docker run -e VIMS --rm --security-opt="seccomp=unconfined" vim-go-test
# vim:ts=2:sts=2:sw=2:et

View file

@ -0,0 +1,35 @@
#!/bin/sh
#
# Install and setup a Vim or Neovim for running tests.
# This should work on both GitHub Actions and people's desktop computers, and
# be 100% independent from any system installed Vim.
#
set -euC
vimgodir=$(cd -P "$(dirname "$0")/.." > /dev/null && pwd)
cd "$vimgodir"
vim=${1:-}
installdir="/tmp/vim-go-test/$1-install"
# Make sure all Go tools and other dependencies are installed.
echo "Installing Go binaries"
export GOPATH=$installdir
export GO111MODULE=off
export PATH=${GOPATH}/bin:$PATH
"$vimgodir/scripts/run-vim" $vim +':silent :GoUpdateBinaries' +':qa'
echo "Installing lint tools"
(
mkdir -p "$installdir/share/vim/vimgo/pack/vim-go/start/"
cd "$installdir/share/vim/vimgo/pack/vim-go/start/"
[ -d "vim-vimhelplint" ] || git clone --depth 1 --quiet https://github.com/machakann/vim-vimhelplint
[ -d "vim-vimlparser" ] || git clone --depth 1 --quiet https://github.com/ynkdir/vim-vimlparser
[ -d "vim-vimlint" ] || git clone --depth 1 --quiet https://github.com/syngan/vim-vimlint
)
echo "vim-go tools installed to: $installdir/share/vim/vimgo/pack/vim-go/start"
# vim:ts=2:sts=2:sw=2:et

44
pack/acp/start/vim-go/scripts/install-vim Normal file → Executable file
View file

@ -1,8 +1,8 @@
#!/bin/sh
#
# Install and setup a Vim or Neovim for running tests.
# This should work on both Travis and people's desktop computers, and be 100%
# independent from any system installed Vim.
# This should work on both GitHub Actions and people's desktop computers, and
# be 100% independent from any system installed Vim.
#
# It will echo the full path to a Vim binary, e.g.:
# /some/path/src/vim
@ -15,28 +15,32 @@ cd "$vimgodir"
vim=${1:-}
case "$vim" in
"vim-7.4")
tag="v7.4.2009"
"vim-8.0")
# This follows the version in Ubuntu LTS. Vim's master branch isn't always
# stable, and we don't want to have the build fail because Vim introduced a
# bug.
tag="v8.0.1453"
giturl="https://github.com/vim/vim"
;;
"vim-8.0")
# This follows the version in Arch Linux. Vim's master branch isn't always
# stable, and we don't want to have the build fail because Vim introduced a
# bug.
tag="v8.0.1542"
"vim-8.2")
# This is the version that's installed by homebrew currently. It doesn't
# have to stay up to date with homebrew, and is only chosen here because
# that's what homebrew was using at the the time and we need a version to
# vimlint with.
tag="v8.2.0200"
giturl="https://github.com/vim/vim"
;;
"nvim")
# Use latest stable version.
tag="v0.3.1"
tag="v0.4.0"
giturl="https://github.com/neovim/neovim"
;;
*)
echo "unknown version: '${1:-}'"
echo "First argument must be 'vim-7.4', 'vim-8.0', or 'nvim'."
echo "First argument must be 'vim-8.0', vim-8.2, or 'nvim'."
exit 1
;;
esac
@ -62,7 +66,7 @@ cd "$srcdir"
if [ "$1" = "nvim" ]; then
# TODO: Use macOS binaries on macOS
curl -Ls https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz |
curl -Ls https://github.com/neovim/neovim/releases/download/$tag/nvim-linux64.tar.gz |
tar xzf - -C /tmp/vim-go-test/
mv /tmp/vim-go-test/nvim-linux64 /tmp/vim-go-test/nvim-install
mkdir -p "$installdir/share/nvim/runtime/pack/vim-go/start"
@ -88,22 +92,6 @@ else
ln -s "$vimgodir" "$installdir/share/vim/vimgo/pack/vim-go/start/vim-go"
fi
# Make sure all Go tools and other dependencies are installed.
echo "Installing Go binaries"
export GOPATH=$installdir
export GO111MODULE=off
export PATH=${GOPATH}/bin:$PATH
"$vimgodir/scripts/run-vim" $vim +':silent :GoUpdateBinaries' +':qa'
echo "Installing lint tools"
(
mkdir -p "$installdir/share/vim/vimgo/pack/vim-go/start/"
cd "$installdir/share/vim/vimgo/pack/vim-go/start/"
[ -d "vim-vimhelplint" ] || git clone --depth 1 --quiet https://github.com/machakann/vim-vimhelplint
[ -d "vim-vimlparser" ] || git clone --depth 1 --quiet https://github.com/ynkdir/vim-vimlparser
[ -d "vim-vimlint" ] || git clone --depth 1 --quiet https://github.com/syngan/vim-vimlint
)
# Don't really need source after successful install.
rm -rf "$srcdir"

Some files were not shown because too many files have changed in this diff Show more