Merge commit 'c7496b281674ca5d9fc6adc558d4cc8a6f824631' into main

main
Buddy Sandidge 7 years ago
commit cb8406634f

@ -0,0 +1,3 @@
[run]
plugins = covimerage
data_file = .coverage.covimerage

@ -0,0 +1,2 @@
.local/
.git/

@ -0,0 +1,17 @@
# http://EditorConfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true
[*.go]
indent_style = tab
indent_size = 4
[Makefile]
indent_style = tab
indent_size = 8

@ -1,9 +1,12 @@
Thanks for improving vim-go! Before you dive in please read the following: Thanks for improving vim-go! Before you dive in please read the following:
1. Please read our 1. Please read our
[FAQ](https://github.com/fatih/vim-go/wiki/FAQ-Troubleshooting), it might [Documentation](https://github.com/fatih/vim-go/blob/master/doc/vim-go.txt),
have answers for your problem it might have a solution to your problem.
2. If you add a new feature please don't forget to update the documentation: 2. If you add a new feature then please don't forget to update the documentation:
[doc/vim-go.txt](doc/vim-go.txt) [doc/vim-go.txt](https://github.com/fatih/vim-go/blob/master/doc/vim-go.txt).
3. If it's a breaking change or exceed +100 lines please open an issue first 3. If it's a breaking change or exceeds 100 lines of code then please open an
and describe the changes you want to make. issue first and describe the changes you want to make.
4. See `:help go-development` for instructions on how to run and write tests. If
you add a new feature be sure you also include a test if feasible.

@ -1,28 +1,22 @@
### Actual behavior ### What did you do? (required. The issue will be **closed** when not provided.)
Write here what's happening ...
### Expected behavior ### What did you expect to happen?
Write here what you're expecting ...
### Steps to reproduce: ### What happened instead?
Please create a reproducible case of your problem. Re produce it
with a minimal `vimrc` with all plugins disabled and only `vim-go`
enabled:
1. ### Configuration (**MUST** fill this out):
2.
3.
### Configuration * Vim version (first two lines from `:version`):
Add here your current configuration and additional information that might be * Go version (`go version`):
useful, such as:
* Go environment (`go env`):
* vim-go version:
* `vimrc` you used to reproduce (use a *minimal* vimrc with other plugins disabled; do not link to a 2,000 line vimrc):
* `vimrc` you used to reproduce
* vim version:
* vim-go version
* go version

@ -1 +1,5 @@
doc/tags .DS_Store
/doc/tags
/.coverage.covimerage
/coverage.xml
*.pyc

@ -0,0 +1,14 @@
language: go
notifications:
email: false
matrix:
include:
- env: SCRIPT="test -c" VIM_VERSION=vim-7.4
- env: SCRIPT="test -c" VIM_VERSION=vim-8.0
- env: SCRIPT="test -c" VIM_VERSION=nvim
- env: SCRIPT=lint VIM_VERSION=vim-8.0
install:
- ./scripts/install-vim $VIM_VERSION
- pip install --user vim-vint covimerage codecov
script:
- ./scripts/$SCRIPT $VIM_VERSION

@ -0,0 +1,7 @@
policies:
ProhibitUnnecessaryDoubleQuote:
enabled: false
ProhibitEqualTildeOperator:
enabled: false
ProhibitNoAbortFunction:
enabled: false

File diff suppressed because it is too large Load Diff

@ -0,0 +1,20 @@
FROM golang:1.9.2
RUN apt-get update -y && \
apt-get install -y build-essential curl git libncurses5-dev python3-pip && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
RUN pip3 install vim-vint
RUN useradd -ms /bin/bash -d /vim-go vim-go
USER vim-go
COPY . /vim-go/
WORKDIR /vim-go
RUN scripts/install-vim vim-7.4
RUN scripts/install-vim vim-8.0
RUN scripts/install-vim nvim
ENTRYPOINT ["make"]

@ -56,3 +56,5 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The Go gopher was designed by Renee French. http://reneefrench.blogspot.com/ The design is licensed under the Creative Commons 3.0 Attributions license. Read this article for more details: https://blog.golang.org/gopher

@ -0,0 +1,29 @@
VIMS ?= vim-7.4 vim-8.0 nvim
all: install test lint
install:
@echo "==> Installing Vims: $(VIMS)"
@for vim in $(VIMS); do \
./scripts/install-vim $$vim; \
done
test:
@echo "==> Running tests for $(VIMS)"
@for vim in $(VIMS); do \
./scripts/test $$vim; \
done
lint:
@echo "==> Running linting tools"
@./scripts/lint vim-8.0
docker:
@echo "==> Building/starting Docker container"
@./scripts/docker-test
clean:
@echo "==> Cleaning /tmp/vim-go-test"
@rm -rf /tmp/vim-go-test
.PHONY: all test install clean lint docker

@ -1,282 +1,71 @@
# 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)
Go (golang) support for Vim, which comes with pre-defined sensible settings (like
auto gofmt on save), with autocomplete, snippet support, improved syntax
highlighting, go toolchain commands, and more. If needed vim-go installs all
necessary binaries for providing seamless Vim integration with current
commands. It's highly customizable and each individual feature can be
disabled/enabled easily.
![vim-go](https://dl.dropboxusercontent.com/u/174404/vim-go-2.png)
<p align="center">
<img style="float: right;" src="assets/vim-go.png" alt="Vim-go logo"/>
</p>
## Features ## Features
* Improved Syntax highlighting with items such as Functions, Operators, Methods. This plugin adds Go language support for Vim, with the following main features:
* Auto completion support via `gocode`
* Better `gofmt` on save, which keeps cursor position and doesn't break your undo * Compile your package with `:GoBuild`, install it with `:GoInstall` or test it
history with `:GoTest`. Run a single tests with `:GoTestFunc`).
* Go to symbol/declaration with `:GoDef` * Quickly execute your current file(s) with `:GoRun`.
* Look up documentation with `:GoDoc` inside Vim or open it in browser * Improved syntax highlighting and folding.
* Automatically import packages via `:GoImport` or plug it into autosave * Completion support via `gocode`.
* Compile your package with `:GoBuild`, install it with `:GoInstall` or test * `gofmt` or `goimports` on save keeps the cursor position and undo history.
them with `:GoTest` (also supports running single tests via `:GoTestFunc`) * Go to symbol/declaration with `:GoDef`.
* Quickly execute your current file/files with `:GoRun` * Look up documentation with `:GoDoc` or `:GoDocBrowser`.
* Automatic `GOPATH` detection based on the directory structure (i.e. `gb` * Easily import packages via `:GoImport`, remove them via `:GoDrop`.
projects, `godep` vendored projects) * Automatic `GOPATH` detection which works with `gb` and `godep`. Change or
* Change or display `GOPATH` with `:GoPath` display `GOPATH` with `:GoPath`.
* Create a coverage profile and display annotated source code in browser to see * See which code is covered by tests with `:GoCoverage`.
which functions are covered with `:GoCoverage` * Add or remove tags on struct fields with `:GoAddTags` and `:GoRemoveTags`.
* Call `gometalinter` with `:GoMetaLinter`, which invokes all possible linters * Call `gometalinter` with `:GoMetaLinter` to invoke all possible linters
(golint, vet, errcheck, deadcode, etc..) and shows the warnings/errors (`golint`, `vet`, `errcheck`, `deadcode`, etc.) and put the result in the
* Lint your code with `:GoLint` quickfix or location list.
* Run your code through `:GoVet` to catch static errors * Lint your code with `:GoLint`, run your code through `:GoVet` to catch static
* Advanced source analysis tools utilizing oracle, such as `:GoImplements`, errors, or make sure errors are checked with `:GoErrCheck`.
`:GoCallees`, and `:GoReferrers` * Advanced source analysis tools utilizing `guru`, such as `:GoImplements`,
* Precise type-safe renaming of identifiers with `:GoRename` `:GoCallees`, and `:GoReferrers`.
* List all source files and dependencies * Precise type-safe renaming of identifiers with `:GoRename`.
* Unchecked error checking with `:GoErrCheck` * ... and many more! Please see [doc/vim-go.txt](doc/vim-go.txt) for more
* Integrated and improved snippets, supporting `ultisnips` or `neosnippet` information.
* Share your current code to [play.golang.org](http://play.golang.org) with `:GoPlay`
* On-the-fly type information about the word under the cursor. Plug it into
your custom vim function.
* Go asm formatting on save
* Tagbar support to show tags of the source code in a sidebar with `gotags`
* Custom vim text objects such as `a function` or `inner function`
list.
* A async launcher for the go command is implemented for Neovim, fully async
building and testing (beta).
* Integrated with the Neovim terminal, launch `:GoRun` and other go commands
in their own new terminal. (beta)
* Alternate between implementation and test code with `:GoAlternate`
## Donation
People have asked for this for a long time, now you can be a fully supporter by [being a patron](https://www.patreon.com/fatih)! This is fully optional and is just a way to support vim-go's ongoing development directly. Thanks!
[https://www.patreon.com/fatih](https://www.patreon.com/fatih)
## Install ## Install
Vim-go follows the standard runtime path structure, so I highly recommend to The [**latest stable release**](https://github.com/fatih/vim-go/releases/latest) is the
use a common and well known plugin manager to install vim-go. Do not use vim-go recommended version to use. If you choose to use the master branch instead,
with other Go oriented vim plugins. For Pathogen just clone the repo. For other please do so with caution; it is a _development_ branch.
plugin managers add the appropriate lines and execute the plugin's install
command. vim-go follows the standard runtime path structure. Below are some helper lines
for popular package managers:
* [Vim 8 packages](http://vimhelp.appspot.com/repeat.txt.html#packages)
* `git clone https://github.com/fatih/vim-go.git ~/.vim/pack/plugins/start/vim-go`
* [Pathogen](https://github.com/tpope/vim-pathogen) * [Pathogen](https://github.com/tpope/vim-pathogen)
* `git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go` * `git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go`
* [vim-plug](https://github.com/junegunn/vim-plug) * [vim-plug](https://github.com/junegunn/vim-plug)
* `Plug 'fatih/vim-go'` * `Plug 'fatih/vim-go'`
* [NeoBundle](https://github.com/Shougo/neobundle.vim)
* `NeoBundle 'fatih/vim-go'`
* [Vundle](https://github.com/gmarik/vundle)
* `Plugin 'fatih/vim-go'`
Please be sure all necessary binaries are installed (such as `gocode`, `godef`, You will also need to install all the necessary binaries. vim-go makes it easy
`goimports`, etc.). You can easily install them with the included to install all of them by providing a command, `:GoInstallBinaries`, which will
`:GoInstallBinaries` command. If invoked, all necessary binaries will be `go get` all the required binaries.
automatically downloaded and installed to your `$GOBIN` environment (if not set
it will use `$GOPATH/bin`). Note that this command requires `git` for fetching
the individual Go packages. Additionally, use `:GoUpdateBinaries` to update the
installed binaries.
### Optional Check out the Install section in [the documentation](doc/vim-go.txt) for more
detailed instructions (`:help go-install`).
* Autocompletion is enabled by default via `<C-x><C-o>`. To get real-time
completion (completion by type) install:
[neocomplete](https://github.com/Shougo/neocomplete.vim) for Vim or
[deoplete](https://github.com/Shougo/deoplete.nvim) and
[deoplete-go](https://github.com/zchee/deoplete-go) for NeoVim
* To display source code tag information on a sidebar install
[tagbar](https://github.com/majutsushi/tagbar).
* For snippet features install:
[neosnippet](https://github.com/Shougo/neosnippet.vim) or
[ultisnips](https://github.com/SirVer/ultisnips).
* Screenshot color scheme is a slightly modified molokai:
[fatih/molokai](https://github.com/fatih/molokai).
* For a better documentation viewer checkout:
[go-explorer](https://github.com/garyburd/go-explorer).
## Usage ## Usage
Many of the plugin's [features](#features) are enabled by default. There are no The full documentation can be found at [doc/vim-go.txt](doc/vim-go.txt). You can
additional settings needed. All usages and commands are listed in display it from within Vim with `:help vim-go`.
`doc/vim-go.txt`. Note that help tags needs to be populated. Check your plugin
manager settings to generate the documentation (some do it automatically).
After that just open the help page to see all commands:
:help vim-go
## Mappings
vim-go has several `<Plug>` mappings which can be used to create custom
mappings. Below are some examples you might find useful:
Run commands such as `go run` for the current file with `<leader>r` or `go
build` and `go test` for the current package with `<leader>b` and `<leader>t`
respectively. Display beautifully annotated source code to see which functions
are covered with `<leader>c`.
```vim
au FileType go nmap <leader>r <Plug>(go-run)
au FileType go nmap <leader>b <Plug>(go-build)
au FileType go nmap <leader>t <Plug>(go-test)
au FileType go nmap <leader>c <Plug>(go-coverage)
```
By default the mapping `gd` is enabled, which opens the target identifier in
current buffer. You can also open the definition/declaration, in a new vertical,
horizontal, or tab, for the word under your cursor:
```vim
au FileType go nmap <Leader>ds <Plug>(go-def-split)
au FileType go nmap <Leader>dv <Plug>(go-def-vertical)
au FileType go nmap <Leader>dt <Plug>(go-def-tab)
```
Open the relevant Godoc for the word under the cursor with `<leader>gd` or open
it vertically with `<leader>gv`
```vim
au FileType go nmap <Leader>gd <Plug>(go-doc)
au FileType go nmap <Leader>gv <Plug>(go-doc-vertical)
```
Or open the Godoc in browser
```vim
au FileType go nmap <Leader>gb <Plug>(go-doc-browser)
```
Show a list of interfaces which is implemented by the type under your cursor
with `<leader>s`
```vim
au FileType go nmap <Leader>s <Plug>(go-implements)
```
Show type info for the word under your cursor with `<leader>i` (useful if you
have disabled auto showing type info via `g:go_auto_type_info`)
```vim
au FileType go nmap <Leader>i <Plug>(go-info)
```
Rename the identifier under the cursor to a new name
```vim
au FileType go nmap <Leader>e <Plug>(go-rename)
```
More `<Plug>` mappings can be seen with `:he go-mappings`. Also these are just
recommendations, you are free to create more advanced mappings or functions
based on `:he go-commands`.
## Settings
Below are some settings you might find useful. For the full list see `:he
go-settings`.
By default syntax-highlighting for Functions, Methods and Structs is disabled.
To change it:
```vim
let g:go_highlight_functions = 1
let g:go_highlight_methods = 1
let g:go_highlight_structs = 1
let g:go_highlight_interfaces = 1
let g:go_highlight_operators = 1
let g:go_highlight_build_constraints = 1
```
Enable goimports to automatically insert import paths instead of gofmt:
```vim
let g:go_fmt_command = "goimports"
```
By default vim-go shows errors for the fmt command, to disable it:
```vim
let g:go_fmt_fail_silently = 1
```
Disable auto fmt on save:
```vim
let g:go_fmt_autosave = 0
```
Disable opening browser after posting your snippet to `play.golang.org`:
```vim
let g:go_play_open_browser = 0
```
By default when `:GoInstallBinaries` is called, the binaries are installed to
`$GOBIN` or `$GOPATH/bin`. To change it:
```vim
let g:go_bin_path = expand("~/.gotools")
let g:go_bin_path = "/home/fatih/.mypath" "or give absolute path
```
### Using with Neovim (beta)
Note: Neovim currently is not a first class citizen for vim-go. You are free
to open bugs but I'm not going to look at them. Even though I'm using Neovim
myself, Neovim itself is still alpha. So vim-go might not work well as good as
in Vim. I'm happy to accept pull requests or very detailed bug reports.
Run `:GoRun` in a new tab, horizontal split or vertical split terminal
```vim
au FileType go nmap <leader>rt <Plug>(go-run-tab)
au FileType go nmap <Leader>rs <Plug>(go-run-split)
au FileType go nmap <Leader>rv <Plug>(go-run-vertical)
```
By default new terminals are opened in a vertical split. To change it
```vim
let g:go_term_mode = "split"
```
By default the testing commands run asynchronously in the background and
display results with `go#jobcontrol#Statusline()`. To make them run in a new
terminal
```vim
let g:go_term_enabled = 1
```
### Using with Syntastic
Sometimes when using both `vim-go` and `syntastic` Vim will start lagging while
saving and opening files. The following fixes this:
```vim
let g:syntastic_go_checkers = ['golint', 'govet', 'errcheck']
let g:syntastic_mode_map = { 'mode': 'active', 'passive_filetypes': ['go'] }
```
## More info
Check out the [Wiki](https://github.com/fatih/vim-go/wiki) page for more
information. It includes
[Screencasts](https://github.com/fatih/vim-go/wiki/Screencasts), an [FAQ
section](https://github.com/fatih/vim-go/wiki/FAQ-Troubleshooting), and many
other [various pieces](https://github.com/fatih/vim-go/wiki) of information.
## Credits Depending on your installation method, you may have to generate the plugin's
[`help tags`](http://vimhelp.appspot.com/helphelp.txt.html#%3Ahelptags)
manually (e.g. `:helptags ALL`).
* Go Authors for official vim plugins We also have an [official vim-go tutorial](https://github.com/fatih/vim-go-tutorial).
* Gocode, Godef, Golint, Oracle, Goimports, Gotags, Errcheck projects and
authors of those projects.
* Other vim-plugins, thanks for inspiration (vim-golang, go.vim, vim-gocode,
vim-godef)
* [Contributors](https://github.com/fatih/vim-go/graphs/contributors) of vim-go
## License ## License
The BSD 3-Clause License - see `LICENSE` for more details The BSD 3-Clause License - see [`LICENSE`](LICENSE) for more details

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

@ -0,0 +1,821 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="173.53481mm"
height="147.26407mm"
viewBox="0 0 614.88711 521.80181"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="vim-go.svg"
style="enable-background:new"
inkscape:export-filename="F:\Go\src\github.com\egonelbre\vim-go\assets\vim-go.png"
inkscape:export-xdpi="46.84"
inkscape:export-ydpi="46.84">
<defs
id="defs4">
<linearGradient
id="gopher-iris"
osb:paint="solid"
gradientTransform="translate(-9.2596241,38.869516)">
<stop
style="stop-color:#394455;stop-opacity:1;"
offset="0"
id="stop4317" />
</linearGradient>
<linearGradient
id="docker-iris"
osb:paint="solid">
<stop
style="stop-color:#394d54;stop-opacity:1;"
offset="0"
id="stop4311" />
</linearGradient>
<linearGradient
id="docker-jaw"
osb:paint="solid">
<stop
style="stop-color:#d4edf1;stop-opacity:1;"
offset="0"
id="stop4305" />
</linearGradient>
<linearGradient
id="docker-eye"
osb:paint="solid">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4299" />
</linearGradient>
<linearGradient
id="docker-line"
osb:paint="solid">
<stop
style="stop-color:#394d54;stop-opacity:1;"
offset="0"
id="stop4293" />
</linearGradient>
<linearGradient
id="docker-body"
osb:paint="solid">
<stop
style="stop-color:#24b8eb;stop-opacity:1;"
offset="0"
id="stop4287" />
</linearGradient>
<linearGradient
id="gopher-limbs"
osb:paint="solid">
<stop
style="stop-color:#e1d6b9;stop-opacity:1;"
offset="0"
id="stop4269" />
</linearGradient>
<linearGradient
id="gopher-nose"
osb:paint="solid">
<stop
style="stop-color:#e1d0cb;stop-opacity:1;"
offset="0"
id="stop4263" />
</linearGradient>
<linearGradient
id="gopher-body"
osb:paint="solid"
gradientTransform="matrix(-0.18574987,-0.98259706,0.98259706,-0.18574987,-1213.2665,1828.8814)">
<stop
style="stop-color:#96d6ff;stop-opacity:1;"
offset="0"
id="stop4334" />
</linearGradient>
<linearGradient
id="linearGradient4253">
<stop
style="stop-color:#bce8ff;stop-opacity:1;"
offset="0"
id="stop4194" />
</linearGradient>
<linearGradient
id="linearGradient4182">
<stop
style="stop-color:#2e3436;stop-opacity:1;"
offset="0"
id="stop4184" />
</linearGradient>
<linearGradient
id="gopher-eye"
osb:paint="solid"
gradientTransform="translate(381.30424,802.02286)">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4178" />
</linearGradient>
<linearGradient
id="gopher-lines"
osb:paint="solid"
gradientTransform="matrix(2.0620253,3.9293227,1.3839016,-0.24027903,2506.9621,8572.3972)">
<stop
style="stop-color:#394655;stop-opacity:1;"
offset="0"
id="stop4166" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-lines"
id="linearGradient4168"
x1="776.14288"
y1="39.505058"
x2="822.42859"
y2="39.505058"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.92105265,0,0,0.92105265,79.548449,262.52483)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-eye"
id="linearGradient4180"
x1="776.14288"
y1="90.770309"
x2="822.42859"
y2="90.770309"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.92105266,0,0,0.92105266,124.54841,215.30684)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-body"
id="linearGradient4336"
x1="-628.69226"
y1="371.77307"
x2="-151.41731"
y2="371.77307"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1,0,0,1,-681.83098,347.55492)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-nose"
id="linearGradient4265"
x1="198.05417"
y1="374.50043"
x2="263.28683"
y2="374.50043"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.65610141,0,0,0.65610141,185.97779,480.81383)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-limbs"
id="linearGradient4271"
x1="730.36273"
y1="373.60995"
x2="831.0592"
y2="373.60995"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.90381797,-0.29515654,-0.62039307,-0.90381797,-597.71307,820.3894)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-limbs"
id="linearGradient4273"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-0.54351115,-0.65417141,-1.0770811,0.54351115,655.01412,667.6722)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-limbs"
id="linearGradient4275"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-0.94401471,-0.3302474,-0.32955964,0.94401471,1151.0861,721.50542)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-limbs"
id="linearGradient4279"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.89463991,0.4064691,0.49110603,-0.89463991,-749.6705,579.40921)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-limbs"
id="linearGradient4281"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.49170605,0.377674,2.0076181,-0.49170605,229.12024,357.65841)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-iris"
id="linearGradient4319"
x1="427.26477"
y1="316.13431"
x2="488.88409"
y2="316.13431"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1,0,0,1,744.54563,401.01143)" />
<linearGradient
inkscape:collect="always"
xlink:href="#gopher-iris"
id="linearGradient4321"
gradientTransform="matrix(5.6994379,2.2315229,-1.9072375,4.8711945,4487.6828,1182.8772)"
gradientUnits="userSpaceOnUse" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.76274166"
inkscape:cx="499.78979"
inkscape:cy="92.336365"
inkscape:document-units="px"
inkscape:current-layer="layer11"
showgrid="false"
inkscape:window-width="1920"
inkscape:window-height="1018"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:snap-bbox="true"
inkscape:bbox-nodes="true"
inkscape:snap-global="false"
showguides="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
type="xygrid"
id="grid4305"
originx="-15.732723"
originy="-274.01154" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer11"
inkscape:label="background"
style="display:none"
transform="translate(-15.732722,-256.54886)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d3e5de;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4347"
width="614.88708"
height="521.80182"
x="15.732722"
y="256.54886"
inkscape:export-filename="vim-go.png"
inkscape:export-xdpi="46.84"
inkscape:export-ydpi="46.84" />
</g>
<g
inkscape:groupmode="layer"
id="layer6"
inkscape:label="shadow"
transform="translate(-15.732722,-256.54886)">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#2e4233;fill-opacity:0.10714285;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 287.3893,695.44531 c -50.0612,-2.78118 -62.1134,11.12305 -91.7793,11.12305 -29.6659,0 -47.28069,-6.48881 -76.01953,-1.85352 -28.738834,4.6353 -40.790093,3.70867 -55.623042,16.6875 -14.832949,12.97883 -21.926707,11.85327 -18.541016,20.39454 1.318705,3.32677 3.956373,1.53579 10.703125,0.83984 115.165183,-11.87969 237.050993,16.53486 337.406243,16.77539 83.20192,0.19942 110.33047,-21.09623 105.22253,-34.76541 -16.86616,-45.13499 -81.24683,-23.67849 -211.36901,-29.20139 z"
id="path4349"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssssssc" />
</g>
<g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="cape-back"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-15.732722,-256.54886)">
<path
style="fill:#0c7a31;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 260.24444,535.87695 c -20.68496,5.13447 -3.94094,36.63825 -23.78246,45.53288 -18.22356,8.16932 -29.87743,27.29784 -48.21487,37.53094 -24.3143,13.56845 -47.25416,17.93122 -70.94376,35.71927 -11.54022,8.66532 -48.036929,3.46906 -49.132109,17.96915 56.226929,-8.73065 86.269619,15.95087 120.882979,20.57024 30.54605,4.07656 53.64011,2.39756 79.48357,-7.50413 89.71977,-34.37532 52.16171,-111.74704 51.81195,-135.28471 -17.69563,-3.28964 -42.98659,-18.78289 -60.1053,-14.53364 z"
id="path4321"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssscsscs" />
</g>
<g
inkscape:groupmode="layer"
id="layer5"
inkscape:label="gopher-body"
style="display:inline;opacity:1"
sodipodi:insensitive="true"
transform="translate(-15.732722,-256.54886)">
<g
style="display:inline;opacity:1"
transform="matrix(-0.34823803,-0.28093567,-0.33018747,0.52325377,856.33627,409.62314)"
id="g4537">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4275);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 419.84023,584.57289 c -1.11092,4.23495 -3.11543,7.14238 -5.84936,9.02308 -2.73394,1.8807 -6.19236,2.76095 -10.13743,3.23943 -3.94504,0.47846 -8.37351,0.59759 -13.05363,0.66122 -4.6801,0.0636 -9.60653,0.0259 -14.5852,-0.15006 -4.97865,-0.17599 -9.67742,-0.66266 -13.94891,-1.44453 -4.27148,-0.78187 -8.12262,-1.83504 -11.28827,-3.15781 -3.16564,-1.32277 -5.63542,-2.92368 -7.07427,-4.89074 -1.43884,-1.96709 -1.83785,-4.30021 -0.94134,-7.07932 0.89648,-2.77911 2.64686,-4.65171 5.05838,-5.71202 2.41152,-1.06032 5.47772,-1.29847 8.97039,-1.04717 3.49268,0.25132 7.40119,0.98198 11.60615,1.60695 4.20496,0.62498 8.71575,1.10136 13.55734,0.95747 4.84159,-0.14387 9.82241,-1.20624 14.59946,-2.18657 4.77703,-0.9803 9.35663,-1.80521 13.2055,-1.76209 3.8489,0.0431 6.93814,0.92314 8.72484,2.84805 1.78673,1.92488 0.0493,13.32997 1.15633,9.09414 z"
id="path4539"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssssssssssssssssc" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 411.66722,570.50504 c -3.64483,-0.3204 -7.91192,0.0353 -12.44327,0.67313 -5.17866,0.72899 -10.69026,1.78243 -16.25596,1.96339 -5.56571,0.181 -10.75654,-0.27799 -15.6406,-0.87383 -4.8841,-0.59575 -9.46828,-1.26261 -13.59381,-1.35067 -4.12552,-0.0881 -7.77812,0.41271 -10.6665,1.77043 -2.88834,1.35772 -5.00621,3.55109 -6.11385,6.60546 -1.10762,3.05438 -0.68341,5.7953 0.96623,8.19507 1.64966,2.39979 4.51594,4.46252 8.19691,6.21125 3.681,1.74874 8.16283,3.1933 13.12136,4.28264 4.95854,1.08935 10.4013,1.79657 16.15733,2.05756 5.756,0.26106 11.2421,0.29972 16.33832,0.21929 5.09618,-0.0804 9.79866,-0.25121 13.94009,-0.87517 1.57579,-0.23741 3.06793,-0.55279 4.47088,-0.96129 2.8331,-0.82603 3.60613,-5.66983 1.06694,-4.35369 -2.35253,1.21937 -5.13009,1.88834 -8.23473,2.27934 -3.78352,0.47652 -8.03435,0.60519 -12.52976,0.67623 -4.49538,0.071 -9.22983,0.0403 -14.01368,-0.12137 -4.78387,-0.16172 -9.29761,-0.62006 -13.39935,-1.36274 -4.10176,-0.74271 -7.79879,-1.74643 -10.8363,-3.01023 -3.03748,-1.2638 -5.40588,-2.79646 -6.78423,-4.6796 -1.37835,-1.88316 -1.75885,-4.11616 -0.89417,-6.78092 0.86467,-2.66475 2.54876,-4.4645 4.86314,-5.48862 2.31437,-1.0241 5.2526,-1.265 8.60072,-1.03925 3.34811,0.22576 7.09649,0.90864 11.13305,1.49473 4.03653,0.5862 8.37113,1.03632 13.02879,0.89877 4.65766,-0.13756 9.45383,-1.14909 14.04535,-2.09377 4.59152,-0.94468 8.9823,-1.75345 12.66755,-1.73592 0.46066,0.002 0.91144,0.0161 1.3482,0.0436 1.1223,0.0708 2.1698,0.20509 3.10067,0.47739 1.0735,0.314 2.95461,-2.6047 -0.11758,-2.94357 -0.49859,-0.055 -1.54942,0.19872 -1.52174,-0.17766 z"
id="path4541"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csscsscssssssssssssssssssssccsssc" />
</g>
<g
transform="matrix(-0.20408679,0.36109427,0.8060854,0.48598006,286.09208,226.24278)"
id="g4640"
style="display:inline;opacity:1">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 767.29926,387.32674 c 11.1235,7.96555 31.77795,11.29978 44.73159,15.54502 12.95363,4.24526 18.14889,9.35948 22.12936,13.37285 3.98046,4.01338 5.94428,7.14463 4.71807,9.52723 -1.2262,2.38259 -5.54351,3.99405 -14.00119,4.81166 -8.45765,0.81761 -15.90978,0.12055 -23.02358,-1.72572 -7.11381,-1.84628 -13.80694,-4.86649 -21.70559,-8.603 -7.89866,-3.73649 -17.3272,-8.0507 -25.81115,-14.18439 -8.48395,-6.13369 -17.62324,-13.90003 -23.14238,-24.13356 -5.51915,-10.23352 -5.78201,-21.34406 -5.37146,-30.88264 0.41055,-9.53859 1.51092,-17.55377 2.71572,-23.74931 1.20482,-6.19553 2.71509,-10.67437 4.77102,-13.66952 2.05591,-2.99513 4.65165,-4.52673 7.71923,-4.52673 3.06759,0 5.70357,1.83092 7.62535,5.49926 1.9218,3.66832 3.04778,9.24444 3.28639,16.76004 0.23861,7.51561 -0.67126,17.08072 0.34029,27.19831 1.01155,10.1176 3.89485,20.79494 15.01833,28.7605 z"
id="path4642"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssssss" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4281);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 760.81735,387.61463 c 8.35351,7.22933 23.40419,11.34465 36.92829,14.85447 13.52408,3.50986 21.76315,7.50998 26.41399,11.29491 4.65086,3.78492 7.04347,6.96136 6.89289,9.28045 -0.15059,2.31908 -3.07202,3.85186 -9.99413,4.53735 -6.92209,0.68549 -13.12478,-0.17957 -19.18856,-2.15841 -6.06375,-1.97886 -12.01277,-5.06603 -19.62326,-8.64782 -7.61047,-3.5818 -16.94465,-7.61787 -24.98938,-13.21535 -8.04472,-5.59749 -15.82286,-12.65396 -20.9022,-21.24583 -5.07935,-8.59186 -6.01346,-17.801 -5.99188,-25.91871 0.0216,-8.1177 0.93462,-15.14861 1.86635,-20.66954 0.93173,-5.52092 2.01706,-9.59713 3.38259,-12.30465 1.36554,-2.70753 3.03466,-4.06947 5.01979,-4.01398 1.98511,0.0555 3.57672,1.84704 4.61437,5.2751 1.03765,3.42807 1.44745,8.54444 1.4737,15.15288 0.0262,6.60845 -0.43638,14.76057 0.91317,23.27473 1.34954,8.51418 4.83074,17.27506 13.18427,24.5044 z"
id="path4644"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssssss" />
</g>
<g
style="display:inline;opacity:1"
id="g4594"
transform="matrix(-0.13664232,-0.29657059,-0.88136995,0.09664282,727.56031,790.52022)">
<path
sodipodi:nodetypes="sssssssssssssssss"
inkscape:connector-curvature="0"
id="path4588"
d="m 767.29926,387.32674 c 11.1235,7.96555 31.77795,11.29978 44.73159,15.54502 12.95363,4.24526 18.14889,9.35948 22.12936,13.37285 3.98046,4.01338 5.94428,7.14463 4.71807,9.52723 -1.2262,2.38259 -5.54351,3.99405 -14.00119,4.81166 -8.45765,0.81761 -15.90978,0.12055 -23.02358,-1.72572 -7.11381,-1.84628 -13.80694,-4.86649 -21.70559,-8.603 -7.89866,-3.73649 -17.3272,-8.0507 -25.81115,-14.18439 -8.48395,-6.13369 -17.62324,-13.90003 -23.14238,-24.13356 -5.51915,-10.23352 -5.78201,-21.34406 -5.37146,-30.88264 0.41055,-9.53859 1.51092,-17.55377 2.71572,-23.74931 1.20482,-6.19553 2.71509,-10.67437 4.77102,-13.66952 2.05591,-2.99513 4.65165,-4.52673 7.71923,-4.52673 3.06759,0 5.70357,1.83092 7.62535,5.49926 1.9218,3.66832 3.04778,9.24444 3.28639,16.76004 0.23861,7.51561 -0.67126,17.08072 0.34029,27.19831 1.01155,10.1176 3.89485,20.79494 15.01833,28.7605 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="sssssssssssssssss"
inkscape:connector-curvature="0"
id="ellipse4590"
d="m 760.81735,387.61463 c 8.35351,7.22933 23.40419,11.34465 36.92829,14.85447 13.52408,3.50986 21.76315,7.50998 26.41399,11.29491 4.65086,3.78492 7.04347,6.96136 6.89289,9.28045 -0.15059,2.31908 -3.07202,3.85186 -9.99413,4.53735 -6.92209,0.68549 -13.12478,-0.17957 -19.18856,-2.15841 -6.06375,-1.97886 -12.01277,-5.06603 -19.62326,-8.64782 -7.61047,-3.5818 -16.94465,-7.61787 -24.98938,-13.21535 -8.04472,-5.59749 -15.82286,-12.65396 -20.9022,-21.24583 -5.07935,-8.59186 -6.01346,-17.801 -5.99188,-25.91871 0.0216,-8.1177 0.93462,-15.14861 1.86635,-20.66954 0.93173,-5.52092 2.01706,-9.59713 3.38259,-12.30465 1.36554,-2.70753 3.03466,-4.06947 5.01979,-4.01398 1.98511,0.0555 3.57672,1.84704 4.61437,5.2751 1.03765,3.42807 1.44745,8.54444 1.4737,15.15288 0.0262,6.60845 -0.43638,14.76057 0.91317,23.27473 1.34954,8.51418 4.83074,17.27506 13.18427,24.5044 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4271);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
style="display:inline"
id="g4533-2"
transform="matrix(-0.60102903,0.32221978,0.53870829,0.77401445,526.12645,47.501077)" />
<g
style="opacity:1"
transform="matrix(-0.32879267,0.17361606,0.20143296,0.28338802,143.13323,319.59452)"
id="g4404">
<path
sodipodi:nodetypes="sssssssssssssssss"
inkscape:connector-curvature="0"
id="path4406"
d="m -626.54672,402.3529 c 2.22767,10.86299 0.34493,21.82632 -3.86747,31.42527 -4.21252,9.59894 -10.55173,17.86115 -17.72096,24.29983 -7.1694,6.43883 -15.25476,11.10591 -24.5716,13.61353 -9.31698,2.50761 -20.94966,4.46936 -31.63903,1.98398 -10.68939,-2.48537 -18.0688,-9.22838 -24.09401,-15.89285 -6.02508,-6.66442 -12.35923,-14.47524 -22.96531,-22.06805 -10.60584,-7.59266 -20.8648,-15.59839 -25.16123,-23.3775 -4.29632,-7.77931 -7.008,-15.66934 -7.81517,-23.39095 -0.80717,-7.7215 0.35908,-14.55922 3.12288,-20.54462 2.76393,-5.98548 7.12557,-11.1208 12.7854,-15.40902 5.65998,-4.28811 12.61751,-7.73606 20.64204,-10.24271 8.02465,-2.50651 17.11262,-4.07552 27.13941,-4.41504 10.0268,-0.3395 20.06604,0.59388 29.76158,2.87504 9.69543,2.2813 19.05511,5.92037 27.47739,11.02309 8.42215,5.10286 15.89307,11.69212 21.60465,19.6287 5.71147,7.93674 13.0738,19.62846 15.30143,30.4913 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-body);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="csssccscsccscscccsccscsssscscscc"
inkscape:connector-curvature="0"
id="path4408"
d="m -784.21409,457.33922 c -0.56136,0.0656 -1.08141,0.1809 -1.55606,0.33615 -0.63289,0.20699 -1.18396,0.48516 -1.6349,0.82686 -0.45093,0.3417 -0.80184,0.74659 -1.02778,1.21891 -0.22595,0.47234 -0.32669,1.01119 -0.27449,1.62035 0.0522,0.60917 0.25282,1.23371 0.57968,1.84938 0.32687,0.61567 0.98957,1.25218 1.83531,1.84156 0.84574,0.58937 1.35671,1.20529 1.82543,1.72857 0.46713,0.52147 1.13451,0.85371 2.02424,0.92674 0.10253,0.008 0.12328,-0.30471 0.0344,-0.32876 -0.78083,-0.20262 -1.25826,-0.72023 -1.71877,-1.11076 -0.4254,-0.46645 -0.87231,-1.01406 -1.62104,-1.54604 -0.74871,-0.53197 -1.47289,-1.09304 -1.77689,-1.63886 -0.30398,-0.54584 -0.49685,-1.10009 -0.55469,-1.64239 -0.0579,-0.54231 0.0245,-1.0222 0.21918,-1.44322 0.19469,-0.42103 0.50198,-0.78371 0.90168,-1.08623 0.39973,-0.30252 0.89062,-0.54587 1.4577,-0.7237 0.28355,-0.0889 0.5872,-0.16119 0.90722,-0.21465 0.32002,-0.0535 0.6576,-0.0885 1.01178,-0.10163 0.70839,-0.0255 1.4163,0.0392 2.10043,0.1987 0.68412,0.15947 1.34499,0.41522 1.93838,0.77329 0.59338,0.35806 1.11885,0.81986 1.52108,1.37653 0.40222,0.55667 0.92117,1.37523 1.07925,2.13677 0.12981,0.62539 0.0734,1.25844 -0.13288,1.83379 -0.0385,0.10712 0.4977,0.29416 0.62787,-0.0111 0.24265,-0.5698 0.23445,-1.24057 0.1026,-1.8741 -0.17834,-0.85666 -0.69031,-1.76937 -1.13671,-2.40019 -0.4464,-0.6308 -1.03123,-1.15292 -1.68895,-1.55276 -0.65772,-0.39984 -1.38674,-0.68003 -2.14271,-0.85021 -0.75599,-0.17016 -1.54036,-0.23166 -2.32498,-0.19142 -0.19617,0.0101 -0.38815,0.0268 -0.57528,0.0484 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
transform="matrix(13.851095,0,0,13.851095,10133.213,-6001.611)" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -753.77185,413.0219 c -0.13663,-2.61847 2.18018,-4.94804 7.2193,-6.20054 7.65443,-1.90257 20.03831,1.84566 27.93811,5.67152 4.33357,2.09883 8.88981,3.89076 12.66635,7.19411 1.28185,1.12133 2.51799,2.28349 3.36855,4.40869 -1.65849,0.577 -4.10492,-0.92134 -5.87278,-2.13046 -6.96771,-4.76531 -14.69502,-8.08983 -22.67695,-9.12646 -6.71591,-0.87187 -8.86923,-3.11022 -14.75541,-2.56175 -3.72583,0.34716 -4.90626,2.13878 -7.88716,2.74489 z"
id="path4365-1-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscsssc" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -720.16989,411.68353 c 0.28532,-2.32502 0.86962,3.90377 -0.31886,5.45995 -4.46007,5.84 -8.20289,12.32072 -12.42083,18.36519 -1.37385,1.96787 -3.29463,0.0414 -2.42738,-2.09874 0.88118,-2.1739 2.06053,-3.99898 3.34915,-5.8153 1.20809,-1.70147 2.81353,-3.0576 3.88834,-4.85958 2.06619,-3.46267 2.39577,-6.62873 4.25443,-10.2393 0.63712,-1.23818 3.5225,0.42546 3.67386,-0.80905 z"
id="path4367-9-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssss" />
</g>
<g
style="display:inline;opacity:1"
id="g4198"
transform="matrix(0.69027452,0,0,0.73815345,642.18876,259.65104)">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -140.71724,398.66408 c -9.31409,71.69689 -25.7611,141.32 -83.87724,188.8641 -73.31672,59.97949 -208.09131,67.90599 -303.42706,10.99618 -27.57065,-16.45805 -49.52457,-62.17665 -53.04177,-91.74122 -7.35191,-61.79791 19.82699,-103.64945 13.47928,-160.67805 -5.05249,-45.39216 -29.63784,-82.95495 -27.30836,-137.00138 1.56315,-36.26681 11.06536,-78.46439 40.50727,-100.88356 38.57103,-29.370718 83.60539,-46.188952 134.68095,-45.031125 72.73731,1.648875 151.17838,6.326503 212.18714,49.939365 43.544,31.12796 68.50323,82.53699 72.90385,135.3004 4.52019,54.19698 -0.16075,104.48555 -6.10406,150.23529 z"
id="path4188"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssss" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4336);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -158.93683,464.92976 c -15.56115,65.9367 -58.42288,127.39267 -134.42207,151.72082 -70.61462,22.6045 -163.49236,17.29949 -232.18476,-25.54762 -26.14623,-16.30879 -46.09162,-61.46233 -48.95901,-89.47579 -6.03547,-58.9646 19.04741,-102.17429 13.30293,-156.59502 -4.7951,-45.42661 -28.02123,-78.34585 -27.29597,-132.22289 0.47399,-35.21112 8.99044,-76.95773 37.82112,-98.79995 36.52466,-27.671205 78.3526,-45.238515 126.45621,-45.012482 76.22124,0.358155 162.16208,5.533182 222.84373,56.658952 55.47879,46.74224 63.38318,129.04796 60.81019,193.3049 -2.12217,52.99813 -7.67242,100.63054 -18.37237,145.96908 z"
id="ellipse4190"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssss" />
</g>
<g
id="g4376"
transform="matrix(0.40138799,-0.13710458,0.13710458,0.40138799,470.81791,82.723801)"
style="opacity:1">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-body);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -626.57295,401.69566 c 2.24713,11.35067 0.36741,22.38948 -3.843,32.03835 -4.21053,9.64886 -10.54997,17.90531 -17.7192,24.34399 -7.1694,6.43883 -15.25457,11.1106 -24.57171,13.61082 -9.31727,2.5002 -20.94956,4.47176 -31.64526,1.82793 -10.69571,-2.64383 -18.09209,-9.81214 -24.14818,-17.25062 -6.05597,-7.43843 -12.44269,-16.56671 -23.09665,-25.35944 -10.65372,-8.79255 -20.95218,-17.78817 -25.30072,-26.87318 -4.34843,-9.08528 -7.1154,-18.36084 -7.98,-27.52156 -0.86459,-9.1606 0.24716,-17.36404 2.9617,-24.58398 2.71467,-7.22004 7.03243,-13.45488 12.66059,-18.5369 5.6283,-5.08191 12.56665,-9.01064 20.59229,-11.48936 8.02576,-2.47858 17.13537,-3.50537 27.20916,-2.66707 10.0738,0.83832 20.1809,3.47234 29.95223,7.6529 9.77122,4.18068 19.21426,9.9086 27.71179,16.89733 8.49741,6.98886 16.03465,15.24007 21.79567,24.41557 5.7609,9.17565 13.1742,22.14471 15.42129,33.49522 z"
id="path4398"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssssss" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -784.27135,455.90422 c -0.56339,0.0147 -1.08437,0.10666 -1.55902,0.26191 -0.63289,0.20699 -1.18231,0.52669 -1.63059,0.93484 -0.44828,0.40815 -0.79558,0.90361 -1.01756,1.4752 -0.22199,0.5716 -0.31844,1.21792 -0.26185,1.93717 0.0566,0.71926 0.26134,1.4471 0.59196,2.157 0.33063,0.7099 0.99621,1.41858 1.84494,2.08284 0.84872,0.66425 1.36325,1.36931 1.83382,1.93901 0.46898,0.56774 1.13678,0.9105 2.02675,0.98962 0.10256,0.009 0.12294,-0.31321 0.034,-0.33899 -0.78143,-0.21746 -1.26048,-0.77583 -1.72293,-1.21489 -0.42768,-0.5236 -0.87838,-1.16625 -1.63058,-1.78505 -0.75217,-0.61879 -1.47924,-1.25213 -1.78697,-1.89162 -0.30772,-0.63951 -0.50455,-1.29287 -0.56648,-1.9378 -0.062,-0.64492 0.0165,-1.22191 0.20772,-1.73042 0.1912,-0.50852 0.49539,-0.94884 0.89287,-1.30706 0.3975,-0.35822 0.88707,-0.63484 1.45426,-0.80994 0.2836,-0.0875 0.58767,-0.1494 0.90851,-0.1822 0.32084,-0.0328 0.65966,-0.0369 1.01552,-0.008 0.71174,0.0585 1.42446,0.24383 2.11396,0.53794 0.6895,0.29412 1.35628,0.69807 1.95502,1.19025 0.59873,0.49218 1.12894,1.07271 1.53474,1.71893 0.4058,0.64623 0.9285,1.5589 1.08808,2.35795 0.13104,0.65619 0.075,1.29927 -0.13103,1.88026 -0.0384,0.10817 0.49808,0.30362 0.62824,-0.002 0.24262,-0.57052 0.23429,-1.24452 0.10166,-1.89748 -0.17938,-0.88293 -0.69436,-1.871 -1.14416,-2.58711 -0.44981,-0.71609 -1.03943,-1.35821 -1.70275,-1.89855 -0.66333,-0.54034 -1.3987,-0.97968 -2.16052,-1.29649 -0.76184,-0.31679 -1.55154,-0.51173 -2.33984,-0.56369 -0.19709,-0.013 -0.38986,-0.0163 -0.57767,-0.0116 z"
id="path4369"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssccscsccscscccsccscsssscscscc"
transform="matrix(13.851095,0,0,13.851095,10133.213,-6001.611)" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -730.27274,382.91266 c 1.8068,-2.76405 6.31309,-3.63001 13.24575,-1.6171 10.53068,3.05761 22.43414,14.97755 28.94834,24.04709 3.57338,4.97534 7.6424,9.78266 9.64772,15.62449 0.68055,1.98294 1.27611,3.97774 0.68898,6.70435 -2.4056,-0.49416 -4.1871,-3.62313 -5.37952,-6.01329 -4.69962,-9.4202 -11.38574,-17.86492 -20.09536,-24.13889 -7.3284,-5.27852 -8.20487,-8.9719 -15.61502,-12.25742 -4.69053,-2.07967 -7.44128,-1.02076 -11.44089,-2.34923 z"
id="path4365-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscsssc" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m -689.31909,403.49962 c 2.08771,-2.1886 -1.9021,4.5559 -4.48533,5.36905 -9.69439,3.05157 -19.01784,7.22624 -28.57811,10.64488 -3.11327,1.11257 -3.94795,-2.11026 -1.30738,-3.72982 2.68251,-1.64492 5.45711,-2.73872 8.35507,-3.75217 2.71578,-0.94874 5.64428,-1.2851 8.27731,-2.4236 5.06052,-2.18718 7.83343,-5.20599 12.75841,-7.67984 1.68866,-0.84854 3.86766,2.73608 4.97603,1.5739 z"
id="path4367-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssss" />
</g>
<g
id="g4634"
transform="matrix(0.13058783,-0.42795023,-0.60869797,-0.11092817,632.15501,956.21909)"
style="display:inline;opacity:1">
<path
sodipodi:nodetypes="sssssssssssssssss"
inkscape:connector-curvature="0"
id="path4636"
d="m 423.50332,581.83521 c -0.004,4.40048 -1.19837,7.58856 -3.37524,9.82844 -2.17687,2.23987 -5.33154,3.55156 -9.14619,4.44292 -3.81465,0.89135 -8.28246,1.39523 -13.05675,1.83828 -4.77428,0.44304 -9.85163,0.79076 -14.95001,1.09928 -5.09838,0.30851 -9.94541,0.34741 -14.40217,0.0862 -4.45676,-0.26122 -8.52354,-0.79908 -11.99271,-1.71189 -3.46915,-0.91282 -6.33736,-2.21356 -8.3562,-4.09288 -2.01885,-1.87935 -3.18709,-4.34475 -3.25466,-7.51083 -0.0676,-3.16607 0.9983,-5.4859 2.92534,-7.0838 1.92703,-1.5979 4.71248,-2.46394 8.09977,-2.84688 3.38729,-0.38293 7.37282,-0.28336 11.77044,-0.16051 4.39762,0.12284 9.21051,0.23456 14.33166,-0.12202 5.12115,-0.35659 10.27171,-1.47349 15.16022,-2.54099 4.88852,-1.06749 9.50395,-2.05149 13.43823,-2.27114 3.9343,-0.21967 7.17754,0.32322 9.39823,2.04598 2.22069,1.72276 3.41425,4.59936 3.41004,8.99986 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4279);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="csscsscssssssssssssssssssssccsssc"
inkscape:connector-curvature="0"
id="path4638"
d="m 411.91406,568.54883 c -3.75011,-0.0271 -8.08701,0.53975 -12.76172,1.28711 -5.34251,0.85413 -11.10706,1.92059 -17.00976,2.32617 -5.9027,0.40562 -11.41103,0.38326 -16.44727,0.41406 -5.03624,0.0309 -9.6045,0.1607 -13.50781,0.85938 -3.9033,0.69867 -7.13503,1.96743 -9.4082,3.96875 -2.27316,2.00131 -3.58535,4.71676 -3.65235,8.17578 -0.067,3.45901 1.21821,6.3073 3.54297,8.58008 2.32476,2.27278 5.68789,3.9795 9.76172,5.25 4.07385,1.27051 8.85237,2.11894 14.05664,2.59765 5.20427,0.47871 10.83381,0.56134 16.70313,0.22266 5.86931,-0.33868 11.47146,-0.78653 16.60547,-1.34961 5.13399,-0.56309 9.79334,-1.22365 13.70703,-2.34375 1.48913,-0.4262 2.86677,-0.9287 4.12695,-1.51953 2.54507,-1.19325 2.05015,-6.17249 -0.0996,-4.54102 -1.99172,1.51153 -4.14364,1.68162 -7.15735,2.35061 -3.67269,0.81527 -8.18136,0.99111 -12.55008,1.3428 -4.3687,0.35167 -8.7789,1.78431 -13.31332,2.07736 -4.53444,0.29304 -8.86787,0.32801 -12.93181,0.0702 -4.06396,-0.25785 -7.85651,-0.78075 -11.12475,-1.64296 -3.26823,-0.86221 -5.99695,-2.08037 -7.8846,-3.81399 -1.88765,-1.73365 -2.92537,-3.9871 -2.97865,-6.80086 -0.0533,-2.81374 0.90176,-4.8192 2.66881,-6.10562 1.76704,-1.28641 5.61732,-0.58475 8.69196,-0.71399 3.07463,-0.12925 6.90624,-0.54484 10.78772,-0.41733 3.88147,0.12754 6.54592,-0.48119 11.04844,-1.2139 4.50252,-0.73264 9.15212,-2.3434 13.88736,-3.72101 4.73523,-1.37761 9.22461,-2.34259 13.00861,-2.55385 0.473,-0.0264 0.93707,-0.0422 1.38868,-0.0449 1.16046,-0.007 2.25007,0.0442 3.25,0.23633 1.15313,0.22156 2.31543,-2.86146 -0.83789,-2.92773 -0.51177,-0.0108 -1.03459,-0.045 -1.57032,-0.0488 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer9"
inkscape:label="gopher-shadow"
style="display:inline;opacity:0.06000001"
sodipodi:insensitive="true"
transform="translate(-15.732722,-256.54886)">
<ellipse
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path4544"
cx="-467.52527"
cy="482.66467"
rx="22.450642"
ry="20.682871"
transform="scale(-1,1)" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 234.60547,309.98047 c -6.62163,-0.0703 -10.7426,0.83465 -15.61133,3.26758 -5.0378,2.51742 -10.044,7.91661 -11.55273,12.45898 -2.26972,6.83348 -0.42196,14.92592 5.01757,21.97656 3.19606,4.1427 6.84938,6.56071 14.60938,9.66993 3.20846,1.28553 7.68985,3.50108 9.95898,4.92382 5.6211,3.52442 9.83526,5.31873 13.54102,5.76563 2.42194,0.29208 3.11523,0.63719 3.11523,1.55469 0,0.89182 -0.7061,1.28567 -2.89062,1.61328 -1.58919,0.23867 -3.77121,0.24076 -4.84961,0.004 -1.95019,-0.42833 -1.9703,-0.40483 -3.65625,4.68555 -3.87667,11.7048 -5.82609,25.85658 -5.80859,42.15625 0.0196,18.31899 1.82597,28.89111 9.58007,56.04688 5.56137,19.47655 7.15656,26.40249 8.58008,37.26171 2.05331,15.66359 1.31467,26.60445 -3.90625,57.79102 -4.8641,29.05517 -5.15869,31.69637 -5.18359,46.54297 -0.0239,14.28001 0.63486,19.84952 3.52539,29.8125 5.44577,18.77032 13.72789,34.11825 23.9082,44.30078 8.00321,8.00498 22.62783,16.26261 41.23438,23.2832 5.47456,2.06566 5.83617,2.12101 6.46679,0.99414 1.72277,-3.07839 3.2087,-3.7772 9.33203,-3.79882 -38.68101,-33.75954 -34.48259,-82.29367 -25.52281,-108.9339 7.33431,-21.80723 31.77025,-53.23407 31.77025,-53.23407 l -22.41052,-1.98245 c 0,0 -7.25969,-42.63753 -13.15682,-59.9065 -22.58603,-66.14023 -29.82384,-120.35922 4.37069,-158.19894 5.84309,-6.46598 12.5988,-11.21335 19.60937,-14.69727 -9.02679,1.89877 -18.30173,4.80561 -26.41601,8.32813 -6.65247,2.88791 -19.01394,9.90994 -18.99415,10.78906 0.009,0.39075 0.30731,1.97487 0.66407,3.52148 0.79845,3.46141 -0.0807,5.55969 -2.20117,5.25782 -1.1871,-0.16901 -1.49742,-0.76108 -1.83008,-3.48633 -0.63121,-5.17109 -3.20076,-9.39815 -9.06836,-14.91797 -9.25402,-8.70552 -17.29671,-12.21829 -29.22461,-12.76172 -1.05756,-0.0482 -2.05405,-0.0778 -3,-0.0879 z m 1.38086,24.10156 c 1.88404,0.0642 3.99413,0.41696 5.88476,1.04492 3.99187,1.32589 12.35644,6.69047 14.31446,9.17969 3.00519,3.82048 1.04901,4.01008 -3.4043,0.33008 -1.74522,-1.44216 -3.36983,-2.6211 -3.60937,-2.6211 -0.23954,0 -2.78812,1.91597 -5.66407,4.25782 -2.87594,2.34185 -5.59815,4.25776 -6.04883,4.25976 -1.88842,0.007 -0.56519,-2.08264 3.10938,-4.91015 4.64288,-3.57262 5.88952,-5.38766 4.12891,-6.00977 -0.64649,-0.22845 -2.92374,-1.13445 -5.06055,-2.01367 -3.0123,-1.23949 -4.52138,-1.50334 -6.71875,-1.17383 -3.06661,0.45987 -3.82178,-0.39095 -1.46485,-1.65234 0.9899,-0.52978 2.64916,-0.75563 4.53321,-0.69141 z m 103.78515,383.73633 c -0.005,0.0152 -0.007,0.0256 -0.0117,0.041 l -0.70118,2.28906 5.65625,1.01562 c 0.0901,0.0162 0.20551,0.0326 0.29688,0.0488 -1.81728,-1.11236 -3.56263,-2.24473 -5.24024,-3.39453 z"
id="path4271"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssscssssssssssscsccsscscssssssscsssscscsssscccccccc" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 328.4205,548.25967 -4.47623,14.88037 c 2.60939,0.0254 9.84161,-6.41982 16.75619,-6.818 76.94638,-4.43102 125.04829,-0.40565 187.26295,-5.40532 1.45456,-0.11689 3.76527,-0.10936 5.20677,0.2079 5.21485,1.14773 8.09003,14.3736 9.3628,13.60525 0.6055,-14.12878 -2.32372,-19.14168 -5.81784,-22.69773 z"
id="path4275"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccsssccc" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="gopher-face"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-15.732722,-256.54886)">
<g
id="g4818"
transform="matrix(-0.65610141,0,0,0.65610141,655.70091,210.42145)">
<path
sodipodi:nodetypes="sssssssssssssssss"
inkscape:connector-curvature="0"
id="path4812"
d="m 547.42756,318.16456 c -0.44046,14.77191 -4.12869,29.02667 -10.38967,42.25266 -6.26099,13.22599 -15.09198,25.42687 -25.80466,35.99686 -10.71268,10.57 -23.30432,19.50822 -37.11826,26.08983 -13.81394,6.58161 -28.85103,10.80263 -44.50193,11.8618 -15.65091,1.05917 -30.4406,-1.15844 -43.81781,-6.16756 -13.37721,-5.00911 -25.3405,-12.8075 -35.30087,-22.80416 -9.96037,-9.99666 -17.91599,-22.19037 -23.26581,-35.90798 -5.34983,-13.71761 -8.0915,-28.95913 -7.64195,-44.98105 0.44955,-16.02192 4.04447,-31.2937 10.1422,-45.07896 6.09773,-13.78526 14.69591,-26.08175 25.16951,-36.25747 10.4736,-10.17571 22.82245,-18.23043 36.46168,-23.66123 13.63924,-5.4308 28.57214,-8.24285 44.22923,-8.02541 15.6571,0.21745 30.56095,3.42714 44.11009,8.94154 13.54914,5.5144 25.7404,13.33722 35.92568,22.91495 10.18529,9.57774 18.36233,20.91345 23.87736,33.53282 5.51504,12.61936 8.36566,26.52144 7.92521,41.29336 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="sssssssssssssssss"
inkscape:connector-curvature="0"
id="ellipse4814"
d="m 539.72249,314.79002 c 10e-4,13.89984 -3.01572,27.53808 -8.51346,40.35257 -5.49774,12.81449 -13.48047,24.80543 -23.37659,35.2527 -9.89612,10.44726 -21.70519,19.34133 -34.78531,25.87862 -13.08011,6.53727 -27.4256,10.71236 -42.3773,11.7667 -14.9517,1.05435 -29.09103,-1.11258 -41.85904,-5.93108 -12.76803,-4.81852 -24.16883,-12.28715 -33.66552,-21.79076 -9.49671,-9.50362 -17.08979,-21.04298 -22.23241,-33.95465 -5.14261,-12.91166 -7.83328,-27.19561 -7.52333,-42.13595 0.30995,-14.94034 3.58995,-29.10832 9.22975,-41.85842 5.63981,-12.7501 13.63743,-24.08168 23.39638,-33.47108 9.75897,-9.38941 21.27795,-16.83842 34.00359,-21.94183 12.72563,-5.10342 26.66067,-7.86812 41.28534,-7.94317 14.62467,-0.0751 28.55938,2.53224 41.26083,7.24431 12.70145,4.71207 24.16709,11.5339 33.81555,20.03646 9.64847,8.50257 17.47884,18.68937 22.90117,30.21241 5.42232,11.52304 8.43889,24.38332 8.44035,38.28317 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-eye);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<circle
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4319);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path4828"
cx="458.07443"
cy="316.13431"
r="30.809652" />
<circle
r="15.152287"
cy="301.99216"
cx="444.43738"
id="circle4830"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-eye);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
transform="matrix(-0.49821858,-0.255998,-0.255998,0.49821858,841.05915,359.59091)"
id="g4822">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 544.2609,323.96628 c -5.95391,12.33766 -15.20034,24.2228 -25.89846,35.91934 -10.69814,11.69654 -22.74349,23.28172 -34.52447,34.21851 -11.78099,10.93679 -23.27607,21.15489 -34.23709,29.30247 -10.96102,8.14759 -21.47285,14.18083 -32.04267,16.95199 -10.56982,2.77117 -20.29711,2.02561 -29.30402,-1.67713 -9.00692,-3.70274 -20.58076,-7.76561 -27.66538,-16.71749 -7.08461,-8.95188 -12.84054,-20.18257 -16.5035,-33.03389 -3.66297,-12.85133 -5.229,-27.32914 -3.92417,-42.72858 1.30484,-15.39944 5.36688,-30.24976 11.81788,-43.75488 6.45101,-13.5051 15.29008,-25.65823 26.00811,-35.78271 10.71803,-10.12447 28.44246,-20.29305 42.24879,-25.86698 13.80633,-5.57394 28.83304,-8.62768 44.20973,-8.80364 15.3767,-0.17594 29.62737,2.52591 41.94358,7.37479 12.31622,4.84887 22.69735,11.85058 30.35956,20.34718 7.66222,8.49661 12.60139,18.48263 14.06496,29.34879 1.4636,10.86615 -0.59894,22.56457 -6.55285,34.90223 z"
id="path4824"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssssss" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 538.18032,322.65868 c -5.17728,11.63182 -13.27733,23.10077 -22.96883,34.40428 -9.69151,11.30351 -20.93897,22.46482 -32.34413,32.7753 -11.40514,10.31051 -22.90789,19.71873 -33.85893,27.13351 -10.95103,7.41476 -21.39599,12.82014 -31.59528,15.28718 -10.19931,2.46703 -19.30202,1.76338 -27.56839,-1.62958 -8.26637,-3.39295 -19.13397,-6.9512 -25.3913,-15.16185 -6.25732,-8.21068 -11.24381,-18.53447 -14.30417,-30.37519 -3.06035,-11.84072 -4.18965,-25.20221 -2.68634,-39.42576 1.5033,-14.22354 5.50837,-27.94818 11.67956,-40.43838 6.17119,-12.4902 14.50792,-23.74111 24.54768,-33.13895 10.03978,-9.39782 26.99021,-19.0621 39.83566,-24.2929 12.84546,-5.2308 26.78412,-8.15811 41.0009,-8.45853 14.21678,-0.30038 27.34319,2.03758 38.64284,6.33106 11.29965,4.29349 20.7704,10.54463 27.74089,18.16875 6.97048,7.62413 11.43794,16.6127 12.81335,26.51165 1.37541,9.89894 -0.36624,20.67759 -5.54351,32.30941 z"
id="path4826"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssssss" />
<circle
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4321);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="path4828-0"
cx="438.70038"
cy="219.30804"
r="27.721321"
transform="matrix(0.98640333,0.16434257,-0.16434257,0.98640333,0,0)" />
<circle
r="13.633434"
cy="205.95601"
cx="431.24106"
id="circle4830-3"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
transform="matrix(0.98640333,0.16434257,-0.16434257,0.98640333,0,0)" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer7"
inkscape:label="gopher-mouth"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-15.732722,-256.54886)">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#2e3436;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 477.59321,477.72343 -6.36763,0.0828 -3.71113,-0.0821 c -1.18372,-0.0262 -2.23819,0.53559 -3.00662,1.36379 -0.76845,0.82822 -1.14658,1.97521 -1.32551,3.22687 l -1.01303,7.08562 -1.40711,7.111 c -0.25342,1.28069 0.0841,2.40965 0.70518,3.23132 0.6211,0.82165 1.57363,1.28978 2.69674,1.31649 l 3.7446,0.0891 7.40657,-0.17258 c 1.42055,-0.0331 2.74014,-0.58514 3.70785,-1.43299 0.96771,-0.84787 1.54004,-2.00084 1.65553,-3.2592 l 0.6476,-7.05621 0.52522,-7.04505 c 0.0935,-1.25398 -0.46676,-2.37726 -1.25366,-3.18163 -0.78689,-0.80437 -1.85738,-1.2842 -3.00457,-1.27716 z"
id="rect4659"
inkscape:connector-curvature="0"
sodipodi:nodetypes="scssscssscssscsss" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 476.43064,479.86835 -5.19684,0.0698 -2.47497,-0.10149 c -0.94018,-0.0386 -1.80825,0.43586 -2.46124,1.11384 -0.65298,0.67797 -1.03424,1.61771 -1.21175,2.64338 l -1.0026,5.79325 -1.25494,5.80832 c -0.22406,1.03701 0.002,1.97056 0.48938,2.64162 0.48783,0.67105 1.26653,1.03411 2.19892,1.07115 l 2.54193,0.101 5.88547,-0.12754 c 1.11447,-0.0242 2.17518,-0.47212 2.97321,-1.1643 0.79803,-0.69218 1.30904,-1.6349 1.43939,-2.66511 l 0.73009,-5.77006 0.63032,-5.76301 c 0.11259,-1.02637 -0.28558,-1.94744 -0.89178,-2.6062 -0.60618,-0.65877 -1.45658,-1.05733 -2.39458,-1.04471 z"
id="rect4661"
inkscape:connector-curvature="0"
sodipodi:nodetypes="scssscssscssscsss" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 447.45177,471.71537 c 0.17729,2.27145 1.57656,4.32647 3.56538,6.17684 1.98881,1.85037 4.73553,3.49055 7.9169,4.83408 3.18137,1.34353 6.76993,2.37673 10.40491,2.92876 3.63499,0.55204 7.31771,0.61337 10.93742,0.17695 3.61969,-0.43645 6.8614,-1.30517 9.67542,-2.37849 2.81402,-1.07332 5.17844,-2.3467 7.04073,-3.75925 1.86231,-1.41254 3.23922,-2.97722 4.10853,-4.72358 0.86932,-1.74636 1.22997,-3.67959 0.91461,-5.76285 -0.31535,-2.08326 -1.29186,-4.11481 -2.79935,-5.98131 -1.5075,-1.86649 -3.53491,-3.56576 -5.91642,-4.97983 -2.3815,-1.41407 -5.11304,-2.54212 -8.12844,-3.28158 -3.0154,-0.73946 -6.31783,-1.09096 -9.93094,-0.97174 -3.6131,0.11924 -7.2186,0.69446 -10.6419,1.64517 -3.4233,0.95069 -6.6496,2.2832 -9.33875,3.91065 -2.68913,1.62746 -4.89892,3.50256 -6.18894,5.61926 -1.32139,2.16817 -1.77021,4.61153 -1.61916,6.54692 z"
id="ellipse4650"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssssss" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4265);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 455.1011,471.20532 c 0.31019,1.80429 1.36577,3.48937 2.98663,4.99917 1.62086,1.5098 3.80505,2.84719 6.28703,3.91437 2.48197,1.06719 5.24944,1.8562 8.07117,2.27071 2.82174,0.4145 5.70079,0.45265 8.53169,0.10713 2.83089,-0.34553 5.35911,-1.02976 7.553,-1.90451 2.19389,-0.87475 4.04484,-1.93848 5.497,-3.12538 1.45217,-1.1869 2.50911,-2.50179 3.13219,-3.93394 0.62308,-1.43214 0.81446,-2.98543 0.48985,-4.63056 -0.32461,-1.64514 -1.13916,-3.22548 -2.3414,-4.6674 -1.20224,-1.44192 -2.78948,-2.74346 -4.65903,-3.82078 -1.86955,-1.07733 -4.01937,-1.92982 -6.38974,-2.4811 -2.37037,-0.55129 -4.96168,-0.80162 -7.76722,-0.68542 -2.80553,0.11621 -5.57317,0.58631 -8.1874,1.34158 -2.61424,0.75528 -5.07126,1.79757 -7.14628,3.06167 -2.07504,1.26412 -3.75959,2.75051 -4.8326,4.37276 -1.07302,1.62225 -1.53509,3.37741 -1.22489,5.1817 z"
id="ellipse4652"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssssss" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 465.13937,460.19393 c 0.45232,1.29294 1.43586,2.44115 2.79664,3.4102 1.36078,0.96906 3.0934,1.76079 4.97332,2.36791 1.87992,0.60712 3.89927,1.0315 5.87533,1.25741 1.97606,0.2259 3.90879,0.25223 5.71982,0.052 1.81102,-0.20028 3.33955,-0.60742 4.63321,-1.17435 1.29367,-0.56695 2.35232,-1.29343 3.18646,-2.14861 0.83413,-0.85519 1.44471,-1.8405 1.79916,-2.93195 0.35445,-1.09146 0.45213,-2.29028 0.21175,-3.55738 -0.24038,-1.2671 -0.80099,-2.48156 -1.64917,-3.57911 -0.84818,-1.09755 -1.9831,-2.07741 -3.35494,-2.8723 -1.37184,-0.7949 -2.98056,-1.40441 -4.76729,-1.7664 -1.78672,-0.36199 -3.75169,-0.47615 -5.82322,-0.29097 -2.07153,0.18518 -4.05358,0.65136 -5.84566,1.3298 -1.79207,0.67844 -3.39432,1.56902 -4.69144,2.60198 -1.29713,1.03296 -2.28898,2.20893 -2.84443,3.45293 -0.55546,1.24399 -0.67186,2.55593 -0.21954,3.84888 z"
id="path4648"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssssssssssss" />
</g>
<g
inkscape:groupmode="layer"
id="layer12"
inkscape:label="gopher-hands"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-15.732722,-256.54886)">
<g
id="g4533"
transform="matrix(-0.28489616,-0.34500545,-0.42832103,0.44649678,715.99765,474.46827)">
<path
sodipodi:nodetypes="sssssssssssssssss"
inkscape:connector-curvature="0"
id="ellipse4523"
d="m 423.50332,581.83521 c -0.004,4.40048 -1.19837,7.58856 -3.37524,9.82844 -2.17687,2.23987 -5.33154,3.55156 -9.14619,4.44292 -3.81465,0.89135 -8.28246,1.39523 -13.05675,1.83828 -4.77428,0.44304 -9.85163,0.79076 -14.95001,1.09928 -5.09838,0.30851 -9.94541,0.34741 -14.40217,0.0862 -4.45676,-0.26122 -8.52354,-0.79908 -11.99271,-1.71189 -3.46915,-0.91282 -6.33736,-2.21356 -8.3562,-4.09288 -2.01885,-1.87935 -3.18709,-4.34475 -3.25466,-7.51083 -0.0676,-3.16607 0.9983,-5.4859 2.92534,-7.0838 1.92703,-1.5979 4.71248,-2.46394 8.09977,-2.84688 3.38729,-0.38293 7.37282,-0.28336 11.77044,-0.16051 4.39762,0.12284 9.21051,0.23456 14.33166,-0.12202 5.12115,-0.35659 10.27171,-1.47349 15.16022,-2.54099 4.88852,-1.06749 9.50395,-2.05149 13.43823,-2.27114 3.9343,-0.21967 7.17754,0.32322 9.39823,2.04598 2.22069,1.72276 3.41425,4.59936 3.41004,8.99986 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4273);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
sodipodi:nodetypes="ssscsscssssssssssssssssssssccsss"
inkscape:connector-curvature="0"
id="path4521"
d="m 411.91406,568.54883 c -3.75011,-0.0271 -8.08701,0.53975 -12.76172,1.28711 -5.34251,0.85413 -11.10706,1.92059 -17.00976,2.32617 -5.9027,0.40562 -11.41103,0.38326 -16.44727,0.41406 -5.03624,0.0309 -9.6045,0.1607 -13.50781,0.85938 -3.9033,0.69867 -7.13503,1.96743 -9.4082,3.96875 -2.27316,2.00131 -3.58535,4.71676 -3.65235,8.17578 -0.067,3.45901 1.21821,6.3073 3.54297,8.58008 2.32476,2.27278 5.68789,3.9795 9.76172,5.25 4.07385,1.27051 8.85237,2.11894 14.05664,2.59765 5.20427,0.47871 10.83381,0.56134 16.70313,0.22266 5.86931,-0.33868 11.47146,-0.78653 16.60547,-1.34961 5.13399,-0.56309 9.79334,-1.22365 13.70703,-2.34375 1.48913,-0.4262 2.86677,-0.9287 4.12695,-1.51953 2.54507,-1.19325 2.05015,-6.17249 -0.0996,-4.54102 -1.99172,1.51153 -4.55969,2.50355 -7.57031,3.20703 -3.66893,0.85731 -7.96668,1.34146 -12.5586,1.76758 -4.59191,0.42612 -9.47527,0.75991 -14.3789,1.05664 -4.90363,0.29673 -9.56506,0.33523 -13.85156,0.084 -4.28652,-0.25124 -8.19851,-0.76855 -11.53516,-1.64649 -3.33664,-0.87795 -6.09539,-2.12996 -8.03711,-3.9375 -1.94173,-1.80756 -3.06587,-4.17751 -3.13086,-7.22265 -0.065,-3.04513 0.96102,-5.2776 2.81445,-6.81446 1.85342,-1.53686 4.53117,-2.36997 7.78907,-2.73828 3.2579,-0.36831 7.09262,-0.27244 11.32226,-0.1543 4.22963,0.11816 8.85767,0.22578 13.7832,-0.11718 4.92553,-0.34297 9.88026,-1.41664 14.58204,-2.44336 4.70178,-1.02671 9.13982,-1.97234 12.92382,-2.1836 0.473,-0.0264 0.93707,-0.0422 1.38868,-0.0449 1.16046,-0.007 2.25007,0.0442 3.25,0.23633 1.15313,0.22156 2.31543,-2.86146 -0.83789,-2.92773 -0.51177,-0.0108 -1.03459,-0.045 -1.57032,-0.0488 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#gopher-lines);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</g>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="cape-front"
style="display:inline"
transform="translate(-15.732722,-256.54886)">
<path
sodipodi:nodetypes="cssscscc"
inkscape:connector-curvature="0"
id="path4248"
d="m 250.62773,531.91504 c -9.09672,21.35801 -15.29674,29.07226 -30.27188,44.83759 -11.50237,12.10933 -28.85117,24.46609 -43.81134,39.61682 -13.55246,13.72509 -26.12338,21.00434 -64.22257,32.01103 -11.97434,3.45934 -44.031036,6.55017 -51.472472,37.30246 C 107.21772,654.7909 183.17617,662.32228 228.40418,636.09787 266.34279,614.10005 317.82474,552.6315 355.9453,547.7268 284.49621,547.05928 263.34291,542.49874 250.62773,531.91504 Z"
style="display:inline;fill:#019833;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:#019833;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 473.29262,543.99873 73.7751,-5.10117 c 0,0 2.29258,1.0455 2.68673,2.11494 7.36409,19.98076 -12.72148,60.84328 -12.72148,60.84328 0,-2.97132 13.53121,-43.94425 -5.91529,-53.46522 -16.4456,-8.05173 -38.16124,-2.06803 -57.82506,-4.39183 z"
id="path4265"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccscsc" />
<path
style="display:inline;fill:#019432;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 249.90625,533.57227 c -8.70868,20.08478 -14.97837,27.83833 -29.55078,43.17968 -11.50237,12.10933 -28.85038,24.46646 -43.81055,39.61719 -13.55246,13.72509 -26.12346,21.00503 -64.22265,32.01172 -10.63128,3.07133 -37.077893,5.86957 -48.087895,27.97656 2.731585,-3.48747 7.206694,-4.8761 9.881319,-8.70029 4.506995,-6.44411 60.824806,-11.61546 75.673426,-21.06752 9.77176,-6.22033 32.61216,-17.69963 44.08393,-25.40211 11.47178,-7.70248 50.16856,-39.82139 59.98047,-41.62695 30.99143,-5.70295 56.04882,-31.95703 56.04882,-31.95703 0,0 -5.76873,-1.34099 -7.30468,-1.69727 -26.4653,-1.9743 -39.57284,-5.58234 -48.29883,-11.28125 -1.77957,-0.42346 -3.78649,-0.89828 -4.39258,-1.05273 z"
id="path4280"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cssscsssscccc" />
<path
style="fill:#01a939;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 250.88543,527.29897 c 4.9284,1.23444 7.57648,5.23948 12.39942,6.83706 14.83134,4.91283 28.22069,8.13985 43.80356,9.2706 19.18619,1.39223 40.09821,1.50171 59.33179,1.15882 36.63136,-0.65304 73.4946,-1.92414 110.08831,-3.70824 19.9513,-0.97271 40.58394,-2.2893 60.49061,-3.94 3.86874,-0.3208 7.97563,-6.05622 11.58825,-4.6353 2.39418,0.94168 2.01049,3.29975 2.64058,5.79412 2.44082,4.93143 0.14511,6.64447 -5.65353,7.64824 -19.43937,3.05253 -39.20884,3.55847 -58.86827,4.40354 -48.01128,2.06378 -96.10464,2.11621 -144.15772,1.62235 -17.00379,-0.17475 -34.11943,0.52285 -50.98827,-1.62235 -13.27515,-1.68819 -26.90453,-3.45163 -39.16825,-8.80707 -4.12399,-1.80091 -7.99437,-2.72852 -8.97266,-7.12095 -0.30759,-1.38101 1.19417,-2.17728 1.88173,-3.29956 0.57446,-0.93767 0.21317,-2.26036 1.23886,-2.84803 1.34064,-0.76812 2.84679,-1.12864 4.34559,-0.75323 z"
id="path4267"
inkscape:connector-curvature="0"
sodipodi:nodetypes="sssssssccssssssss" />
<path
style="fill:#019d35;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 245.9043,528.82812 c -0.24767,0.63868 -0.21658,1.44068 -0.60352,2.07227 -0.68756,1.12228 -2.18845,1.91782 -1.88086,3.29883 0.97829,4.39243 4.84867,5.32018 8.97266,7.12109 12.26372,5.35544 25.89282,7.11845 39.16797,8.80664 16.86884,2.1452 33.98449,1.4483 50.98828,1.62305 48.05308,0.49386 96.14692,0.44073 144.1582,-1.62305 19.65943,-0.84507 39.42782,-1.34981 58.86719,-4.40234 5.79864,-1.00377 8.09512,-2.71701 5.6543,-7.64844 -0.0557,-0.22031 -0.0962,-0.43699 -0.13868,-0.65429 0.48647,4.64963 -6.66572,4.9037 -11.87478,5.92187 -33.64204,6.57569 -68.48165,3.5437 -102.75586,4.0957 -42.87828,0.69057 -93.34812,6.52037 -135.57053,-0.98242 -17.79033,-3.16129 -43.90403,-10.17243 -54.98437,-17.62891 z"
id="path4340"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csssssscccsssc" />
</g>
<g
inkscape:groupmode="layer"
id="layer8"
inkscape:label="vim"
transform="translate(-15.732722,-256.54886)">
<g
id="g4330">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#005d04;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4293"
width="194.71968"
height="194.71968"
x="-29.381023"
y="744.44128"
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
<rect
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
y="753.35699"
x="-20.465342"
height="176.88821"
width="176.88821"
id="rect4283"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#019833;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:4;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
id="text4285"
style="font-style:normal;font-weight:normal;font-size:203.27047729px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;display:inline;fill:#fefefe;fill-opacity:1;stroke:#005d04;stroke-width:4;stroke-linecap:butt;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
transform="matrix(1.0880646,0,-0.29154603,1.0880646,-528.83975,-369.0604)">
<path
sodipodi:nodetypes="cccccccccccccccsc"
inkscape:connector-curvature="0"
id="path4324"
style="font-style:normal;font-variant:normal;font-weight:800;font-stretch:normal;font-family:Eczar;-inkscape-font-specification:'Eczar Ultra-Bold';fill:#fefefe;fill-opacity:1;stroke:#005d04;stroke-width:5.01092911;stroke-linejoin:bevel;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 202.34975,1029.0537 -56.02157,-157.11507 -17.82505,-3.05571 0.25466,-14.0054 89.88914,0 1.52787,8.1486 c -2.7162,2.2069 -5.77193,4.32893 -9.16717,6.36609 -3.22549,2.03714 -6.70561,3.98941 -10.44038,5.85679 l 26.38345,87.17129 39.56921,-89.71773 -21.89934,-3.81964 0.25464,-14.0054 72.06411,0 -68.4991,168.82868 0.25465,0.2547 c -6.28122,1.0184 -13.49612,1.9522 -21.6447,2.8011 -8.14859,0.8487 -16.38207,1.6126 -24.70042,2.2917 z" />
</g>
</g>
<use
x="0"
y="0"
xlink:href="#g4330"
id="use4338"
transform="matrix(0.4546439,-0.10745401,-0.02175104,0.44922994,711.99298,282.73776)"
width="100%"
height="100%" />
</g>
<g
inkscape:groupmode="layer"
id="layer4"
inkscape:label="palette"
style="display:inline"
sodipodi:insensitive="true"
transform="translate(-15.732722,-256.54886)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4168);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4162"
width="40.789474"
height="40.789474"
x="779.60529"
y="21.967466" />
<rect
y="21.967466"
x="824.60529"
height="40.789474"
width="40.789474"
id="rect4170"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4180);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052742;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#bce8ff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4208"
width="40.789474"
height="40.789474"
x="779.60529"
y="86.967468" />
<rect
y="-127.75694"
x="824.60529"
height="40.789474"
width="40.789474"
id="rect4223"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#abccd9;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
transform="scale(1,-1)" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#c3b0cb;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4227"
width="40.789474"
height="40.789474"
x="779.60529"
y="131.96747" />
<rect
y="131.96747"
x="824.60529"
height="40.789474"
width="40.789474"
id="rect4231"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#e1d0cb;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#f5c3d2;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4233"
width="40.789474"
height="40.789474"
x="869.60529"
y="131.96747" />
<rect
y="176.96747"
x="779.60529"
height="40.789474"
width="40.789474"
id="rect4248"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#cec4ad;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
transform="scale(1,-1)"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#96d6ff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4263"
width="40.789474"
height="40.789474"
x="869.60529"
y="-127.75694" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#f2f2ce;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4267"
width="40.789474"
height="40.789474"
x="824.60529"
y="176.96747" />
<rect
y="-327.75693"
x="779.60529"
height="40.789474"
width="40.789474"
id="rect4280"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#24b8eb;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
transform="scale(1,-1)" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#8aa9ff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4284"
width="40.789474"
height="40.789474"
x="824.60529"
y="286.96747" />
<rect
y="331.96747"
x="779.60529"
height="40.789474"
width="40.789474"
id="rect4297"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d4edf1;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#394d54;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4301"
width="40.789474"
height="40.789474"
x="779.60529"
y="241.96747" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#d6e2ff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:9.21052647;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4303"
width="40.789474"
height="40.789474"
x="824.60529"
y="331.96747" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 84 KiB

@ -14,12 +14,12 @@ else
let g:ctrlp_ext_vars = [s:go_decls_var] let g:ctrlp_ext_vars = [s:go_decls_var]
endif endif
function! ctrlp#decls#init() function! ctrlp#decls#init() abort
cal s:enable_syntax() cal s:enable_syntax()
return s:decls return s:decls
endfunction endfunction
function! ctrlp#decls#exit() function! ctrlp#decls#exit() abort
unlet! s:decls s:current_dir s:target unlet! s:decls s:current_dir s:target
endfunction endfunction
@ -28,7 +28,7 @@ endfunction
" a:mode the mode that has been chosen by pressing <cr> <c-v> <c-t> or <c-x> " a:mode the mode that has been chosen by pressing <cr> <c-v> <c-t> or <c-x>
" the values are 'e', 'v', 't' and 'h', respectively " the values are 'e', 'v', 't' and 'h', respectively
" a:str the selected string " a:str the selected string
function! ctrlp#decls#accept(mode, str) function! ctrlp#decls#accept(mode, str) abort
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd() let dir = getcwd()
try try
@ -56,7 +56,7 @@ function! ctrlp#decls#accept(mode, str)
endtry endtry
endfunction endfunction
function! ctrlp#decls#enter() function! ctrlp#decls#enter() abort
let s:current_dir = fnameescape(expand('%:p:h')) let s:current_dir = fnameescape(expand('%:p:h'))
let s:decls = [] let s:decls = []
@ -87,16 +87,12 @@ function! ctrlp#decls#enter()
let command .= printf(" -dir %s", dir) let command .= printf(" -dir %s", dir)
endif endif
let out = system(command) let out = go#util#System(command)
if v:shell_error != 0 if go#util#ShellError() != 0
call go#util#EchoError(out) call go#util#EchoError(out)
return return
endif endif
if exists("l:tmpname")
call delete(l:tmpname)
endif
let result = eval(out) let result = eval(out)
if type(result) != 4 || !has_key(result, 'decls') if type(result) != 4 || !has_key(result, 'decls')
return return
@ -130,7 +126,7 @@ function! ctrlp#decls#enter()
endfor endfor
endfunc endfunc
function! s:enable_syntax() function! s:enable_syntax() abort
if !(has('syntax') && exists('g:syntax_on')) if !(has('syntax') && exists('g:syntax_on'))
return return
endif endif
@ -148,7 +144,7 @@ endfunction
let s:id = g:ctrlp_builtins + len(g:ctrlp_ext_vars) let s:id = g:ctrlp_builtins + len(g:ctrlp_ext_vars)
function! ctrlp#decls#cmd(mode, ...) function! ctrlp#decls#cmd(mode, ...) abort
let s:mode = a:mode let s:mode = a:mode
if a:0 && !empty(a:1) if a:0 && !empty(a:1)
let s:target = a:1 let s:target = a:1
@ -156,3 +152,4 @@ function! ctrlp#decls#cmd(mode, ...)
return s:id return s:id
endfunction endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,150 @@
function! s:code(group, attr) abort
let code = synIDattr(synIDtrans(hlID(a:group)), a:attr, "cterm")
if code =~ '^[0-9]\+$'
return code
endif
endfunction
function! s:color(str, group) abort
let fg = s:code(a:group, "fg")
let bg = s:code(a:group, "bg")
let bold = s:code(a:group, "bold")
let italic = s:code(a:group, "italic")
let reverse = s:code(a:group, "reverse")
let underline = s:code(a:group, "underline")
let color = (empty(fg) ? "" : ("38;5;".fg)) .
\ (empty(bg) ? "" : (";48;5;".bg)) .
\ (empty(bold) ? "" : ";1") .
\ (empty(italic) ? "" : ";3") .
\ (empty(reverse) ? "" : ";7") .
\ (empty(underline) ? "" : ";4")
return printf("\x1b[%sm%s\x1b[m", color, a:str)
endfunction
function! s:sink(str) abort
if len(a:str) < 2
return
endif
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
try
" we jump to the file directory so we can get the fullpath via fnamemodify
" below
execute cd . fnameescape(s:current_dir)
let vals = matchlist(a:str[1], '|\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)|')
" i.e: main.go
let filename = vals[1]
let line = vals[2]
let col = vals[3]
" i.e: /Users/fatih/vim-go/main.go
let filepath = fnamemodify(filename, ":p")
let cmd = get({'ctrl-x': 'split',
\ 'ctrl-v': 'vertical split',
\ 'ctrl-t': 'tabe'}, a:str[0], 'e')
execute cmd fnameescape(filepath)
call cursor(line, col)
silent! norm! zvzz
finally
"jump back to old dir
execute cd . fnameescape(dir)
endtry
endfunction
function! s:source(mode,...) abort
let s:current_dir = expand('%:p:h')
let ret_decls = []
let bin_path = go#path#CheckBinPath('motion')
if empty(bin_path)
return
endif
let command = printf("%s -format vim -mode decls", bin_path)
let command .= " -include ". get(g:, "go_decls_includes", "func,type")
call go#cmd#autowrite()
if a:mode == 0
" current file mode
let fname = expand("%:p")
if a:0 && !empty(a:1)
let fname = a:1
endif
let command .= printf(" -file %s", shellescape(fname))
else
" all functions mode
if a:0 && !empty(a:1)
let s:current_dir = a:1
endif
let command .= printf(" -dir %s", shellescape(s:current_dir))
endif
let out = go#util#System(command)
if go#util#ShellError() != 0
call go#util#EchoError(out)
return
endif
let result = eval(out)
if type(result) != 4 || !has_key(result, 'decls')
return ret_decls
endif
let decls = result.decls
" find the maximum function name
let max_len = 0
for decl in decls
if len(decl.ident)> max_len
let max_len = len(decl.ident)
endif
endfor
for decl in decls
" paddings
let space = " "
for i in range(max_len - len(decl.ident))
let space .= " "
endfor
let pos = printf("|%s:%s:%s|",
\ fnamemodify(decl.filename, ":t"),
\ decl.line,
\ decl.col
\)
call add(ret_decls, printf("%s\t%s %s\t%s",
\ s:color(decl.ident . space, "Function"),
\ s:color(decl.keyword, "Keyword"),
\ s:color(pos, "SpecialComment"),
\ s:color(decl.full, "Comment"),
\))
endfor
return ret_decls
endfunc
function! fzf#decls#cmd(...) abort
let normal_fg = s:code("Normal", "fg")
let normal_bg = s:code("Normal", "bg")
let cursor_fg = s:code("CursorLine", "fg")
let cursor_bg = s:code("CursorLine", "bg")
let colors = printf(" --color %s%s%s%s%s",
\ &background,
\ empty(normal_fg) ? "" : (",fg:".normal_fg),
\ empty(normal_bg) ? "" : (",bg:".normal_bg),
\ empty(cursor_fg) ? "" : (",fg+:".cursor_fg),
\ empty(cursor_bg) ? "" : (",bg+:".cursor_bg),
\)
call fzf#run(fzf#wrap('GoDecls', {
\ 'source': call('<sid>source', a:000),
\ 'options': '-n 1 --ansi --prompt "GoDecls> " --expect=ctrl-t,ctrl-v,ctrl-x'.colors,
\ 'sink*': function('s:sink')
\ }))
endfunction
" vim: sw=2 ts=2 et

@ -4,7 +4,7 @@ if !exists("g:go_alternate_mode")
endif endif
" Test alternates between the implementation of code and the test code. " Test alternates between the implementation of code and the test code.
function! go#alternate#Switch(bang, cmd) function! go#alternate#Switch(bang, cmd) abort
let file = expand('%') let file = expand('%')
if empty(file) if empty(file)
call go#util#EchoError("no buffer name") call go#util#EchoError("no buffer name")
@ -28,3 +28,5 @@ function! go#alternate#Switch(bang, cmd)
execute ":" . a:cmd . " " . alt_file execute ":" . a:cmd . " " . alt_file
endif endif
endfunction endfunction
" vim: sw=2 ts=2 et

@ -11,7 +11,7 @@
" "
" Options: " Options:
" "
" g:go_asmfmt_autosave [default=1] " g:go_asmfmt_autosave [default=0]
" "
" Flag to automatically call :Fmt when file is saved. " Flag to automatically call :Fmt when file is saved.
@ -19,29 +19,33 @@ let s:got_fmt_error = 0
" This is a trimmed-down version of the logic in fmt.vim. " This is a trimmed-down version of the logic in fmt.vim.
function! go#asmfmt#Format() function! go#asmfmt#Format() abort
" Save state. " Save state.
let l:curw = winsaveview() let l:curw = winsaveview()
" Write the current buffer to a tempfile. " Write the current buffer to a tempfile.
let l:tmpname = tempname() let l:tmpname = tempname()
call writefile(getline(1, '$'), l:tmpname) call writefile(go#util#GetLines(), l:tmpname)
" Run asmfmt. " Run asmfmt.
let path = go#path#CheckBinPath("asmfmt") let path = go#path#CheckBinPath("asmfmt")
if empty(path) if empty(path)
return return
endif endif
let out = system(path . ' -w ' . l:tmpname) let out = go#util#System(path . ' -w ' . l:tmpname)
" If there's no error, replace the current file with the output. " If there's no error, replace the current file with the output.
if v:shell_error == 0 if go#util#ShellError() == 0
" Remove undo point caused by BufWritePre. " Remove undo point caused by BufWritePre.
try | silent undojoin | catch | endtry try | silent undojoin | catch | endtry
" Replace the current file with the temp file; then reload the buffer. " Replace the current file with the temp file; then reload the buffer.
let old_fileformat = &fileformat let old_fileformat = &fileformat
" save old file permissions
let original_fperm = getfperm(expand('%'))
call rename(l:tmpname, expand('%')) call rename(l:tmpname, expand('%'))
" restore old file permissions
call setfperm(expand('%'), original_fperm)
silent edit! silent edit!
let &fileformat = old_fileformat let &fileformat = old_fileformat
let &syntax = &syntax let &syntax = &syntax
@ -50,3 +54,16 @@ function! go#asmfmt#Format()
" Restore the cursor/window positions. " Restore the cursor/window positions.
call winrestview(l:curw) call winrestview(l:curw)
endfunction endfunction
function! go#asmfmt#ToggleAsmFmtAutoSave() abort
if get(g:, "go_asmfmt_autosave", 0)
let g:go_asmfmt_autosave = 1
call go#util#EchoProgress("auto asmfmt enabled")
return
end
let g:go_asmfmt_autosave = 0
call go#util#EchoProgress("auto asmfmt disabled")
endfunction
" vim: sw=2 ts=2 et

@ -1,53 +1,57 @@
if !exists("g:go_dispatch_enabled") function! go#cmd#autowrite() abort
let g:go_dispatch_enabled = 0
endif
function! go#cmd#autowrite()
if &autowrite == 1 if &autowrite == 1
silent wall silent! wall
endif endif
endfunction endfunction
" Build builds the source code without producing any output binary. We live in
" Build builds the source code without producting any output binary. We live in
" an editor so the best is to build it to catch errors and fix them. By " an editor so the best is to build it to catch errors and fix them. By
" default it tries to call simply 'go build', but it first tries to get all " default it tries to call simply 'go build', but it first tries to get all
" dependent files for the current folder and passes it to go build. " dependent files for the current folder and passes it to go build.
function! go#cmd#Build(bang, ...) function! go#cmd#Build(bang, ...) abort
" expand all wildcards(i.e: '%' to the current file name) " Create our command arguments. go build discards any results when it
let goargs = map(copy(a:000), "expand(v:val)")
" escape all shell arguments before we pass it to make
let goargs = go#util#Shelllist(goargs, 1)
" create our command arguments. go build discards any results when it
" compiles multiple packages. So we pass the `errors` package just as an " compiles multiple packages. So we pass the `errors` package just as an
" placeholder with the current folder (indicated with '.') " placeholder with the current folder (indicated with '.'). We also pass -i
let args = ["build"] + goargs + [".", "errors"] " that tries to install the dependencies, this has the side effect that it
" caches the build results, so every other build is faster.
let args =
\ ["build"] +
\ map(copy(a:000), "expand(v:val)") +
\ ["-i", ".", "errors"]
" Vim async.
if go#util#has_job()
if get(g:, 'go_echo_command_info', 1)
call go#util#EchoProgress("building dispatched ...")
endif
" if we have nvim, call it asynchronously and return early ;) call s:cmd_job({
if has('nvim') \ 'cmd': ['go'] + args,
\ 'bang': a:bang,
\ 'for': 'GoBuild',
\})
" Nvim async.
elseif has('nvim')
if get(g:, 'go_echo_command_info', 1)
call go#util#EchoProgress("building dispatched ...") call go#util#EchoProgress("building dispatched ...")
call go#jobcontrol#Spawn(a:bang, "build", args)
return
endif endif
let old_gopath = $GOPATH call go#jobcontrol#Spawn(a:bang, "build", "GoBuild", args)
let $GOPATH = go#path#Detect()
" Vim 7.4 without async
else
let default_makeprg = &makeprg let default_makeprg = &makeprg
let &makeprg = "go " . join(args, ' ') let &makeprg = "go " . join(go#util#Shelllist(args), ' ')
let l:listtype = go#list#Type("quickfix") let l:listtype = go#list#Type("GoBuild")
" execute make inside the source folder so we can parse the errors " execute make inside the source folder so we can parse the errors
" correctly " correctly
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd() let dir = getcwd()
try try
execute cd . fnameescape(expand("%:p:h")) execute cd . fnameescape(expand("%:p:h"))
if g:go_dispatch_enabled && exists(':Make') == 2 if l:listtype == "locationlist"
call go#util#EchoProgress("building dispatched ...")
silent! exe 'Make'
elseif l:listtype == "locationlist"
silent! exe 'lmake!' silent! exe 'lmake!'
else else
silent! exe 'make!' silent! exe 'make!'
@ -59,22 +63,41 @@ function! go#cmd#Build(bang, ...)
let errors = go#list#Get(l:listtype) let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
if !empty(errors)
if !a:bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
endif
else else
call go#util#EchoSuccess("[build] SUCCESS") call go#util#EchoSuccess("[build] SUCCESS")
endif endif
let &makeprg = default_makeprg let &makeprg = default_makeprg
let $GOPATH = old_gopath endif
endfunction
" BuildTags sets or shows the current build tags used for tools
function! go#cmd#BuildTags(bang, ...) abort
if a:0
if a:0 == 1 && a:1 == '""'
unlet g:go_build_tags
call go#util#EchoSuccess("build tags are cleared")
else
let g:go_build_tags = a:1
call go#util#EchoSuccess("build tags are changed to: ". a:1)
endif
return
endif
if !exists('g:go_build_tags')
call go#util#EchoSuccess("build tags are not set")
else
call go#util#EchoSuccess("current build tags: ". g:go_build_tags)
endif
endfunction endfunction
" Run runs the current file (and their dependencies if any) in a new terminal. " Run runs the current file (and their dependencies if any) in a new terminal.
function! go#cmd#RunTerm(bang, mode, files) function! go#cmd#RunTerm(bang, mode, files) abort
if empty(a:files) if empty(a:files)
let cmd = "go run ". go#util#Shelljoin(go#tool#Files()) let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
else else
@ -84,17 +107,20 @@ function! go#cmd#RunTerm(bang, mode, files)
endfunction endfunction
" Run runs the current file (and their dependencies if any) and outputs it. " Run runs the current file (and their dependencies if any) and outputs it.
" This is intented to test small programs and play with them. It's not " This is intended to test small programs and play with them. It's not
" suitable for long running apps, because vim is blocking by default and " suitable for long running apps, because vim is blocking by default and
" calling long running apps will block the whole UI. " calling long running apps will block the whole UI.
function! go#cmd#Run(bang, ...) function! go#cmd#Run(bang, ...) abort
if has('nvim') if has('nvim')
call go#cmd#RunTerm(a:bang, '', a:000) call go#cmd#RunTerm(a:bang, '', a:000)
return return
endif endif
let old_gopath = $GOPATH if go#util#has_job()
let $GOPATH = go#path#Detect() " NOTE(arslan): 'term': 'open' case is not implement for +jobs. This means
" executions waiting for stdin will not work. That's why we don't do
" anything. Once this is implemented we're going to make :GoRun async
endif
if go#util#IsWin() if go#util#IsWin()
exec '!go run ' . go#util#Shelljoin(go#tool#Files()) exec '!go run ' . go#util#Shelljoin(go#tool#Files())
@ -104,7 +130,6 @@ function! go#cmd#Run(bang, ...)
redraws! | echon "vim-go: [run] " | echohl Function | echon "SUCCESS"| echohl None redraws! | echon "vim-go: [run] " | echohl Function | echon "SUCCESS"| echohl None
endif endif
let $GOPATH = old_gopath
return return
endif endif
@ -116,11 +141,9 @@ function! go#cmd#Run(bang, ...)
let &makeprg = "go run " . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) let &makeprg = "go run " . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
endif endif
let l:listtype = go#list#Type("quickfix") let l:listtype = go#list#Type("GoRun")
if g:go_dispatch_enabled && exists(':Make') == 2 if l:listtype == "locationlist"
silent! exe 'Make'
elseif l:listtype == "locationlist"
exe 'lmake!' exe 'lmake!'
else else
exe 'make!' exe 'make!'
@ -129,37 +152,50 @@ function! go#cmd#Run(bang, ...)
let items = go#list#Get(l:listtype) let items = go#list#Get(l:listtype)
let errors = go#tool#FilterValids(items) let errors = go#tool#FilterValids(items)
call go#list#Populate(l:listtype, errors) call go#list#Populate(l:listtype, errors, &makeprg)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
endif endif
let $GOPATH = old_gopath
let &makeprg = default_makeprg let &makeprg = default_makeprg
endfunction endfunction
" Install installs the package by simple calling 'go install'. If any argument " Install installs the package by simple calling 'go install'. If any argument
" is given(which are passed directly to 'go install') it tries to install those " is given(which are passed directly to 'go install') it tries to install
" packages. Errors are populated in the location window. " those packages. Errors are populated in the location window.
function! go#cmd#Install(bang, ...) function! go#cmd#Install(bang, ...) abort
" use vim's job functionality to call it asynchronously
if go#util#has_job()
" expand all wildcards(i.e: '%' to the current file name)
let goargs = map(copy(a:000), "expand(v:val)")
if get(g:, 'go_echo_command_info', 1)
call go#util#EchoProgress("installing dispatched ...")
endif
call s:cmd_job({
\ 'cmd': ['go', 'install'] + goargs,
\ 'bang': a:bang,
\ 'for': 'GoInstall',
\})
return
endif
let default_makeprg = &makeprg let default_makeprg = &makeprg
" :make expands '%' and '#' wildcards, so they must also be escaped " :make expands '%' and '#' wildcards, so they must also be escaped
let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
let &makeprg = "go install " . goargs let &makeprg = "go install " . goargs
let l:listtype = go#list#Type("quickfix") let l:listtype = go#list#Type("GoInstall")
" execute make inside the source folder so we can parse the errors " execute make inside the source folder so we can parse the errors
" correctly " correctly
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd() let dir = getcwd()
try try
execute cd . fnameescape(expand("%:p:h")) execute cd . fnameescape(expand("%:p:h"))
if g:go_dispatch_enabled && exists(':Make') == 2 if l:listtype == "locationlist"
call go#util#EchoProgress("building dispatched ...")
silent! exe 'Make'
elseif l:listtype == "locationlist"
silent! exe 'lmake!' silent! exe 'lmake!'
else else
silent! exe 'make!' silent! exe 'make!'
@ -171,177 +207,32 @@ function! go#cmd#Install(bang, ...)
let errors = go#list#Get(l:listtype) let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors)
if !a:bang
call go#list#JumpToFirst(l:listtype)
endif
else
redraws! | echon "vim-go: " | echohl Function | echon "installed to ". $GOPATH | echohl None
endif
let &makeprg = default_makeprg
endfunction
" Test runs `go test` in the current directory. If compile is true, it'll
" compile the tests instead of running them (useful to catch errors in the
" test files). Any other argument is appendend to the final `go test` command
function! go#cmd#Test(bang, compile, ...)
let args = ["test"]
" don't run the test, only compile it. Useful to capture and fix errors or
" to create a test binary.
if a:compile
call add(args, "-c")
endif
if a:0
" expand all wildcards(i.e: '%' to the current file name)
let goargs = map(copy(a:000), "expand(v:val)")
call extend(args, goargs, 1)
else
" only add this if no custom flags are passed
let timeout = get(g:, 'go_test_timeout', '10s')
call add(args, printf("-timeout=%s", timeout))
endif
if a:compile
echon "vim-go: " | echohl Identifier | echon "compiling tests ..." | echohl None
else
echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None
endif
if has('nvim')
if get(g:, 'go_term_enabled', 0)
call go#term#new(a:bang, ["go"] + args)
else
call go#jobcontrol#Spawn(a:bang, "test", args)
endif
return
endif
call go#cmd#autowrite()
redraw
let command = "go " . join(args, ' ')
let out = go#tool#ExecuteInDir(command)
let l:listtype = "quickfix"
if v:shell_error
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
try
execute cd fnameescape(expand("%:p:h"))
let errors = go#tool#ParseErrors(split(out, '\n'))
let errors = go#tool#FilterValids(errors)
finally
execute cd . fnameescape(dir)
endtry
call go#list#Populate(l:listtype, errors)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
elseif empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(out)
endif
echon "vim-go: " | echohl ErrorMsg | echon "[test] FAIL" | echohl None
else else
call go#list#Clean(l:listtype) call go#util#EchoSuccess("installed to ". go#path#Default())
call go#list#Window(l:listtype)
if a:compile
echon "vim-go: " | echohl Function | echon "[test] SUCCESS" | echohl None
else
echon "vim-go: " | echohl Function | echon "[test] PASS" | echohl None
endif
endif endif
endfunction
" Testfunc runs a single test that surrounds the current cursor position.
" Arguments are passed to the `go test` command.
function! go#cmd#TestFunc(bang, ...)
" search flags legend (used only)
" 'b' search backward instead of forward
" 'c' accept a match at the cursor position
" 'n' do Not move the cursor
" 'W' don't wrap around the end of the file
"
" for the full list
" :help search
let test = search("func Test", "bcnW")
if test == 0
echo "vim-go: [test] no test found immediate to cursor"
return
end
let line = getline(test)
let name = split(split(line, " ")[1], "(")[0]
let args = [a:bang, 0, "-run", name . "$"]
if a:0
call extend(args, a:000)
endif
call call('go#cmd#Test', args)
endfunction
" Coverage creates a new cover profile with 'go test -coverprofile' and opens
" a new HTML coverage page from that profile.
function! go#cmd#Coverage(bang, ...)
let l:tmpname=tempname()
let command = "go test -coverprofile=" . l:tmpname . ' ' . go#util#Shelljoin(a:000)
let l:listtype = "quickfix" let &makeprg = default_makeprg
call go#cmd#autowrite()
let out = go#tool#ExecuteInDir(command)
if v:shell_error
let errors = go#tool#ParseErrors(split(out, '\n'))
call go#list#Populate(l:listtype, errors)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
endif
else
" clear previous location list
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
let openHTML = 'go tool cover -html='.l:tmpname
call go#tool#ExecuteInDir(openHTML)
endif
call delete(l:tmpname)
endfunction endfunction
" Generate runs 'go generate' in similar fashion to go#cmd#Build() " Generate runs 'go generate' in similar fashion to go#cmd#Build()
function! go#cmd#Generate(bang, ...) function! go#cmd#Generate(bang, ...) abort
let default_makeprg = &makeprg let default_makeprg = &makeprg
let old_gopath = $GOPATH
let $GOPATH = go#path#Detect()
" :make expands '%' and '#' wildcards, so they must also be escaped " :make expands '%' and '#' wildcards, so they must also be escaped
let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1) let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
if v:shell_error if go#util#ShellError() != 0
let &makeprg = "go generate " . goargs let &makeprg = "go generate " . goargs
else else
let gofiles = go#util#Shelljoin(go#tool#Files(), 1) let gofiles = go#util#Shelljoin(go#tool#Files(), 1)
let &makeprg = "go generate " . goargs . ' ' . gofiles let &makeprg = "go generate " . goargs . ' ' . gofiles
endif endif
let l:listtype = go#list#Type("quickfix") let l:listtype = go#list#Type("GoGenerate")
echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None
if g:go_dispatch_enabled && exists(':Make') == 2 if l:listtype == "locationlist"
silent! exe 'Make'
elseif l:listtype == "locationlist"
silent! exe 'lmake!' silent! exe 'lmake!'
else else
silent! exe 'make!' silent! exe 'make!'
@ -359,7 +250,62 @@ function! go#cmd#Generate(bang, ...)
endif endif
let &makeprg = default_makeprg let &makeprg = default_makeprg
let $GOPATH = old_gopath
endfunction endfunction
" vim:ts=4:sw=4:et " ---------------------
" | Vim job callbacks |
" ---------------------
function s:cmd_job(args) abort
let status_dir = expand('%:p:h')
let started_at = reltime()
call go#statusline#Update(status_dir, {
\ 'desc': "current status",
\ 'type': a:args.cmd[1],
\ 'state': "started",
\})
" autowrite is not enabled for jobs
call go#cmd#autowrite()
function! s:error_info_cb(job, exit_status, data) closure abort
let status = {
\ 'desc': 'last status',
\ 'type': a:args.cmd[1],
\ 'state': "success",
\ }
if a:exit_status
let status.state = "failed"
endif
let elapsed_time = reltimestr(reltime(started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(status_dir, status)
endfunction
let a:args.error_info_cb = funcref('s:error_info_cb')
let callbacks = go#job#Spawn(a:args)
let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ }
" pre start
let dir = getcwd()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let jobdir = fnameescape(expand("%:p:h"))
execute cd . jobdir
call job_start(a:args.cmd, start_options)
" post start
execute cd . fnameescape(dir)
endfunction
" vim: sw=2 ts=2 et

@ -1,79 +1,45 @@
if !exists("g:go_gocode_bin") let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
let g:go_gocode_bin = "gocode"
endif
fu! s:gocodeCurrentBuffer() function! s:gocodeCurrentBuffer() abort
let buf = getline(1, '$')
if &encoding != 'utf-8'
let buf = map(buf, 'iconv(v:val, &encoding, "utf-8")')
endif
if &l:fileformat == 'dos'
" XXX: line2byte() depend on 'fileformat' option.
" so if fileformat is 'dos', 'buf' must include '\r'.
let buf = map(buf, 'v:val."\r"')
endif
let file = tempname() let file = tempname()
call writefile(buf, file) call writefile(go#util#GetLines(), file)
return file return file
endf endfunction
if go#vimproc#has_vimproc()
let s:vim_system = get(g:, 'gocomplete#system_function', 'vimproc#system2')
let s:vim_shell_error = get(g:, 'gocomplete#shell_error_function', 'vimproc#get_last_status')
else
let s:vim_system = get(g:, 'gocomplete#system_function', 'system')
let s:vim_shell_error = ''
endif
fu! s:shell_error()
if empty(s:vim_shell_error)
return v:shell_error
endif
return call(s:vim_shell_error, [])
endf
fu! s:system(str, ...)
return call(s:vim_system, [a:str] + a:000)
endf
fu! s:gocodeShellescape(arg)
if go#vimproc#has_vimproc()
return vimproc#shellescape(a:arg)
endif
try
let ssl_save = &shellslash
set noshellslash
return shellescape(a:arg)
finally
let &shellslash = ssl_save
endtry
endf
fu! s:gocodeCommand(cmd, preargs, args) function! s:gocodeCommand(cmd, preargs, args) abort
for i in range(0, len(a:args) - 1) for i in range(0, len(a:args) - 1)
let a:args[i] = s:gocodeShellescape(a:args[i]) let a:args[i] = go#util#Shellescape(a:args[i])
endfor endfor
for i in range(0, len(a:preargs) - 1) for i in range(0, len(a:preargs) - 1)
let a:preargs[i] = s:gocodeShellescape(a:preargs[i]) let a:preargs[i] = go#util#Shellescape(a:preargs[i])
endfor endfor
let bin_path = go#path#CheckBinPath(g:go_gocode_bin) let bin_path = go#path#CheckBinPath("gocode")
if empty(bin_path) if empty(bin_path)
return return
endif endif
" we might hit cache problems, as gocode doesn't handle well different " We might hit cache problems, as gocode doesn't handle different GOPATHs
" GOPATHS: https://github.com/nsf/gocode/issues/239 " well. See: https://github.com/nsf/gocode/issues/239
let old_gopath = $GOPATH let old_goroot = $GOROOT
let $GOPATH = go#path#Detect() let $GOROOT = go#util#env("goroot")
let result = s:system(printf('%s %s %s %s', s:gocodeShellescape(bin_path), join(a:preargs), s:gocodeShellescape(a:cmd), join(a:args))) try
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
let $GOPATH = old_gopath let cmd = printf('%s -sock %s %s %s %s',
\ go#util#Shellescape(bin_path),
\ socket_type,
\ join(a:preargs),
\ go#util#Shellescape(a:cmd),
\ join(a:args)
\ )
let result = go#util#System(cmd)
finally
let $GOROOT = old_goroot
endtry
if s:shell_error() != 0 if go#util#ShellError() != 0
return "[\"0\", []]" return "[\"0\", []]"
else else
if &encoding != 'utf-8' if &encoding != 'utf-8'
@ -81,22 +47,46 @@ fu! s:gocodeCommand(cmd, preargs, args)
endif endif
return result return result
endif endif
endf endfunction
fu! s:gocodeCurrentBufferOpt(filename) function! s:gocodeCurrentBufferOpt(filename) abort
return '-in=' . a:filename return '-in=' . a:filename
endf endfunction
let s:optionsEnabled = 0
function! s:gocodeEnableOptions() abort
if s:optionsEnabled
return
endif
let bin_path = go#path#CheckBinPath("gocode")
if empty(bin_path)
return
endif
let s:optionsEnabled = 1
call go#util#System(printf('%s set propose-builtins %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_propose_builtins', 1))))
call go#util#System(printf('%s set autobuild %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_autobuild', 1))))
call go#util#System(printf('%s set unimported-packages %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_unimported_packages', 0))))
endfunction
function! s:toBool(val) abort
if a:val | return 'true ' | else | return 'false' | endif
endfunction
function! s:gocodeAutocomplete() abort
call s:gocodeEnableOptions()
fu! s:gocodeAutocomplete()
let filename = s:gocodeCurrentBuffer() let filename = s:gocodeCurrentBuffer()
let result = s:gocodeCommand('autocomplete', let result = s:gocodeCommand('autocomplete',
\ [s:gocodeCurrentBufferOpt(filename), '-f=vim'], \ [s:gocodeCurrentBufferOpt(filename), '-f=vim'],
\ [expand('%:p'), go#util#OffsetCursor()]) \ [expand('%:p'), go#util#OffsetCursor()])
call delete(filename) call delete(filename)
return result return result
endf endfunction
function! go#complete#GetInfo() function! go#complete#GetInfo() abort
let offset = go#util#OffsetCursor()+1 let offset = go#util#OffsetCursor()+1
let filename = s:gocodeCurrentBuffer() let filename = s:gocodeCurrentBuffer()
let result = s:gocodeCommand('autocomplete', let result = s:gocodeCommand('autocomplete',
@ -113,7 +103,7 @@ function! go#complete#GetInfo()
return "" return ""
endif endif
" only one candiate is found " only one candidate is found
if len(out) == 2 if len(out) == 2
return split(out[1], ',,')[0] return split(out[1], ',,')[0]
endif endif
@ -137,7 +127,7 @@ function! go#complete#GetInfo()
return "" return ""
endfunction endfunction
function! go#complete#Info(auto) function! go#complete#Info(auto) abort
" auto is true if we were called by g:go_auto_type_info's autocmd " auto is true if we were called by g:go_auto_type_info's autocmd
let result = go#complete#GetInfo() let result = go#complete#GetInfo()
if !empty(result) if !empty(result)
@ -147,12 +137,12 @@ function! go#complete#Info(auto)
endif endif
endfunction endfunction
function! s:trim_bracket(val) function! s:trim_bracket(val) abort
let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '') let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '')
return a:val return a:val
endfunction endfunction
fu! go#complete#Complete(findstart, base) function! go#complete#Complete(findstart, base) abort
"findstart = 1 when we need to get the text length "findstart = 1 when we need to get the text length
if a:findstart == 1 if a:findstart == 1
execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete() execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete()
@ -167,4 +157,16 @@ fu! go#complete#Complete(findstart, base)
endif endif
endf endf
" vim:ts=4:sw=4:et function! go#complete#ToggleAutoTypeInfo() abort
if get(g:, "go_auto_type_info", 0)
let g:go_auto_type_info = 0
call go#util#EchoProgress("auto type info disabled")
return
end
let g:go_auto_type_info = 1
call go#util#EchoProgress("auto type info enabled")
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,375 @@
let s:toggle = 0
" Buffer creates a new cover profile with 'go test -coverprofile' and changes
" the current buffers highlighting to show covered and uncovered sections of
" the code. If run again it clears the annotation.
function! go#coverage#BufferToggle(bang, ...) abort
if s:toggle
call go#coverage#Clear()
return
endif
if a:0 == 0
return call(function('go#coverage#Buffer'), [a:bang])
endif
return call(function('go#coverage#Buffer'), [a:bang] + a:000)
endfunction
" Buffer creates a new cover profile with 'go test -coverprofile' and changes
" the current buffers highlighting to show covered and uncovered sections of
" the code. Calling it again reruns the tests and shows the last updated
" coverage.
function! go#coverage#Buffer(bang, ...) abort
" we use matchaddpos() which was introduce with 7.4.330, be sure we have
" it: http://ftp.vim.org/vim/patches/7.4/7.4.330
if !exists("*matchaddpos")
call go#util#EchoError("GoCoverage is supported with Vim version 7.4-330 or later")
return -1
endif
" check if there is any test file, if not we just return
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
try
execute cd . fnameescape(expand("%:p:h"))
if empty(glob("*_test.go"))
call go#util#EchoError("no test files available")
return
endif
finally
execute cd . fnameescape(dir)
endtry
let s:toggle = 1
let l:tmpname = tempname()
if get(g:, 'go_echo_command_info', 1)
echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None
endif
if go#util#has_job()
call s:coverage_job({
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname] + a:000,
\ 'custom_cb': function('s:coverage_callback', [l:tmpname]),
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ })
return
endif
let args = [a:bang, 0, "-coverprofile", l:tmpname]
if a:0
call extend(args, a:000)
endif
let disabled_term = 0
if get(g:, 'go_term_enabled')
let disabled_term = 1
let g:go_term_enabled = 0
endif
let id = call('go#test#Test', args)
if disabled_term
let g:go_term_enabled = 1
endif
if has('nvim')
call go#jobcontrol#AddHandler(function('s:coverage_handler'))
let s:coverage_handler_jobs[id] = l:tmpname
return
endif
if go#util#ShellError() == 0
call go#coverage#overlay(l:tmpname)
endif
call delete(l:tmpname)
endfunction
" Clear clears and resets the buffer annotation matches
function! go#coverage#Clear() abort
call clearmatches()
if exists("s:toggle") | let s:toggle = 0 | endif
" remove the autocmd we defined
augroup vim-go-coverage
autocmd!
augroup end
endfunction
" Browser creates a new cover profile with 'go test -coverprofile' and opens
" a new HTML coverage page from that profile in a new browser
function! go#coverage#Browser(bang, ...) abort
let l:tmpname = tempname()
if go#util#has_job()
call s:coverage_job({
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname],
\ 'custom_cb': function('s:coverage_browser_callback', [l:tmpname]),
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ })
return
endif
let args = [a:bang, 0, "-coverprofile", l:tmpname]
if a:0
call extend(args, a:000)
endif
let id = call('go#test#Test', args)
if has('nvim')
call go#jobcontrol#AddHandler(function('s:coverage_browser_handler'))
let s:coverage_browser_handler_jobs[id] = l:tmpname
return
endif
if go#util#ShellError() == 0
let openHTML = 'go tool cover -html='.l:tmpname
call go#tool#ExecuteInDir(openHTML)
endif
call delete(l:tmpname)
endfunction
" Parses a single line from the cover file generated via go test -coverprofile
" and returns a single coverage profile block.
function! go#coverage#parsegocoverline(line) abort
" file:startline.col,endline.col numstmt count
let mx = '\([^:]\+\):\(\d\+\)\.\(\d\+\),\(\d\+\)\.\(\d\+\)\s\(\d\+\)\s\(\d\+\)'
let tokens = matchlist(a:line, mx)
let ret = {}
let ret.file = tokens[1]
let ret.startline = str2nr(tokens[2])
let ret.startcol = str2nr(tokens[3])
let ret.endline = str2nr(tokens[4])
let ret.endcol = str2nr(tokens[5])
let ret.numstmt = tokens[6]
let ret.cnt = tokens[7]
return ret
endfunction
" Generates matches to be added to matchaddpos for the given coverage profile
" block
function! go#coverage#genmatch(cov) abort
let color = 'goCoverageCovered'
if a:cov.cnt == 0
let color = 'goCoverageUncover'
endif
let matches = []
" if start and end are the same, also specify the byte length
" example: foo.go:92.2,92.65 1 0
if a:cov.startline == a:cov.endline
call add(matches, {
\ 'group': color,
\ 'pos': [[a:cov.startline, a:cov.startcol, a:cov.endcol - a:cov.startcol]],
\ 'priority': 2,
\ })
return matches
endif
" add start columns. Because we don't know the length of the of
" the line, we assume it is at maximum 200 bytes. I know this is hacky,
" but that's only way of fixing the issue
call add(matches, {
\ 'group': color,
\ 'pos': [[a:cov.startline, a:cov.startcol, 200]],
\ 'priority': 2,
\ })
" and then the remaining lines
let start_line = a:cov.startline
while start_line < a:cov.endline
let start_line += 1
call add(matches, {
\ 'group': color,
\ 'pos': [[start_line]],
\ 'priority': 2,
\ })
endwhile
" finally end columns
call add(matches, {
\ 'group': color,
\ 'pos': [[a:cov.endline, a:cov.endcol-1]],
\ 'priority': 2,
\ })
return matches
endfunction
" Reads the given coverprofile file and annotates the current buffer
function! go#coverage#overlay(file) abort
if !filereadable(a:file)
return
endif
let lines = readfile(a:file)
" cover mode, by default it's 'set'. Just here for debugging purposes
let mode = lines[0]
" contains matches for matchaddpos()
let matches = []
" first mark all lines as goCoverageNormalText. We use a custom group to not
" interfere with other buffers highlightings. Because the priority is
" lower than the cover and uncover matches, it'll be overridden.
let cnt = 1
while cnt <= line('$')
call add(matches, {'group': 'goCoverageNormalText', 'pos': [cnt], 'priority': 1})
let cnt += 1
endwhile
let fname = expand('%')
" when called for a _test.go file, run the coverage for the actuall file
" file
if fname =~# '^\f\+_test\.go$'
let l:root = split(fname, '_test.go$')[0]
let fname = l:root . ".go"
if !filereadable(fname)
call go#util#EchoError("couldn't find ".fname)
return
endif
" open the alternate file to show the coverage
exe ":edit ". fnamemodify(fname, ":p")
endif
" cov.file includes only the filename itself, without full path
let fname = fnamemodify(fname, ":t")
for line in lines[1:]
let cov = go#coverage#parsegocoverline(line)
" TODO(arslan): for now only include the coverage for the current
" buffer
if fname != fnamemodify(cov.file, ':t')
continue
endif
call extend(matches, go#coverage#genmatch(cov))
endfor
" clear the matches if we leave the buffer
augroup vim-go-coverage
autocmd!
autocmd BufWinLeave <buffer> call go#coverage#Clear()
augroup end
for m in matches
call matchaddpos(m.group, m.pos)
endfor
endfunction
" ---------------------
" | Vim job callbacks |
" ---------------------
"
function s:coverage_job(args)
" autowrite is not enabled for jobs
call go#cmd#autowrite()
let status_dir = expand('%:p:h')
function! s:error_info_cb(job, exit_status, data) closure
let status = {
\ 'desc': 'last status',
\ 'type': "coverage",
\ 'state': "finished",
\ }
if a:exit_status
let status.state = "failed"
endif
call go#statusline#Update(status_dir, status)
endfunction
let a:args.error_info_cb = funcref('s:error_info_cb')
let callbacks = go#job#Spawn(a:args)
let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ }
" pre start
let dir = getcwd()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let jobdir = fnameescape(expand("%:p:h"))
execute cd . jobdir
call go#statusline#Update(status_dir, {
\ 'desc': "current status",
\ 'type': "coverage",
\ 'state': "started",
\})
call job_start(a:args.cmd, start_options)
" post start
execute cd . fnameescape(dir)
endfunction
" coverage_callback is called when the coverage execution is finished
function! s:coverage_callback(coverfile, job, exit_status, data)
if a:exit_status == 0
call go#coverage#overlay(a:coverfile)
endif
call delete(a:coverfile)
endfunction
function! s:coverage_browser_callback(coverfile, job, exit_status, data)
if a:exit_status == 0
let openHTML = 'go tool cover -html='.a:coverfile
call go#tool#ExecuteInDir(openHTML)
endif
call delete(a:coverfile)
endfunction
" -----------------------
" | Neovim job handlers |
" -----------------------
let s:coverage_handler_jobs = {}
let s:coverage_browser_handler_jobs = {}
function! s:coverage_handler(job, exit_status, data) abort
if !has_key(s:coverage_handler_jobs, a:job.id)
return
endif
let l:tmpname = s:coverage_handler_jobs[a:job.id]
if a:exit_status == 0
call go#coverage#overlay(l:tmpname)
endif
call delete(l:tmpname)
unlet s:coverage_handler_jobs[a:job.id]
endfunction
function! s:coverage_browser_handler(job, exit_status, data) abort
if !has_key(s:coverage_browser_handler_jobs, a:job.id)
return
endif
let l:tmpname = s:coverage_browser_handler_jobs[a:job.id]
if a:exit_status == 0
let openHTML = 'go tool cover -html='.l:tmpname
call go#tool#ExecuteInDir(openHTML)
endif
call delete(l:tmpname)
unlet s:coverage_browser_handler_jobs[a:job.id]
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,21 @@
if !exists('g:go_decls_mode')
let g:go_decls_mode = ''
endif
function! go#decls#Decls(mode, ...) abort
if g:go_decls_mode == 'ctrlp'
call ctrlp#init(call("ctrlp#decls#cmd", [a:mode] + a:000))
elseif g:go_decls_mode == 'fzf'
call call("fzf#decls#cmd", [a:mode] + a:000)
else
if globpath(&rtp, 'plugin/ctrlp.vim') != ""
call ctrlp#init(call("ctrlp#decls#cmd", [a:mode] + a:000))
elseif globpath(&rtp, 'plugin/fzf.vim') != ""
call call("fzf#decls#cmd", [a:mode] + a:000)
else
call go#util#EchoError("neither ctrlp.vim nor fzf.vim are installed. Please install either one")
end
end
endfunction
" vim: sw=2 ts=2 et

@ -1,111 +1,317 @@
if !exists("g:go_godef_bin") let s:go_stack = []
let g:go_godef_bin = "godef" let s:go_stack_level = 0
endif
if go#vimproc#has_vimproc()
let s:vim_system = get(g:, 'gocomplete#system_function', 'vimproc#system2')
else
let s:vim_system = get(g:, 'gocomplete#system_function', 'system')
endif
fu! s:system(str, ...) function! go#def#Jump(mode) abort
return call(s:vim_system, [a:str] + a:000) let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
endf
" modified and improved version of vim-godef " so guru right now is slow for some people. previously we were using
function! go#def#Jump(...) " godef which also has it's own quirks. But this issue come up so many
if !len(a:000) " times I've decided to support both. By default we still use guru as it
let arg = "-o=" . go#util#OffsetCursor() " covers all edge cases, but now anyone can switch to godef if they wish
else let bin_name = get(g:, 'go_def_mode', 'guru')
let arg = a:1 if bin_name == 'godef'
if &modified
" Write current unsaved buffer to a temp file and use the modified content
let l:tmpname = tempname()
call writefile(go#util#GetLines(), l:tmpname)
let fname = l:tmpname
endif endif
let bin_path = go#path#CheckBinPath(g:go_godef_bin) let bin_path = go#path#CheckBinPath("godef")
if empty(bin_path)
return
endif
let command = printf("%s -f=%s -o=%s -t", go#util#Shellescape(bin_path),
\ go#util#Shellescape(fname), go#util#OffsetCursor())
let out = go#util#System(command)
if exists("l:tmpname")
call delete(l:tmpname)
endif
elseif bin_name == 'guru'
let bin_path = go#path#CheckBinPath("guru")
if empty(bin_path) if empty(bin_path)
return return
endif endif
let old_gopath = $GOPATH let cmd = [bin_path]
let $GOPATH = go#path#Detect() let stdin_content = ""
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') if &modified
let command = bin_path . " -f=" . shellescape(fname) . " -i " . shellescape(arg) let content = join(go#util#GetLines(), "\n")
let stdin_content = fname . "\n" . strlen(content) . "\n" . content
call add(cmd, "-modified")
endif
if exists('g:go_build_tags')
let tags = get(g:, 'go_build_tags')
call extend(cmd, ["-tags", tags])
endif
" get output of godef let fname = fname.':#'.go#util#OffsetCursor()
let out = s:system(command, join(getbufline(bufnr('%'), 1, '$'), go#util#LineEnding())) call extend(cmd, ["definition", fname])
" jump to it if go#util#has_job()
call s:godefJump(out, "") let l:spawn_args = {
let $GOPATH = old_gopath \ 'cmd': cmd,
endfunction \ 'custom_cb': function('s:jump_to_declaration_cb', [a:mode, bin_name]),
\ }
if &modified
let l:spawn_args.input = stdin_content
endif
function! go#def#JumpMode(mode) call go#util#EchoProgress("searching declaration ...")
let arg = "-o=" . go#util#OffsetCursor()
let bin_path = go#path#CheckBinPath(g:go_godef_bin) call s:def_job(spawn_args)
if empty(bin_path)
return return
endif endif
let old_gopath = $GOPATH let command = join(cmd, " ")
let $GOPATH = go#path#Detect() if &modified
let out = go#util#System(command, stdin_content)
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?') else
let command = bin_path . " -f=" . shellescape(fname) . " -i " . shellescape(arg) let out = go#util#System(command)
endif
else
call go#util#EchoError('go_def_mode value: '. bin_name .' is not valid. Valid values are: [godef, guru]')
return
endif
" get output of godef if go#util#ShellError() != 0
let out = s:system(command, join(getbufline(bufnr('%'), 1, '$'), go#util#LineEnding())) call go#util#EchoError(out)
return
endif
call s:godefJump(out, a:mode) call go#def#jump_to_declaration(out, a:mode, bin_name)
let $GOPATH = old_gopath
endfunction endfunction
function! s:jump_to_declaration_cb(mode, bin_name, job, exit_status, data) abort
if a:exit_status != 0
return
endif
function! s:getOffset() call go#def#jump_to_declaration(a:data[0], a:mode, a:bin_name)
return "-o=" . go#util#OffsetCursor() call go#util#EchoSuccess(fnamemodify(a:data[0], ":t"))
endfunction endfunction
function! go#def#jump_to_declaration(out, mode, bin_name) abort
let final_out = a:out
if a:bin_name == "godef"
" append the type information to the same line so our we can parse it.
" This makes it compatible with guru output.
let final_out = join(split(a:out, '\n'), ':')
endif
function! s:godefJump(out, mode) " strip line ending
let old_errorformat = &errorformat let out = split(final_out, go#util#LineEnding())[0]
let &errorformat = "%f:%l:%c" if go#util#IsWin()
let parts = split(out, '\(^[a-zA-Z]\)\@<!:')
else
let parts = split(out, ':')
endif
if a:out =~ 'godef: ' let filename = parts[0]
let out = substitute(a:out, go#util#LineEnding() . '$', '', '') let line = parts[1]
echom out let col = parts[2]
let ident = parts[3]
" Remove anything newer than the current position, just like basic
" vim tag support
if s:go_stack_level == 0
let s:go_stack = []
else else
let parts = split(a:out, ':') let s:go_stack = s:go_stack[0:s:go_stack_level-1]
" parts[0] contains filename endif
let fileName = parts[0]
" increment the stack counter
let s:go_stack_level += 1
" put the error format into location list so we can jump automatically to " push it on to the jumpstack
" it let stack_entry = {'line': line("."), 'col': col("."), 'file': expand('%:p'), 'ident': ident}
lgetexpr a:out call add(s:go_stack, stack_entry)
" needed for restoring back user setting this is because there are two " needed for restoring back user setting this is because there are two
" modes of switchbuf which we need based on the split mode " modes of switchbuf which we need based on the split mode
let old_switchbuf = &switchbuf let old_switchbuf = &switchbuf
if a:mode == "tab" normal! m'
let &switchbuf = "usetab" 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 get(g:, 'go_def_reuse_buffer', 0) && bufloaded(filename) != 0 && bufwinnr(filename) != -1
" jumpt to existing buffer if it exists
execute bufwinnr(filename) . 'wincmd w'
else
if &modified
let cmd = 'hide edit'
else
let cmd = 'edit'
endif
if bufloaded(fileName) == 0 if a:mode == "tab"
let &switchbuf = "useopen,usetab,newtab"
if bufloaded(filename) == 0
tab split tab split
endif
else else
if a:mode == "split" let cmd = 'sbuf'
endif
elseif a:mode == "split"
split split
elseif a:mode == "vsplit" elseif a:mode == "vsplit"
vsplit vsplit
endif endif
" open the file and jump to line and column
exec cmd fnameescape(fnamemodify(filename, ':.'))
endif
endif endif
call cursor(line, col)
" jump to file now " also align the line to middle of the view
sil ll 1
normal! zz normal! zz
let &switchbuf = old_switchbuf let &switchbuf = old_switchbuf
end
let &errorformat = old_errorformat
endfunction endfunction
function! go#def#SelectStackEntry() abort
let target_window = go#ui#GetReturnWindow()
if empty(target_window)
let target_window = winnr()
endif
let highlighted_stack_entry = matchstr(getline("."), '^..\zs\(\d\+\)')
if !empty(highlighted_stack_entry)
execute target_window . "wincmd w"
call go#def#Stack(str2nr(highlighted_stack_entry))
endif
call go#ui#CloseWindow()
endfunction
function! go#def#StackUI() abort
if len(s:go_stack) == 0
call go#util#EchoError("godef stack empty")
return
endif
let stackOut = ['" <Up>,<Down>:navigate <Enter>:jump <Esc>,q:exit']
let i = 0
while i < len(s:go_stack)
let entry = s:go_stack[i]
let prefix = ""
if i == s:go_stack_level
let prefix = ">"
else
let prefix = " "
endif
call add(stackOut, printf("%s %d %s|%d col %d|%s",
\ prefix, i+1, entry["file"], entry["line"], entry["col"], entry["ident"]))
let i += 1
endwhile
if s:go_stack_level == i
call add(stackOut, "> ")
endif
call go#ui#OpenWindow("GoDef Stack", stackOut, "godefstack")
noremap <buffer> <silent> <CR> :<C-U>call go#def#SelectStackEntry()<CR>
noremap <buffer> <silent> <Esc> :<C-U>call go#ui#CloseWindow()<CR>
noremap <buffer> <silent> q :<C-U>call go#ui#CloseWindow()<CR>
endfunction
function! go#def#StackClear(...) abort
let s:go_stack = []
let s:go_stack_level = 0
endfunction
function! go#def#StackPop(...) abort
if len(s:go_stack) == 0
call go#util#EchoError("godef stack empty")
return
endif
if s:go_stack_level == 0
call go#util#EchoError("at bottom of the godef stack")
return
endif
if !len(a:000)
let numPop = 1
else
let numPop = a:1
endif
let newLevel = str2nr(s:go_stack_level) - str2nr(numPop)
call go#def#Stack(newLevel + 1)
endfunction
function! go#def#Stack(...) abort
if len(s:go_stack) == 0
call go#util#EchoError("godef stack empty")
return
endif
if !len(a:000)
" Display interactive stack
call go#def#StackUI()
return
else
let jumpTarget = a:1
endif
if jumpTarget !~ '^\d\+$'
if jumpTarget !~ '^\s*$'
call go#util#EchoError("location must be a number")
endif
return
endif
let jumpTarget = str2nr(jumpTarget) - 1
if jumpTarget >= 0 && jumpTarget < len(s:go_stack)
let s:go_stack_level = jumpTarget
let target = s:go_stack[s:go_stack_level]
" jump
if expand('%:p') != target["file"]
if &modified
exec 'hide edit' target["file"]
else
exec 'edit' target["file"]
endif
endif
call cursor(target["line"], target["col"])
normal! zz
else
call go#util#EchoError("invalid location. Try :GoDefStack to see the list of valid entries")
endif
endfunction
function s:def_job(args) abort
function! s:error_info_cb(job, exit_status, data) closure
" do not print anything during async definition search&jump
endfunction
let a:args.error_info_cb = funcref('s:error_info_cb')
let callbacks = go#job#Spawn(a:args)
let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ }
if &modified
let l:tmpname = tempname()
call writefile(split(a:args.input, "\n"), l:tmpname, "b")
let l:start_options.in_io = "file"
let l:start_options.in_name = l:tmpname
endif
call job_start(a:args.cmd, start_options)
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,37 @@
func! Test_jump_to_declaration_guru() abort
try
let l:filename = 'def/jump.go'
let lnum = 5
let col = 6
let l:tmp = gotest#load_fixture(l:filename)
let guru_out = printf("%s:%d:%d: defined here as func main", filename, lnum, col)
call go#def#jump_to_declaration(guru_out, "", 'guru')
call assert_equal(filename, bufname("%"))
call assert_equal(lnum, getcurpos()[1])
call assert_equal(col, getcurpos()[2])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_jump_to_declaration_godef() abort
try
let filename = 'def/jump.go'
let lnum = 5
let col = 6
let l:tmp = gotest#load_fixture(l:filename)
let godef_out = printf("%s:%d:%d\ndefined here as func main", filename, lnum, col)
call go#def#jump_to_declaration(godef_out, "", 'godef')
call assert_equal(filename, bufname("%"))
call assert_equal(lnum, getcurpos()[1])
call assert_equal(col, getcurpos()[2])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" vim: sw=2 ts=2 et

@ -1,85 +1,57 @@
" Copyright 2011 The Go Authors. All rights reserved. " Copyright 2011 The Go Authors. All rights reserved.
" Use of this source code is governed by a BSD-style " Use of this source code is governed by a BSD-style
" license that can be found in the LICENSE file. " license that can be found in the LICENSE file.
"
" godoc.vim: Vim command to see godoc.
"
"
" Commands:
"
" :GoDoc
"
" Open the relevant Godoc for either the word[s] passed to the command or
" the, by default, the word under the cursor.
"
" Options:
"
" g:go_godoc_commands [default=1]
"
" Flag to indicate whether to enable the commands listed above.
let s:buf_nr = -1 let s:buf_nr = -1
if !exists("g:go_doc_command") if !exists("g:go_doc_command")
let g:go_doc_command = "godoc" let g:go_doc_command = ["godoc"]
endif
function! go#doc#OpenBrowser(...) abort
" check if we have gogetdoc as it gives us more and accurate information.
" Only supported if we have json_decode as it's not worth to parse the plain
" non-json output of gogetdoc
let bin_path = go#path#CheckBinPath('gogetdoc')
if !empty(bin_path) && exists('*json_decode')
let json_out = s:gogetdoc(1)
if go#util#ShellError() != 0
call go#util#EchoError(json_out)
return
endif endif
if !exists("g:go_doc_options") let out = json_decode(json_out)
let g:go_doc_options = "" if type(out) != type({})
call go#util#EchoError("gogetdoc output is malformed")
endif endif
" returns the package and exported name. exported name might be empty. let import = out["import"]
" ie: fmt and Println let name = out["name"]
" ie: github.com/fatih/set and New let decl = out["decl"]
function! s:godocWord(args)
if !executable('godoc')
echohl WarningMsg
echo "godoc command not found."
echo " install with: go get golang.org/x/tools/cmd/godoc"
echohl None
return []
endif
if !len(a:args) let godoc_url = get(g:, 'go_doc_url', 'https://godoc.org')
let oldiskeyword = &iskeyword if godoc_url isnot 'https://godoc.org'
setlocal iskeyword+=. " strip last '/' character if available
let word = expand('<cword>') let last_char = strlen(godoc_url) - 1
let &iskeyword = oldiskeyword if godoc_url[last_char] == '/'
let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g') let godoc_url = strpart(godoc_url, 0, last_char)
let words = split(word, '\.\ze[^./]\+$')
else
let words = a:args
endif endif
if !len(words) " custom godoc installations expects it
return [] let godoc_url .= "/pkg"
endif endif
let pkg = words[0] let godoc_url .= "/" . import
if len(words) == 1 if decl !~ "^package"
let exported_name = "" let godoc_url .= "#" . name
else
let exported_name = words[1]
endif endif
let packages = go#tool#Imports() echo godoc_url
if has_key(packages, pkg)
let pkg = packages[pkg]
endif
return [pkg, exported_name]
endfunction
function! s:godocNotFound(content) call go#tool#OpenBrowser(godoc_url)
if len(a:content) == 0 return
return 1
endif endif
return a:content =~# '^.*: no such file or directory\n$'
endfunction
function! go#doc#OpenBrowser(...)
let pkgs = s:godocWord(a:000) let pkgs = s:godocWord(a:000)
if empty(pkgs) if empty(pkgs)
return return
@ -93,52 +65,34 @@ function! go#doc#OpenBrowser(...)
call go#tool#OpenBrowser(godoc_url) call go#tool#OpenBrowser(godoc_url)
endfunction endfunction
function! go#doc#Open(newmode, mode, ...) function! go#doc#Open(newmode, mode, ...) abort
let pkgs = s:godocWord(a:000) " With argument: run "godoc [arg]".
if empty(pkgs) if len(a:000)
if empty(go#path#CheckBinPath(g:go_doc_command[0]))
return return
endif endif
let pkg = pkgs[0] let command = printf("%s %s", go#util#Shelljoin(g:go_doc_command), join(a:000, ' '))
let exported_name = pkgs[1] let out = go#util#System(command)
" Without argument: run gogetdoc on cursor position.
let command = g:go_doc_command . ' ' . g:go_doc_options . ' ' . pkg else
let out = s:gogetdoc(0)
silent! let content = system(command) if out == -1
if v:shell_error || s:godocNotFound(content) return
echo 'No documentation found for "' . pkg . '".'
return -1
endif
call s:GodocView(a:newmode, a:mode, content)
if exported_name == ''
silent! normal! gg
return -1
endif endif
" jump to the specified name
if search('^func ' . exported_name . '(')
silent! normal! zt
return -1
endif endif
if search('^type ' . exported_name) if go#util#ShellError() != 0
silent! normal! zt call go#util#EchoError(out)
return -1 return
endif
if search('^\%(const\|var\|type\|\s\+\) ' . pkg . '\s\+=\s')
silent! normal! zt
return -1
endif endif
" nothing found, jump to top call s:GodocView(a:newmode, a:mode, out)
silent! normal! gg
endfunction endfunction
function! s:GodocView(newposition, position, content) function! s:GodocView(newposition, position, content) abort
" reuse existing buffer window if it exists otherwise create a new one " 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) if !bufexists(s:buf_nr)
execute a:newposition execute a:newposition
sil file `="[Godoc]"` sil file `="[Godoc]"`
@ -150,6 +104,25 @@ function! s:GodocView(newposition, position, content)
execute bufwinnr(s:buf_nr) . 'wincmd w' execute bufwinnr(s:buf_nr) . 'wincmd w'
endif endif
" if window was not visible then resize it
if !is_visible
if a:position == "split"
" cap window height to 20, but resize it for smaller contents
let max_height = get(g:, "go_doc_max_height", 20)
let content_height = len(split(a:content, "\n"))
if content_height > max_height
exe 'resize ' . max_height
else
exe 'resize ' . content_height
endif
else
" set a sane maximum width for vertical splits. In this case the minimum
" that fits the godoc for package http without extra linebreaks and line
" numbers on
exe 'vertical resize 84'
endif
endif
setlocal filetype=godoc setlocal filetype=godoc
setlocal bufhidden=delete setlocal bufhidden=delete
setlocal buftype=nofile setlocal buftype=nofile
@ -165,7 +138,92 @@ function! s:GodocView(newposition, position, content)
call append(0, split(a:content, "\n")) call append(0, split(a:content, "\n"))
sil $delete _ sil $delete _
setlocal nomodifiable setlocal nomodifiable
sil normal! gg
" close easily with <esc> or enter
noremap <buffer> <silent> <CR> :<C-U>close<CR>
noremap <buffer> <silent> <Esc> :<C-U>close<CR>
endfunction
function! s:gogetdoc(json) abort
" check if we have 'gogetdoc' and use it automatically
let bin_path = go#path#CheckBinPath('gogetdoc')
if empty(bin_path)
return -1
endif
let cmd = [go#util#Shellescape(bin_path)]
let offset = go#util#OffsetCursor()
let fname = expand("%:p:gs!\\!/!")
let pos = shellescape(fname.':#'.offset)
let cmd += ["-pos", pos]
if a:json
let cmd += ["-json"]
endif
let command = join(cmd, " ")
if &modified
let command .= " -modified"
let out = go#util#System(command, go#util#archive())
else
let out = go#util#System(command)
endif
return out
endfunction
" returns the package and exported name. exported name might be empty.
" ie: fmt and Println
" ie: github.com/fatih/set and New
function! s:godocWord(args) abort
if !executable('godoc')
let msg = "godoc command not found."
let msg .= " install with: go get golang.org/x/tools/cmd/godoc"
call go#util#EchoWarning(msg)
return []
endif
if !len(a:args)
let oldiskeyword = &iskeyword
setlocal iskeyword+=.
let word = expand('<cword>')
let &iskeyword = oldiskeyword
let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
let words = split(word, '\.\ze[^./]\+$')
else
let words = a:args
endif
if !len(words)
return []
endif
let pkg = words[0]
if len(words) == 1
let exported_name = ""
else
let exported_name = words[1]
endif
let packages = go#tool#Imports()
if has_key(packages, pkg)
let pkg = packages[pkg]
endif
return [pkg, exported_name]
endfunction
function! s:godocNotFound(content) abort
if len(a:content) == 0
return 1
endif
return a:content =~# '^.*: no such file or directory\n$'
endfunction endfunction
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -0,0 +1,62 @@
function! go#fillstruct#FillStruct() abort
let l:cmd = ['fillstruct',
\ '-file', bufname(''),
\ '-offset', go#util#OffsetCursor(),
\ '-line', line('.')]
" Read from stdin if modified.
if &modified
call add(l:cmd, '-modified')
let [l:out, l:err] = go#util#Exec(l:cmd, go#util#archive())
else
let [l:out, l:err] = go#util#Exec(l:cmd)
endif
if l:err
call go#util#EchoError(l:out)
return
endif
try
let l:json = json_decode(l:out)
catch
call go#util#EchoError(l:out)
return
endtry
" Output is array:
"[
" {"start": 92, "end": 106, "code": "mail.Address{\n\tName: \"\",\n\tAddress: \"\",\n}"},
" {...second struct...}
" ]
let l:pos = getpos('.')
try
for l:struct in l:json
let l:code = split(l:struct['code'], "\n")
" Add any code before/after the struct.
exe l:struct['start'] . 'go'
let l:code[0] = getline('.')[:col('.')-1] . l:code[0]
exe l:struct['end'] . 'go'
let l:code[len(l:code)-1] .= getline('.')[col('.'):]
" Indent every line except the first one; makes it look nice.
let l:indent = repeat("\t", indent('.') / &tabstop)
for l:i in range(1, len(l:code)-1)
let l:code[l:i] = l:indent . l:code[l:i]
endfor
" Out with the old ...
exe 'normal! ' . l:struct['start'] . 'gov' . l:struct['end'] . 'gox'
" ... in with the new.
call setline('.', l:code[0])
call append('.', l:code[1:])
endfor
finally
call setpos('.', l:pos)
endtry
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,90 @@
func! Test_fillstruct() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import "net/mail"',
\ "var addr = mail.\x1fAddress{}"])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'var addr = mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_fillstruct_line() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import "net/mail"',
\ "\x1f" . 'var addr = mail.Address{}'])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'var addr = mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_fillstruct_two_line() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ "\x1f" . 'func x() { fmt.Println(mail.Address{}, mail.Address{}) }'])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ 'func x() { fmt.Println(mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}, mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}) }'])
finally
"call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_fillstruct_two_cursor() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ "func x() { fmt.Println(mail.Address{}, mail.Ad\x1fdress{}) }"])
call go#fillstruct#FillStruct()
call gotest#assert_buffer(1, [
\ 'import (',
\ '"fmt"',
\ '"net/mail"',
\ ')',
\ 'func x() { fmt.Println(mail.Address{}, mail.Address{',
\ '\tName: "",',
\ '\tAddress: "",',
\ '}) }'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" vim: sw=2 ts=2 et

@ -2,43 +2,21 @@
" Use of this source code is governed by a BSD-style " Use of this source code is governed by a BSD-style
" license that can be found in the LICENSE file. " license that can be found in the LICENSE file.
" "
" fmt.vim: Vim command to format Go files with gofmt. " fmt.vim: Vim command to format Go files with gofmt (and gofmt compatible
" " toorls, such as goimports).
" This filetype plugin add a new commands for go buffers:
"
" :Fmt
"
" Filter the current Go buffer through gofmt.
" It tries to preserve cursor position and avoids
" replacing the buffer with stderr output.
"
" Options:
"
" g:go_fmt_command [default="gofmt"]
"
" Flag naming the gofmt executable to use.
"
" g:go_fmt_autosave [default=1]
"
" Flag to auto call :Fmt when saved file
"
if !exists("g:go_fmt_command") if !exists("g:go_fmt_command")
let g:go_fmt_command = "gofmt" let g:go_fmt_command = "gofmt"
endif endif
if !exists("g:go_goimports_bin") if !exists('g:go_fmt_options')
let g:go_goimports_bin = "goimports" let g:go_fmt_options = ''
endif endif
if !exists('g:go_fmt_fail_silently') if !exists('g:go_fmt_fail_silently')
let g:go_fmt_fail_silently = 0 let g:go_fmt_fail_silently = 0
endif endif
if !exists('g:go_fmt_options')
let g:go_fmt_options = ''
endif
if !exists("g:go_fmt_experimental") if !exists("g:go_fmt_experimental")
let g:go_fmt_experimental = 0 let g:go_fmt_experimental = 0
endif endif
@ -51,7 +29,7 @@ endif
" it doesn't undo changes and break undo history. If you are here reading " it doesn't undo changes and break undo history. If you are here reading
" this and have VimL experience, please look at the function for " this and have VimL experience, please look at the function for
" improvements, patches are welcome :) " improvements, patches are welcome :)
function! go#fmt#Format(withGoimport) function! go#fmt#Format(withGoimport) abort
if g:go_fmt_experimental == 1 if g:go_fmt_experimental == 1
" Using winsaveview to save/restore cursor state has the problem of " Using winsaveview to save/restore cursor state has the problem of
" closing folds on save: " closing folds on save:
@ -67,144 +45,224 @@ function! go#fmt#Format(withGoimport)
catch catch
let l:curw = winsaveview() let l:curw = winsaveview()
endtry endtry
else
" Save cursor position and many other things.
let l:curw=winsaveview()
endif
" Write current unsaved buffer to a temp file
let l:tmpname = tempname()
call writefile(getline(1, '$'), l:tmpname)
if g:go_fmt_experimental == 1
" save our undo file to be restored after we are done. This is needed to " save our undo file to be restored after we are done. This is needed to
" prevent an additional undo jump due to BufWritePre auto command and also " prevent an additional undo jump due to BufWritePre auto command and also
" restore 'redo' history because it's getting being destroyed every " restore 'redo' history because it's getting being destroyed every
" BufWritePre " BufWritePre
let tmpundofile = tempname() let tmpundofile = tempname()
exe 'wundo! ' . tmpundofile exe 'wundo! ' . tmpundofile
else
" Save cursor position and many other things.
let l:curw = winsaveview()
endif endif
" get the command first so we can test it " Write current unsaved buffer to a temp file
let fmt_command = g:go_fmt_command let l:tmpname = tempname() . '.go'
if a:withGoimport == 1 call writefile(go#util#GetLines(), l:tmpname)
let fmt_command = g:go_goimports_bin if go#util#IsWin()
let l:tmpname = tr(l:tmpname, '\', '/')
endif endif
" check if the user has installed command binary. let bin_name = g:go_fmt_command
" For example if it's goimports, let us check if it's installed, if a:withGoimport == 1
" if not the user get's a warning via go#path#CheckBinPath() let bin_name = "goimports"
let bin_path = go#path#CheckBinPath(fmt_command)
if empty(bin_path)
return
endif endif
if fmt_command != "gofmt" let current_col = col('.')
" change GOPATH too, so goimports can pick up the correct library let out = go#fmt#run(bin_name, l:tmpname, expand('%'))
let old_gopath = $GOPATH let diff_offset = len(readfile(l:tmpname)) - line('$')
let $GOPATH = go#path#Detect()
let fmt_command = bin_path if go#util#ShellError() == 0
call go#fmt#update_file(l:tmpname, expand('%'))
elseif g:go_fmt_fail_silently == 0
let errors = s:parse_errors(expand('%'), out)
call s:show_errors(errors)
endif endif
" populate the final command with user based fmt options " We didn't use the temp file, so clean up
let command = fmt_command . ' -w ' call delete(l:tmpname)
if a:withGoimport != 1
let command = command . g:go_fmt_options
endif
if fmt_command == "goimports" if g:go_fmt_experimental == 1
if !exists('b:goimports_vendor_compatible') " restore our undo history
let out = system("goimports --help") silent! exe 'rundo ' . tmpundofile
if out !~ "-srcdir" call delete(tmpundofile)
echohl WarningMsg
echomsg "vim-go: goimports does not support srcdir."
echomsg " update with: :GoUpdateBinaries"
echohl None
else
let b:goimports_vendor_compatible = 1
endif
endif
if exists('b:goimports_vendor_compatible') && b:goimports_vendor_compatible " Restore our cursor/windows positions, folds, etc.
let command = command . '-srcdir ' . fnameescape(expand("%:p:h")) if empty(l:curw)
silent! loadview
else
call winrestview(l:curw)
endif endif
else
" Restore our cursor/windows positions.
call winrestview(l:curw)
endif endif
" execute our command... " be smart and jump to the line the new statement was added/removed
let out = system(command . " " . l:tmpname) call cursor(line('.') + diff_offset, current_col)
if fmt_command != "gofmt" " Syntax highlighting breaks less often.
let $GOPATH = old_gopath syntax sync fromstart
endif endfunction
let l:listtype = "locationlist" " update_file updates the target file with the given formatted source
"if there is no error on the temp file replace the output with the current function! go#fmt#update_file(source, target)
"file (if this fails, we can always check the outputs first line with:
"splitted =~ 'package \w\+')
if v:shell_error == 0
" remove undo point caused via BufWritePre " remove undo point caused via BufWritePre
try | silent undojoin | catch | endtry try | silent undojoin | catch | endtry
" Replace current file with temp file, then reload buffer
let old_fileformat = &fileformat let old_fileformat = &fileformat
call rename(l:tmpname, expand('%')) if exists("*getfperm")
" save file permissions
let original_fperm = getfperm(a:target)
endif
call rename(a:source, a:target)
" restore file permissions
if exists("*setfperm") && original_fperm != ''
call setfperm(a:target , original_fperm)
endif
" reload buffer to reflect latest changes
silent edit! silent edit!
let &fileformat = old_fileformat let &fileformat = old_fileformat
let &syntax = &syntax let &syntax = &syntax
" clean up previous location list, but only if it's due to fmt let l:listtype = go#list#Type("GoFmt")
if exists('b:got_fmt_error') && b:got_fmt_error
let b:got_fmt_error = 0 " the title information was introduced with 7.4-2200
" https://github.com/vim/vim/commit/d823fa910cca43fec3c31c030ee908a14c272640
if has('patch-7.4.2200')
" clean up previous list
if l:listtype == "quickfix"
let l:list_title = getqflist({'title': 1})
else
let l:list_title = getloclist(0, {'title': 1})
endif
else
" can't check the title, so assume that the list was for go fmt.
let l:list_title = {'title': 'Format'}
endif
if has_key(l:list_title, "title") && l:list_title['title'] == "Format"
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype) call go#list#Window(l:listtype)
endif endif
elseif g:go_fmt_fail_silently == 0 endfunction
let splitted = split(out, '\n')
"otherwise get the errors and put them to location list " run runs the gofmt/goimport command for the given source file and returns
" the output of the executed command. Target is the real file to be formatted.
function! go#fmt#run(bin_name, source, target)
let cmd = s:fmt_cmd(a:bin_name, a:source, a:target)
if empty(cmd)
return
endif
let command = join(cmd, " ")
" execute our command...
let out = go#util#System(command)
return out
endfunction
" fmt_cmd returns a dict that contains the command to execute gofmt (or
" goimports). args is dict with
function! s:fmt_cmd(bin_name, source, target)
" check if the user has installed command binary.
" For example if it's goimports, let us check if it's installed,
" if not the user get's a warning via go#path#CheckBinPath()
let bin_path = go#path#CheckBinPath(a:bin_name)
if empty(bin_path)
return []
endif
" start constructing the command
let bin_path = go#util#Shellescape(bin_path)
let cmd = [bin_path]
call add(cmd, "-w")
" add the options for binary (if any). go_fmt_options was by default of type
" string, however to allow customization it's now a dictionary of binary
" name mapping to options.
let opts = g:go_fmt_options
if type(g:go_fmt_options) == type({})
let opts = has_key(g:go_fmt_options, a:bin_name) ? g:go_fmt_options[a:bin_name] : ""
endif
call extend(cmd, split(opts, " "))
if a:bin_name == "goimports"
" lazy check if goimports support `-srcdir`. We should eventually remove
" this in the future
if !exists('b:goimports_vendor_compatible')
let out = go#util#System(bin_path . " --help")
if out !~ "-srcdir"
call go#util#EchoWarning(printf("vim-go: goimports (%s) does not support srcdir. Update with: :GoUpdateBinaries", bin_path))
else
let b:goimports_vendor_compatible = 1
endif
endif
if exists('b:goimports_vendor_compatible') && b:goimports_vendor_compatible
let ssl_save = &shellslash
set noshellslash
" use the filename without the fully qualified name if the tree is
" symlinked into the GOPATH, goimports won't work properly.
call extend(cmd, ["-srcdir", shellescape(a:target)])
let &shellslash = ssl_save
endif
endif
call add(cmd, a:source)
return cmd
endfunction
" parse_errors parses the given errors and returns a list of parsed errors
function! s:parse_errors(filename, content) abort
let splitted = split(a:content, '\n')
" list of errors to be put into location list
let errors = [] let errors = []
for line in splitted for line in splitted
let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)') let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)')
if !empty(tokens) if !empty(tokens)
call add(errors, {"filename": @%, call add(errors,{
\"filename": a:filename,
\"lnum": tokens[2], \"lnum": tokens[2],
\"col": tokens[3], \"col": tokens[3],
\"text": tokens[4]}) \"text": tokens[4],
\ })
endif endif
endfor endfor
if empty(errors)
% | " Couldn't detect gofmt error format, output errors
endif
if !empty(errors)
call go#list#Populate(l:listtype, errors)
echohl Error | echomsg "Gofmt returned error" | echohl None
endif
let b:got_fmt_error = 1 return errors
call go#list#Window(l:listtype, len(errors)) endfunction
" We didn't use the temp file, so clean up
call delete(l:tmpname)
endif
if g:go_fmt_experimental == 1 " show_errors opens a location list and shows the given errors. If the given
" restore our undo history " errors is empty, it closes the the location list
silent! exe 'rundo ' . tmpundofile function! s:show_errors(errors) abort
call delete(tmpundofile) let l:listtype = go#list#Type("GoFmt")
if !empty(a:errors)
call go#list#Populate(l:listtype, a:errors, 'Format')
echohl Error | echomsg "Gofmt returned error" | echohl None
endif endif
if g:go_fmt_experimental == 1 " this closes the window if there are no errors or it opens
" Restore our cursor/windows positions, folds, etc. " it if there is any
if empty(l:curw) call go#list#Window(l:listtype, len(a:errors))
silent! loadview
else
call winrestview(l:curw)
endif
else
" Restore our cursor/windows positions.
call winrestview(l:curw)
endif
endfunction endfunction
function! go#fmt#ToggleFmtAutoSave() abort
if get(g:, "go_fmt_autosave", 1)
let g:go_fmt_autosave = 0
call go#util#EchoProgress("auto fmt disabled")
return
end
let g:go_fmt_autosave = 1
call go#util#EchoProgress("auto fmt enabled")
endfunction
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -0,0 +1,49 @@
func! Test_run_fmt() abort
let actual_file = tempname()
call writefile(readfile("test-fixtures/fmt/hello.go"), actual_file)
let expected = join(readfile("test-fixtures/fmt/hello_golden.go"), "\n")
" run our code
call go#fmt#run("gofmt", actual_file, "test-fixtures/fmt/hello.go")
" this should now contain the formatted code
let actual = join(readfile(actual_file), "\n")
call assert_equal(expected, actual)
endfunc
func! Test_update_file() abort
let expected = join(readfile("test-fixtures/fmt/hello_golden.go"), "\n")
let source_file = tempname()
call writefile(readfile("test-fixtures/fmt/hello_golden.go"), source_file)
let target_file = tempname()
call writefile([""], target_file)
" update_file now
call go#fmt#update_file(source_file, target_file)
" this should now contain the formatted code
let actual = join(readfile(target_file), "\n")
call assert_equal(expected, actual)
endfunc
func! Test_goimports() abort
let $GOPATH = 'test-fixtures/fmt/'
let actual_file = tempname()
call writefile(readfile("test-fixtures/fmt/src/imports/goimports.go"), actual_file)
let expected = join(readfile("test-fixtures/fmt/src/imports/goimports_golden.go"), "\n")
" run our code
call go#fmt#run("goimports", actual_file, "test-fixtures/fmt/src/imports/goimports.go")
" this should now contain the formatted code
let actual = join(readfile(actual_file), "\n")
call assert_equal(expected, actual)
endfunc
" vim: sw=2 ts=2 et

@ -0,0 +1,628 @@
" guru.vim -- Vim integration for the Go guru.
" guru_cmd returns a dict that contains the command to execute guru. args
" is dict with following options:
" mode : guru mode, such as 'implements'
" format : output format, either 'plain' or 'json'
" needs_scope : if 1, adds the current package to the scope
" selected : if 1, means it's a range of selection, otherwise it picks up the
" offset under the cursor
" example output:
" {'cmd' : ['guru', '-json', 'implements', 'demo/demo.go:#66']}
function! s:guru_cmd(args) range abort
let mode = a:args.mode
let format = a:args.format
let needs_scope = a:args.needs_scope
let selected = a:args.selected
let result = {}
let pkg = go#package#ImportPath()
" this is important, check it!
if pkg == -1 && needs_scope
return {'err': "current directory is not inside of a valid GOPATH"}
endif
"return with a warning if the binary doesn't exist
let bin_path = go#path#CheckBinPath("guru")
if empty(bin_path)
return {'err': "bin path not found"}
endif
" start constructing the command
let cmd = [bin_path]
let filename = fnamemodify(expand("%"), ':p:gs?\\?/?')
if &modified
let result.stdin_content = go#util#archive()
call add(cmd, "-modified")
endif
" enable outputting in json format
if format == "json"
call add(cmd, "-json")
endif
" check for any tags
if exists('g:go_build_tags')
let tags = get(g:, 'go_build_tags')
call extend(cmd, ["-tags", tags])
let result.tags = tags
endif
" some modes require scope to be defined (such as callers). For these we
" choose a sensible setting, which is using the current file's package
let scopes = []
if needs_scope
let scopes = [pkg]
endif
" check for any user defined scope setting. users can define the scope,
" in package pattern form. examples:
" golang.org/x/tools/cmd/guru # a single package
" golang.org/x/tools/... # all packages beneath dir
" ... # the entire workspace.
if exists('g:go_guru_scope')
" check that the setting is of type list
if type(get(g:, 'go_guru_scope')) != type([])
return {'err' : "go_guru_scope should of type list"}
endif
let scopes = get(g:, 'go_guru_scope')
endif
" now add the scope to our command if there is any
if !empty(scopes)
" strip trailing slashes for each path in scoped. bug:
" https://github.com/golang/go/issues/14584
let scopes = go#util#StripTrailingSlash(scopes)
" create shell-safe entries of the list
if !go#util#has_job() | let scopes = go#util#Shelllist(scopes) | endif
" guru expect a comma-separated list of patterns, construct it
let l:scope = join(scopes, ",")
let result.scope = l:scope
call extend(cmd, ["-scope", l:scope])
endif
let pos = printf("#%s", go#util#OffsetCursor())
if selected != -1
" means we have a range, get it
let pos1 = go#util#Offset(line("'<"), col("'<"))
let pos2 = go#util#Offset(line("'>"), col("'>"))
let pos = printf("#%s,#%s", pos1, pos2)
endif
let filename .= ':'.pos
call extend(cmd, [mode, filename])
let result.cmd = cmd
return result
endfunction
" sync_guru runs guru in sync mode with the given arguments
function! s:sync_guru(args) abort
let result = s:guru_cmd(a:args)
if has_key(result, 'err')
call go#util#EchoError(result.err)
return -1
endif
if !has_key(a:args, 'disable_progress')
if a:args.needs_scope
call go#util#EchoProgress("analysing with scope ". result.scope .
\ " (see ':help go-guru-scope' if this doesn't work)...")
elseif a:args.mode !=# 'what'
" the query might take time, let us give some feedback
call go#util#EchoProgress("analysing ...")
endif
endif
" run, forrest run!!!
let command = join(result.cmd, " ")
if has_key(result, 'stdin_content')
let out = go#util#System(command, result.stdin_content)
else
let out = go#util#System(command)
endif
if has_key(a:args, 'custom_parse')
call a:args.custom_parse(go#util#ShellError(), out)
else
call s:parse_guru_output(go#util#ShellError(), out, a:args.mode)
endif
return out
endfunc
" async_guru runs guru in async mode with the given arguments
function! s:async_guru(args) abort
let result = s:guru_cmd(a:args)
if has_key(result, 'err')
call go#util#EchoError(result.err)
return
endif
let status_dir = expand('%:p:h')
let statusline_type = printf("%s", a:args.mode)
if !has_key(a:args, 'disable_progress')
if a:args.needs_scope
call go#util#EchoProgress("analysing with scope " . result.scope .
\ " (see ':help go-guru-scope' if this doesn't work)...")
endif
endif
let messages = []
function! s:callback(chan, msg) closure
call add(messages, a:msg)
endfunction
let status = {}
let exitval = 0
function! s:exit_cb(job, exitval) closure
let status = {
\ 'desc': 'last status',
\ 'type': statusline_type,
\ 'state': "finished",
\ }
if a:exitval
let exitval = a:exitval
let status.state = "failed"
endif
call go#statusline#Update(status_dir, status)
endfunction
function! s:close_cb(ch) closure
let out = join(messages, "\n")
if has_key(a:args, 'custom_parse')
call a:args.custom_parse(exitval, out)
else
call s:parse_guru_output(exitval, out, a:args.mode)
endif
endfunction
let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ 'close_cb': funcref("s:close_cb"),
\ }
if has_key(result, 'stdin_content')
let l:tmpname = tempname()
call writefile(split(result.stdin_content, "\n"), l:tmpname, "b")
let l:start_options.in_io = "file"
let l:start_options.in_name = l:tmpname
endif
call go#statusline#Update(status_dir, {
\ 'desc': "current status",
\ 'type': statusline_type,
\ 'state': "analysing",
\})
return job_start(result.cmd, start_options)
endfunc
" run_guru runs the given guru argument
function! s:run_guru(args) abort
if go#util#has_job()
let res = s:async_guru(a:args)
else
let res = s:sync_guru(a:args)
endif
return res
endfunction
" Show 'implements' relation for selected package
function! go#guru#Implements(selected) abort
let args = {
\ 'mode': 'implements',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Report the possible constants, global variables, and concrete types that may
" appear in a value of type error
function! go#guru#Whicherrs(selected) abort
let args = {
\ 'mode': 'whicherrs',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
" TODO(arslan): handle empty case for both sync/async
" if empty(out.out)
" call go#util#EchoSuccess("no error variables found. Try to change the scope with :GoGuruScope")
" return
" endif
call s:run_guru(args)
endfunction
" Describe selected syntax: definition, methods, etc
function! go#guru#Describe(selected) abort
let args = {
\ 'mode': 'describe',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
function! go#guru#DescribeInfo() abort
" json_encode() and friends are introduced with this patch (7.4.1304)
" vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ
" nvim: https://github.com/neovim/neovim/pull/4131
if !exists("*json_decode")
call go#util#EchoError("requires 'json_decode'. Update your Vim/Neovim version.")
return
endif
function! s:info(exit_val, output)
if a:exit_val != 0
return
endif
if a:output[0] !=# '{'
return
endif
if empty(a:output) || type(a:output) != type("")
return
endif
let result = json_decode(a:output)
if type(result) != type({})
call go#util#EchoError(printf("malformed output from guru: %s", a:output))
return
endif
if !has_key(result, 'detail')
" if there is no detail check if there is a description and print it
if has_key(result, "desc")
call go#util#EchoInfo(result["desc"])
return
endif
call go#util#EchoError("detail key is missing. Please open a bug report on vim-go repo.")
return
endif
let detail = result['detail']
let info = ""
" guru gives different information based on the detail mode. Let try to
" extract the most useful information
if detail == "value"
if !has_key(result, 'value')
call go#util#EchoError("value key is missing. Please open a bug report on vim-go repo.")
return
endif
let val = result["value"]
if !has_key(val, 'type')
call go#util#EchoError("type key is missing (value.type). Please open a bug report on vim-go repo.")
return
endif
let info = val["type"]
elseif detail == "type"
if !has_key(result, 'type')
call go#util#EchoError("type key is missing. Please open a bug report on vim-go repo.")
return
endif
let type = result["type"]
if !has_key(type, 'type')
call go#util#EchoError("type key is missing (type.type). Please open a bug report on vim-go repo.")
return
endif
let info = type["type"]
elseif detail == "package"
if !has_key(result, 'package')
call go#util#EchoError("package key is missing. Please open a bug report on vim-go repo.")
return
endif
let package = result["package"]
if !has_key(package, 'path')
call go#util#EchoError("path key is missing (package.path). Please open a bug report on vim-go repo.")
return
endif
let info = printf("package %s", package["path"])
elseif detail == "unknown"
let info = result["desc"]
else
call go#util#EchoError(printf("unknown detail mode found '%s'. Please open a bug report on vim-go repo", detail))
return
endif
call go#util#EchoInfo(info)
endfunction
let args = {
\ 'mode': 'describe',
\ 'format': 'json',
\ 'selected': -1,
\ 'needs_scope': 0,
\ 'custom_parse': function('s:info'),
\ 'disable_progress': 1,
\ }
call s:run_guru(args)
endfunction
" Show possible targets of selected function call
function! go#guru#Callees(selected) abort
let args = {
\ 'mode': 'callees',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Show possible callers of selected function
function! go#guru#Callers(selected) abort
let args = {
\ 'mode': 'callers',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Show path from callgraph root to selected function
function! go#guru#Callstack(selected) abort
let args = {
\ 'mode': 'callstack',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Show free variables of selection
function! go#guru#Freevars(selected) abort
" Freevars requires a selection
if a:selected == -1
call go#util#EchoError("GoFreevars requires a selection (range) of code")
return
endif
let args = {
\ 'mode': 'freevars',
\ 'format': 'plain',
\ 'selected': 1,
\ 'needs_scope': 0,
\ }
call s:run_guru(args)
endfunction
" Show send/receive corresponding to selected channel op
function! go#guru#ChannelPeers(selected) abort
let args = {
\ 'mode': 'peers',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 1,
\ }
call s:run_guru(args)
endfunction
" Show all refs to entity denoted by selected identifier
function! go#guru#Referrers(selected) abort
let args = {
\ 'mode': 'referrers',
\ 'format': 'plain',
\ 'selected': a:selected,
\ 'needs_scope': 0,
\ }
call s:run_guru(args)
endfunction
function! go#guru#SameIdsTimer() abort
call timer_start(200, function('go#guru#SameIds'), {'repeat': -1})
endfunction
function! go#guru#SameIds() abort
" we use matchaddpos() which was introduce with 7.4.330, be sure we have
" it: http://ftp.vim.org/vim/patches/7.4/7.4.330
if !exists("*matchaddpos")
call go#util#EchoError("GoSameIds requires 'matchaddpos'. Update your Vim/Neovim version.")
return
endif
" json_encode() and friends are introduced with this patch (7.4.1304)
" vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ
" nvim: https://github.com/neovim/neovim/pull/4131
if !exists("*json_decode")
call go#util#EchoError("GoSameIds requires 'json_decode'. Update your Vim/Neovim version.")
return
endif
let args = {
\ 'mode': 'what',
\ 'format': 'json',
\ 'selected': -1,
\ 'needs_scope': 0,
\ 'custom_parse': function('s:same_ids_highlight'),
\ }
call s:run_guru(args)
endfunction
function! s:same_ids_highlight(exit_val, output) abort
call go#guru#ClearSameIds() " run after calling guru to reduce flicker.
if a:output[0] !=# '{'
if !get(g:, 'go_auto_sameids', 0)
call go#util#EchoError(a:output)
endif
return
endif
let result = json_decode(a:output)
if type(result) != type({}) && !get(g:, 'go_auto_sameids', 0)
call go#util#EchoError("malformed output from guru")
return
endif
if !has_key(result, 'sameids')
if !get(g:, 'go_auto_sameids', 0)
call go#util#EchoError("no same_ids founds for the given identifier")
endif
return
endif
let poslen = 0
for enclosing in result['enclosing']
if enclosing['desc'] == 'identifier'
let poslen = enclosing['end'] - enclosing['start']
break
endif
endfor
" return when there's no identifier to highlight.
if poslen == 0
return
endif
let same_ids = result['sameids']
" highlight the lines
for item in same_ids
let pos = split(item, ':')
call matchaddpos('goSameId', [[str2nr(pos[-2]), str2nr(pos[-1]), str2nr(poslen)]])
endfor
if get(g:, "go_auto_sameids", 0)
" re-apply SameIds at the current cursor position at the time the buffer
" is redisplayed: e.g. :edit, :GoRename, etc.
augroup vim-go-sameids
autocmd!
autocmd BufWinEnter <buffer> nested call go#guru#SameIds()
augroup end
endif
endfunction
" ClearSameIds returns 0 when it removes goSameId groups and non-zero if no
" goSameId groups are found.
function! go#guru#ClearSameIds() abort
let l:cleared = 0
let m = getmatches()
for item in m
if item['group'] == 'goSameId'
call matchdelete(item['id'])
let l:cleared = 1
endif
endfor
if !l:cleared
return 1
endif
" remove the autocmds we defined
augroup vim-go-sameids
autocmd!
augroup end
return 0
endfunction
function! go#guru#ToggleSameIds() abort
if go#guru#ClearSameIds() != 0
call go#guru#SameIds()
endif
endfunction
function! go#guru#AutoToogleSameIds() abort
if get(g:, "go_auto_sameids", 0)
call go#util#EchoProgress("sameids auto highlighting disabled")
call go#guru#ClearSameIds()
let g:go_auto_sameids = 0
return
endif
call go#util#EchoSuccess("sameids auto highlighting enabled")
let g:go_auto_sameids = 1
endfunction
""""""""""""""""""""""""""""""""""""""""
"" HELPER FUNCTIONS
""""""""""""""""""""""""""""""""""""""""
" This uses Vim's errorformat to parse the output from Guru's 'plain output
" and put it into location list. I believe using errorformat is much more
" easier to use. If we need more power we can always switch back to parse it
" via regex. Match two possible styles of errorformats:
"
" 'file:line.col-line2.col2: message'
" 'file:line:col: message'
"
" We discard line2 and col2 for the first errorformat, because it's not
" useful and location only has the ability to show one line and column
" number
function! s:parse_guru_output(exit_val, output, title) abort
if a:exit_val
call go#util#EchoError(a:output)
return
endif
let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m"
let l:listtype = go#list#Type("_guru")
call go#list#ParseFormat(l:listtype, errformat, a:output, a:title)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
endfun
function! go#guru#Scope(...) abort
if a:0
if a:0 == 1 && a:1 == '""'
unlet g:go_guru_scope
call go#util#EchoSuccess("guru scope is cleared")
else
let g:go_guru_scope = a:000
call go#util#EchoSuccess("guru scope changed to: ". join(a:000, ","))
endif
return
endif
if !exists('g:go_guru_scope')
call go#util#EchoError("guru scope is not set")
else
call go#util#EchoSuccess("current guru scope: ". join(g:go_guru_scope, ","))
endif
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,140 @@
function! go#impl#Impl(...) abort
let recv = ""
let iface = ""
let interactive = 0
let pos = getpos('.')
if a:0 is 0
" Interactive mode if user didn't pass any arguments.
let recv = s:getReceiver()
let iface = input("vim-go: generating method stubs for interface: ")
redraw!
if empty(iface)
call go#util#EchoError('usage: interface type is not provided')
return
endif
elseif a:0 is 1
" we assume the user only passed the interface type,
" i.e: ':GoImpl io.Writer'
let recv = s:getReceiver()
let iface = a:1
elseif a:0 > 2
" user passed receiver and interface type both,
" i.e: 'GoImpl f *Foo io.Writer'
let recv = join(a:000[:-2], ' ')
let iface = a:000[-1]
else
call go#util#EchoError('usage: GoImpl {receiver} {interface}')
return
endif
" Make sure we put the generated code *after* the struct.
if getline(".") =~ "struct "
normal! $%
endif
try
let dirname = fnameescape(expand('%:p:h'))
let [result, err] = go#util#Exec(['impl', '-dir', dirname, recv, iface])
let result = substitute(result, "\n*$", "", "")
if err
call go#util#EchoError(result)
return
endif
if result is# ''
return
end
put =''
silent put =result
finally
call setpos('.', pos)
endtry
endfunction
function! s:getReceiver()
let receiveType = expand("<cword>")
if receiveType == "type"
normal! w
let receiveType = expand("<cword>")
elseif receiveType == "struct"
normal! ge
let receiveType = expand("<cword>")
endif
return printf("%s *%s", tolower(receiveType)[0], receiveType)
endfunction
if exists('*uniq')
function! s:uniq(list)
return uniq(a:list)
endfunction
else
" Note: Believe that the list is sorted
function! s:uniq(list)
let i = len(a:list) - 1
while 0 < i
if a:list[i-1] ==# a:list[i]
call remove(a:list, i)
let i -= 2
else
let i -= 1
endif
endwhile
return a:list
endfunction
endif
function! s:root_dirs() abort
let dirs = []
let root = go#util#env("goroot")
if root !=# '' && isdirectory(root)
call add(dirs, root)
endif
let paths = map(split(go#util#env("gopath"), go#util#PathListSep()), "substitute(v:val, '\\\\', '/', 'g')")
if !empty(filter(paths, 'isdirectory(v:val)'))
call extend(dirs, paths)
endif
return dirs
endfunction
function! s:go_packages(dirs) abort
let pkgs = []
for d in a:dirs
let pkg_root = expand(d . '/pkg/' . go#util#osarch())
call extend(pkgs, split(globpath(pkg_root, '**/*.a', 1), "\n"))
endfor
return map(pkgs, "fnamemodify(v:val, ':t:r')")
endfunction
function! s:interface_list(pkg) abort
let [contents, err] = go#util#Exec(['go', 'doc', a:pkg])
if err
return []
endif
let contents = split(contents, "\n")
call filter(contents, 'v:val =~# ''^type\s\+\h\w*\s\+interface''')
return map(contents, 'a:pkg . "." . matchstr(v:val, ''^type\s\+\zs\h\w*\ze\s\+interface'')')
endfunction
" Complete package and interface for {interface}
function! go#impl#Complete(arglead, cmdline, cursorpos) abort
let words = split(a:cmdline, '\s\+', 1)
if words[-1] ==# ''
return s:uniq(sort(s:go_packages(s:root_dirs())))
elseif words[-1] =~# '^\h\w*$'
return s:uniq(sort(filter(s:go_packages(s:root_dirs()), 'stridx(v:val, words[-1]) == 0')))
elseif words[-1] =~# '^\h\w*\.\%(\h\w*\)\=$'
let [pkg, interface] = split(words[-1], '\.', 1)
echomsg pkg
return s:uniq(sort(filter(s:interface_list(pkg), 'v:val =~? words[-1]')))
else
return []
endif
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,37 @@
func! Test_impl() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ '',
\ ''])
call go#impl#Impl('r', 'reader', 'io.Reader')
call gotest#assert_buffer(1, [
\ 'func (r reader) Read(p []byte) (n int, err error) {',
\ ' panic("not implemented")',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_impl_get() abort
try
let l:tmp = gotest#write_file('a/a.go', [
\ 'package a',
\ '',
\ 'type reader struct {}'])
call go#impl#Impl('io.Reader')
call gotest#assert_buffer(0, [
\ 'package a',
\ '',
\ 'type reader struct {}',
\ '',
\ 'func (r *reader) Read(p []byte) (n int, err error) {',
\ ' panic("not implemented")',
\ '}'])
finally
call delete(l:tmp, 'rf')
endtry
endfunc

@ -4,7 +4,7 @@
" "
" Check out the docs for more information at /doc/vim-go.txt " Check out the docs for more information at /doc/vim-go.txt
" "
function! go#import#SwitchImport(enabled, localname, path, bang) function! go#import#SwitchImport(enabled, localname, path, bang) abort
let view = winsaveview() let view = winsaveview()
let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '') let path = substitute(a:path, '^\s*\(.\{-}\)\s*$', '\1', '')
@ -27,8 +27,8 @@ function! go#import#SwitchImport(enabled, localname, path, bang)
endif endif
if a:bang == "!" if a:bang == "!"
let out = system("go get -u -v ".shellescape(path)) let out = go#util#System("go get -u -v ".shellescape(path))
if v:shell_error if go#util#ShellError() != 0
call s:Error("Can't find import: " . path . ":" . out) call s:Error("Can't find import: " . path . ":" . out)
endif endif
endif endif
@ -205,9 +205,9 @@ function! go#import#SwitchImport(enabled, localname, path, bang)
endfunction endfunction
function! s:Error(s) function! s:Error(s) abort
echohl Error | echo a:s | echohl None echohl Error | echo a:s | echohl None
endfunction endfunction
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -0,0 +1,102 @@
" Spawn returns callbacks to be used with job_start. It's abstracted to be
" used with various go command, such as build, test, install, etc.. This avoid
" us to write the same callback over and over for some commands. It's fully
" customizable so each command can change it to it's own logic.
function go#job#Spawn(args)
let cbs = {
\ 'winnr': winnr(),
\ 'dir': getcwd(),
\ 'jobdir': fnameescape(expand("%:p:h")),
\ 'messages': [],
\ 'args': a:args.cmd,
\ 'bang': 0,
\ 'for': "_job",
\ }
if has_key(a:args, 'bang')
let cbs.bang = a:args.bang
endif
if has_key(a:args, 'for')
let cbs.for = a:args.for
endif
" add final callback to be called if async job is finished
" The signature should be in form: func(job, exit_status, messages)
if has_key(a:args, 'custom_cb')
let cbs.custom_cb = a:args.custom_cb
endif
if has_key(a:args, 'error_info_cb')
let cbs.error_info_cb = a:args.error_info_cb
endif
function cbs.callback(chan, msg) dict
call add(self.messages, a:msg)
endfunction
function cbs.exit_cb(job, exitval) dict
if has_key(self, 'error_info_cb')
call self.error_info_cb(a:job, a:exitval, self.messages)
endif
if get(g:, 'go_echo_command_info', 1)
if a:exitval == 0
call go#util#EchoSuccess("SUCCESS")
else
call go#util#EchoError("FAILED")
endif
endif
if has_key(self, 'custom_cb')
call self.custom_cb(a:job, a:exitval, self.messages)
endif
let l:listtype = go#list#Type(self.for)
if a:exitval == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif
call self.show_errors(l:listtype)
endfunction
function cbs.show_errors(listtype) dict
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
try
execute cd self.jobdir
let errors = go#tool#ParseErrors(self.messages)
let errors = go#tool#FilterValids(errors)
finally
execute cd . fnameescape(self.dir)
endtry
if !len(errors)
" failed to parse errors, output the original content
call go#util#EchoError(self.messages + [self.dir])
return
endif
if self.winnr == winnr()
call go#list#Populate(a:listtype, errors, join(self.args))
call go#list#Window(a:listtype, len(errors))
if !empty(errors) && !self.bang
call go#list#JumpToFirst(a:listtype)
endif
endif
endfunction
" override callback handler if user provided it
if has_key(a:args, 'callback')
let cbs.callback = a:args.callback
endif
" override exit callback handler if user provided it
if has_key(a:args, 'exit_cb')
let cbs.exit_cb = a:args.exit_cb
endif
return cbs
endfunction
" vim: sw=2 ts=2 et

@ -2,62 +2,68 @@
" internal function s:spawn " internal function s:spawn
let s:jobs = {} let s:jobs = {}
" s:handlers is a global event handlers for all jobs started with Spawn() or
" with the internal function s:spawn
let s:handlers = {}
" Spawn is a wrapper around s:spawn. It can be executed by other files and " Spawn is a wrapper around s:spawn. It can be executed by other files and
" scripts if needed. Desc defines the description for printing the status " scripts if needed. Desc defines the description for printing the status
" during the job execution (useful for statusline integration). " during the job execution (useful for statusline integration).
function! go#jobcontrol#Spawn(bang, desc, args) function! go#jobcontrol#Spawn(bang, desc, for, args) abort
" autowrite is not enabled for jobs " autowrite is not enabled for jobs
call go#cmd#autowrite() call go#cmd#autowrite()
let job = s:spawn(a:bang, a:desc, a:args) let job = s:spawn(a:bang, a:desc, a:for, a:args)
return job.id return job.id
endfunction endfunction
" Statusline returns the current status of the job " AddHandler adds a on_exit callback handler and returns the id.
function! go#jobcontrol#Statusline() abort function! go#jobcontrol#AddHandler(handler) abort
if empty(s:jobs) let i = len(s:handlers)
return '' while has_key(s:handlers, string(i))
endif let i += 1
break
let import_path = go#package#ImportPath(expand('%:p:h')) endwhile
let s:handlers[string(i)] = a:handler
for job in values(s:jobs) return string(i)
if job.importpath != import_path endfunction
continue
endif
if job.state == "SUCCESS" " RemoveHandler removes a callback handler by id.
return '' function! go#jobcontrol#RemoveHandler(id) abort
endif unlet s:handlers[a:id]
endfunction
return printf("%s ... [%s]", job.desc, job.state) " spawn spawns a go subcommand with the name and arguments with jobstart. Once a
endfor " job is started a reference will be stored inside s:jobs. The job is started
" inside the current files folder.
function! s:spawn(bang, desc, for, args) abort
let status_type = a:args[0]
let status_dir = expand('%:p:h')
let started_at = reltime()
return '' call go#statusline#Update(status_dir, {
endfunction \ 'desc': "current status",
\ 'type': status_type,
\ 'state': "started",
\})
" spawn spawns a go subcommand with the name and arguments with jobstart. Once
" a job is started a reference will be stored inside s:jobs. spawn changes the
" GOPATH when g:go_autodetect_gopath is enabled. The job is started inside the
" current files folder.
function! s:spawn(bang, desc, args)
let job = { let job = {
\ 'desc': a:desc, \ 'desc': a:desc,
\ 'bang': a:bang, \ 'bang': a:bang,
\ 'winnr': winnr(), \ 'winnr': winnr(),
\ 'importpath': go#package#ImportPath(expand('%:p:h')), \ 'importpath': go#package#ImportPath(),
\ 'state': "RUNNING", \ 'state': "RUNNING",
\ 'stderr' : [], \ 'stderr' : [],
\ 'stdout' : [], \ 'stdout' : [],
\ 'on_stdout': function('s:on_stdout'), \ 'on_stdout': function('s:on_stdout'),
\ 'on_stderr': function('s:on_stderr'), \ 'on_stderr': function('s:on_stderr'),
\ 'on_exit' : function('s:on_exit'), \ 'on_exit' : function('s:on_exit'),
\ 'status_type' : status_type,
\ 'status_dir' : status_dir,
\ 'started_at' : started_at,
\ 'for' : a:for,
\ } \ }
" modify GOPATH if needed
let old_gopath = $GOPATH
let $GOPATH = go#path#Detect()
" execute go build in the files directory " execute go build in the files directory
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
@ -83,9 +89,6 @@ function! s:spawn(bang, desc, args)
execute cd . fnameescape(dir) execute cd . fnameescape(dir)
" restore back GOPATH
let $GOPATH = old_gopath
return job return job
endfunction endfunction
@ -93,29 +96,57 @@ endfunction
" references and also displaying errors in the quickfix window collected by " references and also displaying errors in the quickfix window collected by
" on_stderr handler. If there are no errors and a quickfix window is open, " on_stderr handler. If there are no errors and a quickfix window is open,
" it'll be closed. " it'll be closed.
function! s:on_exit(job_id, exit_status) function! s:on_exit(job_id, exit_status, event) dict abort
let status = {
\ 'desc': 'last status',
\ 'type': self.status_type,
\ 'state': "success",
\ }
if a:exit_status
let status.state = "failed"
endif
let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(self.status_dir, status)
let std_combined = self.stderr + self.stdout let std_combined = self.stderr + self.stdout
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
execute cd self.dir
call s:callback_handlers_on_exit(s:jobs[a:job_id], a:exit_status, std_combined)
let l:listtype = go#list#Type(self.for)
if a:exit_status == 0 if a:exit_status == 0
call go#list#Clean(0) call go#list#Clean(l:listtype)
call go#list#Window(0) call go#list#Window(l:listtype)
let self.state = "SUCCESS" let self.state = "SUCCESS"
call go#util#EchoSuccess("SUCCESS")
if get(g:, 'go_echo_command_info', 1)
call go#util#EchoSuccess("[" . self.status_type . "] SUCCESS")
endif
execute cd . fnameescape(dir)
return return
endif endif
let self.state = "FAILED" let self.state = "FAILED"
call go#util#EchoError("FAILED")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' if get(g:, 'go_echo_command_info', 1)
let dir = getcwd() call go#util#EchoError("[" . self.status_type . "] FAILED")
try endif
execute cd self.dir
let errors = go#tool#ParseErrors(std_combined) let errors = go#tool#ParseErrors(std_combined)
let errors = go#tool#FilterValids(errors) let errors = go#tool#FilterValids(errors)
finally
execute cd . fnameescape(dir) execute cd . fnameescape(dir)
endtry
if !len(errors) if !len(errors)
" failed to parse errors, output the original content " failed to parse errors, output the original content
@ -125,8 +156,7 @@ function! s:on_exit(job_id, exit_status)
" if we are still in the same windows show the list " if we are still in the same windows show the list
if self.winnr == winnr() if self.winnr == winnr()
let l:listtype = "locationlist" call go#list#Populate(l:listtype, errors, self.desc)
call go#list#Populate(l:listtype, errors)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !self.bang if !empty(errors) && !self.bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
@ -134,46 +164,27 @@ function! s:on_exit(job_id, exit_status)
endif endif
endfunction endfunction
" callback_handlers_on_exit runs all handlers for job on exit event.
function! s:callback_handlers_on_exit(job, exit_status, data) abort
if empty(s:handlers)
return
endif
for s:handler in values(s:handlers)
call s:handler(a:job, a:exit_status, a:data)
endfor
endfunction
" on_stdout is the stdout handler for jobstart(). It collects the output of " on_stdout is the stdout handler for jobstart(). It collects the output of
" stderr and stores them to the jobs internal stdout list. " stderr and stores them to the jobs internal stdout list.
function! s:on_stdout(job_id, data) function! s:on_stdout(job_id, data, event) dict abort
call extend(self.stdout, a:data) call extend(self.stdout, a:data)
endfunction endfunction
" on_stderr is the stderr handler for jobstart(). It collects the output of " on_stderr is the stderr handler for jobstart(). It collects the output of
" stderr and stores them to the jobs internal stderr list. " stderr and stores them to the jobs internal stderr list.
function! s:on_stderr(job_id, data) function! s:on_stderr(job_id, data, event) dict abort
call extend(self.stderr, a:data) call extend(self.stderr, a:data)
endfunction endfunction
" abort_all aborts all current jobs created with s:spawn() " vim: sw=2 ts=2 et
function! s:abort_all()
if empty(s:jobs)
return
endif
for id in keys(s:jobs)
if id > 0
silent! call jobstop(id)
endif
endfor
let s:jobs = {}
endfunction
" abort aborts the job with the given name, where name is the first argument
" passed to s:spawn()
function! s:abort(path)
if empty(s:jobs)
return
endif
for job in values(s:jobs)
if job.importpath == path && job.id > 0
silent! call jobstop(job.id)
unlet s:jobs['job.id']
endif
endfor
endfunction
" vim:ts=2:sw=2:et

@ -0,0 +1,56 @@
function! go#keyify#Keyify()
let bin_path = go#path#CheckBinPath("keyify")
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
if empty(bin_path) || !exists('*json_decode')
return
endif
" Get result of command as json, that contains `start`, `end` and `replacement`
let command = printf("%s -json %s:#%s", go#util#Shellescape(bin_path),
\ go#util#Shellescape(fname), go#util#OffsetCursor())
let output = go#util#System(command)
silent! let result = json_decode(output)
" We want to output the error message in case the result isn't a JSON
if type(result) != type({})
call go#util#EchoError(s:chomp(output))
return
endif
" Because keyify returns the byte before the region we want, we goto the
" byte after that
execute "goto" result.start + 1
let start = getpos('.')
execute "goto" result.end
let end = getpos('.')
let vis_start = getpos("'<")
let vis_end = getpos("'>")
" Replace contents between start and end with `replacement`
call setpos("'<", start)
call setpos("'>", end)
let select = 'gv'
" Make sure the visual mode is 'v', to avoid some bugs
normal! gv
if mode() !=# 'v'
let select .= 'v'
endif
silent! execute "normal!" select."\"=result.replacement\<cr>p"
" Replacement text isn't aligned, so it needs fix
normal! '<v'>=
call setpos("'<", vis_start)
call setpos("'>", vis_end)
endfunction
function! s:chomp(string)
return substitute(a:string, '\n\+$', '', '')
endfunction
" vim: sw=2 ts=2 et

@ -10,8 +10,8 @@ if !exists("g:go_metalinter_enabled")
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck'] let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
endif endif
if !exists("g:go_metalinter_deadline") if !exists("g:go_metalinter_excludes")
let g:go_metalinter_deadline = "5s" let g:go_metalinter_excludes = []
endif endif
if !exists("g:go_golint_bin") if !exists("g:go_golint_bin")
@ -24,47 +24,80 @@ endif
function! go#lint#Gometa(autosave, ...) abort function! go#lint#Gometa(autosave, ...) abort
if a:0 == 0 if a:0 == 0
let goargs = expand('%:p:h') let goargs = shellescape(expand('%:p:h'))
else else
let goargs = go#util#Shelljoin(a:000) let goargs = go#util#Shelljoin(a:000)
endif endif
let meta_command = "gometalinter --disable-all"
if a:autosave || empty(g:go_metalinter_command)
let bin_path = go#path#CheckBinPath("gometalinter") let bin_path = go#path#CheckBinPath("gometalinter")
if empty(bin_path) if empty(bin_path)
return return
endif endif
if a:autosave let cmd = [bin_path]
" include only messages for the active buffer let cmd += ["--disable-all"]
let meta_command .= " --include='^" . expand('%:p') . ".*$'"
endif
if a:autosave || empty(g:go_metalinter_command)
" linters " linters
let linters = a:autosave ? g:go_metalinter_autosave_enabled : g:go_metalinter_enabled let linters = a:autosave ? g:go_metalinter_autosave_enabled : g:go_metalinter_enabled
for linter in linters for linter in linters
let meta_command .= " --enable=".linter let cmd += ["--enable=".linter]
endfor
for exclude in g:go_metalinter_excludes
let cmd += ["--exclude=".exclude]
endfor endfor
" deadline " gometalinter has a --tests flag to tell its linters whether to run
let meta_command .= " --deadline=" . g:go_metalinter_deadline " against tests. While not all of its linters respect this flag, for those
" that do, it means if we don't pass --tests, the linter won't run against
" test files. One example of a linter that will not run against tests if
" we do not specify this flag is errcheck.
let cmd += ["--tests"]
" path " path
let meta_command .= " " . goargs let cmd += [expand('%:p:h')]
else else
" the user wants something else, let us use it. " the user wants something else, let us use it.
let meta_command = g:go_metalinter_command let cmd += split(g:go_metalinter_command, " ")
endif
" gometalinter has a default deadline of 5 seconds.
"
" For async mode (s:lint_job), we want to override the default deadline only
" if we have a deadline configured.
"
" For sync mode (go#util#System), always explicitly pass the 5 seconds
" deadline if there is no other deadline configured. If a deadline is
" configured, then use it.
" Call gometalinter asynchronously.
if go#util#has_job() && has('lambda')
let deadline = get(g:, 'go_metalinter_deadline', 0)
if deadline != 0
let cmd += ["--deadline=" . deadline]
endif
call s:lint_job({'cmd': cmd})
return
endif
" We're calling gometalinter synchronously.
let cmd += ["--deadline=" . get(g:, 'go_metalinter_deadline', "5s")]
if a:autosave
" include only messages for the active buffer
let cmd += ["--include='^" . expand('%:p') . ".*$'"]
endif endif
" comment out the following two lines for debugging
" echo meta_command
" return
let out = go#tool#ExecuteInDir(meta_command) let meta_command = join(cmd, " ")
let out = go#util#System(meta_command)
let l:listtype = "quickfix" let l:listtype = go#list#Type("GoMetaLinter")
if v:shell_error == 0 if go#util#ShellError() == 0
redraw | echo redraw | echo
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype) call go#list#Window(l:listtype)
@ -77,7 +110,7 @@ function! go#lint#Gometa(autosave, ...) abort
let errformat = "%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m" let errformat = "%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m"
" Parse and populate our location list " Parse and populate our location list
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n")) call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter')
let errors = go#list#Get(l:listtype) let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
@ -95,20 +128,20 @@ function! go#lint#Golint(...) abort
if empty(bin_path) if empty(bin_path)
return return
endif endif
let bin_path = go#util#Shellescape(bin_path)
if a:0 == 0 if a:0 == 0
let goargs = shellescape(expand('%')) let out = go#util#System(bin_path . " " . go#util#Shellescape(go#package#ImportPath()))
else else
let goargs = go#util#Shelljoin(a:000) let out = go#util#System(bin_path . " " . go#util#Shelljoin(a:000))
endif endif
let out = system(bin_path . " " . goargs)
if empty(out) if empty(out)
echon "vim-go: " | echohl Function | echon "[lint] PASS" | echohl None echon "vim-go: " | echohl Function | echon "[lint] PASS" | echohl None
return return
endif endif
let l:listtype = "quickfix" let l:listtype = go#list#Type("GoLint")
call go#list#Parse(l:listtype, out) call go#list#Parse(l:listtype, out)
let errors = go#list#Get(l:listtype) let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
@ -117,19 +150,19 @@ endfunction
" Vet calls 'go vet' on the current directory. Any warnings are populated in " Vet calls 'go vet' on the current directory. Any warnings are populated in
" the location list " the location list
function! go#lint#Vet(bang, ...) function! go#lint#Vet(bang, ...) abort
call go#cmd#autowrite() call go#cmd#autowrite()
echon "vim-go: " | echohl Identifier | echon "calling vet..." | echohl None echon "vim-go: " | echohl Identifier | echon "calling vet..." | echohl None
if a:0 == 0 if a:0 == 0
let out = go#tool#ExecuteInDir('go vet') let out = go#util#System('go vet ' . go#util#Shellescape(go#package#ImportPath()))
else else
let out = go#tool#ExecuteInDir('go tool vet ' . go#util#Shelljoin(a:000)) let out = go#util#System('go tool vet ' . go#util#Shelljoin(a:000))
endif endif
let l:listtype = "quickfix" let l:listtype = go#list#Type("GoVet")
if v:shell_error if go#util#ShellError() != 0
let errors = go#tool#ParseErrors(split(out, '\n')) let errors = go#tool#ParseErrors(split(out, '\n'))
call go#list#Populate(l:listtype, errors) call go#list#Populate(l:listtype, errors, 'Vet')
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
@ -146,13 +179,13 @@ endfunction
" the location list " the location list
function! go#lint#Errcheck(...) abort function! go#lint#Errcheck(...) abort
if a:0 == 0 if a:0 == 0
let goargs = go#package#ImportPath(expand('%:p:h')) let import_path = go#package#ImportPath()
if goargs == -1 if import_path == -1
echohl Error | echomsg "vim-go: package is not inside GOPATH src" | echohl None echohl Error | echomsg "vim-go: package is not inside GOPATH src" | echohl None
return return
endif endif
else else
let goargs = go#util#Shelljoin(a:000) let import_path = go#util#Shelljoin(a:000)
endif endif
let bin_path = go#path#CheckBinPath(g:go_errcheck_bin) let bin_path = go#path#CheckBinPath(g:go_errcheck_bin)
@ -163,18 +196,17 @@ function! go#lint#Errcheck(...) abort
echon "vim-go: " | echohl Identifier | echon "errcheck analysing ..." | echohl None echon "vim-go: " | echohl Identifier | echon "errcheck analysing ..." | echohl None
redraw redraw
let command = bin_path . ' -abspath ' . goargs let command = go#util#Shellescape(bin_path) . ' -abspath ' . import_path
let out = go#tool#ExecuteInDir(command) let out = go#tool#ExecuteInDir(command)
let l:listtype = "quickfix" let l:listtype = go#list#Type("GoErrCheck")
if v:shell_error if go#util#ShellError() != 0
let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m" let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m"
" Parse and populate our location list " Parse and populate our location list
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n")) call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'Errcheck')
let errors = go#list#Get(l:listtype) let errors = go#list#Get(l:listtype)
if empty(errors) if empty(errors)
echohl Error | echomsg "GoErrCheck returned error" | echohl None echohl Error | echomsg "GoErrCheck returned error" | echohl None
echo out echo out
@ -182,7 +214,8 @@ function! go#lint#Errcheck(...) abort
endif endif
if !empty(errors) if !empty(errors)
call go#list#Populate(l:listtype, errors) echohl Error | echomsg "GoErrCheck found errors" | echohl None
call go#list#Populate(l:listtype, errors, 'Errcheck')
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) if !empty(errors)
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
@ -196,4 +229,101 @@ function! go#lint#Errcheck(...) abort
endfunction endfunction
" vim:ts=4:sw=4:et function! go#lint#ToggleMetaLinterAutoSave() abort
if get(g:, "go_metalinter_autosave", 0)
let g:go_metalinter_autosave = 0
call go#util#EchoProgress("auto metalinter disabled")
return
end
let g:go_metalinter_autosave = 1
call go#util#EchoProgress("auto metalinter enabled")
endfunction
function s:lint_job(args)
let status_dir = expand('%:p:h')
let started_at = reltime()
call go#statusline#Update(status_dir, {
\ 'desc': "current status",
\ 'type': "gometalinter",
\ 'state': "analysing",
\})
" autowrite is not enabled for jobs
call go#cmd#autowrite()
let l:listtype = go#list#Type("GoMetaLinter")
let l:errformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
function! s:callback(chan, msg) closure
let old_errorformat = &errorformat
let &errorformat = l:errformat
try
if l:listtype == "locationlist"
lad a:msg
elseif l:listtype == "quickfix"
caddexpr a:msg
endif
finally
let &errorformat = old_errorformat
endtry
" TODO(jinleileiking): give a configure to jump or not
let l:winnr = winnr()
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
exe l:winnr . "wincmd w"
endfunction
function! s:exit_cb(job, exitval) closure
let status = {
\ 'desc': 'last status',
\ 'type': "gometaliner",
\ 'state': "finished",
\ }
if a:exitval
let status.state = "failed"
endif
let elapsed_time = reltimestr(reltime(started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(status_dir, status)
let errors = go#list#Get(l:listtype)
if empty(errors)
call go#list#Window(l:listtype, len(errors))
elseif has("patch-7.4.2200")
if l:listtype == 'quickfix'
call setqflist([], 'a', {'title': 'GoMetaLinter'})
else
call setloclist(0, [], 'a', {'title': 'GoMetaLinter'})
endif
endif
if get(g:, 'go_echo_command_info', 1)
call go#util#EchoSuccess("linting finished")
endif
endfunction
let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ }
call job_start(a:args.cmd, start_options)
call go#list#Clean(l:listtype)
if get(g:, 'go_echo_command_info', 1)
call go#util#EchoProgress("linting started ...")
endif
endfunction
" vim: sw=2 ts=2 et

@ -2,22 +2,30 @@ if !exists("g:go_list_type")
let g:go_list_type = "" let g:go_list_type = ""
endif endif
if !exists("g:go_list_type_commands")
let g:go_list_type_commands = {}
endif
" Window opens the list with the given height up to 10 lines maximum. " Window opens the list with the given height up to 10 lines maximum.
" Otherwise g:go_loclist_height is used. If no or zero height is given it " Otherwise g:go_loclist_height is used.
" closes the window "
function! go#list#Window(listtype, ...) " If no or zero height is given it closes the window by default.
let l:listtype = go#list#Type(a:listtype) " To prevent this, set g:go_list_autoclose = 0
function! go#list#Window(listtype, ...) abort
" we don't use lwindow to close the location list as we need also the " we don't use lwindow to close the location list as we need also the
" ability to resize the window. So, we are going to use lopen and lclose " ability to resize the window. So, we are going to use lopen and lclose
" for a better user experience. If the number of errors in a current " for a better user experience. If the number of errors in a current
" location list increases/decreases, cwindow will not resize when a new " location list increases/decreases, cwindow will not resize when a new
" updated height is passed. lopen in the other hand resizes the screen. " updated height is passed. lopen in the other hand resizes the screen.
if !a:0 || a:1 == 0 if !a:0 || a:1 == 0
if l:listtype == "locationlist" let autoclose_window = get(g:, 'go_list_autoclose', 1)
if autoclose_window
if a:listtype == "locationlist"
lclose lclose
else else
cclose cclose
endif endif
endif
return return
endif endif
@ -31,7 +39,7 @@ function! go#list#Window(listtype, ...)
endif endif
endif endif
if l:listtype == "locationlist" if a:listtype == "locationlist"
exe 'lopen ' . height exe 'lopen ' . height
else else
exe 'copen ' . height exe 'copen ' . height
@ -39,54 +47,55 @@ function! go#list#Window(listtype, ...)
endfunction endfunction
" Get returns the current list of items from the location list " Get returns the current items from the list
function! go#list#Get(listtype) function! go#list#Get(listtype) abort
let l:listtype = go#list#Type(a:listtype) if a:listtype == "locationlist"
if l:listtype == "locationlist"
return getloclist(0) return getloclist(0)
else else
return getqflist() return getqflist()
endif endif
endfunction endfunction
" Populate populate the location list with the given items " Populate populate the list with the given items
function! go#list#Populate(listtype, items) function! go#list#Populate(listtype, items, title) abort
let l:listtype = go#list#Type(a:listtype) if a:listtype == "locationlist"
if l:listtype == "locationlist"
call setloclist(0, a:items, 'r') 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
else else
call setqflist(a:items, 'r') call setqflist(a:items, 'r')
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
endif endif
endfunction endfunction
function! go#list#PopulateWin(winnr, items) " Parse parses the given items based on the specified errorformat and
call setloclist(a:winnr, a:items, 'r') " populates the list.
endfunction function! go#list#ParseFormat(listtype, errformat, items, title) abort
" Parse parses the given items based on the specified errorformat nad
" populates the location list.
function! go#list#ParseFormat(listtype, errformat, items)
let l:listtype = go#list#Type(a:listtype)
" backup users errorformat, will be restored once we are finished " backup users errorformat, will be restored once we are finished
let old_errorformat = &errorformat let old_errorformat = &errorformat
" parse and populate the location list " parse and populate the location list
let &errorformat = a:errformat let &errorformat = a:errformat
if l:listtype == "locationlist" try
if a:listtype == "locationlist"
lgetexpr a:items lgetexpr a:items
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
else else
cgetexpr a:items cgetexpr a:items
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
endif endif
finally
"restore back "restore back
let &errorformat = old_errorformat let &errorformat = old_errorformat
endtry
endfunction endfunction
" Parse parses the given items based on the global errorformat and " Parse parses the given items based on the global errorformat and
" populates the location list. " populates the list.
function! go#list#Parse(listtype, items) function! go#list#Parse(listtype, items) abort
let l:listtype = go#list#Type(a:listtype) if a:listtype == "locationlist"
if l:listtype == "locationlist"
lgetexpr a:items lgetexpr a:items
else else
cgetexpr a:items cgetexpr a:items
@ -94,9 +103,8 @@ function! go#list#Parse(listtype, items)
endfunction endfunction
" JumpToFirst jumps to the first item in the location list " JumpToFirst jumps to the first item in the location list
function! go#list#JumpToFirst(listtype) function! go#list#JumpToFirst(listtype) abort
let l:listtype = go#list#Type(a:listtype) if a:listtype == "locationlist"
if l:listtype == "locationlist"
ll 1 ll 1
else else
cc 1 cc 1
@ -104,23 +112,58 @@ function! go#list#JumpToFirst(listtype)
endfunction endfunction
" Clean cleans the location list " Clean cleans the location list
function! go#list#Clean(listtype) function! go#list#Clean(listtype) abort
let l:listtype = go#list#Type(a:listtype) if a:listtype == "locationlist"
if l:listtype == "locationlist"
lex [] lex []
else else
cex [] cex []
endif endif
endfunction endfunction
function! go#list#Type(listtype) function! s:listtype(listtype) abort
if g:go_list_type == "locationlist" if g:go_list_type == "locationlist"
return "locationlist" return "locationlist"
elseif g:go_list_type == "quickfix" elseif g:go_list_type == "quickfix"
return "quickfix" return "quickfix"
else endif
return a:listtype return a:listtype
endfunction
" s:default_list_type_commands is the defaults that will be used for each of
" the supported commands (see documentation for g:go_list_type_commands). When
" defining a default, quickfix should be used if the command operates on
" multiple files, while locationlist should be used if the command operates on a
" single file or buffer. Keys that begin with an underscore are not supported
" in g:go_list_type_commands.
let s:default_list_type_commands = {
\ "GoBuild": "quickfix",
\ "GoErrCheck": "quickfix",
\ "GoFmt": "locationlist",
\ "GoGenerate": "quickfix",
\ "GoInstall": "quickfix",
\ "GoLint": "quickfix",
\ "GoMetaLinter": "quickfix",
\ "GoModifyTags": "locationlist",
\ "GoRename": "quickfix",
\ "GoRun": "quickfix",
\ "GoTest": "quickfix",
\ "GoVet": "quickfix",
\ "_guru": "locationlist",
\ "_term": "locationlist",
\ "_job": "locationlist",
\ }
function! go#list#Type(for) abort
let l:listtype = s:listtype(get(s:default_list_type_commands, a:for))
if l:listtype == "0"
call go#util#EchoError(printf(
\ "unknown list type command value found ('%s'). Please open a bug report in the vim-go repo.",
\ a:for))
let l:listtype = "quickfix"
endif endif
return get(g:go_list_type_commands, a:for, l:listtype)
endfunction endfunction
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -1,233 +0,0 @@
" oracle.vim -- Vim integration for the Go oracle.
"
" Part of this plugin was taken directly from the oracle repo, however it's
" massively changed for a better integration into vim-go. Thanks Alan Donovan
" for the first iteration based on quickfix! - Fatih Arslan
"
if !exists("g:go_oracle_bin")
let g:go_oracle_bin = "oracle"
endif
" Parses (via regex) Oracle's 'plain' format output and puts them into a
" location list
func! s:loclist(output)
let llist = []
" Parse GNU-style 'file:line.col-line.col: message' format.
let mx = '^\(\a:[\\/][^:]\+\|[^:]\+\):\(\d\+\):\(\d\+\):\(.*\)$'
for line in split(a:output, "\n")
let ml = matchlist(line, mx)
" Ignore non-match lines or warnings
if ml == [] || ml[4] =~ '^ warning:'
continue
endif
let item = {
\ 'filename': ml[1],
\ 'text': ml[4],
\ 'lnum': ml[2],
\ 'col': ml[3],
\}
let bnr = bufnr(fnameescape(ml[1]))
if bnr != -1
let item['bufnr'] = bnr
endif
call add(llist, item)
endfor
call go#list#Populate("locationlist", llist)
call go#list#Window("locationlist", len(llist))
endfun
" This uses Vim's errorformat to parse the output from Oracle's 'plain output
" and put it into location list. I believe using errorformat is much more
" easier to use. If we need more power we can always switch back to parse it
" via regex.
func! s:loclistSecond(output)
" backup users errorformat, will be restored once we are finished
let old_errorformat = &errorformat
" match two possible styles of errorformats:
"
" 'file:line.col-line2.col2: message'
" 'file:line:col: message'
"
" We discard line2 and col2 for the first errorformat, because it's not
" useful and location only has the ability to show one line and column
" number
let errformat = "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m"
call go#list#ParseFormat("locationlist", errformat, split(a:output, "\n"))
let errors = go#list#Get("locationlist")
call go#list#Window("locationlist", len(errors))
endfun
func! s:RunOracle(mode, selected, needs_package) range abort
let fname = expand('%:p')
let dname = expand('%:p:h')
let pkg = go#package#ImportPath(dname)
if exists('g:go_oracle_scope')
" let the user defines the scope, must be a space separated string,
" example: 'fmt math net/http'
let scopes = split(get(g:, 'go_oracle_scope'))
elseif a:needs_package || exists('g:go_oracle_include_tests') && pkg != -1
" give import path so it includes all _test.go files too
let scopes = [pkg]
else
" best usable way, only pass the package itself, without the test
" files
let scopes = go#tool#Files()
endif
"return with a warning if the bin doesn't exist
let bin_path = go#path#CheckBinPath(g:go_oracle_bin)
if empty(bin_path)
return
endif
if exists('g:go_oracle_tags')
let tags = get(g:, 'go_oracle_tags')
else
let tags = ""
endif
if a:selected != -1
let pos1 = go#util#Offset(line("'<"), col("'<"))
let pos2 = go#util#Offset(line("'>"), col("'>"))
let cmd = printf('%s -format plain -pos=%s:#%d,#%d -tags=%s %s',
\ bin_path,
\ shellescape(fname), pos1, pos2, tags, a:mode)
else
let pos = go#util#OffsetCursor()
let cmd = printf('%s -format plain -pos=%s:#%d -tags=%s %s',
\ bin_path,
\ shellescape(fname), pos, tags, a:mode)
endif
" strip trailing slashes for each path in scoped. bug:
" https://github.com/golang/go/issues/14584
let scopes = go#util#StripTrailingSlash(scopes)
" now append each scope to the end as Oracle's scope parameter. It can be
" a packages or go files, dependent on the User's own choice. For more
" info check Oracle's User Manual section about scopes:
" https://docs.google.com/document/d/1SLk36YRjjMgKqe490mSRzOPYEDe0Y_WQNRv-EiFYUyw/view#heading=h.nwso96pj07q8
let cmd .= ' ' . go#util#Shelljoin(scopes)
echon "vim-go: " | echohl Identifier | echon "analysing ..." | echohl None
let old_gopath = $GOPATH
let $GOPATH = go#path#Detect()
let out = system(cmd)
let $GOPATH = old_gopath
if v:shell_error
" unfortunaly oracle outputs a very long stack trace that is not
" parsable to show the real error. But the main issue is usually the
" package which doesn't build.
redraw | echon "vim-go: " | echohl Statement | echon out | echohl None
return ""
endif
return out
endfunc
function! go#oracle#Scope(...)
if a:0
if a:0 == 1 && a:1 == '""'
unlet g:go_oracle_scope
echon "vim-go: " | echohl Function | echon "oracle scope is cleared"| echohl None
else
let g:go_oracle_scope = join(a:000, ' ')
echon "vim-go: " | echohl Function | echon "oracle scope changed to: '". g:go_oracle_scope ."'" | echohl None
endif
return
endif
if !exists('g:go_oracle_scope')
echon "vim-go: " | echohl Function | echon "oracle scope is not set"| echohl None
else
echon "vim-go: " | echohl Function | echon "current oracle scope: '". g:go_oracle_scope ."'" | echohl None
endif
endfunction
function! go#oracle#Tags(...)
if a:0
if a:0 == 1 && a:1 == '""'
unlet g:go_oracle_tags
echon "vim-go: " | echohl Function | echon "oracle tags is cleared"| echohl None
else
let g:go_oracle_tags = a:1
echon "vim-go: " | echohl Function | echon "oracle tags changed to: '". g:go_oracle_tags ."'" | echohl None
endif
return
endif
if !exists('g:go_oracle_tags')
echon "vim-go: " | echohl Function | echon "oracle tags is not set"| echohl None
else
echon "vim-go: " | echohl Function | echon "current oracle tags: '". g:go_oracle_tags ."'" | echohl None
endif
endfunction
" Show 'implements' relation for selected package
function! go#oracle#Implements(selected)
let out = s:RunOracle('implements', a:selected, 0)
call s:loclistSecond(out)
endfunction
" Describe selected syntax: definition, methods, etc
function! go#oracle#Describe(selected)
let out = s:RunOracle('describe', a:selected, 0)
call s:loclistSecond(out)
endfunction
" Show possible targets of selected function call
function! go#oracle#Callees(selected)
let out = s:RunOracle('callees', a:selected, 1)
call s:loclistSecond(out)
endfunction
" Show possible callers of selected function
function! go#oracle#Callers(selected)
let out = s:RunOracle('callers', a:selected, 1)
call s:loclistSecond(out)
endfunction
" Show path from callgraph root to selected function
function! go#oracle#Callstack(selected)
let out = s:RunOracle('callstack', a:selected, 1)
call s:loclistSecond(out)
endfunction
" Show free variables of selection
function! go#oracle#Freevars(selected)
" Freevars requires a selection
if a:selected == -1
echon "vim-go: " | echohl Statement | echon "GoFreevars requires a selection (range) of code "| echohl None
return
endif
let out = s:RunOracle('freevars', a:selected, 0)
call s:loclistSecond(out)
endfunction
" Show send/receive corresponding to selected channel op
function! go#oracle#ChannelPeers(selected)
let out = s:RunOracle('peers', a:selected, 1)
call s:loclistSecond(out)
endfunction
" Show all refs to entity denoted by selected identifier
function! go#oracle#Referrers(selected)
let out = s:RunOracle('referrers', a:selected, 0)
call s:loclistSecond(out)
endfunction
" vim:ts=4:sw=4:et
"

@ -28,13 +28,13 @@ if len(s:goarch) == 0
endif endif
endif endif
function! go#package#Paths() function! go#package#Paths() abort
let dirs = [] let dirs = []
if !exists("s:goroot") if !exists("s:goroot")
if executable('go') if executable('go')
let s:goroot = substitute(system('go env GOROOT'), '\n', '', 'g') let s:goroot = go#util#env("goroot")
if v:shell_error if go#util#ShellError() != 0
echomsg '''go env GOROOT'' failed' echomsg '''go env GOROOT'' failed'
endif endif
else else
@ -46,7 +46,7 @@ function! go#package#Paths()
let dirs += [s:goroot] let dirs += [s:goroot]
endif endif
let workspaces = split($GOPATH, go#util#PathListSep()) let workspaces = split(go#path#Default(), go#util#PathListSep())
if workspaces != [] if workspaces != []
let dirs += workspaces let dirs += workspaces
endif endif
@ -54,31 +54,33 @@ function! go#package#Paths()
return dirs return dirs
endfunction endfunction
function! go#package#ImportPath(arg) " ImportPath returns the import path in the current directory it was executed
let path = fnamemodify(resolve(a:arg), ':p') function! go#package#ImportPath() abort
let dirs = go#package#Paths() let out = go#tool#ExecuteInDir("go list")
if go#util#ShellError() != 0
for dir in dirs return -1
if len(dir) && match(path, dir) == 0
let workspace = dir
endif endif
endfor
if !exists('workspace') let import_path = 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 import_path[0] ==# '_'
return -1 return -1
endif endif
let srcdir = substitute(workspace . '/src/', '//', '/', '') return import_path
return substitute(path, srcdir, '', '')
endfunction endfunction
function! go#package#FromPath(arg)
function! go#package#FromPath(arg) abort
let path = fnamemodify(resolve(a:arg), ':p') let path = fnamemodify(resolve(a:arg), ':p')
let dirs = go#package#Paths() let dirs = go#package#Paths()
for dir in dirs for dir in dirs
if len(dir) && match(path, dir) == 0 if len(dir) && match(path, dir) == 0
let workspace = dir let workspace = dir
break
endif endif
endfor endfor
@ -86,17 +88,19 @@ function! go#package#FromPath(arg)
return -1 return -1
endif endif
let path = substitute(path, '/*$', '', '')
let workspace = substitute(workspace . '/src/', '/+', '', '')
if isdirectory(path) if isdirectory(path)
return substitute(path, workspace . 'src/', '', '') return substitute(path, workspace, '', '')
else else
return substitute(substitute(path, workspace . 'src/', '', ''), return substitute(substitute(path, workspace, '', ''),
\ '/' . fnamemodify(path, ':t'), '', '') \ '/' . fnamemodify(path, ':t'), '', '')
endif endif
endfunction endfunction
function! go#package#CompleteMembers(package, member) function! go#package#CompleteMembers(package, member) abort
silent! let content = system('godoc ' . a:package) silent! let content = go#util#System('godoc ' . a:package)
if v:shell_error || !len(content) if go#util#ShellError() || !len(content)
return [] return []
endif endif
let lines = filter(split(content, "\n"),"v:val !~ '^\\s\\+$'") let lines = filter(split(content, "\n"),"v:val !~ '^\\s\\+$'")
@ -113,11 +117,11 @@ function! go#package#CompleteMembers(package, member)
endtry endtry
endfunction endfunction
function! go#package#Complete(ArgLead, CmdLine, CursorPos) function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
let words = split(a:CmdLine, '\s\+', 1) let words = split(a:CmdLine, '\s\+', 1)
" do not complete package members for these commands " do not complete package members for these commands
let neglect_commands = ["GoImportAs", "GoOracleScope"] let neglect_commands = ["GoImportAs", "GoGuruScope"]
if len(words) > 2 && index(neglect_commands, words[0]) == -1 if len(words) > 2 && index(neglect_commands, words[0]) == -1
" Complete package members " Complete package members
@ -157,4 +161,4 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos)
return sort(keys(ret)) return sort(keys(ret))
endfunction endfunction
" vim:sw=4:et " vim: sw=2 ts=2 et

@ -4,13 +4,18 @@
" :GoPath is used " :GoPath is used
let s:initial_go_path = "" let s:initial_go_path = ""
" GoPath sets or returns the current GOPATH. If no arguments are passed it " GoPath sets or echos the current GOPATH. If no arguments are passed it
" echoes the current GOPATH, if an argument is passed it replaces the current " echoes the current GOPATH, if an argument is passed it replaces the current
" GOPATH with it. If two double quotes are passed (the empty string in go), " GOPATH with it. If two double quotes are passed (the empty string in go),
" it'll clear the GOPATH and will restore to the initial GOPATH. " it'll clear the GOPATH and will restore to the initial GOPATH.
function! go#path#GoPath(...) function! go#path#GoPath(...) abort
" no argument, show GOPATH
if len(a:000) == 0
echo go#path#Default()
return
endif
" we have an argument, replace GOPATH " we have an argument, replace GOPATH
if len(a:000)
" clears the current manually set GOPATH and restores it to the " clears the current manually set GOPATH and restores it to the
" initial GOPATH, which was set when Vim was started. " initial GOPATH, which was set when Vim was started.
if len(a:000) == 1 && a:1 == '""' if len(a:000) == 1 && a:1 == '""'
@ -26,29 +31,24 @@ function! go#path#GoPath(...)
echon "vim-go: " | echohl Function | echon "GOPATH changed to ". a:1 | echohl None echon "vim-go: " | echohl Function | echon "GOPATH changed to ". a:1 | echohl None
let s:initial_go_path = $GOPATH let s:initial_go_path = $GOPATH
let $GOPATH = a:1 let $GOPATH = a:1
return
endif
echo go#path#Detect()
endfunction endfunction
" Default returns the default GOPATH. If there is a single GOPATH it returns " Default returns the default GOPATH. If GOPATH is not set, it uses the
" it. For multiple GOPATHS separated with a the OS specific separator, only " default GOPATH set starting with Go 1.8. This GOPATH can be retrieved via
" the first one is returned " 'go env GOPATH'
function! go#path#Default() function! go#path#Default() abort
let go_paths = split($GOPATH, go#util#PathListSep()) if $GOPATH == ""
" use default GOPATH via go env
if len(go_paths) == 1 return go#util#env("gopath")
return $GOPATH
endif endif
return go_paths[0] return $GOPATH
endfunction endfunction
" HasPath checks whether the given path exists in GOPATH environment variable " HasPath checks whether the given path exists in GOPATH environment variable
" or not " or not
function! go#path#HasPath(path) function! go#path#HasPath(path) abort
let go_paths = split($GOPATH, go#util#PathListSep()) let go_paths = split(go#path#Default(), go#util#PathListSep())
let last_char = strlen(a:path) - 1 let last_char = strlen(a:path) - 1
" check cases of '/foo/bar/' and '/foo/bar' " check cases of '/foo/bar/' and '/foo/bar'
@ -69,13 +69,8 @@ endfunction
" Godeps, GB, it will modify the GOPATH so those directories take precedence " Godeps, GB, it will modify the GOPATH so those directories take precedence
" over the current GOPATH. It also detects diretories whose are outside " over the current GOPATH. It also detects diretories whose are outside
" GOPATH. " GOPATH.
function! go#path#Detect() function! go#path#Detect() abort
let gopath = $GOPATH let gopath = go#path#Default()
" don't lookup for godeps if autodetect is disabled.
if !get(g:, "go_autodetect_gopath", 1)
return gopath
endif
let current_dir = fnameescape(expand('%:p:h')) let current_dir = fnameescape(expand('%:p:h'))
@ -83,8 +78,16 @@ function! go#path#Detect()
" fetched from a customizable list. The user should define any new package " fetched from a customizable list. The user should define any new package
" management tool by it's own. " management tool by it's own.
" src folder outside $GOPATH " src folders outside $GOPATH
let src_root = finddir("src", current_dir .";") let src_roots = finddir("src", current_dir .";", -1)
" for cases like GOPATH/src/foo/src/bar, pick up GOPATH/src instead of
" GOPATH/src/foo/src
let src_root = ""
if len(src_roots) > 0
let src_root = src_roots[-1]
endif
if !empty(src_root) if !empty(src_root)
let src_path = fnamemodify(src_root, ':p:h:h') . go#util#PathSep() let src_path = fnamemodify(src_root, ':p:h:h') . go#util#PathSep()
@ -110,24 +113,28 @@ function! go#path#Detect()
endif endif
endif endif
" Fix up the case where initial $GOPATH is empty,
" and we end up with a trailing :
let gopath = substitute(gopath, ":$", "", "")
return gopath return gopath
endfunction endfunction
" BinPath returns the binary path of installed go tools. " BinPath returns the binary path of installed go tools.
function! go#path#BinPath() function! go#path#BinPath() abort
let bin_path = "" let bin_path = ""
" check if our global custom path is set, if not check if $GOBIN is set so " check if our global custom path is set, if not check if $GOBIN is set so
" we can use it, otherwise use $GOPATH + '/bin' " we can use it, otherwise use default GOPATH
if exists("g:go_bin_path") if exists("g:go_bin_path")
let bin_path = g:go_bin_path let bin_path = g:go_bin_path
elseif $GOBIN != "" elseif $GOBIN != ""
let bin_path = $GOBIN let bin_path = $GOBIN
elseif $GOPATH != ""
let bin_path = expand(go#path#Default() . "/bin/")
else else
" could not find anything let go_paths = split(go#path#Default(), go#util#PathListSep())
if len(go_paths) == 0
return "" "nothing found
endif
let bin_path = expand(go_paths[0] . "/bin/")
endif endif
return bin_path return bin_path
@ -135,32 +142,39 @@ endfunction
" CheckBinPath checks whether the given binary exists or not and returns the " CheckBinPath checks whether the given binary exists or not and returns the
" path of the binary. It returns an empty string doesn't exists. " path of the binary. It returns an empty string doesn't exists.
function! go#path#CheckBinPath(binpath) function! go#path#CheckBinPath(binpath) abort
" remove whitespaces if user applied something like 'goimports ' " remove whitespaces if user applied something like 'goimports '
let binpath = substitute(a:binpath, '^\s*\(.\{-}\)\s*$', '\1', '') let binpath = substitute(a:binpath, '^\s*\(.\{-}\)\s*$', '\1', '')
" save off original path
let old_path = $PATH
" check if we have an appropriate bin_path
let go_bin_path = go#path#BinPath()
if !empty(go_bin_path)
" append our GOBIN and GOPATH paths and be sure they can be found there...
" let us search in our GOBIN and GOPATH paths
let $PATH = go_bin_path . go#util#PathListSep() . $PATH
endif
" if it's in PATH just return it " if it's in PATH just return it
if executable(binpath) if executable(binpath)
return binpath if exists('*exepath')
let binpath = exepath(binpath)
endif endif
let $PATH = old_path
" just get the basename if go#util#IsUsingCygwinShell() == 1
let basename = fnamemodify(binpath, ":t") return go#path#CygwinPath(binpath)
" check if we have an appropriate bin_path
let go_bin_path = go#path#BinPath()
if empty(go_bin_path)
echo "vim-go: could not find '" . basename . "'. Run :GoInstallBinaries to fix it."
return ""
endif endif
" append our GOBIN and GOPATH paths and be sure they can be found there... return binpath
" let us search in our GOBIN and GOPATH paths endif
let old_path = $PATH
let $PATH = $PATH . go#util#PathListSep() .go_bin_path
" just get the basename
let basename = fnamemodify(binpath, ":t")
if !executable(basename) if !executable(basename)
echo "vim-go: could not find '" . basename . "'. Run :GoInstallBinaries to fix it." call go#util#EchoError(printf("could not find '%s'. Run :GoInstallBinaries to fix it", basename))
" restore back! " restore back!
let $PATH = old_path let $PATH = old_path
return "" return ""
@ -168,7 +182,15 @@ function! go#path#CheckBinPath(binpath)
let $PATH = old_path let $PATH = old_path
if go#util#IsUsingCygwinShell() == 1
return go#path#CygwinPath(a:binpath)
endif
return go_bin_path . go#util#PathSep() . basename return go_bin_path . go#util#PathSep() . basename
endfunction endfunction
" vim:ts=4:sw=4:et function! go#path#CygwinPath(path)
return substitute(a:path, '\\', '/', "g")
endfunction
" vim: sw=2 ts=2 et

@ -3,7 +3,7 @@ if !exists("g:go_play_open_browser")
endif endif
function! go#play#Share(count, line1, line2) function! go#play#Share(count, line1, line2) abort
if !executable('curl') if !executable('curl')
echohl ErrorMsg | echomsg "vim-go: require 'curl' command" | echohl None echohl ErrorMsg | echomsg "vim-go: require 'curl' command" | echohl None
return return
@ -13,14 +13,14 @@ function! go#play#Share(count, line1, line2)
let share_file = tempname() let share_file = tempname()
call writefile(split(content, "\n"), share_file, "b") call writefile(split(content, "\n"), share_file, "b")
let command = "curl -s -X POST http://play.golang.org/share --data-binary '@".share_file."'" let command = "curl -s -X POST https://play.golang.org/share --data-binary '@".share_file."'"
let snippet_id = system(command) let snippet_id = go#util#System(command)
" we can remove the temp file because it's now posted. " we can remove the temp file because it's now posted.
call delete(share_file) call delete(share_file)
if v:shell_error if go#util#ShellError() != 0
echo 'A error has occured. Run this command to see what the problem is:' echo 'A error has occurred. Run this command to see what the problem is:'
echo command echo command
return return
endif endif
@ -42,7 +42,7 @@ function! go#play#Share(count, line1, line2)
endfunction endfunction
function! s:get_visual_content() function! s:get_visual_content() abort
let save_regcont = @" let save_regcont = @"
let save_regtype = getregtype('"') let save_regtype = getregtype('"')
silent! normal! gvy silent! normal! gvy
@ -55,7 +55,7 @@ endfunction
" http://stackoverflow.com/questions/1533565/how-to-get-visually-selected-text-in-vimscript " http://stackoverflow.com/questions/1533565/how-to-get-visually-selected-text-in-vimscript
" another function that returns the content of visual selection, it's not used " another function that returns the content of visual selection, it's not used
" but might be useful in the future " but might be useful in the future
function! s:get_visual_selection() function! s:get_visual_selection() abort
let [lnum1, col1] = getpos("'<")[1:2] let [lnum1, col1] = getpos("'<")[1:2]
let [lnum2, col2] = getpos("'>")[1:2] let [lnum2, col2] = getpos("'>")[1:2]
@ -70,25 +70,4 @@ function! s:get_visual_selection()
return join(lines, "\n") return join(lines, "\n")
endfunction endfunction
" following two functions are from: https://github.com/mattn/gist-vim " vim: sw=2 ts=2 et
" thanks @mattn
function! s:get_browser_command()
let go_play_browser_command = get(g:, 'go_play_browser_command', '')
if go_play_browser_command == ''
if has('win32') || has('win64')
let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%'
elseif has('mac') || has('macunix') || has('gui_macvim') || system('uname') =~? '^darwin'
let go_play_browser_command = 'open %URL%'
elseif executable('xdg-open')
let go_play_browser_command = 'xdg-open %URL%'
elseif executable('firefox')
let go_play_browser_command = 'firefox %URL% &'
else
let go_play_browser_command = ''
endif
endif
return go_play_browser_command
endfunction
" vim:ts=4:sw=4:et

@ -2,26 +2,34 @@ if !exists("g:go_gorename_bin")
let g:go_gorename_bin = "gorename" let g:go_gorename_bin = "gorename"
endif endif
if !exists("g:go_gorename_prefill") " Set the default value. A value of "1" is a shortcut for this, for
let g:go_gorename_prefill = 1 " compatibility reasons.
function! s:default() abort
if !exists("g:go_gorename_prefill") || g:go_gorename_prefill == 1
let g:go_gorename_prefill = 'expand("<cword>") =~# "^[A-Z]"' .
\ '? go#util#pascalcase(expand("<cword>"))' .
\ ': go#util#camelcase(expand("<cword>"))'
endif endif
endfunction
call s:default()
function! go#rename#Rename(bang, ...) abort
call s:default()
function! go#rename#Rename(bang, ...) let to_identifier = ""
let to = ""
if a:0 == 0 if a:0 == 0
let from = expand("<cword>") let ask = printf("vim-go: rename '%s' to: ", expand("<cword>"))
let ask = printf("vim-go: rename '%s' to: ", from) if g:go_gorename_prefill != ''
if g:go_gorename_prefill let to_identifier = input(ask, eval(g:go_gorename_prefill))
let to = input(ask, from)
else else
let to = input(ask) let to_identifier = input(ask)
endif endif
redraw! redraw!
if empty(to) if empty(to_identifier)
return return
endif endif
else else
let to = a:1 let to_identifier = a:1
endif endif
" return with a warning if the bin doesn't exist " return with a warning if the bin doesn't exist
@ -32,31 +40,106 @@ function! go#rename#Rename(bang, ...)
let fname = expand('%:p') let fname = expand('%:p')
let pos = go#util#OffsetCursor() let pos = go#util#OffsetCursor()
let cmd = printf('%s -offset %s -to %s', shellescape(bin_path), shellescape(printf('%s:#%d', fname, pos)), shellescape(to)) let offset = printf('%s:#%d', fname, pos)
let out = go#tool#ExecuteInDir(cmd) " no need to escape for job call
let bin_path = go#util#has_job() ? bin_path : shellescape(bin_path)
let offset = go#util#has_job() ? offset : shellescape(offset)
let to_identifier = go#util#has_job() ? to_identifier : shellescape(to_identifier)
" strip out newline on the end that gorename puts. If we don't remove, it let cmd = [bin_path, "-offset", offset, "-to", to_identifier]
" will trigger the 'Hit ENTER to continue' prompt
let clean = split(out, '\n') " check for any tags
if exists('g:go_build_tags')
let tags = get(g:, 'go_build_tags')
call extend(cmd, ["-tags", tags])
endif
if go#util#has_job()
call go#util#EchoProgress(printf("renaming to '%s' ...", to_identifier))
call s:rename_job({
\ 'cmd': cmd,
\ 'bang': a:bang,
\})
return
endif
let command = join(cmd, " ")
let out = go#tool#ExecuteInDir(command)
let splitted = split(out, '\n')
call s:parse_errors(go#util#ShellError(), a:bang, splitted)
endfunction
function s:rename_job(args)
let messages = []
function! s:callback(chan, msg) closure
call add(messages, a:msg)
endfunction
let status_dir = expand('%:p:h')
function! s:exit_cb(job, exitval) closure
let status = {
\ 'desc': 'last status',
\ 'type': "gorename",
\ 'state': "finished",
\ }
if a:exitval
let status.state = "failed"
endif
let l:listtype = "quickfix" call go#statusline#Update(status_dir, status)
if v:shell_error
let errors = go#tool#ParseErrors(split(out, '\n')) call s:parse_errors(a:exitval, a:args.bang, messages)
call go#list#Populate(l:listtype, errors) endfunction
let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ }
call go#statusline#Update(status_dir, {
\ 'desc': "current status",
\ 'type': "gorename",
\ 'state': "started",
\})
call job_start(a:args.cmd, start_options)
endfunction
function s:parse_errors(exit_val, bang, out)
" reload all files to reflect the new changes. We explicitly call
" checktime to trigger a reload of all files. See
" http://www.mail-archive.com/vim@vim.org/msg05900.html for more info
" about the autoread bug
let current_autoread = &autoread
set autoread
silent! checktime
let &autoread = current_autoread
let l:listtype = go#list#Type("GoRename")
if a:exit_val != 0
call go#util#EchoError("FAILED")
let errors = go#tool#ParseErrors(a:out)
call go#list#Populate(l:listtype, errors, 'Rename')
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
elseif empty(errors) elseif empty(errors)
" failed to parse errors, output the original content " failed to parse errors, output the original content
call go#util#EchoError(out) call go#util#EchoError(a:out)
endif endif
return return
else endif
" strip out newline on the end that gorename puts. If we don't remove, it
" will trigger the 'Hit ENTER to continue' prompt
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype) call go#list#Window(l:listtype)
redraw | echon "vim-go: " | echohl Function | echon clean[0] | echohl None call go#util#EchoSuccess(a:out[0])
endif
" refresh the buffer so we can see the new content " refresh the buffer so we can see the new content
" TODO(arslan): also find all other buffers and refresh them too. For this " TODO(arslan): also find all other buffers and refresh them too. For this
@ -65,5 +148,13 @@ function! go#rename#Rename(bang, ...)
silent execute ":e" silent execute ":e"
endfunction endfunction
" vim:ts=4:sw=4:et " Commandline completion: original, unexported camelCase, and exported
" " CamelCase.
function! go#rename#Complete(lead, cmdline, cursor)
let l:word = expand('<cword>')
return filter(uniq(sort(
\ [l:word, go#util#camelcase(l:word), go#util#pascalcase(l:word)])),
\ 'strpart(v:val, 0, len(a:lead)) == a:lead')
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,112 @@
" Statusline
""""""""""""""""""""""""""""""""
" s:statuses is a global reference to all statuses. It stores the statuses per
" import paths (map[string]status), where each status is unique per its
" type. Current status dict is in form:
" {
" 'desc' : 'Job description',
" 'state' : 'Job state, such as success, failure, etc..',
" 'type' : 'Job type, such as build, test, etc..'
" 'created_at' : 'Time it was created as seconds since 1st Jan 1970'
" }
let s:statuses = {}
" timer_id for cleaner
let s:timer_id = 0
" last_status stores the last generated text per status
let s:last_status = ""
" Show returns the current status of the job for 20 seconds (configurable). It
" displays it in form of 'desc: [type|state]' if there is any state available,
" if not it returns an empty string. This function should be plugged directly
" into the statusline.
function! go#statusline#Show() abort
" lazy initialiation of the cleaner
if !s:timer_id
" clean every 60 seconds all statuses
let interval = get(g:, 'go_statusline_duration', 60000)
let s:timer_id = timer_start(interval, function('go#statusline#Clear'), {'repeat': -1})
endif
" nothing to show
if empty(s:statuses)
return ''
endif
let status_dir = expand('%:p:h')
if !has_key(s:statuses, status_dir)
return ''
endif
let status = s:statuses[status_dir]
if !has_key(status, 'desc') || !has_key(status, 'state') || !has_key(status, 'type')
return ''
endif
let status_text = printf("[%s|%s]", status.type, status.state)
if empty(status_text)
return ''
endif
" only update highlight if status has changed.
if status_text != s:last_status
if status.state =~ "success" || status.state =~ "finished" || status.state =~ "pass"
hi goStatusLineColor cterm=bold ctermbg=76 ctermfg=22
elseif status.state =~ "started" || status.state =~ "analysing" || status.state =~ "compiling"
hi goStatusLineColor cterm=bold ctermbg=208 ctermfg=88
elseif status.state =~ "failed"
hi goStatusLineColor cterm=bold ctermbg=196 ctermfg=52
endif
endif
let s:last_status = status_text
return status_text
endfunction
" Update updates (adds) the statusline for the given status_dir with the
" given status dict. It overrides any previously set status.
function! go#statusline#Update(status_dir, status) abort
let a:status.created_at = reltime()
let s:statuses[a:status_dir] = a:status
" force to update the statusline, otherwise the user needs to move the
" cursor
exe 'let &ro = &ro'
" before we stop the timer, check if we have any previous jobs to be cleaned
" up. Otherwise every job will reset the timer when this function is called
" and thus old jobs will never be cleaned
call go#statusline#Clear(0)
" also reset the timer, so the user has time to see it in the statusline.
" Setting the timer_id to 0 will trigger a new cleaner routine.
call timer_stop(s:timer_id)
let s:timer_id = 0
endfunction
" Clear clears all currently stored statusline data. The timer_id argument is
" just a placeholder so we can pass it to a timer_start() function if needed.
function! go#statusline#Clear(timer_id) abort
for [status_dir, status] in items(s:statuses)
let elapsed_time = reltimestr(reltime(status.created_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
if str2nr(elapsed_time) > 10
call remove(s:statuses, status_dir)
endif
endfor
if len(s:statuses) == 0
let s:statuses = {}
endif
" force to update the statusline, otherwise the user needs to move the
" cursor
exe 'let &ro = &ro'
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,214 @@
" mapped to :GoAddTags
function! go#tags#Add(start, end, count, ...) abort
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
let offset = 0
if a:count == -1
let offset = go#util#OffsetCursor()
endif
let test_mode = 0
call call("go#tags#run", [a:start, a:end, offset, "add", fname, test_mode] + a:000)
endfunction
" mapped to :GoRemoveTags
function! go#tags#Remove(start, end, count, ...) abort
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
let offset = 0
if a:count == -1
let offset = go#util#OffsetCursor()
endif
let test_mode = 0
call call("go#tags#run", [a:start, a:end, offset, "remove", fname, test_mode] + a:000)
endfunction
" run runs gomodifytag. This is an internal test so we can test it
function! go#tags#run(start, end, offset, mode, fname, test_mode, ...) abort
" do not split this into multiple lines, somehow tests fail in that case
let args = {'mode': a:mode,'start': a:start,'end': a:end,'offset': a:offset,'fname': a:fname,'cmd_args': a:000}
if &modified
let args["modified"] = 1
endif
let result = s:create_cmd(args)
if has_key(result, 'err')
call go#util#EchoError(result.err)
return -1
endif
let command = join(result.cmd, " ")
if &modified
let filename = expand("%:p:gs!\\!/!")
let content = join(go#util#GetLines(), "\n")
let in = filename . "\n" . strlen(content) . "\n" . content
let out = go#util#System(command, in)
else
let out = go#util#System(command)
endif
if go#util#ShellError() != 0
call go#util#EchoError(out)
return
endif
if a:test_mode
exe 'edit ' . a:fname
endif
call s:write_out(out)
if a:test_mode
exe 'write! ' . a:fname
endif
endfunc
" write_out writes back the given output to the current buffer
func s:write_out(out) abort
" not a json output
if a:out[0] !=# '{'
return
endif
" nothing to do
if empty(a:out) || type(a:out) != type("")
return
endif
let result = json_decode(a:out)
if type(result) != type({})
call go#util#EchoError(printf("malformed output from gomodifytags: %s", a:out))
return
endif
let lines = result['lines']
let start_line = result['start']
let end_line = result['end']
let index = 0
for line in range(start_line, end_line)
call setline(line, lines[index])
let index += 1
endfor
if has_key(result, 'errors')
let l:winnr = winnr()
let l:listtype = go#list#Type("GoModifyTags")
call go#list#ParseFormat(l:listtype, "%f:%l:%c:%m", result['errors'], "gomodifytags")
call go#list#Window(l:listtype, len(result['errors']))
"prevent jumping to quickfix list
exe l:winnr . "wincmd w"
endif
endfunc
" create_cmd returns a dict that contains the command to execute gomodifytags
func s:create_cmd(args) abort
if !exists("*json_decode")
return {'err': "requires 'json_decode'. Update your Vim/Neovim version."}
endif
let bin_path = go#path#CheckBinPath('gomodifytags')
if empty(bin_path)
return {'err': "gomodifytags does not exist"}
endif
let bin_path = go#util#Shellescape(bin_path)
let l:start = a:args.start
let l:end = a:args.end
let l:offset = a:args.offset
let l:mode = a:args.mode
let l:cmd_args = a:args.cmd_args
let l:modifytags_transform = get(g:, 'go_addtags_transform', "snakecase")
" start constructing the command
let cmd = [bin_path]
call extend(cmd, ["-format", "json"])
call extend(cmd, ["-file", go#util#Shellescape(a:args.fname)])
call extend(cmd, ["-transform", l:modifytags_transform])
if has_key(a:args, "modified")
call add(cmd, "-modified")
endif
if l:offset != 0
call extend(cmd, ["-offset", l:offset])
else
let range = printf("%d,%d", l:start, l:end)
call extend(cmd, ["-line", range])
endif
if l:mode == "add"
let l:tags = []
let l:options = []
if !empty(l:cmd_args)
for item in l:cmd_args
let splitted = split(item, ",")
" tag only
if len(splitted) == 1
call add(l:tags, splitted[0])
endif
" options only
if len(splitted) == 2
call add(l:tags, splitted[0])
call add(l:options, printf("%s=%s", splitted[0], splitted[1]))
endif
endfor
endif
" construct options
if !empty(l:options)
call extend(cmd, ["-add-options", join(l:options, ",")])
else
" default value
if empty(l:tags)
let l:tags = ["json"]
endif
" construct tags
call extend(cmd, ["-add-tags", join(l:tags, ",")])
endif
elseif l:mode == "remove"
if empty(l:cmd_args)
call add(cmd, "-clear-tags")
else
let l:tags = []
let l:options = []
for item in l:cmd_args
let splitted = split(item, ",")
" tag only
if len(splitted) == 1
call add(l:tags, splitted[0])
endif
" options only
if len(splitted) == 2
call add(l:options, printf("%s=%s", splitted[0], splitted[1]))
endif
endfor
" construct tags
if !empty(l:tags)
call extend(cmd, ["-remove-tags", join(l:tags, ",")])
endif
" construct options
if !empty(l:options)
call extend(cmd, ["-remove-options", join(l:options, ",")])
endif
endif
else
return {'err': printf("unknown mode: %s", l:mode)}
endif
return {'cmd': cmd}
endfunc
" vim: sw=2 ts=2 et

@ -0,0 +1,22 @@
func! Test_add_tags() abort
try
let l:tmp = gotest#load_fixture('tags/add_all_input.go')
silent call go#tags#run(0, 0, 40, "add", bufname(''), 1)
call gotest#assert_fixture('tags/add_all_golden.go')
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_remove_tags() abort
try
let l:tmp = gotest#load_fixture('tags/remove_all_input.go')
silent call go#tags#run(0, 0, 40, "remove", bufname(''), 1)
call gotest#assert_fixture('tags/remove_all_golden.go')
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" vim:ts=2:sts=2:sw=2:et

@ -0,0 +1,53 @@
let s:current_file = expand("<sfile>")
function! go#template#create() abort
let l:go_template_use_pkg = get(g:, 'go_template_use_pkg', 0)
let l:root_dir = fnamemodify(s:current_file, ':h:h:h')
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
let l:package_name = -1
if isdirectory(expand('%:p:h'))
execute cd . fnameescape(expand('%:p:h'))
let l:package_name = go#tool#PackageName()
endif
" if we can't figure out any package name(no Go files or non Go package
" files) from the directory create the template or use the cwd
" as the name
if l:package_name == -1 && l:go_template_use_pkg != 1
let l:filename = fnamemodify(expand("%"), ':t')
if l:filename =~ "_test.go$"
let l:template_file = get(g:, 'go_template_test_file', "hello_world_test.go")
else
let l:template_file = get(g:, 'go_template_file', "hello_world.go")
endif
let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file)
silent exe '0r ' . fnameescape(l:template_path)
elseif l:package_name == -1 && l:go_template_use_pkg == 1
" cwd is now the dir of the package
let l:path = fnamemodify(getcwd(), ':t')
let l:content = printf("package %s", l:path)
call append(0, l:content)
else
let l:content = printf("package %s", l:package_name)
call append(0, l:content)
endif
$delete _
execute cd . fnameescape(dir)
endfunction
function! go#template#ToggleAutoCreate() abort
if get(g:, "go_template_autocreate", 1)
let g:go_template_autocreate = 0
call go#util#EchoProgress("auto template create disabled")
return
end
let g:go_template_autocreate = 1
call go#util#EchoProgress("auto template create enabled")
endfunction
" vim: sw=2 ts=2 et

@ -7,22 +7,19 @@ let s:jobs = {}
" new creates a new terminal with the given command. Mode is set based on the " new creates a new terminal with the given command. Mode is set based on the
" global variable g:go_term_mode, which is by default set to :vsplit " global variable g:go_term_mode, which is by default set to :vsplit
function! go#term#new(bang, cmd) function! go#term#new(bang, cmd) abort
return go#term#newmode(a:bang, a:cmd, g:go_term_mode) return go#term#newmode(a:bang, a:cmd, g:go_term_mode)
endfunction endfunction
" new creates a new terminal with the given command and window mode. " new creates a new terminal with the given command and window mode.
function! go#term#newmode(bang, cmd, mode) function! go#term#newmode(bang, cmd, mode) abort
let mode = a:mode let mode = a:mode
if empty(mode) if empty(mode)
let mode = g:go_term_mode let mode = g:go_term_mode
endif endif
" modify GOPATH if needed
let old_gopath = $GOPATH
let $GOPATH = go#path#Detect()
" execute go build in the files directory " execute go build in the files directory
let l:winnr = winnr()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd() let dir = getcwd()
@ -49,32 +46,37 @@ function! go#term#newmode(bang, cmd, mode)
execute cd . fnameescape(dir) execute cd . fnameescape(dir)
" restore back GOPATH
let $GOPATH = old_gopath
let job.id = id let job.id = id
let job.cmd = a:cmd
startinsert startinsert
" resize new term if needed. " resize new term if needed.
let height = get(g:, 'go_term_height', winheight(0)) let height = get(g:, 'go_term_height', winheight(0))
let width = get(g:, 'go_term_width', winwidth(0)) let width = get(g:, 'go_term_width', winwidth(0))
" we are careful how to resize. for example it's vertical we don't change " we are careful how to resize. for example it's vsplit we don't change
" the height. The below command resizes the buffer " the height. The below command resizes the buffer
if a:mode == "split"
exe 'resize ' . height if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew"
elseif a:mode == "vertical"
exe 'vertical resize ' . width exe 'vertical resize ' . width
elseif mode =~ "split" || mode =~ "new"
exe 'resize ' . height
endif endif
" we also need to resize the pty, so there you go... " we also need to resize the pty, so there you go...
call jobresize(id, width, height) call jobresize(id, width, height)
let s:jobs[id] = job let s:jobs[id] = job
stopinsert
if l:winnr !=# winnr()
exe l:winnr . "wincmd w"
endif
return id return id
endfunction endfunction
function! s:on_stdout(job_id, data) function! s:on_stdout(job_id, data, event) dict abort
if !has_key(s:jobs, a:job_id) if !has_key(s:jobs, a:job_id)
return return
endif endif
@ -83,7 +85,7 @@ function! s:on_stdout(job_id, data)
call extend(job.stdout, a:data) call extend(job.stdout, a:data)
endfunction endfunction
function! s:on_stderr(job_id, data) function! s:on_stderr(job_id, data, event) dict abort
if !has_key(s:jobs, a:job_id) if !has_key(s:jobs, a:job_id)
return return
endif endif
@ -92,37 +94,47 @@ function! s:on_stderr(job_id, data)
call extend(job.stderr, a:data) call extend(job.stderr, a:data)
endfunction endfunction
function! s:on_exit(job_id, data) function! s:on_exit(job_id, exit_status, event) dict abort
if !has_key(s:jobs, a:job_id) if !has_key(s:jobs, a:job_id)
return return
endif endif
let job = s:jobs[a:job_id] let job = s:jobs[a:job_id]
let l:listtype = "locationlist" let l:listtype = go#list#Type("_term")
" usually there is always output so never branch into this clause " usually there is always output so never branch into this clause
if empty(job.stdout) if empty(job.stdout)
call go#list#Clean(l:listtype) call go#list#Clean(l:listtype)
call go#list#Window(l:listtype) call go#list#Window(l:listtype)
else unlet s:jobs[a:job_id]
return
endif
let errors = go#tool#ParseErrors(job.stdout) let errors = go#tool#ParseErrors(job.stdout)
let errors = go#tool#FilterValids(errors) let errors = go#tool#FilterValids(errors)
if !empty(errors) if !empty(errors)
" close terminal we don't need it " close terminal we don't need it anymore
close close
call go#list#Populate(l:listtype, errors) call go#list#Populate(l:listtype, errors, job.cmd)
call go#list#Window(l:listtype, len(errors)) call go#list#Window(l:listtype, len(errors))
if !self.bang if !self.bang
call go#list#JumpToFirst(l:listtype) call go#list#JumpToFirst(l:listtype)
endif endif
else unlet s:jobs[a:job_id]
call go#list#Clean(l:listtype) return
call go#list#Window(l:listtype)
endif endif
endif " tests are passing clean the list and close the list. But we only can
" close them from a normal view, so jump back, close the list and then
" again jump back to the terminal
wincmd p
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
wincmd p
unlet s:jobs[a:job_id] unlet s:jobs[a:job_id]
endfunction endfunction
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

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

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

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

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

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

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

@ -0,0 +1,16 @@
package main
type Server struct {
Name string `json:"name"`
ID int `json:"id"`
MyHomeAddress string `json:"my_home_address"`
SubDomains []string `json:"sub_domains"`
Empty string `json:"empty"`
Example int64 `json:"example"`
Example2 string `json:"example_2"`
Bar struct {
Four string `json:"four"`
Five string `json:"five"`
} `json:"bar"`
Lala interface{} `json:"lala"`
}

@ -0,0 +1,16 @@
package main
type Server struct {
Name string
ID int
MyHomeAddress string
SubDomains []string
Empty string
Example int64
Example2 string
Bar struct {
Four string
Five string
}
Lala interface{}
}

@ -0,0 +1,16 @@
package main
type Server struct {
Name string
ID int
MyHomeAddress string
SubDomains []string
Empty string
Example int64
Example2 string
Bar struct {
Four string
Five string
}
Lala interface{}
}

@ -0,0 +1,16 @@
package main
type Server struct {
Name string `json:"name"`
ID int `json:"id"`
MyHomeAddress string `json:"my_home_address"`
SubDomains []string `json:"sub_domains"`
Empty string `json:"empty"`
Example int64 `json:"example"`
Example2 string `json:"example_2"`
Bar struct {
Four string `json:"four"`
Five string `json:"five"`
} `json:"bar"`
Lala interface{} `json:"lala"`
}

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

@ -0,0 +1,7 @@
package mock
import "testing"
func Fail(t *testing.T) {
t.Fatal("another package badness")
}

@ -0,0 +1,58 @@
package play
import (
"sync"
"testing"
"play/mock"
)
func TestTopSubHelper(t *testing.T) {
t.Run("sub", func(t *testing.T) {
t.Log("log message")
t.Error("sub badness")
})
t.Error("badness")
helper(t)
}
func TestMultiline(t *testing.T) {
t.Error("this is an error\nand a second line, too")
}
func TestSub(t *testing.T) {
t.Run("indented", func(t *testing.T) {
t.Error("this is a sub-test error\nand a second line, too")
})
}
func TestOK(t *testing.T) {
t.Run("log", func(t *testing.T) {
t.Log("goodness")
})
}
// TestMocked tests behavior similar to what users may experience when using
// github.com/golang/mock/gomock.
func TestMocked(t *testing.T) {
mock.Fail(t)
}
func TestPanic(t *testing.T) {
panic("worst ever")
}
func TestConcurrentPanic(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
go func() {
panic("concurrent fail")
wg.Done()
}()
wg.Wait()
}
func helper(t *testing.T) {
t.Helper()
t.Fatal("helper badness")
}

@ -0,0 +1,389 @@
" Test runs `go test` in the current directory. If compile is true, it'll
" compile the tests instead of running them (useful to catch errors in the
" test files). Any other argument is appended to the final `go test` command.
function! go#test#Test(bang, compile, ...) abort
let args = ["test"]
" don't run the test, only compile it. Useful to capture and fix errors.
if a:compile
let testfile = tempname() . ".vim-go.test"
call extend(args, ["-c", "-o", testfile])
endif
if exists('g:go_build_tags')
let tags = get(g:, 'go_build_tags')
call extend(args, ["-tags", tags])
endif
if a:0
let goargs = a:000
" do not expand for coverage mode as we're passing the arg ourself
if a:1 != '-coverprofile'
" expand all wildcards(i.e: '%' to the current file name)
let goargs = map(copy(a:000), "expand(v:val)")
endif
if !(has('nvim') || go#util#has_job())
let goargs = go#util#Shelllist(goargs, 1)
endif
call extend(args, goargs, 1)
else
" only add this if no custom flags are passed
let timeout = get(g:, 'go_test_timeout', '10s')
call add(args, printf("-timeout=%s", timeout))
endif
if get(g:, 'go_echo_command_info', 1)
if a:compile
call go#util#EchoProgress("compiling tests ...")
else
call go#util#EchoProgress("testing...")
endif
endif
if go#util#has_job()
" use vim's job functionality to call it asynchronously
let job_args = {
\ 'cmd': ['go'] + args,
\ 'bang': a:bang,
\ 'winnr': winnr(),
\ 'dir': getcwd(),
\ 'compile_test': a:compile,
\ 'jobdir': fnameescape(expand("%:p:h")),
\ }
call s:test_job(job_args)
return
elseif has('nvim')
" use nvims's job functionality
if get(g:, 'go_term_enabled', 0)
let id = go#term#new(a:bang, ["go"] + args)
else
let id = go#jobcontrol#Spawn(a:bang, "test", "GoTest", args)
endif
return id
endif
call go#cmd#autowrite()
redraw
let command = "go " . join(args, ' ')
let out = go#tool#ExecuteInDir(command)
" TODO(bc): When the output is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
let l:listtype = go#list#Type("GoTest")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd()
execute cd fnameescape(expand("%:p:h"))
if go#util#ShellError() != 0
call go#list#ParseFormat(l:listtype, s:errorformat(), split(out, '\n'), command)
let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:bang
call go#list#JumpToFirst(l:listtype)
elseif empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(out)
endif
call go#util#EchoError("[test] FAIL")
else
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
if a:compile
call go#util#EchoSuccess("[test] SUCCESS")
else
call go#util#EchoSuccess("[test] PASS")
endif
endif
execute cd . fnameescape(dir)
endfunction
" Testfunc runs a single test that surrounds the current cursor position.
" Arguments are passed to the `go test` command.
function! go#test#Func(bang, ...) abort
" search flags legend (used only)
" 'b' search backward instead of forward
" 'c' accept a match at the cursor position
" 'n' do Not move the cursor
" 'W' don't wrap around the end of the file
"
" for the full list
" :help search
let test = search('func \(Test\|Example\)', "bcnW")
if test == 0
echo "vim-go: [test] no test found immediate to cursor"
return
end
let line = getline(test)
let name = split(split(line, " ")[1], "(")[0]
let args = [a:bang, 0, "-run", name . "$"]
if a:0
call extend(args, a:000)
endif
call call('go#test#Test', args)
endfunction
function! s:test_job(args) abort
let status_dir = expand('%:p:h')
let started_at = reltime()
let status = {
\ 'desc': 'current status',
\ 'type': "test",
\ 'state': "started",
\ }
if a:args.compile_test
let status.state = "compiling"
endif
call go#statusline#Update(status_dir, status)
" autowrite is not enabled for jobs
call go#cmd#autowrite()
let messages = []
function! s:callback(chan, msg) closure
call add(messages, a:msg)
endfunction
function! s:exit_cb(job, exitval) closure
let status = {
\ 'desc': 'last status',
\ 'type': "test",
\ 'state': "pass",
\ }
if a:args.compile_test
let status.state = "success"
endif
if a:exitval
let status.state = "failed"
endif
if get(g:, 'go_echo_command_info', 1)
if a:exitval == 0
if a:args.compile_test
call go#util#EchoSuccess("[test] SUCCESS")
else
call go#util#EchoSuccess("[test] PASS")
endif
else
call go#util#EchoError("[test] FAIL")
endif
endif
let elapsed_time = reltimestr(reltime(started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)
call go#statusline#Update(status_dir, status)
let l:listtype = go#list#Type("GoTest")
if a:exitval == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif
" TODO(bc): When messages is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
call s:show_errors(a:args, a:exitval, messages)
endfunction
let start_options = {
\ 'callback': funcref("s:callback"),
\ 'exit_cb': funcref("s:exit_cb"),
\ }
" pre start
let dir = getcwd()
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let jobdir = fnameescape(expand("%:p:h"))
execute cd . jobdir
call job_start(a:args.cmd, start_options)
" post start
execute cd . fnameescape(dir)
endfunction
" show_errors parses the given list of lines of a 'go test' output and returns
" a quickfix compatible list of errors. It's intended to be used only for go
" test output.
function! s:show_errors(args, exit_val, messages) abort
let l:listtype = go#list#Type("GoTest")
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
try
execute cd a:args.jobdir
call go#list#ParseFormat(l:listtype, s:errorformat(), a:messages, join(a:args.cmd))
let errors = go#list#Get(l:listtype)
finally
execute cd . fnameescape(a:args.dir)
endtry
if !len(errors)
" failed to parse errors, output the original content
call go#util#EchoError(a:messages)
call go#util#EchoError(a:args.dir)
return
endif
if a:args.winnr == winnr()
call go#list#Window(l:listtype, len(errors))
if !empty(errors) && !a:args.bang
call go#list#JumpToFirst(l:listtype)
endif
endif
endfunction
let s:efm= ""
function! s:errorformat() abort
" NOTE(arslan): once we get JSON output everything will be easier :).
" TODO(bc): When the output is JSON, the JSON should be run through a
" filter to produce lines that are more easily described by errorformat.
" https://github.com/golang/go/issues/2981.
let goroot = go#util#goroot()
if s:efm != ""
return s:efm
endif
" each level of test indents the test output 4 spaces.
" TODO(bc): figure out how to use 0 or more groups of four spaces for the
" indentation. '%\\( %\\)%#' should work, but doesn't.
let indent = " %#"
" match compiler errors
let format = "%f:%l:%c: %m"
" ignore `go test -v` output for starting tests
let format .= ",%-G=== RUN %.%#"
" ignore `go test -v` output for passing tests
let format .= ",%-G" . indent . "--- PASS: %.%#"
" Match failure lines.
"
" Test failures start with '--- FAIL: ', followed by the test name followed
" by a space the duration of the test in parentheses
"
" e.g.:
" '--- FAIL: TestSomething (0.00s)'
if get(g:, 'go_test_show_name', 0)
let format .= ",%+G" . indent . "--- FAIL: %.%#"
else
let format .= ",%-G" . indent . "--- FAIL: %.%#"
endif
" Matches test output lines.
"
" All test output lines start with the test indentation and a tab, followed
" by the filename, a colon, the line number, another colon, a space, and the
" message. e.g.:
" '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
let format .= ",%A" . indent . "%\\t%\\+%f:%l: %m"
" Match the 2nd and later lines of multi-line output. These lines are
" indented the number of spaces for the level of nesting of the test,
" followed by two tabs, followed by the message.
"
" Treat these lines as if they are stand-alone lines of output by using %G.
" It would also be valid to treat these lines as if they were the
" continuation of a multi-line error by using %C instead of %G, but that
" would also require that all test errors using a %A or %E modifier to
" indicate that they're multiple lines of output, but in that case the lines
" get concatenated in the quickfix list, which is not what users typically
" want when writing a newline into their test output.
let format .= ",%G" . indent . "%\\t%\\{2}%m"
" set the format for panics.
" In addition to 'panic', check for 'fatal error' to support older versions
" of Go that used 'fatal error'.
"
" Panics come in two flavors. When the goroutine running the tests panics,
" `go test` recovers and tries to exit more cleanly. In that case, the panic
" message is suffixed with ' [recovered]'. If the panic occurs in a
" different goroutine, it will not be suffixed with ' [recovered]'.
let format .= ",%+Afatal error: %.%# [recovered]"
let format .= ",%+Apanic: %.%# [recovered]"
let format .= ",%+Afatal error: %.%#"
let format .= ",%+Apanic: %.%#"
" Match address lines in stacktraces produced by panic.
"
" Address lines in the stack trace have leading tabs, followed by the path
" to the file. The file path is followed by a colon and then the line number
" within the file where the panic occurred. After that there's a space and
" hexadecimal number.
"
" e.g.:
" '\t/usr/local/go/src/time.go:1313 +0x5d'
" panicaddress, and readyaddress are identical except for
" panicaddress sets the filename and line number.
let panicaddress = "%\\t%f:%l +0x%[0-9A-Fa-f]%\\+"
let readyaddress = "%\\t%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
" stdlib address is identical to readyaddress, except it matches files
" inside GOROOT.
let stdlibaddress = "%\\t" . goroot . "%\\f%\\+:%\\d%\\+ +0x%[0-9A-Fa-f]%\\+"
" Match and ignore the running goroutine line.
let format .= ",%-Cgoroutine %\\d%\\+ [running]:"
" Match address lines that refer to stdlib, but consider them informational
" only. This is to catch the lines after the first address line in the
" running goroutine of a panic stack trace. Ideally, this wouldn't be
" necessary, but when a panic happens in the goroutine running a test, it's
" recovered and another panic is created, so the stack trace actually has
" the line that caused the original panic a couple of addresses down the
" stack.
let format .= ",%-C" . stdlibaddress
" Match address lines in the first matching goroutine. This means the panic
" message will only be shown as the error message in the first address of
" the running goroutine's stack.
let format .= ",%Z" . panicaddress
" Match and ignore panic address without being part of a multi-line message.
" This is to catch those lines that come after the top most non-standard
" library line in stack traces.
let format .= ",%-G" . readyaddress
" Match and ignore exit status lines (produced when go test panics) whether
" part of a multi-line message or not, because these lines sometimes come
" before and sometimes after panic stacktraces.
let format .= ",%-Cexit status %[0-9]%\\+"
"let format .= ",exit status %[0-9]%\\+"
" Match and ignore exit failure lines whether part of a multi-line message
" or not, because these lines sometimes come before and sometimes after
" panic stacktraces.
let format .= ",%-CFAIL%\\t%.%#"
"let format .= ",FAIL%\\t%.%#"
" Match and ignore everything else in multi-line messages.
let format .= ",%-C%.%#"
" Match and ignore everything else not in a multi-line message:
let format .= ",%-G%.%#"
let s:efm = format
return s:efm
endfunction
" vim: sw=2 ts=2 et

@ -0,0 +1,123 @@
func! Test_GoTest() abort
let expected = [
\ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'},
\ {'lnum': 13, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'sub badness'},
\ {'lnum': 15, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'badness'},
\ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'},
\ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 25, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'},
\ {'lnum': 42, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
\ ]
call s:test('play/play_test.go', expected)
endfunc
func! Test_GoTestConcurrentPanic()
let expected = [
\ {'lnum': 49, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: concurrent fail'}
\ ]
call s:test('play/play_test.go', expected, "-run", "TestConcurrentPanic")
endfunc
func! Test_GoTestVerbose() abort
let expected = [
\ {'lnum': 12, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'log message'},
\ {'lnum': 13, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'sub badness'},
\ {'lnum': 15, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'badness'},
\ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'},
\ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 25, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
\ {'lnum': 31, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'goodness'},
\ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'},
\ {'lnum': 42, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
\ ]
call s:test('play/play_test.go', expected, "-v")
endfunc
func! Test_GoTestCompilerError() abort
let expected = [
\ {'lnum': 6, 'bufnr': 6, 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'syntax error: unexpected newline, expecting comma or )'}
\ ]
call s:test('compilerror/compilerror_test.go', expected)
endfunc
func! s:test(file, expected, ...) abort
if has('nvim')
" nvim mostly shows test errors correctly, but the the expected errors are
" slightly different; buffer numbers are not the same and stderr doesn't
" seem to be redirected to the job, so the lines from the panic aren't in
" the output to be parsed, and hence are not in the quickfix lists. Once
" those two issues are resolved, this early return should be removed so
" the tests will run for Neovim, too.
return
endif
let $GOPATH = fnameescape(expand("%:p:h")) . '/test-fixtures/test'
silent exe 'e ' . $GOPATH . '/src/' . a:file
" clear the quickfix lists
call setqflist([], 'r')
let args = [1,0]
if a:0
let args += a:000
endif
" run the tests
call call(function('go#test#Test'), args)
let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile
" for some reason, when run headless, the quickfix lists includes a line
" that should have been filtered out; remove it manually. The line is not
" present when run manually.
let i = 0
while i < len(actual)
if actual[i].text =~# '^=== RUN .*'
call remove(actual, i)
endif
let i += 1
endwhile
call assert_equal(len(a:expected), len(actual), "number of errors")
if len(a:expected) != len(actual)
return
endif
let i = 0
while i < len(a:expected)
let expected_item = a:expected[i]
let actual_item = actual[i]
let i += 1
call assert_equal(expected_item.bufnr, actual_item.bufnr, "bufnr")
call assert_equal(expected_item.lnum, actual_item.lnum, "lnum")
call assert_equal(expected_item.col, actual_item.col, "col")
call assert_equal(expected_item.vcol, actual_item.vcol, "vcol")
call assert_equal(expected_item.nr, actual_item.nr, "nr")
call assert_equal(expected_item.pattern, actual_item.pattern, "pattern")
let expected_text = s:normalize_durations(expected_item.text)
let actual_text = s:normalize_durations(actual_item.text)
call assert_equal(expected_text, actual_text, "text")
call assert_equal(expected_item.type, actual_item.type, "type")
call assert_equal(expected_item.valid, actual_item.valid, "valid")
endwhile
endfunc
func! s:normalize_durations(str) abort
return substitute(a:str, '[0-9]\+\(\.[0-9]\+\)\?s', '0.000s', 'g')
endfunc
" vim: sw=2 ts=2 et

@ -6,6 +6,10 @@ if !exists("g:go_textobj_include_function_doc")
let g:go_textobj_include_function_doc = 1 let g:go_textobj_include_function_doc = 1
endif endif
if !exists("g:go_textobj_include_variable")
let g:go_textobj_include_variable = 1
endif
" ( ) motions " ( ) motions
" { } motions " { } motions
" s for sentence " s for sentence
@ -13,14 +17,14 @@ endif
" < > " < >
" t for tag " t for tag
function! go#textobj#Function(mode) function! go#textobj#Function(mode) abort
let offset = go#util#OffsetCursor() let offset = go#util#OffsetCursor()
let fname = expand("%:p") let fname = shellescape(expand("%:p"))
if &modified if &modified
" Write current unsaved buffer to a temp file and use the modified content " Write current unsaved buffer to a temp file and use the modified content
let l:tmpname = tempname() let l:tmpname = tempname()
call writefile(getline(1, '$'), l:tmpname) call writefile(go#util#GetLines(), l:tmpname)
let fname = l:tmpname let fname = l:tmpname
endif endif
@ -36,8 +40,8 @@ function! go#textobj#Function(mode)
let command .= " -parse-comments" let command .= " -parse-comments"
endif endif
let out = system(command) let out = go#util#System(command)
if v:shell_error != 0 if go#util#ShellError() != 0
call go#util#EchoError(out) call go#util#EchoError(out)
return return
endif endif
@ -60,6 +64,16 @@ function! go#textobj#Function(mode)
" want's to include doc comments for function declarations " want's to include doc comments for function declarations
if has_key(info, 'doc') && g:go_textobj_include_function_doc if has_key(info, 'doc') && g:go_textobj_include_function_doc
call cursor(info.doc.line, info.doc.col) call cursor(info.doc.line, info.doc.col)
elseif info['sig']['name'] == '' && g:go_textobj_include_variable
" one liner anonymous functions
if info.lbrace.line == info.rbrace.line
" jump to first nonblack char, to get the correct column
call cursor(info.lbrace.line, 0 )
normal! ^
call cursor(info.func.line, col("."))
else
call cursor(info.func.line, info.rbrace.col)
endif
else else
call cursor(info.func.line, info.func.col) call cursor(info.func.line, info.func.col)
endif endif
@ -84,7 +98,7 @@ function! go#textobj#Function(mode)
call cursor(info.rbrace.line-1, 1) call cursor(info.rbrace.line-1, 1)
endfunction endfunction
function! go#textobj#FunctionJump(mode, direction) function! go#textobj#FunctionJump(mode, direction) abort
" get count of the motion. This should be done before all the normal " get count of the motion. This should be done before all the normal
" expressions below as those reset this value(because they have zero " expressions below as those reset this value(because they have zero
" count!). We abstract -1 because the index starts from 0 in motion. " count!). We abstract -1 because the index starts from 0 in motion.
@ -103,11 +117,11 @@ function! go#textobj#FunctionJump(mode, direction)
let offset = go#util#OffsetCursor() let offset = go#util#OffsetCursor()
let fname = expand("%:p") let fname = shellescape(expand("%:p"))
if &modified if &modified
" Write current unsaved buffer to a temp file and use the modified content " Write current unsaved buffer to a temp file and use the modified content
let l:tmpname = tempname() let l:tmpname = tempname()
call writefile(getline(1, '$'), l:tmpname) call writefile(go#util#GetLines(), l:tmpname)
let fname = l:tmpname let fname = l:tmpname
endif endif
@ -129,8 +143,8 @@ function! go#textobj#FunctionJump(mode, direction)
let command .= " -parse-comments" let command .= " -parse-comments"
endif endif
let out = system(command) let out = go#util#System(command)
if v:shell_error != 0 if go#util#ShellError() != 0
call go#util#EchoError(out) call go#util#EchoError(out)
return return
endif endif
@ -177,4 +191,4 @@ function! go#textobj#FunctionJump(mode, direction)
keepjumps call cursor(info.func.line, 1) keepjumps call cursor(info.func.line, 1)
endfunction endfunction
" vim:ts=2:sw=2:et " vim: sw=2 ts=2 et

@ -1,38 +1,72 @@
function! go#tool#Files() " From "go list -h".
function! go#tool#ValidFiles(...)
let l:list = ["GoFiles", "CgoFiles", "IgnoredGoFiles", "CFiles", "CXXFiles",
\ "MFiles", "HFiles", "FFiles", "SFiles", "SwigFiles", "SwigCXXFiles",
\ "SysoFiles", "TestGoFiles", "XTestGoFiles"]
" Used as completion
if len(a:000) > 0
let l:list = filter(l:list, 'strpart(v:val, 0, len(a:1)) == a:1')
endif
return l:list
endfunction
function! go#tool#Files(...) abort
if len(a:000) > 0
let source_files = a:000
else
let source_files = ['GoFiles']
endif
let combined = ''
for sf in source_files
" Strip dot in case people used ":GoFiles .GoFiles".
let sf = substitute(sf, '^\.', '', '')
" Make sure the passed options are valid.
if index(go#tool#ValidFiles(), sf) == -1
echoerr "unknown source file variable: " . sf
endif
if go#util#IsWin() if go#util#IsWin()
let command = 'go list -f "{{range $f := .GoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}"' let combined .= '{{range $f := .' . sf . '}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}\{{$f}}{{printf \"\n\"}}{{end}}'
else else
let command = "go list -f '{{range $f := .GoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}'" let combined .= "{{range $f := ." . sf . "}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}{{range $f := .CgoFiles}}{{$.Dir}}/{{$f}}{{printf \"\\n\"}}{{end}}"
endif endif
let out = go#tool#ExecuteInDir(command) endfor
let out = go#tool#ExecuteInDir('go list -f ' . shellescape(combined))
return split(out, '\n') return split(out, '\n')
endfunction endfunction
function! go#tool#Deps() function! go#tool#Deps() abort
if go#util#IsWin() if go#util#IsWin()
let command = 'go list -f "{{range $f := .Deps}}{{$f}}{{printf \"\n\"}}{{end}}"' let format = '{{range $f := .Deps}}{{$f}}{{printf \"\n\"}}{{end}}'
else else
let command = "go list -f $'{{range $f := .Deps}}{{$f}}\n{{end}}'" let format = "{{range $f := .Deps}}{{$f}}\n{{end}}"
endif endif
let command = 'go list -f '.shellescape(format)
let out = go#tool#ExecuteInDir(command) let out = go#tool#ExecuteInDir(command)
return split(out, '\n') return split(out, '\n')
endfunction endfunction
function! go#tool#Imports() function! go#tool#Imports() abort
let imports = {} let imports = {}
if go#util#IsWin() if go#util#IsWin()
let command = 'go list -f "{{range $f := .Imports}}{{$f}}{{printf \"\n\"}}{{end}}"' let format = '{{range $f := .Imports}}{{$f}}{{printf \"\n\"}}{{end}}'
else else
let command = "go list -f $'{{range $f := .Imports}}{{$f}}\n{{end}}'" let format = "{{range $f := .Imports}}{{$f}}{{printf \"\\n\"}}{{end}}"
endif endif
let command = 'go list -f '.shellescape(format)
let out = go#tool#ExecuteInDir(command) let out = go#tool#ExecuteInDir(command)
if v:shell_error if go#util#ShellError() != 0
echo out echo out
return imports return imports
endif endif
for package_path in split(out, '\n') for package_path in split(out, '\n')
let cmd = "go list -f {{.Name}} " . package_path let cmd = "go list -f '{{.Name}}' " . shellescape(package_path)
let package_name = substitute(go#tool#ExecuteInDir(cmd), '\n$', '', '') let package_name = substitute(go#tool#ExecuteInDir(cmd), '\n$', '', '')
let imports[package_name] = package_path let imports[package_name] = package_path
endfor endfor
@ -40,7 +74,28 @@ function! go#tool#Imports()
return imports return imports
endfunction endfunction
function! go#tool#ParseErrors(lines) function! go#tool#Info(auto) abort
let l:mode = get(g:, 'go_info_mode', 'gocode')
if l:mode == 'gocode'
call go#complete#Info(a:auto)
elseif l:mode == 'guru'
call go#guru#DescribeInfo()
else
call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [gocode, guru]')
endif
endfunction
function! go#tool#PackageName() abort
let command = "go list -f \"{{.Name}}\""
let out = go#tool#ExecuteInDir(command)
if go#util#ShellError() != 0
return -1
endif
return split(out, '\n')[0]
endfunction
function! go#tool#ParseErrors(lines) abort
let errors = [] let errors = []
for line in a:lines for line in a:lines
@ -72,7 +127,7 @@ endfunction
"FilterValids filters the given items with only items that have a valid "FilterValids filters the given items with only items that have a valid
"filename. Any non valid filename is filtered out. "filename. Any non valid filename is filtered out.
function! go#tool#FilterValids(items) function! go#tool#FilterValids(items) abort
" Remove any nonvalid filename from the location list to avoid opening an " Remove any nonvalid filename from the location list to avoid opening an
" empty buffer. See https://github.com/fatih/vim-go/issues/287 for " empty buffer. See https://github.com/fatih/vim-go/issues/287 for
" details. " details.
@ -107,49 +162,57 @@ function! go#tool#FilterValids(items)
endfunction endfunction
function! go#tool#ExecuteInDir(cmd) abort function! go#tool#ExecuteInDir(cmd) abort
let old_gopath = $GOPATH " Verify that the directory actually exists. If the directory does not
let $GOPATH = go#path#Detect() " exist, then assume that the a:cmd should not be executed. Callers expect
" to check v:shell_error (via go#util#ShellError()), so execute a command
" that will return an error as if a:cmd was run and exited with an error.
" This helps avoid errors when working with plugins that use virtual files
" that don't actually exist on the file system (e.g. vim-fugitive's
" GitDiff).
if !isdirectory(expand("%:p:h"))
let [out, err] = go#util#Exec(["false"])
return ''
endif
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
let dir = getcwd() let dir = getcwd()
try try
execute cd . fnameescape(expand("%:p:h")) execute cd . fnameescape(expand("%:p:h"))
let out = system(a:cmd) let out = go#util#System(a:cmd)
finally finally
execute cd . fnameescape(dir) execute cd . fnameescape(dir)
endtry endtry
let $GOPATH = old_gopath
return out return out
endfunction endfunction
" Exists checks whether the given importpath exists or not. It returns 0 if " Exists checks whether the given importpath exists or not. It returns 0 if
" the importpath exists under GOPATH. " the importpath exists under GOPATH.
function! go#tool#Exists(importpath) function! go#tool#Exists(importpath) abort
let command = "go list ". a:importpath let command = "go list ". a:importpath
let out = go#tool#ExecuteInDir(command) let out = go#tool#ExecuteInDir(command)
if v:shell_error if go#util#ShellError() != 0
return -1 return -1
endif endif
return 0 return 0
endfunction endfunction
" following two functions are from: https://github.com/mattn/gist-vim " following two functions are from: https://github.com/mattn/gist-vim
" thanks @mattn " thanks @mattn
function! s:get_browser_command() function! s:get_browser_command() abort
let go_play_browser_command = get(g:, 'go_play_browser_command', '') let go_play_browser_command = get(g:, 'go_play_browser_command', '')
if go_play_browser_command == '' if go_play_browser_command == ''
if go#util#IsWin() if go#util#IsWin()
let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%' let go_play_browser_command = '!start rundll32 url.dll,FileProtocolHandler %URL%'
elseif has('mac') || has('macunix') || has('gui_macvim') || system('uname') =~? '^darwin' elseif go#util#IsMac()
let go_play_browser_command = 'open %URL%' let go_play_browser_command = 'open %URL%'
elseif executable('xdg-open') elseif executable('xdg-open')
let go_play_browser_command = 'xdg-open %URL%' let go_play_browser_command = 'xdg-open %URL%'
elseif executable('firefox') elseif executable('firefox')
let go_play_browser_command = 'firefox %URL% &' let go_play_browser_command = 'firefox %URL% &'
elseif executable('chromium')
let go_play_browser_command = 'chromium %URL% &'
else else
let go_play_browser_command = '' let go_play_browser_command = ''
endif endif
@ -157,7 +220,7 @@ function! s:get_browser_command()
return go_play_browser_command return go_play_browser_command
endfunction endfunction
function! go#tool#OpenBrowser(url) function! go#tool#OpenBrowser(url) abort
let cmd = s:get_browser_command() let cmd = s:get_browser_command()
if len(cmd) == 0 if len(cmd) == 0
redraw redraw
@ -168,15 +231,15 @@ function! go#tool#OpenBrowser(url)
return return
endif endif
if cmd =~ '^!' if cmd =~ '^!'
let cmd = substitute(cmd, '%URL%', '\=shellescape(a:url)', 'g') let cmd = substitute(cmd, '%URL%', '\=escape(shellescape(a:url),"#")', 'g')
silent! exec cmd silent! exec cmd
elseif cmd =~ '^:[A-Z]' elseif cmd =~ '^:[A-Z]'
let cmd = substitute(cmd, '%URL%', '\=a:url', 'g') let cmd = substitute(cmd, '%URL%', '\=escape(a:url,"#")', 'g')
exec cmd exec cmd
else else
let cmd = substitute(cmd, '%URL%', '\=shellescape(a:url)', 'g') let cmd = substitute(cmd, '%URL%', '\=shellescape(a:url)', 'g')
call system(cmd) call go#util#System(cmd)
endif endif
endfunction endfunction
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -0,0 +1,23 @@
func! Test_ExecuteInDir() abort
let l:tmp = gotest#write_file('a/a.go', ['package a'])
try
let l:out = go#tool#ExecuteInDir("pwd")
call assert_equal(l:tmp . "/src/a\n", l:out)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
func! Test_ExecuteInDir_nodir() abort
let l:tmp = go#util#tempdir("executeindir")
exe ':e ' . l:tmp . '/new-dir/a'
try
let l:out = go#tool#ExecuteInDir("pwd")
call assert_equal('', l:out)
finally
call delete(l:tmp, 'rf')
endtry
endfunc
" vim: sw=2 ts=2 et

@ -1,7 +1,12 @@
let s:buf_nr = -1 let s:buf_nr = -1
"OpenWindow opens a new scratch window and put's the content into the window "OpenWindow opens a new scratch window and put's the content into the window
function! go#ui#OpenWindow(title, content) function! go#ui#OpenWindow(title, content, filetype) abort
" Ensure there's only one return window in this session/tabpage
call go#util#Windo("unlet! w:vim_go_return_window")
" Mark the window we're leaving as such
let w:vim_go_return_window = 1
" reuse existing buffer window if it exists otherwise create a new one " reuse existing buffer window if it exists otherwise create a new one
if !bufexists(s:buf_nr) if !bufexists(s:buf_nr)
execute 'botright new' execute 'botright new'
@ -14,24 +19,21 @@ function! go#ui#OpenWindow(title, content)
execute bufwinnr(s:buf_nr) . 'wincmd w' execute bufwinnr(s:buf_nr) . 'wincmd w'
endif endif
" Resize window to content length
" Keep minimum height to 10, if there is more just increase it that it
" occupies all results
let buffer_height = 10
if len(a:content) < buffer_height
exe 'resize ' . buffer_height
else
exe 'resize' . len(a:content) exe 'resize' . len(a:content)
endif
execute "setlocal filetype=".a:filetype
" some sane default values for a readonly buffer " some sane default values for a readonly buffer
setlocal filetype=vimgo
setlocal bufhidden=delete setlocal bufhidden=delete
setlocal buftype=nofile setlocal buftype=nofile
setlocal noswapfile setlocal noswapfile
setlocal nobuflisted setlocal nobuflisted
setlocal winfixheight setlocal winfixheight
setlocal cursorline " make it easy to distinguish setlocal cursorline " make it easy to distinguish
setlocal nonumber
setlocal norelativenumber
setlocal showbreak=""
" we need this to purge the buffer content " we need this to purge the buffer content
setlocal modifiable setlocal modifiable
@ -47,23 +49,45 @@ function! go#ui#OpenWindow(title, content)
" set it back to non modifiable " set it back to non modifiable
setlocal nomodifiable setlocal nomodifiable
" Remove the '... [New File]' message line from the command line
echon
endfunction endfunction
function! go#ui#GetReturnWindow() abort
for l:wn in range(1, winnr("$"))
if !empty(getwinvar(l:wn, "vim_go_return_window"))
return l:wn
endif
endfor
endfunction
" CloseWindow closes the current window " CloseWindow closes the current window
function! go#ui#CloseWindow() function! go#ui#CloseWindow() abort
close " Close any window associated with the ui buffer, if it's there
echo "" if bufexists(s:buf_nr)
let ui_window_number = bufwinnr(s:buf_nr)
if ui_window_number != -1
execute ui_window_number . 'close'
endif
endif
"return to original window, if it's there
let l:rw = go#ui#GetReturnWindow()
if !empty(l:rw)
execute l:rw . 'wincmd w'
unlet! w:vim_go_return_window
endif
endfunction endfunction
" OpenDefinition parses the current line and jumps to it by openening a new " OpenDefinition parses the current line and jumps to it by openening a new
" tab " tab
function! go#ui#OpenDefinition(filter) function! go#ui#OpenDefinition(filter) abort
let curline = getline('.') let curline = getline('.')
" don't touch our first line or any blank line " don't touch our first line or any blank line
if curline =~ a:filter || curline =~ "^$" if curline =~ a:filter || curline =~ "^$"
" supress information about calling this function " suppress information about calling this function
echo "" echo ""
return return
endif endif
@ -87,3 +111,4 @@ function! go#ui#OpenDefinition(filter)
norm! zz norm! zz
endfunction endfunction
" vim: sw=2 ts=2 et

@ -1,5 +1,5 @@
" PathSep returns the appropriate OS specific path separator. " PathSep returns the appropriate OS specific path separator.
function! go#util#PathSep() function! go#util#PathSep() abort
if go#util#IsWin() if go#util#IsWin()
return '\' return '\'
endif endif
@ -7,7 +7,7 @@ function! go#util#PathSep()
endfunction endfunction
" PathListSep returns the appropriate OS specific path list separator. " PathListSep returns the appropriate OS specific path list separator.
function! go#util#PathListSep() function! go#util#PathListSep() abort
if go#util#IsWin() if go#util#IsWin()
return ";" return ";"
endif endif
@ -15,7 +15,7 @@ function! go#util#PathListSep()
endfunction endfunction
" LineEnding returns the correct line ending, based on the current fileformat " LineEnding returns the correct line ending, based on the current fileformat
function! go#util#LineEnding() function! go#util#LineEnding() abort
if &fileformat == 'dos' if &fileformat == 'dos'
return "\r\n" return "\r\n"
elseif &fileformat == 'mac' elseif &fileformat == 'mac'
@ -25,8 +25,14 @@ function! go#util#LineEnding()
return "\n" return "\n"
endfunction endfunction
" Join joins any number of path elements into a single path, adding a
" Separator if necessary and returns the result
function! go#util#Join(...) abort
return join(a:000, go#util#PathSep())
endfunction
" IsWin returns 1 if current OS is Windows or 0 otherwise " IsWin returns 1 if current OS is Windows or 0 otherwise
function! go#util#IsWin() function! go#util#IsWin() abort
let win = ['win16', 'win32', 'win64', 'win95'] let win = ['win16', 'win32', 'win64', 'win95']
for w in win for w in win
if (has(w)) if (has(w))
@ -37,9 +43,136 @@ function! go#util#IsWin()
return 0 return 0
endfunction endfunction
" IsMac returns 1 if current OS is macOS or 0 otherwise.
function! go#util#IsMac() abort
return has('mac') ||
\ has('macunix') ||
\ has('gui_macvim') ||
\ go#util#System('uname') =~? '^darwin'
endfunction
" Checks if using:
" 1) Windows system,
" 2) And has cygpath executable,
" 3) And uses *sh* as 'shell'
function! go#util#IsUsingCygwinShell()
return go#util#IsWin() && executable('cygpath') && &shell =~ '.*sh.*'
endfunction
function! go#util#has_job() abort
" job was introduced in 7.4.xxx however there are multiple bug fixes and one
" of the latest is 8.0.0087 which is required for a stable async API.
return has('job') && has("patch-8.0.0087")
endfunction
let s:env_cache = {}
" env returns the go environment variable for the given key. Where key can be
" GOARCH, GOOS, GOROOT, etc... It caches the result and returns the cached
" version.
function! go#util#env(key) abort
let l:key = tolower(a:key)
if has_key(s:env_cache, l:key)
return s:env_cache[l:key]
endif
if executable('go')
let l:var = call('go#util#'.l:key, [])
if go#util#ShellError() != 0
call go#util#EchoError(printf("'go env %s' failed", toupper(l:key)))
return ''
endif
else
let l:var = eval("$".toupper(a:key))
endif
let s:env_cache[l:key] = l:var
return l:var
endfunction
" goarch returns 'go env GOARCH'. This is an internal function and shouldn't
" be used. Instead use 'go#util#env("goarch")'
function! go#util#goarch() abort
return substitute(go#util#System('go env GOARCH'), '\n', '', 'g')
endfunction
" goos returns 'go env GOOS'. This is an internal function and shouldn't
" be used. Instead use 'go#util#env("goos")'
function! go#util#goos() abort
return substitute(go#util#System('go env GOOS'), '\n', '', 'g')
endfunction
" goroot returns 'go env GOROOT'. This is an internal function and shouldn't
" be used. Instead use 'go#util#env("goroot")'
function! go#util#goroot() abort
return substitute(go#util#System('go env GOROOT'), '\n', '', 'g')
endfunction
" gopath returns 'go env GOPATH'. This is an internal function and shouldn't
" be used. Instead use 'go#util#env("gopath")'
function! go#util#gopath() abort
return substitute(go#util#System('go env GOPATH'), '\n', '', 'g')
endfunction
function! go#util#osarch() abort
return go#util#env("goos") . '_' . go#util#env("goarch")
endfunction
" Run a shell command.
"
" It will temporary set the shell to /bin/sh for Unix-like systems if possible,
" so that we always use a standard POSIX-compatible Bourne shell (and not e.g.
" csh, fish, etc.) See #988 and #1276.
function! s:system(cmd, ...) abort
" Preserve original shell and shellredir values
let l:shell = &shell
let l:shellredir = &shellredir
if !go#util#IsWin() && executable('/bin/sh')
set shell=/bin/sh shellredir=>%s\ 2>&1
endif
try
return call('system', [a:cmd] + a:000)
finally
" Restore original values
let &shell = l:shell
let &shellredir = l:shellredir
endtry
endfunction
" System runs a shell command "str". Every arguments after "str" is passed to
" stdin.
function! go#util#System(str, ...) abort
return call('s:system', [a:str] + a:000)
endfunction
" Exec runs a shell command "cmd", which must be a list, one argument per item.
" Every list entry will be automatically shell-escaped
" Every other argument is passed to stdin.
function! go#util#Exec(cmd, ...) abort
if len(a:cmd) == 0
call go#util#EchoError("go#util#Exec() called with empty a:cmd")
return
endif
" CheckBinPath will show a warning for us.
let l:bin = go#path#CheckBinPath(a:cmd[0])
if empty(l:bin)
return ["", 1]
endif
let l:out = call('s:system', [go#util#Shelljoin([l:bin] + a:cmd[1:])] + a:000)
return [l:out, go#util#ShellError()]
endfunction
function! go#util#ShellError() abort
return v:shell_error
endfunction
" StripPath strips the path's last character if it's a path separator. " StripPath strips the path's last character if it's a path separator.
" example: '/foo/bar/' -> '/foo/bar' " example: '/foo/bar/' -> '/foo/bar'
function! go#util#StripPathSep(path) function! go#util#StripPathSep(path) abort
let last_char = strlen(a:path) - 1 let last_char = strlen(a:path) - 1
if a:path[last_char] == go#util#PathSep() if a:path[last_char] == go#util#PathSep()
return strpart(a:path, 0, last_char) return strpart(a:path, 0, last_char)
@ -50,13 +183,13 @@ endfunction
" StripTrailingSlash strips the trailing slash from the given path list. " StripTrailingSlash strips the trailing slash from the given path list.
" example: ['/foo/bar/'] -> ['/foo/bar'] " example: ['/foo/bar/'] -> ['/foo/bar']
function! go#util#StripTrailingSlash(paths) function! go#util#StripTrailingSlash(paths) abort
return map(copy(a:paths), 'go#util#StripPathSep(v:val)') return map(copy(a:paths), 'go#util#StripPathSep(v:val)')
endfunction endfunction
" Shelljoin returns a shell-safe string representation of arglist. The " Shelljoin returns a shell-safe string representation of arglist. The
" {special} argument of shellescape() may optionally be passed. " {special} argument of shellescape() may optionally be passed.
function! go#util#Shelljoin(arglist, ...) function! go#util#Shelljoin(arglist, ...) abort
try try
let ssl_save = &shellslash let ssl_save = &shellslash
set noshellslash set noshellslash
@ -70,9 +203,19 @@ function! go#util#Shelljoin(arglist, ...)
endtry endtry
endfunction endfunction
fu! go#util#Shellescape(arg)
try
let ssl_save = &shellslash
set noshellslash
return shellescape(a:arg)
finally
let &shellslash = ssl_save
endtry
endf
" Shelllist returns a shell-safe representation of the items in the given " Shelllist returns a shell-safe representation of the items in the given
" arglist. The {special} argument of shellescape() may optionally be passed. " arglist. The {special} argument of shellescape() may optionally be passed.
function! go#util#Shelllist(arglist, ...) function! go#util#Shelllist(arglist, ...) abort
try try
let ssl_save = &shellslash let ssl_save = &shellslash
set noshellslash set noshellslash
@ -86,7 +229,7 @@ function! go#util#Shelllist(arglist, ...)
endfunction endfunction
" Returns the byte offset for line and column " Returns the byte offset for line and column
function! go#util#Offset(line, col) function! go#util#Offset(line, col) abort
if &encoding != 'utf-8' if &encoding != 'utf-8'
let sep = go#util#LineEnding() let sep = go#util#LineEnding()
let buf = a:line == 1 ? '' : (join(getline(1, a:line-1), sep) . sep) let buf = a:line == 1 ? '' : (join(getline(1, a:line-1), sep) . sep)
@ -97,27 +240,159 @@ function! go#util#Offset(line, col)
endfunction endfunction
" "
" Returns the byte offset for the cursor " Returns the byte offset for the cursor
function! go#util#OffsetCursor() function! go#util#OffsetCursor() abort
return go#util#Offset(line('.'), col('.')) return go#util#Offset(line('.'), col('.'))
endfunction endfunction
" TODO(arslan): I couldn't parameterize the highlight types. Check if we can " Windo is like the built-in :windo, only it returns to the window the command
" simplify the following functions " was issued from
function! go#util#Windo(command) abort
let s:currentWindow = winnr()
try
execute "windo " . a:command
finally
execute s:currentWindow. "wincmd w"
unlet s:currentWindow
endtry
endfunction
function! go#util#EchoSuccess(msg) " snippetcase converts the given word to given preferred snippet setting type
redraws! | echon "vim-go: " | echohl Function | echon a:msg | echohl None " case.
function! go#util#snippetcase(word) abort
let l:snippet_case = get(g:, 'go_addtags_transform', "snakecase")
if l:snippet_case == "snakecase"
return go#util#snakecase(a:word)
elseif l:snippet_case == "camelcase"
return go#util#camelcase(a:word)
else
return a:word " do nothing
endif
endfunction endfunction
function! go#util#EchoError(msg) " snakecase converts a string to snake case. i.e: FooBar -> foo_bar
redraws! | echon "vim-go: " | echohl ErrorMsg | echon a:msg | echohl None " Copied from tpope/vim-abolish
function! go#util#snakecase(word) abort
let word = substitute(a:word, '::', '/', 'g')
let word = substitute(word, '\(\u\+\)\(\u\l\)', '\1_\2', 'g')
let word = substitute(word, '\(\l\|\d\)\(\u\)', '\1_\2', 'g')
let word = substitute(word, '[.-]', '_', 'g')
let word = tolower(word)
return word
endfunction endfunction
function! go#util#EchoWarning(msg) " camelcase converts a string to camel case. e.g. FooBar or foo_bar will become
redraws! | echon "vim-go: " | echohl WarningMsg | echon a:msg | echohl None " fooBar.
" Copied from tpope/vim-abolish.
function! go#util#camelcase(word) abort
let word = substitute(a:word, '-', '_', 'g')
if word !~# '_' && word =~# '\l'
return substitute(word, '^.', '\l&', '')
else
return substitute(word, '\C\(_\)\=\(.\)', '\=submatch(1)==""?tolower(submatch(2)) : toupper(submatch(2))','g')
endif
endfunction endfunction
" pascalcase converts a string to 'PascalCase'. e.g. fooBar or foo_bar will
" become FooBar.
function! go#util#pascalcase(word) abort
let word = go#util#camelcase(a:word)
return toupper(word[0]) . word[1:]
endfunction
" Echo a message to the screen and highlight it with the group in a:hi.
"
" The message can be a list or string; every line with be :echomsg'd separately.
function! s:echo(msg, hi)
let l:msg = []
if type(a:msg) != type([])
let l:msg = split(a:msg, "\n")
else
let l:msg = a:msg
endif
" Tabs display as ^I or <09>, so manually expand them.
let l:msg = map(l:msg, 'substitute(v:val, "\t", " ", "")')
exe 'echohl ' . a:hi
for line in l:msg
echom "vim-go: " . line
endfor
echohl None
endfunction
function! go#util#EchoSuccess(msg)
call s:echo(a:msg, 'Function')
endfunction
function! go#util#EchoError(msg)
call s:echo(a:msg, 'ErrorMsg')
endfunction
function! go#util#EchoWarning(msg)
call s:echo(a:msg, 'WarningMsg')
endfunction
function! go#util#EchoProgress(msg) function! go#util#EchoProgress(msg)
redraws! | echon "vim-go: " | echohl Identifier | echon a:msg | echohl None call s:echo(a:msg, 'Identifier')
endfunction
function! go#util#EchoInfo(msg)
call s:echo(a:msg, 'Debug')
endfunction
" Get all lines in the buffer as a a list.
function! go#util#GetLines()
let buf = getline(1, '$')
if &encoding != 'utf-8'
let buf = map(buf, 'iconv(v:val, &encoding, "utf-8")')
endif
if &l:fileformat == 'dos'
" XXX: line2byte() depend on 'fileformat' option.
" so if fileformat is 'dos', 'buf' must include '\r'.
let buf = map(buf, 'v:val."\r"')
endif
return buf
endfunction
" Convert the current buffer to the "archive" format of
" golang.org/x/tools/go/buildutil:
" https://godoc.org/golang.org/x/tools/go/buildutil#ParseOverlayArchive
"
" > The archive consists of a series of files. Each file consists of a name, a
" > decimal file size and the file contents, separated by newlinews. No newline
" > follows after the file contents.
function! go#util#archive()
let l:buffer = join(go#util#GetLines(), "\n")
return expand("%:p:gs!\\!/!") . "\n" . strlen(l:buffer) . "\n" . l:buffer
endfunction
" Make a named temporary directory which starts with "prefix".
"
" Unfortunately Vim's tempname() is not portable enough across various systems;
" see: https://github.com/mattn/vim-go/pull/3#discussion_r138084911
function! go#util#tempdir(prefix) abort
" See :help tempfile
if go#util#IsWin()
let l:dirs = [$TMP, $TEMP, 'c:\tmp', 'c:\temp']
else
let l:dirs = [$TMPDIR, '/tmp', './', $HOME]
endif
let l:dir = ''
for l:d in dirs
if !empty(l:d) && filewritable(l:d) == 2
let l:dir = l:d
break
endif
endfor
if l:dir == ''
echoerr 'Unable to find directory to store temporary directory in'
return
endif
" Not great randomness, but "good enough" for our purpose here.
let l:rnd = sha256(printf('%s%s', localtime(), fnamemodify(bufname(''), ":p")))
let l:tmp = printf("%s/%s%s", l:dir, a:prefix, l:rnd)
call mkdir(l:tmp, 'p', 0700)
return l:tmp
endfunction endfunction
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -1,21 +0,0 @@
"Check if has vimproc
function! go#vimproc#has_vimproc()
if !exists('g:go#use_vimproc')
if go#util#IsWin()
try
call vimproc#version()
let exists_vimproc = 1
catch
let exists_vimproc = 0
endtry
else
let exists_vimproc = 0
endif
let g:go#use_vimproc = exists_vimproc
endif
return g:go#use_vimproc
endfunction
" vim:ts=4:sw=4:et

@ -0,0 +1,105 @@
" Write a Go file to a temporary directory and append this directory to $GOPATH.
"
" The file will written to a:path, which is relative to the temporary directory,
" and this file will be loaded as the current buffer.
"
" The cursor will be placed on the character before any 0x1f byte.
"
" The full path to the created directory is returned, it is the caller's
" responsibility to clean that up!
fun! gotest#write_file(path, contents) abort
let l:dir = go#util#tempdir("vim-go-test/testrun/")
let $GOPATH .= ':' . l:dir
let l:full_path = l:dir . '/src/' . a:path
call mkdir(fnamemodify(l:full_path, ':h'), 'p')
call writefile(a:contents, l:full_path)
exe 'cd ' . l:dir . '/src'
silent exe 'e! ' . a:path
" Set cursor.
let l:lnum = 1
for l:line in a:contents
let l:m = match(l:line, "\x1f")
if l:m > -1
call setpos('.', [0, l:lnum, l:m, 0])
call setline('.', substitute(getline('.'), "\x1f", '', ''))
break
endif
let l:lnum += 1
endfor
return l:dir
endfun
" Load a fixture file from test-fixtures.
"
" The file will be copied to a new GOPATH-compliant temporary directory and
" loaded as the current buffer.
fun! gotest#load_fixture(path) abort
let l:dir = go#util#tempdir("vim-go-test/testrun/")
let $GOPATH .= ':' . l:dir
let l:full_path = l:dir . '/src/' . a:path
call mkdir(fnamemodify(l:full_path, ':h'), 'p')
exe 'cd ' . l:dir . '/src'
silent exe 'noautocmd e ' . a:path
silent exe printf('read %s/test-fixtures/%s', g:vim_go_root, a:path)
silent noautocmd w!
return l:dir
endfun
" Diff the contents of the current buffer to a:want, which should be a list.
" If a:skipHeader is true we won't bother with the package and import
" declarations; so e.g.:
"
" let l:diff = s:diff_buffer(1, ['_ = mail.Address{}'])
"
" will pass, whereas otherwise you'd have to:
"
" let l:diff = s:diff_buffer(0, ['package main', 'import "net/mail", '_ = mail.Address{}'])
fun! gotest#assert_buffer(skipHeader, want) abort
let l:buffer = go#util#GetLines()
if a:skipHeader
for l:lnum in range(0, len(l:buffer) - 1)
" Bit rudimentary, but works reasonably well.
if match(l:buffer[l:lnum], '^\v(func|var|const|import \(|\))') > -1
" vint bug: https://github.com/Kuniwak/vint/issues/179
" vint: -ProhibitUsingUndeclaredVariable
let l:buffer = l:buffer[l:lnum:len(l:buffer)]
break
endif
endfor
endif
" Using ' is often easier so we don't have to escape ".
let l:want = map(a:want, 'substitute(v:val, "\\\\t", "\t", "")')
let l:tmp = go#util#tempdir('assert_buffer')
try
call writefile(l:buffer, l:tmp . '/have')
call writefile(l:want, l:tmp . '/want')
call go#fmt#run('gofmt', l:tmp . '/have', l:tmp . '/have')
call go#fmt#run('gofmt', l:tmp . '/want', l:tmp . '/want')
let [l:out, l:err] = go#util#Exec(["diff", "-u", l:tmp . '/have', l:tmp . '/want'])
finally
call delete(l:tmp . '/have')
call delete(l:tmp . '/want')
call delete(l:tmp, 'd')
endtry
if l:err || l:out != ''
let v:errors = extend(v:errors, split(l:out, "\n"))
endif
endfun
" Diff the contents of the current buffer to the fixture file in a:path.
fun! gotest#assert_fixture(path) abort
let l:want = readfile(printf('%s/test-fixtures/%s', g:vim_go_root, a:path))
call gotest#assert_buffer(0, l:want)
endfun
" vim: sw=2 ts=2 et

@ -0,0 +1,70 @@
let s:save_cpo = &cpoptions
set cpoptions&vim
let s:source = {
\ 'name': 'decls',
\ 'description': 'GoDecls implementation for unite',
\ 'syntax': 'uniteSource__Decls',
\ 'action_table': {},
\ 'hooks': {},
\ }
function! unite#sources#decls#define()
return s:source
endfunction
function! s:source.gather_candidates(args, context) abort
let l:bin_path = go#path#CheckBinPath('motion')
if empty(l:bin_path)
return []
endif
let l:path = expand(get(a:args, 0, '%:p:h'))
if isdirectory(l:path)
let l:mode = 'dir'
elseif filereadable(l:path)
let l:mode = 'file'
else
return []
endif
let l:include = get(g:, 'go_decls_includes', 'func,type')
let l:command = printf('%s -format vim -mode decls -include %s -%s %s', l:bin_path, l:include, l:mode, shellescape(l:path))
let l:candidates = []
try
let l:result = eval(unite#util#system(l:command))
let l:candidates = get(l:result, 'decls', [])
catch
call unite#print_source_error(['command returned invalid response.', v:exception], s:source.name)
endtry
return map(l:candidates, "{
\ 'word': printf('%s :%d :%s', fnamemodify(v:val.filename, ':~:.'), v:val.line, v:val.full),
\ 'kind': 'jump_list',
\ 'action__path': v:val.filename,
\ 'action__line': v:val.line,
\ 'action__col': v:val.col,
\ }")
endfunction
function! s:source.hooks.on_syntax(args, context) abort
syntax match uniteSource__Decls_Filepath /[^:]*\ze:/ contained containedin=uniteSource__Decls
syntax match uniteSource__Decls_Line /\d\+\ze :/ contained containedin=uniteSource__Decls
syntax match uniteSource__Decls_WholeFunction /\vfunc %(\([^)]+\) )?[^(]+/ contained containedin=uniteSource__Decls
syntax match uniteSource__Decls_Function /\S\+\ze(/ contained containedin=uniteSource__Decls_WholeFunction
syntax match uniteSource__Decls_WholeType /type \S\+/ contained containedin=uniteSource__Decls
syntax match uniteSource__Decls_Type /\v( )@<=\S+/ contained containedin=uniteSource__Decls_WholeType
highlight default link uniteSource__Decls_Filepath Comment
highlight default link uniteSource__Decls_Line LineNr
highlight default link uniteSource__Decls_Function Function
highlight default link uniteSource__Decls_Type Type
syntax match uniteSource__Decls_Separator /:/ contained containedin=uniteSource__Decls conceal
syntax match uniteSource__Decls_SeparatorFunction /func / contained containedin=uniteSource__Decls_WholeFunction conceal
syntax match uniteSource__Decls_SeparatorType /type / contained containedin=uniteSource__Decls_WholeType conceal
endfunction
let &cpoptions = s:save_cpo
unlet s:save_cpo
" vim: sw=2 ts=2 et

@ -4,10 +4,10 @@
" "
" compiler/go.vim: Vim compiler file for Go. " compiler/go.vim: Vim compiler file for Go.
if exists("current_compiler") if exists("g:current_compiler")
finish finish
endif endif
let current_compiler = "go" let g:current_compiler = "go"
if exists(":CompilerSet") != 2 if exists(":CompilerSet") != 2
command -nargs=* CompilerSet setlocal <args> command -nargs=* CompilerSet setlocal <args>
@ -38,4 +38,4 @@ CompilerSet errorformat+=%-G%.%# " All lines not matching a
let &cpo = s:save_cpo let &cpo = s:save_cpo
unlet s:save_cpo unlet s:save_cpo
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

File diff suppressed because it is too large Load Diff

@ -18,6 +18,8 @@ function! s:gofiletype_post()
let &g:fileencodings = s:current_fileencodings let &g:fileencodings = s:current_fileencodings
endfunction endfunction
augroup vim-go-filetype
autocmd!
au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix
au BufRead *.go call s:gofiletype_pre("go") au BufRead *.go call s:gofiletype_pre("go")
au BufReadPost *.go call s:gofiletype_post() au BufReadPost *.go call s:gofiletype_post()
@ -27,5 +29,6 @@ au BufRead *.s call s:gofiletype_pre("asm")
au BufReadPost *.s call s:gofiletype_post() au BufReadPost *.s call s:gofiletype_post()
au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl
augroup end
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -15,3 +15,5 @@ setlocal commentstring=//\ %s
setlocal noexpandtab setlocal noexpandtab
command! -nargs=0 AsmFmt call go#asmfmt#Format() command! -nargs=0 AsmFmt call go#asmfmt#Format()
" vim: sw=2 ts=2 et

@ -29,7 +29,15 @@ if get(g:, "go_doc_keywordprg_enabled", 1)
endif endif
if get(g:, "go_def_mapping_enabled", 1) if get(g:, "go_def_mapping_enabled", 1)
" these are default Vim mappings, we're overriding them to make them
" useful again for Go source code
nnoremap <buffer> <silent> gd :GoDef<cr> nnoremap <buffer> <silent> gd :GoDef<cr>
nnoremap <buffer> <silent> <C-]> :GoDef<cr>
nnoremap <buffer> <silent> <C-LeftMouse> <LeftMouse>:GoDef<cr>
nnoremap <buffer> <silent> g<LeftMouse> <LeftMouse>:GoDef<cr>
nnoremap <buffer> <silent> <C-w><C-]> :<C-u>call go#def#Jump("split")<CR>
nnoremap <buffer> <silent> <C-w>] :<C-u>call go#def#Jump("split")<CR>
nnoremap <buffer> <silent> <C-t> :<C-U>call go#def#StackPop(v:count1)<cr>
endif endif
if get(g:, "go_textobj_enabled", 1) if get(g:, "go_textobj_enabled", 1)
@ -50,8 +58,76 @@ if get(g:, "go_textobj_enabled", 1)
xnoremap <buffer> <silent> [[ :<c-u>call go#textobj#FunctionJump('v', 'prev')<cr> xnoremap <buffer> <silent> [[ :<c-u>call go#textobj#FunctionJump('v', 'prev')<cr>
endif endif
if get(g:, "go_auto_type_info", 0) if get(g:, "go_auto_type_info", 0) || get(g:, "go_auto_sameids", 0)
setlocal updatetime=800 let &l:updatetime= get(g:, "go_updatetime", 800)
endif endif
" vim:ts=4:sw=4:et " NOTE(arslan): experimental, disabled by default, doesn't work well. No
" documentation as well. If anyone feels adventurous, enable the following and
" try to search for Go identifiers ;)
"
" if get(g:, "go_sameid_search_enabled", 0)
" autocmd FileType go nnoremap <buffer> <silent> * :<c-u>call Sameids_search(0)<CR>
" autocmd FileType go nnoremap <buffer> <silent> # :<c-u>call Sameids_search(1)<CR>
" autocmd FileType go nnoremap <buffer> <silent> n :<c-u>call Sameids_repeat(0)<CR>
" autocmd FileType go nnoremap <buffer> <silent> N :<c-u>call Sameids_repeat(1)<CR>
" autocmd FileType go cabbrev nohlsearch <C-r>=Sameids_nohlsearch()<CR>
" endif
" " mode 0: next 1: prev
" function! Sameids_repeat(mode)
" let matches = getmatches()
" if empty(matches)
" return
" endif
" let cur_offset = go#util#OffsetCursor()
" " reverse list to make it easy to find the prev occurrence
" if a:mode
" call reverse(matches)
" endif
" for m in matches
" if !has_key(m, "group")
" return
" endif
" if m.group != "goSameId"
" return
" endif
" let offset = go#util#Offset(m.pos1[0], m.pos1[1])
" if a:mode && cur_offset > offset
" call cursor(m.pos1[0], m.pos1[1])
" return
" elseif !a:mode && cur_offset < offset
" call cursor(m.pos1[0], m.pos1[1])
" return
" endif
" endfor
" " reached start/end, jump to the end/start
" let initial_match = matches[0]
" if !has_key(initial_match, "group")
" return
" endif
" if initial_match.group != "goSameId"
" return
" endif
" call cursor(initial_match.pos1[0], initial_match.pos1[1])
" endfunction
" function! Sameids_search(mode)
" call go#guru#SameIds()
" call Sameids_repeat(a:mode)
" endfunction
" function! Sameids_nohlsearch()
" call go#guru#ClearSameIds()
" return "nohlsearch"
" endfunction
" vim: sw=2 ts=2 et

@ -1,38 +1,59 @@
" gorename " -- gorename
command! -nargs=? GoRename call go#rename#Rename(<bang>0,<f-args>) command! -nargs=? -complete=customlist,go#rename#Complete GoRename call go#rename#Rename(<bang>0, <f-args>)
" oracle " -- guru
command! -nargs=* -complete=customlist,go#package#Complete GoOracleScope call go#oracle#Scope(<f-args>) command! -nargs=* -complete=customlist,go#package#Complete GoGuruScope call go#guru#Scope(<f-args>)
command! -range=% GoImplements call go#oracle#Implements(<count>) command! -range=% GoImplements call go#guru#Implements(<count>)
command! -range=% GoCallees call go#oracle#Callees(<count>) command! -range=% GoWhicherrs call go#guru#Whicherrs(<count>)
command! -range=% GoDescribe call go#oracle#Describe(<count>) command! -range=% GoCallees call go#guru#Callees(<count>)
command! -range=% GoCallers call go#oracle#Callers(<count>) command! -range=% GoDescribe call go#guru#Describe(<count>)
command! -range=% GoCallstack call go#oracle#Callstack(<count>) command! -range=% GoCallers call go#guru#Callers(<count>)
command! -range=% GoFreevars call go#oracle#Freevars(<count>) command! -range=% GoCallstack call go#guru#Callstack(<count>)
command! -range=% GoChannelPeers call go#oracle#ChannelPeers(<count>) command! -range=% GoFreevars call go#guru#Freevars(<count>)
command! -range=% GoReferrers call go#oracle#Referrers(<count>) command! -range=% GoChannelPeers call go#guru#ChannelPeers(<count>)
command! -nargs=? GoOracleTags call go#oracle#Tags(<f-args>) command! -range=% GoReferrers call go#guru#Referrers(<count>)
" tool command! -range=0 GoSameIds call go#guru#SameIds()
command! -nargs=0 GoFiles echo go#tool#Files() command! -range=0 GoSameIdsClear call go#guru#ClearSameIds()
command! -range=0 GoSameIdsToggle call go#guru#ToggleSameIds()
command! -range=0 GoSameIdsAutoToggle call go#guru#AutoToogleSameIds()
" -- tags
command! -nargs=* -range GoAddTags call go#tags#Add(<line1>, <line2>, <count>, <f-args>)
command! -nargs=* -range GoRemoveTags call go#tags#Remove(<line1>, <line2>, <count>, <f-args>)
" -- tool
command! -nargs=* -complete=customlist,go#tool#ValidFiles GoFiles echo go#tool#Files(<f-args>)
command! -nargs=0 GoDeps echo go#tool#Deps() command! -nargs=0 GoDeps echo go#tool#Deps()
command! -nargs=* GoInfo call go#complete#Info(0) command! -nargs=* GoInfo call go#tool#Info(0)
command! -nargs=0 GoAutoTypeInfoToggle call go#complete#ToggleAutoTypeInfo()
" cmd " -- cmd
command! -nargs=* -bang GoBuild call go#cmd#Build(<bang>0,<f-args>) command! -nargs=* -bang GoBuild call go#cmd#Build(<bang>0,<f-args>)
command! -nargs=? -bang GoBuildTags call go#cmd#BuildTags(<bang>0, <f-args>)
command! -nargs=* -bang GoGenerate call go#cmd#Generate(<bang>0,<f-args>) command! -nargs=* -bang GoGenerate call go#cmd#Generate(<bang>0,<f-args>)
command! -nargs=* -bang -complete=file GoRun call go#cmd#Run(<bang>0,<f-args>) command! -nargs=* -bang -complete=file GoRun call go#cmd#Run(<bang>0,<f-args>)
command! -nargs=* -bang GoInstall call go#cmd#Install(<bang>0, <f-args>) command! -nargs=* -bang GoInstall call go#cmd#Install(<bang>0, <f-args>)
command! -nargs=* -bang GoTest call go#cmd#Test(<bang>0, 0, <f-args>)
command! -nargs=* -bang GoTestFunc call go#cmd#TestFunc(<bang>0, <f-args>) " -- test
command! -nargs=* -bang GoTestCompile call go#cmd#Test(<bang>0, 1, <f-args>) command! -nargs=* -bang GoTest call go#test#Test(<bang>0, 0, <f-args>)
command! -nargs=* -bang GoCoverage call go#cmd#Coverage(<bang>0, <f-args>) command! -nargs=* -bang GoTestFunc call go#test#Func(<bang>0, <f-args>)
command! -nargs=* -bang GoTestCompile call go#test#Test(<bang>0, 1, <f-args>)
" -- cover
command! -nargs=* -bang GoCoverage call go#coverage#Buffer(<bang>0, <f-args>)
command! -nargs=* -bang GoCoverageClear call go#coverage#Clear()
command! -nargs=* -bang GoCoverageToggle call go#coverage#BufferToggle(<bang>0, <f-args>)
command! -nargs=* -bang GoCoverageBrowser call go#coverage#Browser(<bang>0, <f-args>)
" -- play " -- play
command! -nargs=0 -range=% GoPlay call go#play#Share(<count>, <line1>, <line2>) command! -nargs=0 -range=% GoPlay call go#play#Share(<count>, <line1>, <line2>)
" -- def " -- def
command! -nargs=* -range GoDef :call go#def#Jump(<f-args>) command! -nargs=* -range GoDef :call go#def#Jump('')
command! -nargs=? GoDefPop :call go#def#StackPop(<f-args>)
command! -nargs=? GoDefStack :call go#def#Stack(<f-args>)
command! -nargs=? GoDefStackClear :call go#def#StackClear(<f-args>)
" -- doc " -- doc
command! -nargs=* -range -complete=customlist,go#package#Complete GoDoc call go#doc#Open('new', 'split', <f-args>) command! -nargs=* -range -complete=customlist,go#package#Complete GoDoc call go#doc#Open('new', 'split', <f-args>)
@ -40,8 +61,12 @@ command! -nargs=* -range -complete=customlist,go#package#Complete GoDocBrowser c
" -- fmt " -- fmt
command! -nargs=0 GoFmt call go#fmt#Format(-1) command! -nargs=0 GoFmt call go#fmt#Format(-1)
command! -nargs=0 GoFmtAutoSaveToggle call go#fmt#ToggleFmtAutoSave()
command! -nargs=0 GoImports call go#fmt#Format(1) command! -nargs=0 GoImports call go#fmt#Format(1)
" -- asmfmt
command! -nargs=0 GoAsmFmtAutoSaveToggle call go#asmfmt#ToggleAsmFmtAutoSave()
" -- import " -- import
command! -nargs=? -complete=customlist,go#package#Complete GoDrop call go#import#SwitchImport(0, '', <f-args>, '') command! -nargs=? -complete=customlist,go#package#Complete GoDrop call go#import#SwitchImport(0, '', <f-args>, '')
command! -nargs=1 -bang -complete=customlist,go#package#Complete GoImport call go#import#SwitchImport(1, '', <f-args>, '<bang>') command! -nargs=1 -bang -complete=customlist,go#package#Complete GoImport call go#import#SwitchImport(1, '', <f-args>, '<bang>')
@ -49,6 +74,7 @@ command! -nargs=* -bang -complete=customlist,go#package#Complete GoImportAs call
" -- linters " -- linters
command! -nargs=* GoMetaLinter call go#lint#Gometa(0, <f-args>) command! -nargs=* GoMetaLinter call go#lint#Gometa(0, <f-args>)
command! -nargs=0 GoMetaLinterAutoSaveToggle call go#lint#ToggleMetaLinterAutoSave()
command! -nargs=* GoLint call go#lint#Golint(<f-args>) command! -nargs=* GoLint call go#lint#Golint(<f-args>)
command! -nargs=* -bang GoVet call go#lint#Vet(<bang>0, <f-args>) command! -nargs=* -bang GoVet call go#lint#Vet(<bang>0, <f-args>)
command! -nargs=* -complete=customlist,go#package#Complete GoErrCheck call go#lint#Errcheck(<f-args>) command! -nargs=* -complete=customlist,go#package#Complete GoErrCheck call go#lint#Errcheck(<f-args>)
@ -56,10 +82,20 @@ command! -nargs=* -complete=customlist,go#package#Complete GoErrCheck call go#li
" -- alternate " -- alternate
command! -bang GoAlternate call go#alternate#Switch(<bang>0, '') command! -bang GoAlternate call go#alternate#Switch(<bang>0, '')
" -- ctrlp " -- decls
if globpath(&rtp, 'plugin/ctrlp.vim') != "" command! -nargs=? -complete=file GoDecls call go#decls#Decls(0, <q-args>)
command! -nargs=? -complete=file GoDecls call ctrlp#init(ctrlp#decls#cmd(0, <q-args>)) command! -nargs=? -complete=dir GoDeclsDir call go#decls#Decls(1, <q-args>)
command! -nargs=? -complete=dir GoDeclsDir call ctrlp#init(ctrlp#decls#cmd(1, <q-args>))
endif " -- impl
command! -nargs=* -complete=customlist,go#impl#Complete GoImpl call go#impl#Impl(<f-args>)
" -- template
command! -nargs=0 GoTemplateAutoCreateToggle call go#template#ToggleAutoCreate()
" -- keyify
command! -nargs=0 GoKeyify call go#keyify#Keyify()
" -- fillstruct
command! -nargs=0 GoFillStruct call go#fillstruct#FillStruct()
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -20,31 +20,43 @@ endif
nnoremap <silent> <Plug>(go-build) :<C-u>call go#cmd#Build(!g:go_jump_to_error)<CR> nnoremap <silent> <Plug>(go-build) :<C-u>call go#cmd#Build(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-generate) :<C-u>call go#cmd#Generate(!g:go_jump_to_error)<CR> nnoremap <silent> <Plug>(go-generate) :<C-u>call go#cmd#Generate(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-install) :<C-u>call go#cmd#Install(!g:go_jump_to_error)<CR> nnoremap <silent> <Plug>(go-install) :<C-u>call go#cmd#Install(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-test) :<C-u>call go#cmd#Test(!g:go_jump_to_error, 0)<CR> nnoremap <silent> <Plug>(go-test) :<C-u>call go#test#Test(!g:go_jump_to_error, 0)<CR>
nnoremap <silent> <Plug>(go-test-func) :<C-u>call go#cmd#TestFunc(!g:go_jump_to_error)<CR> nnoremap <silent> <Plug>(go-test-func) :<C-u>call go#test#Func(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-test-compile) :<C-u>call go#cmd#Test(!g:go_jump_to_error, 1)<CR> nnoremap <silent> <Plug>(go-test-compile) :<C-u>call go#test#Test(!g:go_jump_to_error, 1)<CR>
nnoremap <silent> <Plug>(go-coverage) :<C-u>call go#cmd#Coverage(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-coverage) :<C-u>call go#coverage#Buffer(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-coverage-clear) :<C-u>call go#coverage#Clear()<CR>
nnoremap <silent> <Plug>(go-coverage-toggle) :<C-u>call go#coverage#BufferToggle(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-coverage-browser) :<C-u>call go#coverage#Browser(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-files) :<C-u>call go#tool#Files()<CR> nnoremap <silent> <Plug>(go-files) :<C-u>call go#tool#Files()<CR>
nnoremap <silent> <Plug>(go-deps) :<C-u>call go#tool#Deps()<CR> nnoremap <silent> <Plug>(go-deps) :<C-u>call go#tool#Deps()<CR>
nnoremap <silent> <Plug>(go-info) :<C-u>call go#complete#Info(0)<CR> nnoremap <silent> <Plug>(go-info) :<C-u>call go#tool#Info(0)<CR>
nnoremap <silent> <Plug>(go-import) :<C-u>call go#import#SwitchImport(1, '', expand('<cword>'), '')<CR> nnoremap <silent> <Plug>(go-import) :<C-u>call go#import#SwitchImport(1, '', expand('<cword>'), '')<CR>
nnoremap <silent> <Plug>(go-imports) :<C-u>call go#fmt#Format(1)<CR>
nnoremap <silent> <Plug>(go-implements) :<C-u>call go#oracle#Implements(-1)<CR> nnoremap <silent> <Plug>(go-implements) :<C-u>call go#guru#Implements(-1)<CR>
nnoremap <silent> <Plug>(go-callees) :<C-u>call go#oracle#Callees(-1)<CR> nnoremap <silent> <Plug>(go-callees) :<C-u>call go#guru#Callees(-1)<CR>
nnoremap <silent> <Plug>(go-callers) :<C-u>call go#oracle#Callers(-1)<CR> nnoremap <silent> <Plug>(go-callers) :<C-u>call go#guru#Callers(-1)<CR>
nnoremap <silent> <Plug>(go-describe) :<C-u>call go#oracle#Describe(-1)<CR> nnoremap <silent> <Plug>(go-describe) :<C-u>call go#guru#Describe(-1)<CR>
nnoremap <silent> <Plug>(go-callstack) :<C-u>call go#oracle#Callstack(-1)<CR> nnoremap <silent> <Plug>(go-callstack) :<C-u>call go#guru#Callstack(-1)<CR>
nnoremap <silent> <Plug>(go-freevars) :<C-u>call go#oracle#Freevars(-1)<CR> xnoremap <silent> <Plug>(go-freevars) :<C-u>call go#guru#Freevars(0)<CR>
nnoremap <silent> <Plug>(go-channelpeers) :<C-u>call go#oracle#ChannelPeers(-1)<CR> nnoremap <silent> <Plug>(go-channelpeers) :<C-u>call go#guru#ChannelPeers(-1)<CR>
nnoremap <silent> <Plug>(go-referrers) :<C-u>call go#oracle#Referrers(-1)<CR> nnoremap <silent> <Plug>(go-referrers) :<C-u>call go#guru#Referrers(-1)<CR>
nnoremap <silent> <Plug>(go-sameids) :<C-u>call go#guru#SameIds()<CR>
nnoremap <silent> <Plug>(go-whicherrs) :<C-u>call go#guru#Whicherrs(-1)<CR>
nnoremap <silent> <Plug>(go-sameids-toggle) :<C-u>call go#guru#ToggleSameIds()<CR>
nnoremap <silent> <Plug>(go-rename) :<C-u>call go#rename#Rename(!g:go_jump_to_error)<CR> nnoremap <silent> <Plug>(go-rename) :<C-u>call go#rename#Rename(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-def) :<C-u>call go#def#Jump()<CR> nnoremap <silent> <Plug>(go-def) :<C-u>call go#def#Jump('')<CR>
nnoremap <silent> <Plug>(go-def-vertical) :<C-u>call go#def#JumpMode("vsplit")<CR> nnoremap <silent> <Plug>(go-def-vertical) :<C-u>call go#def#Jump("vsplit")<CR>
nnoremap <silent> <Plug>(go-def-split) :<C-u>call go#def#JumpMode("split")<CR> nnoremap <silent> <Plug>(go-def-split) :<C-u>call go#def#Jump("split")<CR>
nnoremap <silent> <Plug>(go-def-tab) :<C-u>call go#def#JumpMode("tab")<CR> nnoremap <silent> <Plug>(go-def-tab) :<C-u>call go#def#Jump("tab")<CR>
nnoremap <silent> <Plug>(go-def-pop) :<C-u>call go#def#StackPop()<CR>
nnoremap <silent> <Plug>(go-def-stack) :<C-u>call go#def#Stack()<CR>
nnoremap <silent> <Plug>(go-def-stack-clear) :<C-u>call go#def#StackClear()<CR>
nnoremap <silent> <Plug>(go-doc) :<C-u>call go#doc#Open("new", "split")<CR> nnoremap <silent> <Plug>(go-doc) :<C-u>call go#doc#Open("new", "split")<CR>
nnoremap <silent> <Plug>(go-doc-tab) :<C-u>call go#doc#Open("tabnew", "tabe")<CR> nnoremap <silent> <Plug>(go-doc-tab) :<C-u>call go#doc#Open("tabnew", "tabe")<CR>
@ -53,8 +65,11 @@ nnoremap <silent> <Plug>(go-doc-split) :<C-u>call go#doc#Open("new", "split")<CR
nnoremap <silent> <Plug>(go-doc-browser) :<C-u>call go#doc#OpenBrowser()<CR> nnoremap <silent> <Plug>(go-doc-browser) :<C-u>call go#doc#OpenBrowser()<CR>
nnoremap <silent> <Plug>(go-metalinter) :<C-u>call go#lint#Gometa(0)<CR> nnoremap <silent> <Plug>(go-metalinter) :<C-u>call go#lint#Gometa(0)<CR>
nnoremap <silent> <Plug>(go-lint) :<C-u>call go#lint#Golint()<CR>
nnoremap <silent> <Plug>(go-vet) :<C-u>call go#lint#Vet(!g:go_jump_to_error)<CR> nnoremap <silent> <Plug>(go-vet) :<C-u>call go#lint#Vet(!g:go_jump_to_error)<CR>
nnoremap <silent> <Plug>(go-alternate-edit) :<C-u>call go#alternate#Switch(0, "edit")<CR> nnoremap <silent> <Plug>(go-alternate-edit) :<C-u>call go#alternate#Switch(0, "edit")<CR>
nnoremap <silent> <Plug>(go-alternate-vertical) :<C-u>call go#alternate#Switch(0, "vsplit")<CR> nnoremap <silent> <Plug>(go-alternate-vertical) :<C-u>call go#alternate#Switch(0, "vsplit")<CR>
nnoremap <silent> <Plug>(go-alternate-split) :<C-u>call go#alternate#Switch(0, "split")<CR> nnoremap <silent> <Plug>(go-alternate-split) :<C-u>call go#alternate#Switch(0, "split")<CR>
" vim: sw=2 ts=2 et

@ -3,13 +3,8 @@ if exists("g:go_loaded_gosnippets")
endif endif
let g:go_loaded_gosnippets = 1 let g:go_loaded_gosnippets = 1
" by default UltiSnips function! s:GoUltiSnips() abort
if !exists("g:go_snippet_engine") if get(g:, 'did_plugin_ultisnips') isnot 1
let g:go_snippet_engine = "ultisnips"
endif
function! s:GoUltiSnips()
if globpath(&rtp, 'plugin/UltiSnips.vim') == ""
return return
endif endif
@ -20,27 +15,53 @@ function! s:GoUltiSnips()
endif endif
endfunction endfunction
function! s:GoNeosnippet() function! s:GoNeosnippet() abort
if globpath(&rtp, 'plugin/neosnippet.vim') == "" if get(g:, 'loaded_neosnippet') isnot 1
return return
endif endif
let g:neosnippet#enable_snipmate_compatibility = 1 let g:neosnippet#enable_snipmate_compatibility = 1
let gosnippets_dir = globpath(&rtp, 'gosnippets/snippets') let l:gosnippets_dir = globpath(&rtp, 'gosnippets/snippets')
if type(g:neosnippet#snippets_directory) == type([]) if type(g:neosnippet#snippets_directory) == type([])
let g:neosnippet#snippets_directory += [gosnippets_dir] let g:neosnippet#snippets_directory += [l:gosnippets_dir]
elseif type(g:neosnippet#snippets_directory) == type("") elseif type(g:neosnippet#snippets_directory) == type("")
if strlen(g:neosnippet#snippets_directory) > 0 if strlen(g:neosnippet#snippets_directory) > 0
let g:neosnippet#snippets_directory = g:neosnippet#snippets_directory . "," . gosnippets_dir let g:neosnippet#snippets_directory = g:neosnippet#snippets_directory . "," . l:gosnippets_dir
else else
let g:neosnippet#snippets_directory = gosnippets_dir let g:neosnippet#snippets_directory = l:gosnippets_dir
endif
endif
endfunction
function! s:GoMinisnip() abort
if get(g:, 'loaded_minisnip') isnot 1
return
endif endif
if exists('g:minisnip_dir')
let g:minisnip_dir .= ':' . globpath(&rtp, 'gosnippets/minisnip')
else
let g:minisnip_dir = globpath(&rtp, 'gosnippets/minisnip')
endif endif
endfunction endfunction
if g:go_snippet_engine == "ultisnips"
let s:engine = get(g:, 'go_snippet_engine', 'automatic')
if s:engine is? "automatic"
if get(g:, 'did_plugin_ultisnips') is 1
call s:GoUltiSnips() call s:GoUltiSnips()
elseif g:go_snippet_engine == "neosnippet" elseif get(g:, 'loaded_neosnippet') is 1
call s:GoNeosnippet() call s:GoNeosnippet()
elseif get(g:, 'loaded_minisnip') is 1
call s:GoMinisnip()
endif endif
elseif s:engine is? "ultisnips"
call s:GoUltiSnips()
elseif s:engine is? "neosnippet"
call s:GoNeosnippet()
elseif s:engine is? "minisnip"
call s:GoMinisnip()
endif
" vim: sw=2 ts=2 et

@ -45,7 +45,7 @@ function! s:SetTagbar()
\ 'ctype' : 't', \ 'ctype' : 't',
\ 'ntype' : 'n' \ 'ntype' : 'n'
\ }, \ },
\ 'ctagsbin' : expand(bin_path), \ 'ctagsbin' : bin_path,
\ 'ctagsargs' : '-sort -silent' \ 'ctagsargs' : '-sort -silent'
\ } \ }
endif endif
@ -53,3 +53,5 @@ endfunction
call s:SetTagbar() call s:SetTagbar()
" vim: sw=2 ts=2 et

@ -0,0 +1,7 @@
if exists("b:did_ftplugin")
finish
endif
runtime! ftplugin/html.vim
" vim: sw=2 ts=2 et

@ -110,7 +110,7 @@ snippet gpl
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>. * along with this program; if not, see <http://www.gnu.org/licenses/>.
* *
* Copyright (C) ${1:Author}, `strftime("%Y")` * Copyright (C) ${1:Author}, `!v strftime("%Y")`
*/ */
${0} ${0}
endsnippet endsnippet
@ -143,6 +143,13 @@ else {
} }
endsnippet endsnippet
# if inline error
snippet ife "If with inline erro"
if err := ${1:condition}; err != nil {
${0:${VISUAL}}
}
endsnippet
# error snippet # error snippet
snippet errn "Error return " !b snippet errn "Error return " !b
if err != nil { if err != nil {
@ -151,14 +158,15 @@ if err != nil {
${0} ${0}
endsnippet endsnippet
# error snippet # error log snippet
snippet errt "Error test fatal " !b snippet errl "Error with log.Fatal(err)" !b
if err != nil { if err != nil {
t.Fatal(err) log.Fatal(err)
} }
${0} ${0}
endsnippet endsnippet
# error multiple return
snippet errn, "Error return with two return values" !b snippet errn, "Error return with two return values" !b
if err != nil { if err != nil {
return ${1:nil}, err return ${1:nil}, err
@ -166,6 +174,23 @@ if err != nil {
${0} ${0}
endsnippet endsnippet
# error panic
snippet errp "Error panic" !b
if err != nil {
panic(${1})
}
${0}
endsnippet
# error test
snippet errt "Error test fatal " !b
if err != nil {
t.Fatal(err)
}
${0}
endsnippet
# error handle
snippet errh "Error handle and return" !b snippet errh "Error handle and return" !b
if err != nil { if err != nil {
${1} ${1}
@ -174,8 +199,14 @@ if err != nil {
${0} ${0}
endsnippet endsnippet
# json field tag
snippet json "\`json:key\`" snippet json "\`json:key\`"
\`json:"${1:keyName}"\` \`json:"${1:`!v go#util#snippetcase(matchstr(getline("."), '\w\+'))`}"\`
endsnippet
# yaml field tag
snippet yaml "\`yaml:key\`"
\`yaml:"${1:`!v go#util#snippetcase(matchstr(getline("."), '\w\+'))`}"\`
endsnippet endsnippet
# fallthrough # fallthrough
@ -327,6 +358,18 @@ func Test${1:Function}(t *testing.T) {
} }
endsnippet endsnippet
snippet hf "http.HandlerFunc" !b
func ${1:handler}(w http.ResponseWriter, r *http.Request) {
${0:fmt.Fprintf(w, "hello world")}
}
endsnippet
snippet hhf "mux.HandleFunc" !b
${1:http}.HandleFunc("${2:/}", func(w http.ResponseWriter, r *http.Request) {
${0:fmt.Fprintf(w, "hello world")}
})
endsnippet
# quick test server # quick test server
snippet tsrv "httptest.NewServer" snippet tsrv "httptest.NewServer"
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -351,6 +394,21 @@ if err != nil {
} }
endsnippet endsnippet
snippet example "func ExampleXYZ() { ... }"
func Example${1:Method}() {
${0:${VISUAL}}
// Output:
}
endsnippet
snippet benchmark "func BenchmarkXYZ(b *testing.B) { ... }"
func Benchmark${1:Method}(b *testing.B) {
for i := 0; i < b.N; i++ {
${0:${VISUAL}}
}
}
endsnippet
# variable declaration # variable declaration
snippet var "var x Type [= ...]" snippet var "var x Type [= ...]"
var ${1:x} ${2:Type}${3: = ${0:value}} var ${1:x} ${2:Type}${3: = ${0:value}}
@ -372,7 +430,6 @@ if !reflect.DeepEqual(${1:expected}, ${2:actual}) {
} }
endsnippet endsnippet
global !p global !p
import re import re

@ -0,0 +1,3 @@
if !reflect.DeepEqual({{+got+}}, {{+want+}}) {
t.Errorf("\ngot: %#v\nwant: %#v\n", {{+~\~2+}}, {{+~\~2+}})
}

@ -0,0 +1,3 @@
if err != nil {
return {{+err+}}
}

@ -0,0 +1,4 @@
if err != nil {
t.Fatal(err)
}
{{++}}

@ -0,0 +1,3 @@
if err != nil {
return errors.Wrap(err, "{{++}}")
}

@ -0,0 +1,3 @@
// {{++}}
func {{+~\~1+}}() {
}

@ -0,0 +1 @@
fmt.Printf("%#v\n", {{++}})

@ -0,0 +1,3 @@
for i := 0; i < {{++}}; i++ {
{{++}}
}

@ -0,0 +1,2 @@
// Package {{+~expand('%:p:h:t')+}} {{++}}
package {{+~\~2+}}

@ -0,0 +1,2 @@
fmt.Sprintf("{{++}}", {{++}})

@ -118,6 +118,14 @@ snippet else
else { else {
${0} ${0}
} }
# if inline error
snippet ife
abbr if err := ...; err != nil { ... }
if err := ${1:condition}; err != nil {
${0}
}
# error snippet # error snippet
snippet errn snippet errn
abbr if err != nil { ... } abbr if err != nil { ... }
@ -132,6 +140,13 @@ abbr if err != nil { ... }
t.Fatal(err) t.Fatal(err)
} }
# error snippet in log.Fatal
snippet errl
abbr if err != nil { ... }
if err != nil {
log.Fatal(err)
}
# error snippet with two return values # error snippet with two return values
snippet errn, snippet errn,
abbr if err != nil { return [...], err } abbr if err != nil { return [...], err }
@ -149,11 +164,24 @@ abbr if err != nil { return }
} }
${0} ${0}
# error snippet with panic
snippet errp
abbr if err != nil { ... }
if err != nil {
panic(${1})
}
${0}
# json snippet # json snippet
snippet json snippet json
abbr \`json:key\` abbr \`json:key\`
\`json:"${1:keyName}"\` \`json:"${1:keyName}"\`
# yaml snippet
snippet yaml
abbr \`yaml:key\`
\`yaml:"${1:keyName}"\`
# fallthrough # fallthrough
snippet ft snippet ft
abbr fallthrough abbr fallthrough
@ -305,6 +333,19 @@ abbr if err != nil { t.Fatalf(...) }
if err != nil { if err != nil {
t.Fatalf("${1}") t.Fatalf("${1}")
} }
# test example
snippet example
func Example${1:Method}() {
${0}
// Output:
}
# test benchmark
snippet benchmark
func Benchmark${1:Method}(b *testing.B) {
for i := 0; i < b.N; i++ {
${0}
}
}
# variable declaration # variable declaration
snippet var snippet var
abbr var x Type [= ...] abbr var x Type [= ...]
@ -323,3 +364,15 @@ abbr equals: test two identifiers with DeepEqual
fmt.Printf("%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\n\n", filepath.Base(file), line, $1, $2) fmt.Printf("%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\n\n", filepath.Base(file), line, $1, $2)
t.FailNow() t.FailNow()
} }
snippet hf
abbr http.HandlerFunc
func ${1:handler}(w http.ResponseWriter, r *http.Request) {
${0:fmt.Fprintf(w, "hello world")}
}
snippet hhf
abbr mux.HandleFunc(...)
${1:http}.HandleFunc("${2:/}", func(w http.ResponseWriter, r *http.Request) {
${0:fmt.Fprintf(w, "hello world")}
})

@ -75,4 +75,4 @@ function! GoIndent(lnum)
return ind return ind
endfunction endfunction
" vim:ts=4:sw=4:et " vim: sw=2 ts=2 et

@ -30,15 +30,17 @@ function! GetGoHTMLTmplIndent(lnum)
" If need to indent based on last line " If need to indent based on last line
let last_line = getline(a:lnum-1) let last_line = getline(a:lnum-1)
if last_line =~ '^\s*{{\s*\%(if\|else\|range\|with\|define\|block\).*}}' if last_line =~ '^\s*{{-\=\s*\%(if\|else\|range\|with\|define\|block\).*}}'
let ind += sw let ind += sw
endif endif
" End of FuncMap block " End of FuncMap block
let current_line = getline(a:lnum) let current_line = getline(a:lnum)
if current_line =~ '^\s*{{\s*\%(else\|end\).*}}' if current_line =~ '^\s*{{-\=\s*\%(else\|end\).*}}'
let ind -= sw let ind -= sw
endif endif
return ind return ind
endfunction endfunction
" vim: sw=2 ts=2 et

@ -4,43 +4,74 @@ if exists("g:go_loaded_install")
endif endif
let g:go_loaded_install = 1 let g:go_loaded_install = 1
" Not using the has('patch-7.4.1689') syntax because that wasn't added until
" 7.4.237, and we want to be sure this works for everyone (this is also why
" we're not using utils#EchoError()).
"
" Version 7.4.1689 was chosen because that's what the most recent Ubuntu LTS
" release (16.04) uses.
if
\ get(g:, 'go_version_warning', 1) != 0 &&
\ (v:version < 704 || (v:version == 704 && !has('patch1689')))
\ && !has('nvim')
echohl Error
echom "vim-go requires Vim 7.4.1689 or Neovim, but you're using an older version."
echom "Please update your Vim for the best vim-go experience."
echom "If you really want to continue you can set this to make the error go away:"
echom " let g:go_version_warning = 0"
echom "Note that some features may error out or behave incorrectly."
echom "Please do not report bugs unless you're using Vim 7.4.1689 or newer."
echohl None
" Make sure people see this.
sleep 2
endif
" these packages are used by vim-go and can be automatically installed if " these packages are used by vim-go and can be automatically installed if
" needed by the user with GoInstallBinaries " needed by the user with GoInstallBinaries
let s:packages = [ let s:packages = {
\ "github.com/nsf/gocode", \ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt'],
\ "github.com/alecthomas/gometalinter", \ 'errcheck': ['github.com/kisielk/errcheck'],
\ "golang.org/x/tools/cmd/goimports", \ 'fillstruct': ['github.com/davidrjenni/reftools/cmd/fillstruct'],
\ "github.com/rogpeppe/godef", \ 'gocode': ['github.com/nsf/gocode', {'windows': '-ldflags -H=windowsgui'}],
\ "golang.org/x/tools/cmd/oracle", \ 'godef': ['github.com/rogpeppe/godef'],
\ "golang.org/x/tools/cmd/gorename", \ 'gogetdoc': ['github.com/zmb3/gogetdoc'],
\ "github.com/golang/lint/golint", \ 'goimports': ['golang.org/x/tools/cmd/goimports'],
\ "github.com/kisielk/errcheck", \ 'golint': ['github.com/golang/lint/golint'],
\ "github.com/jstemmer/gotags", \ 'gometalinter': ['github.com/alecthomas/gometalinter'],
\ "github.com/klauspost/asmfmt/cmd/asmfmt", \ 'gomodifytags': ['github.com/fatih/gomodifytags'],
\ "github.com/fatih/motion", \ 'gorename': ['golang.org/x/tools/cmd/gorename'],
\ ] \ 'gotags': ['github.com/jstemmer/gotags'],
\ 'guru': ['golang.org/x/tools/cmd/guru'],
\ 'impl': ['github.com/josharian/impl'],
\ 'keyify': ['github.com/dominikh/go-tools/cmd/keyify'],
\ 'motion': ['github.com/fatih/motion'],
\ }
" These commands are available on any filetypes " These commands are available on any filetypes
command! GoInstallBinaries call s:GoInstallBinaries(-1) command! -nargs=* -complete=customlist,s:complete GoInstallBinaries call s:GoInstallBinaries(-1, <f-args>)
command! GoUpdateBinaries call s:GoInstallBinaries(1) command! -nargs=* -complete=customlist,s:complete GoUpdateBinaries call s:GoInstallBinaries(1, <f-args>)
command! -nargs=? -complete=dir GoPath call go#path#GoPath(<f-args>) command! -nargs=? -complete=dir GoPath call go#path#GoPath(<f-args>)
fun! s:complete(lead, cmdline, cursor)
return filter(keys(s:packages), 'strpart(v:val, 0, len(a:lead)) == a:lead')
endfun
" GoInstallBinaries downloads and install all necessary binaries stated in the " GoInstallBinaries downloads and installs binaries defined in s:packages to
" packages variable. It uses by default $GOBIN or $GOPATH/bin as the binary " $GOBIN or $GOPATH/bin. GoInstallBinaries will update already installed
" target install directory. GoInstallBinaries doesn't install binaries if they " binaries only if updateBinaries = 1. By default, all packages in s:packages
" exist, to update current binaries pass 1 to the argument. " will be installed, but the set can be limited by passing the desired
function! s:GoInstallBinaries(updateBinaries) " packages in the unnamed arguments.
if $GOPATH == "" function! s:GoInstallBinaries(updateBinaries, ...)
echohl Error let err = s:CheckBinaries()
echomsg "vim.go: $GOPATH is not set" if err != 0
echohl None
return return
endif endif
let err = s:CheckBinaries() if go#path#Default() == ""
if err != 0 echohl Error
echomsg "vim.go: $GOPATH is not set and 'go env GOPATH' returns empty"
echohl None
return return
endif endif
@ -53,7 +84,7 @@ function! s:GoInstallBinaries(updateBinaries)
let old_path = $PATH let old_path = $PATH
" vim's executable path is looking in PATH so add our go_bin path to it " vim's executable path is looking in PATH so add our go_bin path to it
let $PATH = $PATH . go#util#PathListSep() .go_bin_path let $PATH = go_bin_path . go#util#PathListSep() . $PATH
" when shellslash is set on MS-* systems, shellescape puts single quotes " when shellslash is set on MS-* systems, shellescape puts single quotes
" around the output string. cmd on Windows does not handle single quotes " around the output string. cmd on Windows does not handle single quotes
@ -65,35 +96,59 @@ function! s:GoInstallBinaries(updateBinaries)
set noshellslash set noshellslash
endif endif
let cmd = "go get -u -v " let cmd = "go get -v "
if get(g:, "go_get_update", 1) != 0
let cmd .= "-u "
endif
let s:go_version = matchstr(system("go version"), '\d.\d.\d') let s:go_version = matchstr(go#util#System("go version"), '\d.\d.\d')
" https://github.com/golang/go/issues/10791 " https://github.com/golang/go/issues/10791
if s:go_version > "1.4.0" && s:go_version < "1.5.0" if s:go_version > "1.4.0" && s:go_version < "1.5.0"
let cmd .= "-f " let cmd .= "-f "
endif endif
for pkg in s:packages " Filter packages from arguments (if any).
let basename = fnamemodify(pkg, ":t") let l:packages = {}
let binname = "go_" . basename . "_bin" if a:0 > 0
for l:bin in a:000
let l:pkg = get(s:packages, l:bin, [])
if len(l:pkg) == 0
call go#util#EchoError('unknown binary: ' . l:bin)
return
endif
let l:packages[l:bin] = l:pkg
endfor
else
let l:packages = s:packages
endif
let l:platform = ''
if go#util#IsWin()
let l:platform = 'windows'
endif
for [binary, pkg] in items(l:packages)
let l:importPath = pkg[0]
let l:goGetFlags = len(pkg) > 1 ? get(pkg[1], l:platform, '') : ''
let bin = basename let binname = "go_" . binary . "_bin"
let bin = binary
if exists("g:{binname}") if exists("g:{binname}")
let bin = g:{binname} let bin = g:{binname}
endif endif
if !executable(bin) || a:updateBinaries == 1 if !executable(bin) || a:updateBinaries == 1
if a:updateBinaries == 1 if a:updateBinaries == 1
echo "vim-go: Updating ". basename .". Reinstalling ". pkg . " to folder " . go_bin_path echo "vim-go: Updating " . binary . ". Reinstalling ". importPath . " to folder " . go_bin_path
else else
echo "vim-go: ". basename ." not found. Installing ". pkg . " to folder " . go_bin_path echo "vim-go: ". binary ." not found. Installing ". importPath . " to folder " . go_bin_path
endif endif
let out = go#util#System(printf('%s %s %s', cmd, l:goGetFlags, shellescape(importPath)))
let out = system(cmd . shellescape(pkg)) if go#util#ShellError() != 0
if v:shell_error echom "Error installing " . importPath . ": " . out
echo "Error installing ". pkg . ": " . out
endif endif
endif endif
endfor endfor
@ -123,6 +178,10 @@ endfunction
" ============================================================================ " ============================================================================
" "
function! s:echo_go_info() function! s:echo_go_info()
if !get(g:, "go_echo_go_info", 1)
return
endif
if !exists('v:completed_item') || empty(v:completed_item) if !exists('v:completed_item') || empty(v:completed_item)
return return
endif endif
@ -139,35 +198,81 @@ function! s:echo_go_info()
redraws! | echo "vim-go: " | echohl Function | echon item.info | echohl None redraws! | echo "vim-go: " | echohl Function | echon item.info | echohl None
endfunction endfunction
augroup vim-go function! s:auto_type_info()
autocmd!
" GoInfo automatic update " GoInfo automatic update
if get(g:, "go_auto_type_info", 0) if get(g:, "go_auto_type_info", 0)
autocmd CursorHold *.go nested call go#complete#Info(1) call go#tool#Info(1)
endif endif
endfunction
" Echo the identifier information when completion is done. Useful to see function! s:auto_sameids()
" the signature of a function, etc... " GoSameId automatic update
if exists('##CompleteDone') if get(g:, "go_auto_sameids", 0)
autocmd CompleteDone *.go nested call s:echo_go_info() call go#guru#SameIds()
endif endif
endfunction
function! s:fmt_autosave()
" Go code formatting on save " Go code formatting on save
if get(g:, "go_fmt_autosave", 1) if get(g:, "go_fmt_autosave", 1)
autocmd BufWritePre *.go call go#fmt#Format(-1) call go#fmt#Format(-1)
endif endif
endfunction
function! s:asmfmt_autosave()
" Go asm formatting on save " Go asm formatting on save
if get(g:, "go_asmfmt_autosave", 1) if get(g:, "go_asmfmt_autosave", 0)
autocmd BufWritePre *.s call go#asmfmt#Format() call go#asmfmt#Format()
endif endif
endfunction
function! s:metalinter_autosave()
" run gometalinter on save " run gometalinter on save
if get(g:, "go_metalinter_autosave", 0) if get(g:, "go_metalinter_autosave", 0)
autocmd BufWritePost *.go call go#lint#Gometa(1) call go#lint#Gometa(1)
endif endif
augroup END endfunction
function! s:template_autocreate()
" create new template from scratch
if get(g:, "go_template_autocreate", 1)
call go#template#create()
endif
endfunction
augroup vim-go
autocmd!
autocmd CursorHold *.go call s:auto_type_info()
autocmd CursorHold *.go call s:auto_sameids()
" Echo the identifier information when completion is done. Useful to see
" the signature of a function, etc...
if exists('##CompleteDone')
autocmd CompleteDone *.go call s:echo_go_info()
endif
" vim:ts=4:sw=4:et autocmd BufWritePre *.go call s:fmt_autosave()
autocmd BufWritePre *.s call s:asmfmt_autosave()
autocmd BufWritePost *.go call s:metalinter_autosave()
autocmd BufNewFile *.go call s:template_autocreate()
" clear SameIds when the buffer is unloaded so that loading another buffer
" in the same window doesn't highlight the most recently matched
" identifier's positions.
autocmd BufWinEnter *.go call go#guru#ClearSameIds()
autocmd BufEnter *.go
\ if get(g:, 'go_autodetect_gopath', 0) && !exists('b:old_gopath')
\| let b:old_gopath = exists('$GOPATH') ? $GOPATH : -1
\| let $GOPATH = go#path#Detect()
\| endif
autocmd BufLeave *.go
\ if exists('b:old_gopath')
\| if b:old_gopath isnot -1
\| let $GOPATH = b:old_gopath
\| endif
\| unlet b:old_gopath
\| endif
augroup end
" vim: sw=2 ts=2 et

@ -0,0 +1,93 @@
# ============================================================================
# FILE: decls.py
# AUTHOR: delphinus <delphinus@remora.cx>
# License: MIT license
# ============================================================================
import os
import subprocess
import json
import denite.util
from .base import Base
DECLS_SYNTAX_HIGHLIGHT = [
{'name': 'FilePath', 're': r'[^:]*\ze:', 'link': 'Comment'},
{'name': 'Line', 're': r'\d\+\ze :', 'link': 'LineNr'},
{'name': 'WholeFunction', 're': r'\vfunc %(\([^)]+\) )?[^(]+'},
{'name': 'Function', 'parent': 'WholeFunction',
're': r'\S\+\ze(', 'link': 'Function'},
{'name': 'WholeType', 're': r'type \S\+'},
{'name': 'Type', 'parent': 'WholeType',
're': r'\v( )@<=\S+', 'link': 'Type'},
{'name': 'Separator', 're': r':', 'conceal': True},
{'name': 'SeparatorFunction', 'parent': 'WholeFunction',
're': r'func ', 'conceal': True},
{'name': 'SeparatorType', 'parent': 'WholeType',
're': r'type ', 'conceal': True},
]
class Source(Base):
def __init__(self, vim):
super().__init__(vim)
self.name = 'decls'
self.kind = 'file'
def gather_candidates(self, context):
bin_path = self.vim.call('go#path#CheckBinPath', 'motion')
if bin_path == '':
return []
expand = context['args'][0] if context['args'] else '%:p:h'
target = self.vim.funcs.expand(expand)
if os.path.isdir(target):
mode = 'dir'
elif os.path.isfile(target):
mode = 'file'
else:
return []
if self.vim.funcs.exists('g:go_decls_includes'):
include = self.vim.eval('g:go_decls_includes')
else:
include = 'func,type'
command = [bin_path, '-mode', 'decls', '-include', include,
'-' + mode, target]
try:
cmd = subprocess.run(command, stdout=subprocess.PIPE, check=True)
except subprocess.CalledProcessError as err:
denite.util.error(self.vim,
'command returned invalid response: ' + str(err))
return []
txt = cmd.stdout.decode('utf-8')
output = json.loads(txt, encoding='utf-8')
def make_candidates(row):
name = self.vim.funcs.fnamemodify(row['filename'], ':~:.')
return {
'word': '{0} :{1} :{2}'.format(name, row['line'], row['full']),
'action__path': row['filename'],
'action__line': row['line'],
'action__col': row['col'],
}
return list(map(make_candidates, output['decls']))
def highlight(self):
for syn in DECLS_SYNTAX_HIGHLIGHT:
containedin = self.syntax_name
containedin += '_' + syn['parent'] if 'parent' in syn else ''
conceal = ' conceal' if 'conceal' in syn else ''
self.vim.command(
'syntax match {0}_{1} /{2}/ contained containedin={3}{4}'
.format(self.syntax_name, syn['name'], syn['re'],
containedin, conceal))
if 'link' in syn:
self.vim.command('highlight default link {0}_{1} {2}'.format(
self.syntax_name, syn['name'], syn['link']))

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

Loading…
Cancel
Save