├── .github └── ISSUE_TEMPLATE ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── client.go ├── docs ├── IDE_integration.md └── autocomplete_formats.md ├── emacs-company ├── README.md └── company-go.el ├── emacs └── go-autocomplete.el ├── gocode.go ├── internal ├── cache │ ├── context.go │ ├── importer.go │ ├── samepath_unix.go │ └── samepath_windows.go ├── gbimporter │ └── gbimporter.go ├── lookdot │ ├── lookdot.go │ └── lookdot_test.go └── suggest │ ├── candidate.go │ ├── cursorcontext.go │ ├── formatters.go │ ├── formatters_test.go │ ├── stdlib.go │ ├── suggest.go │ ├── suggest_test.go │ └── testdata │ ├── test.0001 │ ├── out.expected │ └── test.go.in │ ├── test.0002 │ ├── out.expected │ └── test.go.in │ ├── test.0003 │ ├── out.expected │ └── test.go.in │ ├── test.0004 │ ├── out.expected │ └── test.go.in │ ├── test.0005 │ ├── b.go │ ├── out.expected │ └── test.go.in │ ├── test.0006 │ ├── b.go │ ├── out.expected │ └── test.go.in │ ├── test.0007 │ ├── out.expected │ └── test.go.in │ ├── test.0008 │ ├── out.expected │ └── test.go.in │ ├── test.0009 │ ├── out.expected │ └── test.go.in │ ├── test.0010 │ ├── out.expected │ └── test.go.in │ ├── test.0011 │ ├── out.expected │ └── test.go.in │ ├── test.0012 │ ├── out.expected │ └── test.go.in │ ├── test.0013 │ ├── out.expected │ └── test.go.in │ ├── test.0014 │ ├── out.expected │ └── test.go.in │ ├── test.0015 │ ├── out.expected │ └── test.go.in │ ├── test.0016 │ ├── out.expected │ └── test.go.in │ ├── test.0017 │ ├── out.expected │ └── test.go.in │ ├── test.0018 │ ├── out.expected │ └── test.go.in │ ├── test.0019 │ ├── out.expected │ └── test.go.in │ ├── test.0020 │ ├── out.expected │ └── test.go.in │ ├── test.0021 │ ├── out.expected │ └── test.go.in │ ├── test.0022 │ ├── out.expected │ └── test.go.in │ ├── test.0023 │ ├── out.expected │ └── test.go.in │ ├── test.0024 │ ├── out.expected │ └── test.go.in │ ├── test.0025 │ ├── out.expected │ └── test.go.in │ ├── test.0026 │ ├── out.expected │ └── test.go.in │ ├── test.0027 │ ├── out.expected │ └── test.go.in │ ├── test.0028 │ ├── out.expected │ └── test.go.in │ ├── test.0029 │ ├── out.expected │ └── test.go.in │ ├── test.0030 │ ├── out.expected │ └── test.go.in │ ├── test.0031 │ ├── out.expected │ └── test.go.in │ ├── test.0032 │ ├── out.expected │ └── test.go.in │ ├── test.0033 │ ├── out.expected │ └── test.go.in │ ├── test.0034 │ ├── out.expected │ └── test.go.in │ ├── test.0035 │ ├── out.expected │ └── test.go.in │ ├── test.0036 │ ├── out.expected │ └── test.go.in │ ├── test.0037 │ ├── out.expected │ └── test.go.in │ ├── test.0038 │ ├── out.expected │ └── test.go.in │ ├── test.0039 │ ├── out.expected │ └── test.go.in │ ├── test.0040 │ ├── out.expected │ └── test.go.in │ ├── test.0041 │ ├── out.expected │ └── test.go.in │ ├── test.0042 │ ├── out.expected │ └── test.go.in │ ├── test.0043 │ ├── out.expected │ └── test.go.in │ ├── test.0044 │ ├── out.expected │ └── test.go.in │ ├── test.0045 │ ├── out.expected │ └── test.go.in │ ├── test.0046 │ ├── out.expected │ └── test.go.in │ ├── test.0047 │ ├── out.expected │ └── test.go.in │ ├── test.0048 │ ├── out.expected │ └── test.go.in │ ├── test.0049 │ ├── out.expected │ └── test.go.in │ ├── test.0050 │ ├── out.expected │ └── test.go.in │ ├── test.0051 │ ├── out.expected │ └── test.go.in │ ├── test.0052 │ ├── out.expected │ └── test.go.in │ ├── test.0053 │ ├── b.go │ ├── out.expected │ └── test.go.in │ ├── test.0054 │ ├── b.go │ ├── out.expected │ └── test.go.in │ ├── test.0055 │ ├── out.expected │ └── test.go.in │ ├── test.0056 │ ├── out.expected │ └── test.go.in │ ├── test.0057 │ ├── out.expected │ └── test.go.in │ ├── test.0058 │ ├── out.expected │ └── test.go.in │ ├── test.0059 │ ├── out.expected │ └── test.go.in │ ├── test.0060 │ ├── out.expected │ └── test.go.in │ ├── test.0061 │ ├── out.expected │ └── test.go.in │ ├── test.0062 │ ├── out.expected │ └── test.go.in │ ├── test.0063 │ ├── out.expected │ └── test.go.in │ ├── test.0064 │ ├── config.json │ ├── out.expected │ └── test.go.in │ ├── test.0065 │ ├── config.json │ ├── out.expected │ └── test.go.in │ ├── test.0066 │ ├── config.json │ ├── out.expected │ └── test.go.in │ ├── test.0067 │ ├── config.json │ ├── out.expected │ └── test.go.in │ ├── test.0068 │ ├── config.json │ ├── out.expected │ └── test.go.in │ ├── test.0069 │ ├── config.json │ ├── out.expected │ └── test.go.in │ ├── test.0070 │ ├── config.json │ ├── out.expected │ └── test.go.in │ ├── test.0071 │ ├── out.expected │ └── test.go.in │ ├── test.0072 │ ├── out.expected │ └── test.go.in │ └── test.0073 │ ├── out.expected │ └── test.go.in ├── nvim ├── autoload │ └── gocomplete.vim ├── ftplugin │ └── go │ │ └── gocomplete.vim ├── pathogen_update.sh ├── symlink.sh └── update.sh ├── os_posix.go ├── os_windows.go ├── server.go ├── subl3 ├── README.md ├── gocode.py └── syntax │ ├── GoSublime-Go.tmLanguage │ ├── GoSublime-Go.tmLanguage.json │ ├── GoSublime-HTML.tmLanguage │ ├── GoSublime-HTML.tmLanguage.json │ ├── GoSublime-Template.tmLanguage │ ├── GoSublime-Template.tmLanguage.json │ └── LICENSE.md ├── utils.go └── vim ├── autoload └── gocomplete.vim ├── ftplugin └── go │ └── gocomplete.vim ├── pathogen_update.sh ├── symlink.sh └── update.sh /.github/ISSUE_TEMPLATE: -------------------------------------------------------------------------------- 1 | Please answer these questions before submitting your issue. Thanks! 2 | Also, remember that to restart gocode, you have to run `gocode close`. 3 | 4 | 5 | ### What version of Go are you using (`go version`)? 6 | 7 | 8 | ### What operating system and processor architecture are you using (`go env`)? 9 | 10 | 11 | ### What is the debug output of `gocode`? 12 | 13 | Run `gocode close` and then run `gocode -s -debug` to restart gocode in debug mode. 14 | Try to complete once again from your editor, and paste the output printed by gocode here. 15 | 16 | ### What (client-side) flags are you passing into `gocode`? 17 | 18 | Please note here if you are using `-source`, `-builtin`, `-ignore-case`, `-unimported-packages`, or `-fallback-to-source`. 19 | 20 | ### What editor are using? 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.8 2 | *.a 3 | *.out 4 | gocode 5 | gocode.exe 6 | goremote 7 | gocodetest 8 | *.swp 9 | listidents 10 | showcursor 11 | showsmap 12 | rename 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.9.x 5 | - 1.10.x 6 | - 1.11.x 7 | - 1.12.x 8 | - 1.13.x 9 | - 1.14.x 10 | - master 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010 nsf 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **IMPORTANT**: This repository is currently in maintenance mode. 2 | For a better autocompletion experience with Go, we suggest you use the Go language server, [gopls](https://github.com/golang/tools/blob/master/gopls/README.md). 3 | 4 | If you are looking for a version of `gocode` that works with Go modules, please see [stamblerre/gocode](https://github.com/stamblerre/gocode). 5 | 6 | --- 7 | 8 | [![Build Status](https://travis-ci.org/mdempsky/gocode.svg?branch=master)](https://travis-ci.org/mdempsky/gocode) 9 | 10 | ## github.com/mdempsky/gocode 11 | 12 | This version of gocode is a fork of the [original](https://github.com/nsf/gocode), which is no longer supported. This fork should work for all versions of Go > 1.8. It only works for `$GOPATH` projects. For a version of gocode that works with Modules, please see [github.com/stamblerre/gocode](https://github.com/stamblerre/gocode). 13 | 14 | ## An autocompletion daemon for the Go programming language 15 | 16 | Gocode is a helper tool which is intended to be integrated with your source code editor, like vim, neovim and emacs. It provides several advanced capabilities, which currently includes: 17 | 18 | - Context-sensitive autocompletion 19 | 20 | It is called *daemon*, because it uses client/server architecture for caching purposes. In particular, it makes autocompletions very fast. Typical autocompletion time with warm cache is 30ms, which is barely noticeable. 21 | 22 | Also watch the [demo screencast](https://nosmileface.dev/images/gocode-demo.swf). 23 | 24 | ![Gocode in vim](https://nosmileface.dev/images/gocode-screenshot.png) 25 | 26 | ![Gocode in emacs](https://nosmileface.dev/images/emacs-gocode.png) 27 | 28 | ### Setup 29 | 30 | 1. You should have a correctly installed Go compiler environment and your personal workspace ($GOPATH). If you have no idea what **$GOPATH** is, take a look [here](http://golang.org/doc/code.html). Please make sure that your **$GOPATH/bin** is available in your **$PATH**. This is important, because most editors assume that **gocode** binary is available in one of the directories, specified by your **$PATH** environment variable. Otherwise manually copy the **gocode** binary from **$GOPATH/bin** to a location which is part of your **$PATH** after getting it in step 2. 31 | 32 | Do these steps only if you understand why you need to do them: 33 | 34 | `export GOPATH=$HOME/goprojects` 35 | 36 | `export PATH=$PATH:$GOPATH/bin` 37 | 38 | 2. Then you need to install gocode: 39 | 40 | `go get -u github.com/mdempsky/gocode` (-u flag for "update") 41 | 42 | Windows users should consider doing this instead: 43 | 44 | `go get -u -ldflags -H=windowsgui github.com/mdempsky/gocode` 45 | 46 | That way on the Windows OS gocode will be built as a GUI application and doing so solves hanging window issues with some of the editors. 47 | 48 | **Note: If you are updating your version of gocode, you will need to run `gocode close` to restart it.** 49 | 50 | 3. Next steps are editor specific. See below. 51 | 52 | ### Vim setup 53 | 54 | #### Vim manual installation 55 | 56 | Note: As of go 1.5 there is no $GOROOT/misc/vim script. Suggested installation is via [vim-go plugin](https://github.com/fatih/vim-go). 57 | 58 | In order to install vim scripts, you need to fulfill the following steps: 59 | 60 | 1. Install official Go vim scripts from **$GOROOT/misc/vim**. If you did that already, proceed to the step 2. 61 | 62 | 2. Install gocode vim scripts. Usually it's enough to do the following: 63 | 64 | 2.1. `vim/update.sh` 65 | 66 | **update.sh** script does the following: 67 | 68 | #!/bin/sh 69 | mkdir -p "$HOME/.vim/autoload" 70 | mkdir -p "$HOME/.vim/ftplugin/go" 71 | cp "${0%/*}/autoload/gocomplete.vim" "$HOME/.vim/autoload" 72 | cp "${0%/*}/ftplugin/go/gocomplete.vim" "$HOME/.vim/ftplugin/go" 73 | 74 | 2.2. Alternatively, you can create symlinks using symlink.sh script in order to avoid running update.sh after every gocode update. 75 | 76 | **symlink.sh** script does the following: 77 | 78 | #!/bin/sh 79 | cd "${0%/*}" 80 | ROOTDIR=`pwd` 81 | mkdir -p "$HOME/.vim/autoload" 82 | mkdir -p "$HOME/.vim/ftplugin/go" 83 | ln -s "$ROOTDIR/autoload/gocomplete.vim" "$HOME/.vim/autoload/" 84 | ln -s "$ROOTDIR/ftplugin/go/gocomplete.vim" "$HOME/.vim/ftplugin/go/" 85 | 86 | 3. Make sure vim has filetype plugin enabled. Simply add that to your **.vimrc**: 87 | 88 | `filetype plugin on` 89 | 90 | 4. Autocompletion should work now. Use `` for autocompletion (omnifunc autocompletion). 91 | 92 | #### Using Vundle in Vim 93 | 94 | Add the following line to your **.vimrc**: 95 | 96 | `Plugin 'mdempsky/gocode', {'rtp': 'vim/'}` 97 | 98 | And then update your packages by running `:PluginInstall`. 99 | 100 | #### Using vim-plug in Vim 101 | 102 | Add the following line to your **.vimrc**: 103 | 104 | `Plug 'mdempsky/gocode', { 'rtp': 'vim', 'do': '~/.vim/plugged/gocode/vim/symlink.sh' }` 105 | 106 | And then update your packages by running `:PlugInstall`. 107 | 108 | #### Other 109 | 110 | Alternatively take a look at the vundle/pathogen friendly repo: https://github.com/Blackrush/vim-gocode. 111 | 112 | ### Neovim setup 113 | #### Neovim manual installation 114 | 115 | Neovim users should also follow `Vim manual installation`, except that you should goto `gocode/nvim` in step 2, and remember that, the Neovim configuration file is `~/.config/nvim/init.vim`. 116 | 117 | #### Using Vundle in Neovim 118 | 119 | Add the following line to your **init.vim**: 120 | 121 | `Plugin 'mdempsky/gocode', {'rtp': 'nvim/'}` 122 | 123 | And then update your packages by running `:PluginInstall`. 124 | 125 | #### Using vim-plug in Neovim 126 | 127 | Add the following line to your **init.vim**: 128 | 129 | `Plug 'mdempsky/gocode', { 'rtp': 'nvim', 'do': '~/.config/nvim/plugged/gocode/nvim/symlink.sh' }` 130 | 131 | And then update your packages by running `:PlugInstall`. 132 | 133 | ### Emacs setup 134 | 135 | In order to install emacs script, you need to fulfill the following steps: 136 | 137 | 1. Install [auto-complete-mode](http://www.emacswiki.org/emacs/AutoComplete) 138 | 139 | 2. Copy **emacs/go-autocomplete.el** file from the gocode source distribution to a directory which is in your 'load-path' in emacs. 140 | 141 | 3. Add these lines to your **.emacs**: 142 | 143 | (require 'go-autocomplete) 144 | (require 'auto-complete-config) 145 | (ac-config-default) 146 | 147 | Also, there is an alternative plugin for emacs using company-mode. See `emacs-company/README` for installation instructions. 148 | 149 | If you're a MacOSX user, you may find that script useful: https://github.com/purcell/exec-path-from-shell. It helps you with setting up the right environment variables as Go and gocode require it. By default it pulls the PATH, but don't forget to add the GOPATH as well, e.g.: 150 | 151 | ``` 152 | (when (memq window-system '(mac ns)) 153 | (exec-path-from-shell-initialize) 154 | (exec-path-from-shell-copy-env "GOPATH")) 155 | ``` 156 | 157 | ### Sublime Text 3 setup 158 | 159 | A plugin for Sublime Text 3 is provided in the `subl3` directory of this repository. To install it: 160 | 161 | 1. Copy the plugin into your Sublime Text 3 `Packages` directory: 162 | 163 | $ cp -r $GOPATH/src/github.com/mdempsky/gocode/subl3 ~/.config/sublime-text-3/Packages/ 164 | 165 | 2. Rename the plugin directory from `subl3` to `gocode`: 166 | 167 | $ mv ~/.config/sublime-text-3/Packages/subl3 ~/.config/sublime-text-3/Packages/gocode 168 | 169 | 3. Open the Command Pallete (`Ctrl+Shift+P`) and run the `Package Control: List Packages` command. You should see `gocode` listed as an active plugin. 170 | 171 | ### Debugging 172 | 173 | If something went wrong, the first thing you may want to do is manually start the gocode daemon with a debug mode enabled and in a separate terminal window. It will show you all the stack traces, panics if any and additional info about autocompletion requests. Shutdown the daemon if it was already started and run a new one explicitly with a debug mode enabled: 174 | 175 | `gocode exit` 176 | 177 | `gocode -s -debug` 178 | 179 | Please, report bugs, feature suggestions and other rants to the [github issue tracker](http://github.com/mdempsky/gocode/issues) of this project. 180 | 181 | ### Developing 182 | 183 | There is [Guide for IDE/editor plugin developers](docs/IDE_integration.md). 184 | 185 | If you have troubles, please, contact me and I will try to do my best answering your questions. You can contact me via email. Or for short question find me on IRC: #go-nuts @ freenode. 186 | 187 | ### Misc 188 | 189 | - It's a good idea to use the latest git version always. I'm trying to keep it in a working state. 190 | - Use `go install` (not `go build`) for building a local source tree. The objects in `pkg/` are needed for Gocode to work. 191 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go/build" 7 | "io/ioutil" 8 | "log" 9 | "net/rpc" 10 | "os" 11 | "path/filepath" 12 | "strconv" 13 | "time" 14 | 15 | "runtime/debug" 16 | 17 | "github.com/mdempsky/gocode/internal/cache" 18 | "github.com/mdempsky/gocode/internal/suggest" 19 | ) 20 | 21 | func doClient() { 22 | // Client is a short-lived program. 23 | // Disable GC to make it faster 24 | debug.SetGCPercent(-1) 25 | 26 | if *g_debug { 27 | start := time.Now() 28 | defer func() { 29 | elapsed := time.Since(start) 30 | log.Printf("Elapsed duration: %v\n", elapsed) 31 | }() 32 | } 33 | 34 | var command string 35 | if flag.NArg() > 0 { 36 | command = flag.Arg(0) 37 | switch command { 38 | case "autocomplete", "exit": 39 | // these are valid commands 40 | case "close": 41 | // "close" is an alias for "exit" 42 | command = "exit" 43 | default: 44 | fmt.Printf("gocode: unknown subcommand: %q\nRun 'gocode -help' for usage.\n", command) 45 | os.Exit(2) 46 | } 47 | } 48 | 49 | // client 50 | var client *rpc.Client 51 | if *g_sock != "none" { 52 | addr := *g_addr 53 | if *g_sock == "unix" { 54 | addr = getSocketPath() 55 | } 56 | 57 | var err error 58 | client, err = rpc.Dial(*g_sock, addr) 59 | if err != nil { 60 | if command == "exit" { 61 | log.Fatal(err) 62 | } 63 | 64 | if *g_sock == "unix" { 65 | _ = os.Remove(addr) 66 | } 67 | err = tryStartServer() 68 | if err != nil { 69 | log.Fatalf("Failed to start server: %s\n", err) 70 | } 71 | client, err = tryToConnect(*g_sock, addr) 72 | if err != nil { 73 | log.Fatalf("Failed to connect to %q: %s\n", addr, err) 74 | } 75 | } 76 | defer client.Close() 77 | } 78 | 79 | switch command { 80 | case "autocomplete": 81 | cmdAutoComplete(client) 82 | case "exit": 83 | cmdExit(client) 84 | } 85 | } 86 | 87 | func tryStartServer() error { 88 | path := get_executable_filename() 89 | args := []string{os.Args[0], "-s", "-sock", *g_sock, "-addr", *g_addr} 90 | if *g_cache { 91 | args = append(args, "-cache") 92 | } 93 | cwd, _ := os.Getwd() 94 | 95 | var err error 96 | stdin, err := os.Open(os.DevNull) 97 | if err != nil { 98 | return err 99 | } 100 | stdout, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0) 101 | if err != nil { 102 | return err 103 | } 104 | stderr, err := os.OpenFile(os.DevNull, os.O_WRONLY, 0) 105 | if err != nil { 106 | return err 107 | } 108 | 109 | procattr := os.ProcAttr{Dir: cwd, Env: os.Environ(), Files: []*os.File{stdin, stdout, stderr}} 110 | p, err := os.StartProcess(path, args, &procattr) 111 | if err != nil { 112 | return err 113 | } 114 | 115 | return p.Release() 116 | } 117 | 118 | func tryToConnect(network, address string) (*rpc.Client, error) { 119 | start := time.Now() 120 | for { 121 | client, err := rpc.Dial(network, address) 122 | if err != nil && time.Since(start) < time.Second { 123 | continue 124 | } 125 | return client, err 126 | } 127 | } 128 | 129 | func cmdAutoComplete(c *rpc.Client) { 130 | var req AutoCompleteRequest 131 | req.Filename, req.Data, req.Cursor = prepareFilenameDataCursor() 132 | req.Context = cache.PackContext(&build.Default) 133 | req.Source = *g_source 134 | req.Builtin = *g_builtin 135 | req.IgnoreCase = *g_ignore_case 136 | req.UnimportedPackages = *g_unimported_packages 137 | req.FallbackToSource = *g_fallback_to_source 138 | 139 | var res AutoCompleteReply 140 | var err error 141 | if c == nil { 142 | s := Server{} 143 | err = s.AutoComplete(&req, &res) 144 | } else { 145 | err = c.Call("Server.AutoComplete", &req, &res) 146 | } 147 | if err != nil { 148 | log.Fatal(err) 149 | } 150 | 151 | fmt := suggest.Formatters[*g_format] 152 | if fmt == nil { 153 | fmt = suggest.NiceFormat 154 | } 155 | fmt(os.Stdout, res.Candidates, res.Len) 156 | } 157 | 158 | func cmdExit(c *rpc.Client) { 159 | if c == nil { 160 | return 161 | } 162 | var req ExitRequest 163 | var res ExitReply 164 | if err := c.Call("Server.Exit", &req, &res); err != nil { 165 | log.Fatal(err) 166 | } 167 | } 168 | 169 | func prepareFilenameDataCursor() (string, []byte, int) { 170 | var file []byte 171 | var err error 172 | 173 | if *g_input != "" { 174 | file, err = ioutil.ReadFile(*g_input) 175 | } else { 176 | file, err = ioutil.ReadAll(os.Stdin) 177 | } 178 | 179 | if err != nil { 180 | log.Fatal(err) 181 | } 182 | 183 | filename := *g_input 184 | offset := "" 185 | switch flag.NArg() { 186 | case 2: 187 | offset = flag.Arg(1) 188 | case 3: 189 | filename = flag.Arg(1) // Override default filename 190 | offset = flag.Arg(2) 191 | } 192 | 193 | if filename != "" { 194 | filename, _ = filepath.Abs(filename) 195 | } 196 | 197 | cursor := -1 198 | if offset != "" { 199 | if offset[0] == 'c' || offset[0] == 'C' { 200 | cursor, _ = strconv.Atoi(offset[1:]) 201 | cursor = runeToByteOffset(file, cursor) 202 | } else { 203 | cursor, _ = strconv.Atoi(offset) 204 | } 205 | } 206 | 207 | return filename, file, cursor 208 | } 209 | -------------------------------------------------------------------------------- /docs/IDE_integration.md: -------------------------------------------------------------------------------- 1 | 2 | # IDE Integration Guide # 3 | 4 | This guide should help programmers to develop Go lang plugins for IDEs and editors. It documents command arguments and output formats, and also mentions common pitfails. Note that gocode is not intended to be used from command-line by human. 5 | 6 | ## Code Completion Assistance ## 7 | 8 | Gocode proposes completion depending on current scope and context. Currently some obvious features are missed: 9 | * No keywords completion (no context-sensitive neither absolute) 10 | * No package names completion 11 | * No completion proposal priority 12 | * Information about context not passed to output, i.e. gocode does not report if you've typed `st.` or `fn(` 13 | 14 | Also keep in mind following things: 15 | * Editor keeps unsaved file copy in memory, so you should pass file content via stdin, or mirror it to temporary file and use `-in=*` parameter. Gocode does not support more than one unsaved file. 16 | * You should also pass full path (relative or absolute) to target file as parameter, otherwise completion will be incomplete because other files from the same package will not be resolved. 17 | * If coder started to type identifier like `Pr`, gocode will produce completions `Printf`, `Produce`, etc. In other words, completion contains identifier prefix and is already filtered. Filtering uses case-sensitive comparison if possible, and fallbacks to case-insensitive comparison. 18 | * If you want to see built-in identifiers like `uint32`, `error`, etc, you can call `gocode set propose-builtins yes` once. 19 | 20 | Use autocomplete command to produce completion assistance for particular position at file: 21 | ```bash 22 | # Read source from file and show completions for character at offset 449 from beginning 23 | gocode -f=json --in=server.go autocomplete 449 24 | # Read source from stdin (more suitable for editor since it keeps unsaved file copy in memory) 25 | gocode -f=json autocomplete 449 26 | # You can also pass target file path along with position, it's used to find other files from the same package 27 | gocode -f=json autocomplete server.go 889 28 | # By default gocode interprets offset as bytes offset, but 'c' or 'C' prefix means that offset is unicode code points offset 29 | gocode -f=json autocomplete server.go c619 30 | ``` 31 | 32 | ## Server-side Debug Mode ## 33 | 34 | There is a special server-side debug mode available in order to help developers with gocode integration. Invoke the gocode's server manually passing the following arguments: 35 | ```bash 36 | # make sure gocode server isn't running 37 | gocode close 38 | # -s for "server" 39 | # -debug for special server-side debug mode 40 | gocode -s -debug 41 | ``` 42 | 43 | After that when your editor sends autocompletion requests, the server will print some information about them to the stdout, for example: 44 | ``` 45 | Got autocompletion request for '/home/nsf/tmp/gobug/main.go' 46 | Cursor at: 52 47 | ------------------------------------------------------- 48 | package main 49 | 50 | import "bytes" 51 | 52 | func main() { 53 | bytes.F# 54 | } 55 | ------------------------------------------------------- 56 | Offset: 1 57 | Number of candidates found: 2 58 | Candidates are: 59 | func Fields(s []byte) [][]byte 60 | func FieldsFunc(s []byte, f func(rune) bool) [][]byte 61 | ======================================================= 62 | ``` 63 | 64 | Note that '#' symbol is inserted at the cursor location as gocode sees it. This debug mode is useful when you need to make sure your editor sends the right position in all cases. Keep in mind that Go source files are UTF-8 files, try inserting non-english comments before the completion location to check if everything works properly. 65 | 66 | [Output formats reference.](autocomplete_formats.md) 67 | -------------------------------------------------------------------------------- /docs/autocomplete_formats.md: -------------------------------------------------------------------------------- 1 | 2 | # Description of Completion Assistance Formats # 3 | 4 | Use `-f` parameter for `autocomplete` command to set format. "nice" format is the default and fallback. 5 | 6 | Following formats supported: 7 | * json 8 | * nice 9 | * vim 10 | * godit 11 | * emacs 12 | * csv 13 | 14 | ## json ### 15 | Generic JSON format. Example (manually formatted): 16 | ```json 17 | [6, [{ 18 | "class": "func", 19 | "name": "client_auto_complete", 20 | "type": "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)" 21 | }, { 22 | "class": "func", 23 | "name": "client_close", 24 | "type": "func(cli *rpc.Client, Arg0 int) int" 25 | }, { 26 | "class": "func", 27 | "name": "client_cursor_type_pkg", 28 | "type": "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)" 29 | }, { 30 | "class": "func", 31 | "name": "client_drop_cache", 32 | "type": "func(cli *rpc.Client, Arg0 int) int" 33 | }, { 34 | "class": "func", 35 | "name": "client_highlight", 36 | "type": "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)" 37 | }, { 38 | "class": "func", 39 | "name": "client_set", 40 | "type": "func(cli *rpc.Client, Arg0, Arg1 string) string" 41 | }, { 42 | "class": "func", 43 | "name": "client_status", 44 | "type": "func(cli *rpc.Client, Arg0 int) string" 45 | } 46 | ]] 47 | ``` 48 | Limitations: 49 | * `class` can be one of: `func`, `package`, `var`, `type`, `const`, `PANIC` 50 | * `PANIC` means suspicious error inside gocode 51 | * `name` is text which can be inserted 52 | * `type` can be used to create code assistance hint 53 | * You can re-format type by using following approach: if `class` is prefix of `type`, delete this prefix and add another prefix `class` + " " + `name`. 54 | 55 | ## nice ## 56 | You can use it to test from command-line. 57 | ``` 58 | Found 7 candidates: 59 | func client_auto_complete(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int) 60 | func client_close(cli *rpc.Client, Arg0 int) int 61 | func client_cursor_type_pkg(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string) 62 | func client_drop_cache(cli *rpc.Client, Arg0 int) int 63 | func client_highlight(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int) 64 | func client_set(cli *rpc.Client, Arg0, Arg1 string) string 65 | func client_status(cli *rpc.Client, Arg0 int) string 66 | ``` 67 | 68 | ## vim ## 69 | Format designed to be used in VIM scripts. Example: 70 | ``` 71 | [6, [{'word': 'client_auto_complete(', 'abbr': 'func client_auto_complete(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)', 'info': 'func client_auto_complete(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)'}, {'word': 'client_close(', 'abbr': 'func client_close(cli *rpc.Client, Arg0 int) int', 'info': 'func client_close(cli *rpc.Client, Arg0 int) int'}, {'word': 'client_cursor_type_pkg(', 'abbr': 'func client_cursor_type_pkg(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)', 'info': 'func client_cursor_type_pkg(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)'}, {'word': 'client_drop_cache(', 'abbr': 'func client_drop_cache(cli *rpc.Client, Arg0 int) int', 'info': 'func client_drop_cache(cli *rpc.Client, Arg0 int) int'}, {'word': 'client_highlight(', 'abbr': 'func client_highlight(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)', 'info': 'func client_highlight(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)'}, {'word': 'client_set(', 'abbr': 'func client_set(cli *rpc.Client, Arg0, Arg1 string) string', 'info': 'func client_set(cli *rpc.Client, Arg0, Arg1 string) string'}, {'word': 'client_status(', 'abbr': 'func client_status(cli *rpc.Client, Arg0 int) string', 'info': 'func client_status(cli *rpc.Client, Arg0 int) string'}]] 72 | ``` 73 | 74 | ## godit ## 75 | Example: 76 | ``` 77 | 6,,7 78 | func client_auto_complete(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int),,client_auto_complete( 79 | func client_close(cli *rpc.Client, Arg0 int) int,,client_close( 80 | func client_cursor_type_pkg(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string),,client_cursor_type_pkg( 81 | func client_drop_cache(cli *rpc.Client, Arg0 int) int,,client_drop_cache( 82 | func client_highlight(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int),,client_highlight( 83 | func client_set(cli *rpc.Client, Arg0, Arg1 string) string,,client_set( 84 | func client_status(cli *rpc.Client, Arg0 int) string,,client_status( 85 | ``` 86 | 87 | ## emacs ## 88 | Format designed to be used in Emacs scripts. Example: 89 | ``` 90 | client_auto_complete,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int) 91 | client_close,,func(cli *rpc.Client, Arg0 int) int 92 | client_cursor_type_pkg,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string) 93 | client_drop_cache,,func(cli *rpc.Client, Arg0 int) int 94 | client_highlight,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int) 95 | client_set,,func(cli *rpc.Client, Arg0, Arg1 string) string 96 | client_status,,func(cli *rpc.Client, Arg0 int) string 97 | ``` 98 | 99 | ## sexp ## 100 | Output in form of S-Expressions. Example: 101 | ``` 102 | ((func "client_auto_complete" "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)" "gocode")(func "client_close" "func(cli *rpc.Client, Arg0 int) int" "gocode")(func "client_cursor_type_pkg" "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)" "gocode")(func "client_drop_cache" "func(cli *rpc.Client, Arg0 int) int" "gocode")(func "client_highlight" "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)" "gocode")(func "client_set" "func(cli *rpc.Client, Arg0, Arg1 string) string" "gocode")(func "client_status" "func(cli *rpc.Client, Arg0 int) string" "gocode")) 103 | ``` 104 | 105 | ## csv ## 106 | Comma-separated values format which has small size. Example: 107 | ```csv 108 | func,,client_auto_complete,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int) 109 | func,,client_close,,func(cli *rpc.Client, Arg0 int) int 110 | func,,client_cursor_type_pkg,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string) 111 | func,,client_drop_cache,,func(cli *rpc.Client, Arg0 int) int 112 | func,,client_highlight,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int) 113 | func,,client_set,,func(cli *rpc.Client, Arg0, Arg1 string) string 114 | func,,client_status,,func(cli *rpc.Client, Arg0 int) string 115 | ``` 116 | -------------------------------------------------------------------------------- /emacs-company/README.md: -------------------------------------------------------------------------------- 1 | # Company-go 2 | Company-go is an alternative emacs plugin for autocompletion. It uses [company-mode](http://company-mode.github.io). 3 | Completion will start automatically whenever the current symbol is preceded by a `.`, or after you type `company-minimum-prefix-length` letters. 4 | 5 | ## Setup 6 | Install `company` and `company-go`. 7 | 8 | Add the following to your emacs-config: 9 | 10 | ```lisp 11 | (require 'company) ; load company mode 12 | (require 'company-go) ; load company mode go backend 13 | ``` 14 | 15 | ## Possible improvements 16 | 17 | ```lisp 18 | (setq company-tooltip-limit 20) ; bigger popup window 19 | (setq company-idle-delay .3) ; decrease delay before autocompletion popup shows 20 | (setq company-echo-delay 0) ; remove annoying blinking 21 | (setq company-begin-commands '(self-insert-command)) ; start autocompletion only after typing 22 | ``` 23 | 24 | ### Only use company-mode with company-go in go-mode 25 | By default company-mode loads every backend it has. If you want to only have company-mode enabled in go-mode add the following to your emacs-config: 26 | 27 | ```lisp 28 | (add-hook 'go-mode-hook (lambda () 29 | (set (make-local-variable 'company-backends) '(company-go)) 30 | (company-mode))) 31 | ``` 32 | 33 | ### Color customization 34 | 35 | ```lisp 36 | (custom-set-faces 37 | '(company-preview 38 | ((t (:foreground "darkgray" :underline t)))) 39 | '(company-preview-common 40 | ((t (:inherit company-preview)))) 41 | '(company-tooltip 42 | ((t (:background "lightgray" :foreground "black")))) 43 | '(company-tooltip-selection 44 | ((t (:background "steelblue" :foreground "white")))) 45 | '(company-tooltip-common 46 | ((((type x)) (:inherit company-tooltip :weight bold)) 47 | (t (:inherit company-tooltip)))) 48 | '(company-tooltip-common-selection 49 | ((((type x)) (:inherit company-tooltip-selection :weight bold)) 50 | (t (:inherit company-tooltip-selection))))) 51 | ``` 52 | -------------------------------------------------------------------------------- /emacs/go-autocomplete.el: -------------------------------------------------------------------------------- 1 | ;;; go-autocomplete.el --- auto-complete-mode backend for go-mode 2 | 3 | ;; Copyright (C) 2010 4 | 5 | ;; Author: Mikhail Kuryshev 6 | ;; Keywords: languages 7 | ;; Package-Requires: ((auto-complete "1.4.0")) 8 | 9 | ;; This program is free software; you can redistribute it and/or modify 10 | ;; it under the terms of the GNU General Public License as published by 11 | ;; the Free Software Foundation, either version 3 of the License, or 12 | ;; (at your option) any later version. 13 | 14 | ;; This program is distributed in the hope that it will be useful, 15 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | ;; GNU General Public License for more details. 18 | 19 | ;; You should have received a copy of the GNU General Public License 20 | ;; along with this program. If not, see . 21 | 22 | ;;; Commentary: 23 | 24 | ;; Ensure that go-autocomplete in your load-path and add to your ~/.emacs 25 | ;; following line: 26 | ;; 27 | ;; (require 'go-autocomplete) 28 | 29 | ;; Also you could setup any combination (for example M-TAB) 30 | ;; for invoking auto-complete: 31 | ;; 32 | ;; (require 'auto-complete-config) 33 | ;; (define-key ac-mode-map (kbd "M-TAB") 'auto-complete) 34 | 35 | ;;; Code: 36 | 37 | (eval-when-compile 38 | (require 'cl)) 39 | 40 | (require 'auto-complete) 41 | 42 | (declare-function yas-expand-snippet "yasnippet") 43 | 44 | (defgroup go-autocomplete nil 45 | "auto-complete for go language." 46 | :prefix "ac-go-" 47 | :group 'auto-complete) 48 | 49 | (defcustom ac-go-expand-arguments-into-snippets t 50 | "Expand function arguments into snippets. This feature requires `yasnippet'." 51 | :type 'boolean 52 | :group 'go-autocomplete) 53 | 54 | ;; Close gocode daemon at exit unless it was already running 55 | (eval-after-load "go-mode" 56 | '(progn 57 | (let* ((user (or (getenv "USER") "all")) 58 | (sock (format (concat temporary-file-directory "gocode-daemon.%s") user))) 59 | (unless (file-exists-p sock) 60 | (add-hook 'kill-emacs-hook #'(lambda () 61 | (ignore-errors 62 | (call-process "gocode" nil nil nil "close")))))))) 63 | 64 | ;(defvar go-reserved-keywords 65 | ; '("break" "case" "chan" "const" "continue" "default" "defer" "else" 66 | ; "fallthrough" "for" "func" "go" "goto" "if" "import" "interface" 67 | ; "map" "package" "range" "return" "select" "struct" "switch" "type" "var") 68 | ; "Go reserved keywords.") 69 | 70 | (defun ac-comphist-sort (db collection prefix &optional threshold) 71 | ;; redefine to disable sorting 72 | (let (result 73 | (n 0) 74 | (total 0) 75 | (cur 0)) 76 | (setq result (mapcar (lambda (a) 77 | (when (and cur threshold) 78 | (if (>= cur (* total threshold)) 79 | (setq cur nil) 80 | (incf n) 81 | (incf cur (cdr a)))) 82 | (car a)) 83 | (mapcar (lambda (string) 84 | (let ((score (ac-comphist-score db string prefix))) 85 | (incf total score) 86 | (cons string score))) 87 | collection))) 88 | (if threshold 89 | (cons n result) 90 | result))) 91 | 92 | (defun ac-go-invoke-autocomplete () 93 | (let ((temp-buffer (generate-new-buffer "*gocode*"))) 94 | (unwind-protect 95 | (progn 96 | (call-process-region (point-min) 97 | (point-max) 98 | "gocode" 99 | nil 100 | temp-buffer 101 | nil 102 | "-f=emacs" 103 | "autocomplete" 104 | (or (buffer-file-name) "") 105 | (concat "c" (int-to-string (- (point) 1)))) 106 | (with-current-buffer temp-buffer (buffer-string))) 107 | (kill-buffer temp-buffer)))) 108 | 109 | (defun ac-go-format-autocomplete (buffer-contents) 110 | (sort 111 | (split-string buffer-contents "\n" t) 112 | (lambda (a b) (string< (downcase a) 113 | (downcase b))))) 114 | 115 | (defun ac-go-get-candidates (strings) 116 | (let ((prop (lambda (entry) 117 | (let* ((name (nth 0 entry)) 118 | (summary (nth 1 entry)) 119 | (symbol (substring summary 0 1))) 120 | (propertize name 121 | 'summary summary 122 | 'symbol symbol)))) 123 | (split (lambda (strings) 124 | (mapcar (lambda (str) 125 | (split-string str ",," t)) 126 | strings)))) 127 | (mapcar prop (funcall split strings)))) 128 | 129 | (defun ac-go-action () 130 | (let ((item (cdr ac-last-completion))) 131 | (when (stringp item) 132 | (let ((symbol (get-text-property 0 'summary item))) 133 | (message "%s" symbol) 134 | (when (and (featurep 'yasnippet) ac-go-expand-arguments-into-snippets) 135 | (ac-go-insert-yas-snippet-string symbol)))))) 136 | 137 | (defun ac-go-insert-yas-snippet-string (s) 138 | (let ((ret "") (pos (point)) match-res match args) 139 | (save-match-data 140 | (setq match-res (string-match "func(." s)) 141 | (when (and match-res (= 0 match-res)) 142 | (setq match (match-string 0 s)) 143 | (unless (string= match "func()") 144 | (setq args (ac-go-split-args s)) 145 | (dolist (arg args) 146 | (setq ret (concat ret "${" arg "}, "))) 147 | (when (> (length ret) 2) 148 | (setq ret (substring ret 0 (- (length ret) 2))))) 149 | (setq ret (concat "(" ret ")")) 150 | (yas-expand-snippet ret pos pos))))) 151 | 152 | (defun ac-go-split-args (args-str) 153 | (let ((cur 5) 154 | (pre 5) 155 | (unmatch-l-paren-count 1) 156 | (args (list)) 157 | c) 158 | (while (> unmatch-l-paren-count 0) 159 | (setq c (aref args-str cur)) 160 | (cond ((= ?\( c) 161 | (setq unmatch-l-paren-count (1+ unmatch-l-paren-count))) 162 | ((= ?\) c) 163 | (setq unmatch-l-paren-count (1- unmatch-l-paren-count)) 164 | (when (= 0 unmatch-l-paren-count) 165 | (push (substring args-str pre cur) args))) 166 | ((= ?\, c) 167 | (when (= 1 unmatch-l-paren-count) 168 | (push (substring args-str pre cur) args) 169 | (setq cur (+ cur 2)) 170 | (setq pre cur)))) 171 | (setq cur (1+ cur))) 172 | (nreverse args))) 173 | 174 | (defun ac-go-document (item) 175 | (if (stringp item) 176 | (let ((s (get-text-property 0 'summary item))) 177 | (message "%s" s) 178 | nil))) 179 | 180 | (defun ac-go-candidates () 181 | (let ((candidates (ac-go-get-candidates 182 | (ac-go-format-autocomplete (ac-go-invoke-autocomplete))))) 183 | (if (equal candidates '("PANIC")) 184 | (progn 185 | (message "GOCODE PANIC: Please check your code by \"go build\"") 186 | nil) 187 | candidates))) 188 | 189 | (defun ac-go-prefix () 190 | (or (ac-prefix-symbol) 191 | (let ((c (char-before))) 192 | (when (eq ?\. c) 193 | (point))))) 194 | 195 | (ac-define-source go 196 | '((candidates . ac-go-candidates) 197 | (candidate-face . ac-candidate-face) 198 | (selection-face . ac-selection-face) 199 | (document . ac-go-document) 200 | (action . ac-go-action) 201 | (prefix . ac-go-prefix) 202 | (requires . 0) 203 | (cache))) 204 | 205 | (add-to-list 'ac-modes 'go-mode) 206 | 207 | (add-hook 'go-mode-hook #'(lambda () 208 | (add-to-list 'ac-sources 'ac-source-go))) 209 | 210 | (provide 'go-autocomplete) 211 | ;;; go-autocomplete.el ends here 212 | -------------------------------------------------------------------------------- /gocode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | ) 9 | 10 | var ( 11 | g_is_server = flag.Bool("s", false, "run a server instead of a client") 12 | g_cache = flag.Bool("cache", false, "use the cache importer") 13 | g_format = flag.String("f", "nice", "output format (vim | emacs | sexp | nice | csv | json)") 14 | g_input = flag.String("in", "", "use this file instead of stdin input") 15 | g_sock = flag.String("sock", defaultSocketType, "socket type (unix | tcp | none)") 16 | g_addr = flag.String("addr", "127.0.0.1:37373", "address for tcp socket") 17 | g_debug = flag.Bool("debug", false, "enable server-side debug mode") 18 | g_source = flag.Bool("source", false, "use source importer") 19 | g_builtin = flag.Bool("builtin", false, "propose completions for built-in functions and types") 20 | g_ignore_case = flag.Bool("ignore-case", false, "do case-insensitive matching") 21 | g_unimported_packages = flag.Bool("unimported-packages", false, "propose completions for standard library packages not explicitly imported") 22 | g_fallback_to_source = flag.Bool("fallback-to-source", false, "if importing a package fails, fallback to the source importer") 23 | ) 24 | 25 | func getSocketPath() string { 26 | user := os.Getenv("USER") 27 | if user == "" { 28 | user = "all" 29 | } 30 | return filepath.Join(os.TempDir(), fmt.Sprintf("gocode-daemon.%s", user)) 31 | } 32 | 33 | func usage() { 34 | fmt.Fprintf(os.Stderr, 35 | "Usage: %s [-s] [-f=] [-in=] [-sock=] [-addr=]\n"+ 36 | " []\n\n", 37 | os.Args[0]) 38 | fmt.Fprintf(os.Stderr, 39 | "Flags:\n") 40 | flag.PrintDefaults() 41 | fmt.Fprintf(os.Stderr, 42 | "\nCommands:\n"+ 43 | " autocomplete [] main autocompletion command\n"+ 44 | " exit terminate the gocode daemon\n") 45 | } 46 | 47 | func main() { 48 | flag.Usage = usage 49 | flag.Parse() 50 | 51 | if *g_is_server { 52 | doServer(*g_cache) 53 | } else { 54 | doClient() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /internal/cache/context.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import "go/build" 4 | 5 | // PackedContext is a copy of build.Context without the func fields. 6 | // 7 | // TODO(mdempsky): Not sure this belongs here. 8 | type PackedContext struct { 9 | GOARCH string 10 | GOOS string 11 | GOROOT string 12 | GOPATH string 13 | CgoEnabled bool 14 | UseAllFiles bool 15 | Compiler string 16 | BuildTags []string 17 | ReleaseTags []string 18 | InstallSuffix string 19 | } 20 | 21 | func PackContext(ctx *build.Context) PackedContext { 22 | return PackedContext{ 23 | GOARCH: ctx.GOARCH, 24 | GOOS: ctx.GOOS, 25 | GOROOT: ctx.GOROOT, 26 | GOPATH: ctx.GOPATH, 27 | CgoEnabled: ctx.CgoEnabled, 28 | UseAllFiles: ctx.UseAllFiles, 29 | Compiler: ctx.Compiler, 30 | BuildTags: ctx.BuildTags, 31 | ReleaseTags: ctx.ReleaseTags, 32 | InstallSuffix: ctx.InstallSuffix, 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /internal/cache/importer.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | goimporter "go/importer" 7 | "go/token" 8 | "go/types" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | "sync" 13 | "time" 14 | 15 | "golang.org/x/tools/go/gcexportdata" 16 | ) 17 | 18 | // We need to mangle go/build.Default to make gcimporter work as 19 | // intended, so use a lock to protect against concurrent accesses. 20 | var buildDefaultLock sync.Mutex 21 | 22 | // Mu must be held while using the cache importer. 23 | var Mu sync.Mutex 24 | 25 | var importCache = importerCache{ 26 | fset: token.NewFileSet(), 27 | imports: make(map[string]importCacheEntry), 28 | } 29 | 30 | func NewImporter(ctx *PackedContext, filename string, fallbackToSource bool, logger func(string, ...interface{})) types.ImporterFrom { 31 | importCache.clean() 32 | 33 | imp := &importer{ 34 | ctx: ctx, 35 | importerCache: &importCache, 36 | fallbackToSource: fallbackToSource, 37 | logf: logger, 38 | } 39 | gbroot, gbvendor := GetGbProjectPaths(ctx, filename) 40 | if gbroot != "" { 41 | imp.gbroot, imp.gbvendor = gbroot, gbvendor 42 | } 43 | return imp 44 | } 45 | 46 | type importer struct { 47 | *importerCache 48 | gbroot, gbvendor string 49 | ctx *PackedContext 50 | fallbackToSource bool 51 | logf func(string, ...interface{}) 52 | } 53 | 54 | type importerCache struct { 55 | fset *token.FileSet 56 | imports map[string]importCacheEntry 57 | } 58 | 59 | type importCacheEntry struct { 60 | pkg *types.Package 61 | mtime time.Time 62 | } 63 | 64 | func (i *importer) Import(importPath string) (*types.Package, error) { 65 | return i.ImportFrom(importPath, "", 0) 66 | } 67 | 68 | func (i *importer) ImportFrom(importPath, srcDir string, mode types.ImportMode) (*types.Package, error) { 69 | buildDefaultLock.Lock() 70 | defer buildDefaultLock.Unlock() 71 | 72 | origDef := build.Default 73 | defer func() { build.Default = origDef }() 74 | 75 | def := &build.Default 76 | // The gb root of a project can be used as a $GOPATH because it contains pkg/. 77 | def.GOPATH = i.ctx.GOPATH 78 | if i.gbroot != "" { 79 | def.GOPATH = i.gbroot 80 | } 81 | def.GOARCH = i.ctx.GOARCH 82 | def.GOOS = i.ctx.GOOS 83 | def.GOROOT = i.ctx.GOROOT 84 | def.CgoEnabled = i.ctx.CgoEnabled 85 | def.UseAllFiles = i.ctx.UseAllFiles 86 | def.Compiler = i.ctx.Compiler 87 | def.BuildTags = i.ctx.BuildTags 88 | def.ReleaseTags = i.ctx.ReleaseTags 89 | def.InstallSuffix = i.ctx.InstallSuffix 90 | def.SplitPathList = i.splitPathList 91 | def.JoinPath = i.joinPath 92 | 93 | i.logf("importing: %v, srcdir: %v", importPath, srcDir) 94 | filename, path := gcexportdata.Find(importPath, srcDir) 95 | entry, ok := i.imports[path] 96 | if filename == "" { 97 | i.logf("no gcexportdata file for %s", path) 98 | // If there is no export data, check the cache. 99 | // TODO(rstambler): Develop a better heuristic for entry eviction. 100 | if ok && time.Since(entry.mtime) <= time.Minute*20 { 101 | return entry.pkg, nil 102 | } 103 | // If there is no cache entry and the user has configured the correct 104 | // setting, import and cache using the source importer. 105 | var pkg *types.Package 106 | var err error 107 | if i.fallbackToSource { 108 | i.logf("cache: falling back to the source importer for %s", path) 109 | pkg, err = goimporter.For("source", nil).Import(path) 110 | } else { 111 | i.logf("cache: falling back to the source default for %s", path) 112 | pkg, err = goimporter.Default().Import(path) 113 | } 114 | if pkg == nil { 115 | i.logf("failed to fall back to another importer for %s: %v", pkg, err) 116 | return nil, err 117 | } 118 | entry = importCacheEntry{pkg, time.Now()} 119 | i.imports[path] = entry 120 | return entry.pkg, nil 121 | } 122 | 123 | // If there is export data for the package. 124 | fi, err := os.Stat(filename) 125 | if err != nil { 126 | i.logf("could not stat %s", filename) 127 | return nil, err 128 | } 129 | if entry.mtime != fi.ModTime() { 130 | f, err := os.Open(filename) 131 | if err != nil { 132 | return nil, err 133 | } 134 | in, err := gcexportdata.NewReader(f) 135 | if err != nil { 136 | return nil, err 137 | } 138 | pkg, err := gcexportdata.Read(in, i.fset, make(map[string]*types.Package), path) 139 | if err != nil { 140 | return nil, err 141 | } 142 | entry = importCacheEntry{pkg, fi.ModTime()} 143 | i.imports[path] = entry 144 | } 145 | 146 | return entry.pkg, nil 147 | } 148 | 149 | // Delete random files to keep the cache at most 100 entries. 150 | // Only call while holding the importer's mutex. 151 | func (i *importerCache) clean() { 152 | for k := range i.imports { 153 | if len(i.imports) <= 100 { 154 | break 155 | } 156 | delete(i.imports, k) 157 | } 158 | } 159 | 160 | func (i *importer) splitPathList(list string) []string { 161 | res := filepath.SplitList(list) 162 | if i.gbroot != "" { 163 | res = append(res, i.gbroot, i.gbvendor) 164 | } 165 | return res 166 | } 167 | 168 | func (i *importer) joinPath(elem ...string) string { 169 | res := filepath.Join(elem...) 170 | 171 | if i.gbroot != "" { 172 | // Want to rewrite "$GBROOT/(vendor/)?pkg/$GOOS_$GOARCH(_)?" 173 | // into "$GBROOT/pkg/$GOOS-$GOARCH(-)?". 174 | // Note: gb doesn't use vendor/pkg. 175 | if gbrel, err := filepath.Rel(i.gbroot, res); err == nil { 176 | gbrel = filepath.ToSlash(gbrel) 177 | gbrel, _ = match(gbrel, "vendor/") 178 | if gbrel, ok := match(gbrel, fmt.Sprintf("pkg/%s_%s", i.ctx.GOOS, i.ctx.GOARCH)); ok { 179 | gbrel, hasSuffix := match(gbrel, "_") 180 | 181 | // Reassemble into result. 182 | if hasSuffix { 183 | gbrel = "-" + gbrel 184 | } 185 | gbrel = fmt.Sprintf("pkg/%s-%s/", i.ctx.GOOS, i.ctx.GOARCH) + gbrel 186 | gbrel = filepath.FromSlash(gbrel) 187 | res = filepath.Join(i.gbroot, gbrel) 188 | } 189 | } 190 | } 191 | return res 192 | } 193 | 194 | func match(s, prefix string) (string, bool) { 195 | rest := strings.TrimPrefix(s, prefix) 196 | return rest, len(rest) < len(s) 197 | } 198 | 199 | // GetGbProjectPaths checks whether we'are in a gb project and returns 200 | // gbroot and gbvendor 201 | func GetGbProjectPaths(ctx *PackedContext, filename string) (string, string) { 202 | slashed := filepath.ToSlash(filename) 203 | i := strings.LastIndex(slashed, "/vendor/src/") 204 | if i < 0 { 205 | i = strings.LastIndex(slashed, "/src/") 206 | } 207 | if i > 0 { 208 | gbroot := filepath.FromSlash(slashed[:i]) 209 | gbvendor := filepath.Join(gbroot, "vendor") 210 | 211 | paths := filepath.SplitList(ctx.GOPATH) 212 | if len(paths) == 0 { 213 | return "", "" 214 | } 215 | 216 | // If there is a slash at end of GOROOT or GOPATH, we'll 217 | // consider this file is inside a gb project wrongly. 218 | if trimmedGoroot := strings.TrimRight(ctx.GOROOT, "\\/"); SamePath(gbroot, trimmedGoroot) { 219 | return "", "" 220 | } 221 | for _, path := range paths { 222 | trimmed := strings.TrimRight(path, "\\/") 223 | if SamePath(trimmed, gbroot) || SamePath(trimmed, gbvendor) { 224 | return "", "" 225 | } 226 | } 227 | 228 | return gbroot, gbvendor 229 | } 230 | 231 | return "", "" 232 | } 233 | -------------------------------------------------------------------------------- /internal/cache/samepath_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package cache 4 | 5 | // SamePath checks two file paths for their equality based on the current filesystem 6 | func SamePath(a, b string) bool { 7 | return a == b 8 | } 9 | -------------------------------------------------------------------------------- /internal/cache/samepath_windows.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package cache 4 | 5 | import ( 6 | "strings" 7 | ) 8 | 9 | // SamePath checks two file paths for their equality based on the current filesystem 10 | func SamePath(a, b string) bool { 11 | return strings.EqualFold(a, b) 12 | } 13 | -------------------------------------------------------------------------------- /internal/gbimporter/gbimporter.go: -------------------------------------------------------------------------------- 1 | package gbimporter 2 | 3 | import ( 4 | "fmt" 5 | "go/build" 6 | "go/types" 7 | "path/filepath" 8 | "strings" 9 | "sync" 10 | 11 | "github.com/mdempsky/gocode/internal/cache" 12 | ) 13 | 14 | // We need to mangle go/build.Default to make gcimporter work as 15 | // intended, so use a lock to protect against concurrent accesses. 16 | var buildDefaultLock sync.Mutex 17 | 18 | // importer implements types.ImporterFrom and provides transparent 19 | // support for gb-based projects. 20 | type importer struct { 21 | ctx *cache.PackedContext 22 | gbroot string 23 | gbpaths []string 24 | underlying types.ImporterFrom 25 | logf func(string, ...interface{}) 26 | } 27 | 28 | func New(ctx *cache.PackedContext, filename string, underlying types.Importer, logger func(string, ...interface{})) types.ImporterFrom { 29 | imp := &importer{ 30 | ctx: ctx, 31 | underlying: underlying.(types.ImporterFrom), 32 | logf: logger, 33 | } 34 | 35 | gbroot, gbvendor := cache.GetGbProjectPaths(ctx, filename) 36 | if gbroot != "" { 37 | imp.gbroot = gbroot 38 | imp.gbpaths = append(filepath.SplitList(imp.ctx.GOPATH), gbroot, gbvendor) 39 | } 40 | return imp 41 | } 42 | 43 | func (i *importer) Import(path string) (*types.Package, error) { 44 | return i.ImportFrom(path, "", 0) 45 | } 46 | 47 | func (i *importer) ImportFrom(path, srcDir string, mode types.ImportMode) (*types.Package, error) { 48 | buildDefaultLock.Lock() 49 | defer buildDefaultLock.Unlock() 50 | 51 | origDef := build.Default 52 | defer func() { build.Default = origDef }() 53 | 54 | def := &build.Default 55 | def.GOARCH = i.ctx.GOARCH 56 | def.GOOS = i.ctx.GOOS 57 | def.GOROOT = i.ctx.GOROOT 58 | def.GOPATH = i.ctx.GOPATH 59 | def.CgoEnabled = i.ctx.CgoEnabled 60 | def.UseAllFiles = i.ctx.UseAllFiles 61 | def.Compiler = i.ctx.Compiler 62 | def.BuildTags = i.ctx.BuildTags 63 | def.ReleaseTags = i.ctx.ReleaseTags 64 | def.InstallSuffix = i.ctx.InstallSuffix 65 | 66 | def.SplitPathList = i.splitPathList 67 | def.JoinPath = i.joinPath 68 | 69 | pkg, err := i.underlying.ImportFrom(path, srcDir, mode) 70 | if pkg == nil { 71 | i.logf("no package found for %s: %v", path, err) 72 | return nil, err 73 | } 74 | return pkg, nil 75 | } 76 | 77 | func (i *importer) splitPathList(list string) []string { 78 | if i.gbroot != "" { 79 | return i.gbpaths 80 | } 81 | return filepath.SplitList(list) 82 | } 83 | 84 | func (i *importer) joinPath(elem ...string) string { 85 | res := filepath.Join(elem...) 86 | 87 | if i.gbroot != "" { 88 | // Want to rewrite "$GBROOT/(vendor/)?pkg/$GOOS_$GOARCH(_)?" 89 | // into "$GBROOT/pkg/$GOOS-$GOARCH(-)?". 90 | // Note: gb doesn't use vendor/pkg. 91 | if gbrel, err := filepath.Rel(i.gbroot, res); err == nil { 92 | gbrel = filepath.ToSlash(gbrel) 93 | gbrel, _ = match(gbrel, "vendor/") 94 | if gbrel, ok := match(gbrel, fmt.Sprintf("pkg/%s_%s", i.ctx.GOOS, i.ctx.GOARCH)); ok { 95 | gbrel, hasSuffix := match(gbrel, "_") 96 | 97 | // Reassemble into result. 98 | if hasSuffix { 99 | gbrel = "-" + gbrel 100 | } 101 | gbrel = fmt.Sprintf("pkg/%s-%s/", i.ctx.GOOS, i.ctx.GOARCH) + gbrel 102 | gbrel = filepath.FromSlash(gbrel) 103 | res = filepath.Join(i.gbroot, gbrel) 104 | } 105 | } 106 | } 107 | 108 | return res 109 | } 110 | 111 | func match(s, prefix string) (string, bool) { 112 | rest := strings.TrimPrefix(s, prefix) 113 | return rest, len(rest) < len(s) 114 | } 115 | -------------------------------------------------------------------------------- /internal/lookdot/lookdot.go: -------------------------------------------------------------------------------- 1 | package lookdot 2 | 3 | import "go/types" 4 | 5 | type Visitor func(obj types.Object) 6 | 7 | func Walk(tv *types.TypeAndValue, v Visitor) bool { 8 | switch { 9 | case tv.IsType(): 10 | walk(tv.Type, false, false, v) 11 | case tv.IsValue(): 12 | walk(tv.Type, tv.Addressable(), true, v) 13 | default: 14 | return false 15 | } 16 | return true 17 | } 18 | 19 | func walk(typ0 types.Type, addable0, value bool, v Visitor) { 20 | // Enumerating valid selector expression identifiers is 21 | // surprisingly nuanced. 22 | 23 | // found is a map from selector identifiers to the objects 24 | // they select. Nil entries are used to track objects that 25 | // have already been reported to the visitor and to indicate 26 | // ambiguous identifiers. 27 | found := make(map[string]types.Object) 28 | 29 | addObj := func(obj types.Object, valid bool) { 30 | id := obj.Id() 31 | switch otherObj, isPresent := found[id]; { 32 | case !isPresent: 33 | if valid { 34 | found[id] = obj 35 | } else { 36 | found[id] = nil 37 | } 38 | case otherObj != nil: 39 | // Ambiguous selector. 40 | found[id] = nil 41 | } 42 | } 43 | 44 | // visited keeps track of named types that we've already 45 | // visited. We only need to track named types, because 46 | // recursion can only happen through embedded struct fields, 47 | // which must be either a named type or a pointer to a named 48 | // type. 49 | visited := make(map[*types.Named]bool) 50 | 51 | type todo struct { 52 | typ types.Type 53 | addable bool 54 | } 55 | 56 | var cur, next []todo 57 | cur = []todo{{typ0, addable0}} 58 | 59 | for { 60 | if len(cur) == 0 { 61 | // Flush discovered objects to visitor function. 62 | for id, obj := range found { 63 | if obj != nil { 64 | v(obj) 65 | found[id] = nil 66 | } 67 | } 68 | 69 | // Move unvisited types from next to cur. 70 | // It's important to check between levels to 71 | // ensure that ambiguous selections are 72 | // correctly handled. 73 | cur = next[:0] 74 | for _, t := range next { 75 | nt := namedOf(t.typ) 76 | if nt == nil { 77 | if _, ok := t.typ.(*types.Basic); ok { 78 | continue 79 | } 80 | panic("panic: embedded struct field without name?") 81 | } 82 | if !visited[nt] { 83 | cur = append(cur, t) 84 | } 85 | } 86 | next = nil 87 | 88 | if len(cur) == 0 { 89 | break 90 | } 91 | } 92 | 93 | now := cur[0] 94 | cur = cur[1:] 95 | 96 | // Look for methods declared on a named type. 97 | { 98 | typ, addable := chasePointer(now.typ) 99 | if !addable { 100 | addable = now.addable 101 | } 102 | if typ, ok := typ.(*types.Named); ok { 103 | visited[typ] = true 104 | for i, n := 0, typ.NumMethods(); i < n; i++ { 105 | m := typ.Method(i) 106 | addObj(m, addable || !hasPtrRecv(m)) 107 | } 108 | } 109 | } 110 | 111 | // Look for interface methods. 112 | if typ, ok := now.typ.Underlying().(*types.Interface); ok { 113 | for i, n := 0, typ.NumMethods(); i < n; i++ { 114 | addObj(typ.Method(i), true) 115 | } 116 | } 117 | 118 | // Look for struct fields. 119 | { 120 | typ, addable := chasePointer(now.typ.Underlying()) 121 | if !addable { 122 | addable = now.addable 123 | } 124 | if typ, ok := typ.Underlying().(*types.Struct); ok { 125 | for i, n := 0, typ.NumFields(); i < n; i++ { 126 | f := typ.Field(i) 127 | addObj(f, value) 128 | if f.Anonymous() { 129 | next = append(next, todo{f.Type(), addable}) 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } 136 | 137 | // namedOf returns the named type T when given T or *T. 138 | // Otherwise, it returns nil. 139 | func namedOf(typ types.Type) *types.Named { 140 | if ptr, isPtr := typ.(*types.Pointer); isPtr { 141 | typ = ptr.Elem() 142 | } 143 | res, _ := typ.(*types.Named) 144 | return res 145 | } 146 | 147 | func hasPtrRecv(m *types.Func) bool { 148 | _, ok := m.Type().(*types.Signature).Recv().Type().(*types.Pointer) 149 | return ok 150 | } 151 | 152 | func chasePointer(typ types.Type) (types.Type, bool) { 153 | if ptr, isPtr := typ.(*types.Pointer); isPtr { 154 | return ptr.Elem(), true 155 | } 156 | return typ, false 157 | } 158 | -------------------------------------------------------------------------------- /internal/lookdot/lookdot_test.go: -------------------------------------------------------------------------------- 1 | package lookdot_test 2 | 3 | import ( 4 | "go/ast" 5 | "go/build" 6 | "go/importer" 7 | "go/parser" 8 | "go/token" 9 | "go/types" 10 | "reflect" 11 | "sort" 12 | "testing" 13 | 14 | "github.com/mdempsky/gocode/internal/lookdot" 15 | ) 16 | 17 | const src = ` 18 | package p 19 | 20 | import "time" 21 | 22 | type S struct { x int; y int } 23 | func (S) Sv() 24 | func (*S) Sp() 25 | var s S 26 | 27 | type Q struct { Z } 28 | var q Q 29 | 30 | type I interface { f(); g() } 31 | 32 | type P *S 33 | 34 | type T1 struct { *T2 } 35 | type T2 struct { *T1 } 36 | func (*T1) t1() 37 | func (*T2) t2() 38 | 39 | type X int 40 | func (*X) x() 41 | type X1 struct { X } 42 | type X2 struct { *X } 43 | type X12 struct { X1; X2 } 44 | 45 | type A1 int 46 | func (A1) A() int 47 | type A2 int 48 | func (A2) A() int 49 | type A struct { A1; A2; } 50 | 51 | type B1 int 52 | func (B1) b() 53 | type B2 struct { b int; B1 } 54 | 55 | var loc time.Location 56 | ` 57 | 58 | var tests = []struct { 59 | lhs string 60 | want []string 61 | }{ 62 | {"S", []string{"Sv"}}, 63 | {"*S", []string{"Sv", "Sp"}}, 64 | {"S{}", []string{"Sv", "x", "y"}}, 65 | {"s", []string{"Sv", "Sp", "x", "y"}}, 66 | 67 | {"I", []string{"f", "g"}}, 68 | {"I(nil)", []string{"f", "g"}}, 69 | {"(*I)(nil)", nil}, 70 | 71 | // golang.org/issue/15708 72 | {"*T1", []string{"t1", "t2"}}, 73 | {"T1", []string{"t2"}}, 74 | 75 | // golang.org/issue/9060 76 | {"error", []string{"Error"}}, 77 | {"struct { error }", []string{"Error"}}, 78 | {"interface { error }", []string{"Error"}}, 79 | 80 | // golang.org/issue/15722 81 | {"P", nil}, 82 | 83 | {"X1", nil}, 84 | {"X2", []string{"x"}}, 85 | {"X12", nil}, 86 | 87 | {"A", nil}, 88 | 89 | {"B2", nil}, 90 | 91 | {"loc", []string{"String"}}, 92 | } 93 | 94 | func TestWalk(t *testing.T) { 95 | fset := token.NewFileSet() 96 | file, err := parser.ParseFile(fset, "src.go", src, 0) 97 | if file == nil { 98 | t.Fatal(err) 99 | } 100 | 101 | cfg := types.Config{ 102 | Importer: importer.Default(), 103 | Error: func(error) {}, 104 | } 105 | pkg, err := cfg.Check("p", fset, []*ast.File{file}, nil) 106 | if pkg == nil { 107 | t.Fatal(err) 108 | } 109 | 110 | // Add a test case for Go 1.11. 111 | if contains(build.Default.ReleaseTags, "go1.11") { 112 | tests = append(tests, struct { 113 | lhs string 114 | want []string 115 | }{"q", []string{"Z"}}) 116 | } 117 | 118 | for _, test := range tests { 119 | tv, err := types.Eval(fset, pkg, token.NoPos, test.lhs) 120 | if err != nil { 121 | t.Errorf("Eval(%q) failed: %v", test.lhs, err) 122 | continue 123 | } 124 | 125 | var got []string 126 | visitor := func(obj types.Object) { 127 | // TODO(mdempsky): Should Walk be responsible 128 | // for filtering out inaccessible objects? 129 | if obj.Exported() || obj.Pkg() == pkg { 130 | got = append(got, obj.Name()) 131 | } 132 | } 133 | 134 | if !lookdot.Walk(&tv, visitor) { 135 | t.Errorf("Walk(%q) returned false", test.lhs) 136 | continue 137 | } 138 | 139 | sort.Strings(got) 140 | sort.Strings(test.want) 141 | 142 | if !reflect.DeepEqual(got, test.want) { 143 | t.Errorf("Look(%q): got %v, want %v", test.lhs, got, test.want) 144 | continue 145 | } 146 | } 147 | } 148 | 149 | func contains(haystack []string, needle string) bool { 150 | for _, item := range haystack { 151 | if item == needle { 152 | return true 153 | } 154 | } 155 | return false 156 | } 157 | -------------------------------------------------------------------------------- /internal/suggest/candidate.go: -------------------------------------------------------------------------------- 1 | package suggest 2 | 3 | import ( 4 | "fmt" 5 | "go/ast" 6 | "go/types" 7 | "sort" 8 | "strings" 9 | ) 10 | 11 | type Candidate struct { 12 | Class string `json:"class"` 13 | PkgPath string `json:"package"` 14 | Name string `json:"name"` 15 | Type string `json:"type"` 16 | Receiver string `json:"receiver,omitempty"` 17 | } 18 | 19 | func (c Candidate) Suggestion() string { 20 | switch { 21 | case c.Class != "func": 22 | return c.Name 23 | case strings.HasPrefix(c.Type, "func()"): 24 | return c.Name + "()" 25 | default: 26 | return c.Name + "(" 27 | } 28 | } 29 | 30 | func (c Candidate) String() string { 31 | if c.Class == "func" { 32 | return fmt.Sprintf("%s %s%s", c.Class, c.Name, strings.TrimPrefix(c.Type, "func")) 33 | } 34 | return fmt.Sprintf("%s %s %s", c.Class, c.Name, c.Type) 35 | } 36 | 37 | type candidatesByClassAndName []Candidate 38 | 39 | func (s candidatesByClassAndName) Len() int { return len(s) } 40 | func (s candidatesByClassAndName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 41 | 42 | func (s candidatesByClassAndName) Less(i, j int) bool { 43 | if s[i].Class != s[j].Class { 44 | return s[i].Class < s[j].Class 45 | } 46 | return s[i].Name < s[j].Name 47 | } 48 | 49 | type objectFilter func(types.Object) bool 50 | 51 | var objectFilters = map[string]objectFilter{ 52 | "const": func(obj types.Object) bool { _, ok := obj.(*types.Const); return ok }, 53 | "func": func(obj types.Object) bool { _, ok := obj.(*types.Func); return ok }, 54 | "package": func(obj types.Object) bool { _, ok := obj.(*types.PkgName); return ok }, 55 | "type": func(obj types.Object) bool { _, ok := obj.(*types.TypeName); return ok }, 56 | "var": func(obj types.Object) bool { _, ok := obj.(*types.Var); return ok }, 57 | } 58 | 59 | func classifyObject(obj types.Object) string { 60 | switch obj.(type) { 61 | case *types.Builtin: 62 | return "func" 63 | case *types.Const: 64 | return "const" 65 | case *types.Func: 66 | return "func" 67 | case *types.Nil: 68 | return "const" 69 | case *types.PkgName: 70 | return "package" 71 | case *types.TypeName: 72 | return "type" 73 | case *types.Var: 74 | return "var" 75 | } 76 | panic(fmt.Sprintf("unhandled types.Object: %T", obj)) 77 | } 78 | 79 | type candidateCollector struct { 80 | exact []types.Object 81 | badcase []types.Object 82 | imports []*ast.ImportSpec 83 | localpkg *types.Package 84 | partial string 85 | filter objectFilter 86 | builtin bool 87 | ignoreCase bool 88 | } 89 | 90 | func (b *candidateCollector) getCandidates() []Candidate { 91 | objs := b.exact 92 | if objs == nil { 93 | objs = b.badcase 94 | } 95 | 96 | var res []Candidate 97 | for _, obj := range objs { 98 | res = append(res, b.asCandidate(obj)) 99 | } 100 | sort.Sort(candidatesByClassAndName(res)) 101 | return res 102 | } 103 | 104 | func (b *candidateCollector) asCandidate(obj types.Object) Candidate { 105 | objClass := classifyObject(obj) 106 | var typ types.Type 107 | switch objClass { 108 | case "const", "func", "var": 109 | typ = obj.Type() 110 | case "type": 111 | typ = obj.Type().Underlying() 112 | } 113 | 114 | var typStr string 115 | switch t := typ.(type) { 116 | case *types.Interface: 117 | typStr = "interface" 118 | case *types.Struct: 119 | typStr = "struct" 120 | default: 121 | if _, isBuiltin := obj.(*types.Builtin); isBuiltin { 122 | typStr = builtinTypes[obj.Name()] 123 | } else if t != nil { 124 | typStr = types.TypeString(t, b.qualify) 125 | } 126 | } 127 | 128 | path := "builtin" 129 | if pkg := obj.Pkg(); pkg != nil { 130 | path = pkg.Path() 131 | } 132 | 133 | var receiver string 134 | if sig, ok := typ.(*types.Signature); ok && sig.Recv() != nil { 135 | receiver = types.TypeString(sig.Recv().Type(), func(*types.Package) string { 136 | return "" 137 | }) 138 | } 139 | 140 | return Candidate{ 141 | Class: objClass, 142 | PkgPath: path, 143 | Name: obj.Name(), 144 | Type: typStr, 145 | Receiver: receiver, 146 | } 147 | } 148 | 149 | var builtinTypes = map[string]string{ 150 | // Universe. 151 | "append": "func(slice []Type, elems ..Type) []Type", 152 | "cap": "func(v Type) int", 153 | "close": "func(c chan<- Type)", 154 | "complex": "func(real FloatType, imag FloatType) ComplexType", 155 | "copy": "func(dst []Type, src []Type) int", 156 | "delete": "func(m map[Key]Type, key Key)", 157 | "imag": "func(c ComplexType) FloatType", 158 | "len": "func(v Type) int", 159 | "make": "func(Type, size IntegerType) Type", 160 | "new": "func(Type) *Type", 161 | "panic": "func(v interface{})", 162 | "print": "func(args ...Type)", 163 | "println": "func(args ...Type)", 164 | "real": "func(c ComplexType) FloatType", 165 | "recover": "func() interface{}", 166 | 167 | // Package unsafe. 168 | "Alignof": "func(x Type) uintptr", 169 | "Sizeof": "func(x Type) uintptr", 170 | "Offsetof": "func(x Type) uintptr", 171 | } 172 | 173 | func (b *candidateCollector) qualify(pkg *types.Package) string { 174 | if pkg == b.localpkg { 175 | return "" 176 | } 177 | 178 | // the *types.Package we are asked to qualify might _not_ be imported 179 | // by the file in which we are asking for candidates. Hence... we retain 180 | // the default of pkg.Name() as the qualifier 181 | 182 | for _, i := range b.imports { 183 | // given the import spec has been correctly parsed (by virtue of 184 | // its existence) we can safely byte-index the path value knowing 185 | // that len("\"") == 1 186 | iPath := i.Path.Value[1 : len(i.Path.Value)-1] 187 | 188 | if iPath == pkg.Path() { 189 | if i.Name != nil && i.Name.Name != "." { 190 | return i.Name.Name 191 | } else { 192 | return pkg.Name() 193 | } 194 | } 195 | } 196 | 197 | return pkg.Name() 198 | } 199 | 200 | func (b *candidateCollector) appendObject(obj types.Object) { 201 | if obj.Pkg() != b.localpkg { 202 | if obj.Parent() == types.Universe { 203 | if !b.builtin { 204 | return 205 | } 206 | } else if !obj.Exported() { 207 | return 208 | } 209 | } 210 | 211 | // TODO(mdempsky): Reconsider this functionality. 212 | if b.filter != nil && !b.filter(obj) { 213 | return 214 | } 215 | if !b.ignoreCase && (b.filter != nil || strings.HasPrefix(obj.Name(), b.partial)) { 216 | b.exact = append(b.exact, obj) 217 | } else if strings.HasPrefix(strings.ToLower(obj.Name()), strings.ToLower(b.partial)) { 218 | b.badcase = append(b.badcase, obj) 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /internal/suggest/cursorcontext.go: -------------------------------------------------------------------------------- 1 | package suggest 2 | 3 | import ( 4 | "bytes" 5 | "go/scanner" 6 | "go/token" 7 | ) 8 | 9 | type tokenIterator struct { 10 | tokens []tokenItem 11 | pos int 12 | } 13 | 14 | type tokenItem struct { 15 | tok token.Token 16 | lit string 17 | } 18 | 19 | func (i tokenItem) String() string { 20 | if i.tok.IsLiteral() { 21 | return i.lit 22 | } 23 | return i.tok.String() 24 | } 25 | 26 | func newTokenIterator(src []byte, cursor int) (tokenIterator, int) { 27 | fset := token.NewFileSet() 28 | file := fset.AddFile("", fset.Base(), len(src)) 29 | cursorPos := file.Pos(cursor) 30 | 31 | var s scanner.Scanner 32 | s.Init(file, src, nil, scanner.ScanComments) 33 | tokens := make([]tokenItem, 0, 1000) 34 | lastPos := token.NoPos 35 | for { 36 | pos, tok, lit := s.Scan() 37 | if tok == token.EOF || pos >= cursorPos { 38 | break 39 | } 40 | tokens = append(tokens, tokenItem{ 41 | tok: tok, 42 | lit: lit, 43 | }) 44 | lastPos = pos 45 | } 46 | return tokenIterator{ 47 | tokens: tokens, 48 | pos: len(tokens) - 1, 49 | }, int(cursorPos - lastPos) 50 | } 51 | 52 | func (ti *tokenIterator) token() tokenItem { 53 | return ti.tokens[ti.pos] 54 | } 55 | 56 | func (ti *tokenIterator) prev() bool { 57 | if ti.pos <= 0 { 58 | return false 59 | } 60 | ti.pos-- 61 | return true 62 | } 63 | 64 | var bracket_pairs_map = map[token.Token]token.Token{ 65 | token.RPAREN: token.LPAREN, 66 | token.RBRACK: token.LBRACK, 67 | token.RBRACE: token.LBRACE, 68 | } 69 | 70 | func (ti *tokenIterator) skipToLeft(left, right token.Token) bool { 71 | if ti.token().tok == left { 72 | return true 73 | } 74 | balance := 1 75 | for balance != 0 { 76 | if !ti.prev() { 77 | return false 78 | } 79 | switch ti.token().tok { 80 | case right: 81 | balance++ 82 | case left: 83 | balance-- 84 | } 85 | } 86 | return true 87 | } 88 | 89 | // when the cursor is at the ')' or ']' or '}', move the cursor to an opposite 90 | // bracket pair, this functions takes nested bracket pairs into account 91 | func (ti *tokenIterator) skipToBalancedPair() bool { 92 | right := ti.token().tok 93 | left := bracket_pairs_map[right] 94 | return ti.skipToLeft(left, right) 95 | } 96 | 97 | // Move the cursor to the open brace of the current block, taking nested blocks 98 | // into account. 99 | func (ti *tokenIterator) skipToLeftCurly() bool { 100 | return ti.skipToLeft(token.LBRACE, token.RBRACE) 101 | } 102 | 103 | // Extract the type expression right before the enclosing curly bracket block. 104 | // Examples (# - the cursor): 105 | // &lib.Struct{Whatever: 1, Hel#} // returns "lib.Struct" 106 | // X{#} // returns X 107 | // The idea is that we check if this type expression is a type and it is, we 108 | // can apply special filtering for autocompletion results. 109 | func (ti *tokenIterator) extractLiteralType() (res string) { 110 | if !ti.skipToLeftCurly() { 111 | return "" 112 | } 113 | origPos := ti.pos 114 | if !ti.prev() { 115 | return "" 116 | } 117 | 118 | // A composite literal type must end with either "ident", 119 | // "ident.ident", or "struct { ... }". 120 | switch ti.token().tok { 121 | case token.IDENT: 122 | if !ti.prev() { 123 | return "" 124 | } 125 | if ti.token().tok == token.PERIOD { 126 | if !ti.prev() { 127 | return "" 128 | } 129 | if ti.token().tok != token.IDENT { 130 | return "" 131 | } 132 | if !ti.prev() { 133 | return "" 134 | } 135 | } 136 | case token.RBRACE: 137 | ti.skipToBalancedPair() 138 | if !ti.prev() { 139 | return "" 140 | } 141 | if ti.token().tok != token.STRUCT { 142 | return "" 143 | } 144 | if !ti.prev() { 145 | return "" 146 | } 147 | } 148 | 149 | // Continuing backwards, we might see "[]", "[...]", "[expr]", 150 | // or "map[T]". 151 | for ti.token().tok == token.RBRACK { 152 | ti.skipToBalancedPair() 153 | if !ti.prev() { 154 | return "" 155 | } 156 | if ti.token().tok == token.MAP { 157 | if !ti.prev() { 158 | return "" 159 | } 160 | } 161 | } 162 | 163 | return joinTokens(ti.tokens[ti.pos+1 : origPos]) 164 | } 165 | 166 | // Starting from the token under the cursor move back and extract something 167 | // that resembles a valid Go primary expression. Examples of primary expressions 168 | // from Go spec: 169 | // x 170 | // 2 171 | // (s + ".txt") 172 | // f(3.1415, true) 173 | // Point{1, 2} 174 | // m["foo"] 175 | // s[i : j + 1] 176 | // obj.color 177 | // f.p[i].x() 178 | // 179 | // As you can see we can move through all of them using balanced bracket 180 | // matching and applying simple rules 181 | // E.g. 182 | // Point{1, 2}.m["foo"].s[i : j + 1].MethodCall(a, func(a, b int) int { return a + b }). 183 | // Can be seen as: 184 | // Point{ }.m[ ].s[ ].MethodCall( ). 185 | // Which boils the rules down to these connected via dots: 186 | // ident 187 | // ident[] 188 | // ident{} 189 | // ident() 190 | // Of course there are also slightly more complicated rules for brackets: 191 | // ident{}.ident()[5][4](), etc. 192 | func (ti *tokenIterator) extractExpr() string { 193 | orig := ti.pos 194 | 195 | // Contains the type of the previously scanned token (initialized with 196 | // the token right under the cursor). This is the token to the *right* of 197 | // the current one. 198 | prev := ti.token().tok 199 | loop: 200 | for { 201 | if !ti.prev() { 202 | return joinTokens(ti.tokens[:orig]) 203 | } 204 | switch ti.token().tok { 205 | case token.PERIOD: 206 | // If the '.' is not followed by IDENT, it's invalid. 207 | if prev != token.IDENT { 208 | break loop 209 | } 210 | case token.IDENT: 211 | // Valid tokens after IDENT are '.', '[', '{' and '('. 212 | switch prev { 213 | case token.PERIOD, token.LBRACK, token.LBRACE, token.LPAREN: 214 | // all ok 215 | default: 216 | break loop 217 | } 218 | case token.RBRACE: 219 | // This one can only be a part of type initialization, like: 220 | // Dummy{}.Hello() 221 | // It is valid Go if Hello method is defined on a non-pointer receiver. 222 | if prev != token.PERIOD { 223 | break loop 224 | } 225 | ti.skipToBalancedPair() 226 | case token.RPAREN, token.RBRACK: 227 | // After ']' and ')' their opening counterparts are valid '[', '(', 228 | // as well as the dot. 229 | switch prev { 230 | case token.PERIOD, token.LBRACK, token.LPAREN: 231 | // all ok 232 | default: 233 | break loop 234 | } 235 | ti.skipToBalancedPair() 236 | default: 237 | break loop 238 | } 239 | prev = ti.token().tok 240 | } 241 | return joinTokens(ti.tokens[ti.pos+1 : orig]) 242 | } 243 | 244 | // Given a slice of token_item, reassembles them into the original literal 245 | // expression. 246 | func joinTokens(tokens []tokenItem) string { 247 | var buf bytes.Buffer 248 | for i, tok := range tokens { 249 | if i > 0 { 250 | buf.WriteByte(' ') 251 | } 252 | buf.WriteString(tok.String()) 253 | } 254 | return buf.String() 255 | } 256 | 257 | type cursorContext int 258 | 259 | const ( 260 | unknownContext cursorContext = iota 261 | emptyResultsContext 262 | selectContext 263 | compositeLiteralContext 264 | ) 265 | 266 | func deduceCursorContext(file []byte, cursor int) (cursorContext, string, string) { 267 | iter, off := newTokenIterator(file, cursor) 268 | if len(iter.tokens) == 0 { 269 | return unknownContext, "", "" 270 | } 271 | 272 | // See if we have a partial identifier to work with. 273 | var partial string 274 | tok := iter.token() 275 | switch { 276 | case tok.tok.IsKeyword(), tok.tok == token.IDENT: 277 | // we're '.' 278 | // parse as Partial and figure out decl 279 | 280 | partial = tok.String() 281 | // If it happens that the cursor is past the end of the literal, 282 | // means there is a space between the literal and the cursor, think 283 | // of it as no context, because that's what it really is. 284 | if off > len(tok.String()) { 285 | return unknownContext, "", "" 286 | } 287 | partial = partial[:off] 288 | if !iter.prev() { 289 | return unknownContext, "", partial 290 | } 291 | } 292 | switch tok.tok { 293 | case token.CHAR, token.COMMENT, token.FLOAT, token.IMAG, token.INT, token.STRING: 294 | return emptyResultsContext, "", partial 295 | } 296 | switch iter.token().tok { 297 | case token.PERIOD: 298 | return selectContext, iter.extractExpr(), partial 299 | case token.COMMA, token.LBRACE: 300 | // This can happen for struct fields: 301 | // &Struct{Hello: 1, Wor#} // (# - the cursor) 302 | // Let's try to find the struct type 303 | return compositeLiteralContext, iter.extractLiteralType(), partial 304 | } 305 | return unknownContext, "", partial 306 | } 307 | -------------------------------------------------------------------------------- /internal/suggest/formatters.go: -------------------------------------------------------------------------------- 1 | package suggest 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | type Formatter func(w io.Writer, candidates []Candidate, num int) 10 | 11 | var Formatters = map[string]Formatter{ 12 | "csv": csvFormat, 13 | "csv-with-package": csvFormat, 14 | "emacs": emacsFormat, 15 | "sexp": sexpFormat, 16 | "godit": goditFormat, 17 | "json": jsonFormat, 18 | "nice": NiceFormat, 19 | "vim": vimFormat, 20 | } 21 | 22 | func NiceFormat(w io.Writer, candidates []Candidate, num int) { 23 | if candidates == nil { 24 | fmt.Fprintf(w, "Nothing to complete.\n") 25 | return 26 | } 27 | 28 | fmt.Fprintf(w, "Found %d candidates:\n", len(candidates)) 29 | for _, c := range candidates { 30 | fmt.Fprintf(w, " %s\n", c.String()) 31 | } 32 | } 33 | 34 | func vimFormat(w io.Writer, candidates []Candidate, num int) { 35 | if candidates == nil { 36 | fmt.Fprint(w, "[0, []]") 37 | return 38 | } 39 | 40 | fmt.Fprintf(w, "[%d, [", num) 41 | for i, c := range candidates { 42 | if i != 0 { 43 | fmt.Fprintf(w, ", ") 44 | } 45 | 46 | word := c.Suggestion() 47 | abbr := c.String() 48 | fmt.Fprintf(w, "{'word': '%s', 'abbr': '%s', 'info': '%s'}", word, abbr, abbr) 49 | } 50 | fmt.Fprintf(w, "]]") 51 | } 52 | 53 | func goditFormat(w io.Writer, candidates []Candidate, num int) { 54 | fmt.Fprintf(w, "%d,,%d\n", num, len(candidates)) 55 | for _, c := range candidates { 56 | fmt.Fprintf(w, "%s,,%s\n", c.String(), c.Suggestion()) 57 | } 58 | } 59 | 60 | func emacsFormat(w io.Writer, candidates []Candidate, num int) { 61 | for _, c := range candidates { 62 | var hint string 63 | switch { 64 | case c.Class == "func": 65 | hint = c.Type 66 | case c.Type == "": 67 | hint = c.Class 68 | default: 69 | hint = c.Class + " " + c.Type 70 | } 71 | fmt.Fprintf(w, "%s,,%s\n", c.Name, hint) 72 | } 73 | } 74 | 75 | func sexpFormat(w io.Writer, candidates []Candidate, num int) { 76 | fmt.Fprint(w, "(") 77 | for _, c := range candidates { 78 | fmt.Fprintf(w, "(%s \"%s\" \"%s\" \"%s\")", c.Class, c.Name, c.Type, c.PkgPath) 79 | } 80 | fmt.Fprint(w, ")") 81 | } 82 | 83 | func csvFormat(w io.Writer, candidates []Candidate, num int) { 84 | for _, c := range candidates { 85 | fmt.Fprintf(w, "%s,,%s,,%s,,%s\n", c.Class, c.Name, c.Type, c.PkgPath) 86 | } 87 | } 88 | 89 | func jsonFormat(w io.Writer, candidates []Candidate, num int) { 90 | var x []interface{} 91 | if candidates != nil { 92 | x = []interface{}{num, candidates} 93 | } 94 | json.NewEncoder(w).Encode(x) 95 | } 96 | -------------------------------------------------------------------------------- /internal/suggest/formatters_test.go: -------------------------------------------------------------------------------- 1 | package suggest_test 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/mdempsky/gocode/internal/suggest" 8 | ) 9 | 10 | func TestFormatters(t *testing.T) { 11 | // TODO(mdempsky): More comprehensive test. 12 | 13 | num := len("client") 14 | candidates := []suggest.Candidate{{ 15 | Class: "func", 16 | PkgPath: "gocode", 17 | Name: "client_auto_complete", 18 | Type: "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)", 19 | }, { 20 | Class: "func", 21 | PkgPath: "gocode", 22 | Name: "client_close", 23 | Type: "func(cli *rpc.Client, Arg0 int) int", 24 | }, { 25 | Class: "func", 26 | PkgPath: "gocode", 27 | Name: "client_cursor_type_pkg", 28 | Type: "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)", 29 | }, { 30 | Class: "func", 31 | PkgPath: "gocode", 32 | Name: "client_drop_cache", 33 | Type: "func(cli *rpc.Client, Arg0 int) int", 34 | }, { 35 | Class: "func", 36 | PkgPath: "gocode", 37 | Name: "client_highlight", 38 | Type: "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)", 39 | }, { 40 | Class: "func", 41 | PkgPath: "gocode", 42 | Name: "client_set", 43 | Type: "func(cli *rpc.Client, Arg0, Arg1 string) string", 44 | }, { 45 | Class: "func", 46 | PkgPath: "gocode", 47 | Name: "client_status", 48 | Type: "func(cli *rpc.Client, Arg0 int) string", 49 | }} 50 | 51 | var tests = [...]struct { 52 | name string 53 | want string 54 | }{ 55 | {"json", `[6,[{"class":"func","package":"gocode","name":"client_auto_complete","type":"func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)"},{"class":"func","package":"gocode","name":"client_close","type":"func(cli *rpc.Client, Arg0 int) int"},{"class":"func","package":"gocode","name":"client_cursor_type_pkg","type":"func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)"},{"class":"func","package":"gocode","name":"client_drop_cache","type":"func(cli *rpc.Client, Arg0 int) int"},{"class":"func","package":"gocode","name":"client_highlight","type":"func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)"},{"class":"func","package":"gocode","name":"client_set","type":"func(cli *rpc.Client, Arg0, Arg1 string) string"},{"class":"func","package":"gocode","name":"client_status","type":"func(cli *rpc.Client, Arg0 int) string"}]] 56 | `}, 57 | {"nice", `Found 7 candidates: 58 | func client_auto_complete(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int) 59 | func client_close(cli *rpc.Client, Arg0 int) int 60 | func client_cursor_type_pkg(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string) 61 | func client_drop_cache(cli *rpc.Client, Arg0 int) int 62 | func client_highlight(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int) 63 | func client_set(cli *rpc.Client, Arg0, Arg1 string) string 64 | func client_status(cli *rpc.Client, Arg0 int) string 65 | `}, 66 | {"vim", `[6, [{'word': 'client_auto_complete(', 'abbr': 'func client_auto_complete(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)', 'info': 'func client_auto_complete(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)'}, {'word': 'client_close(', 'abbr': 'func client_close(cli *rpc.Client, Arg0 int) int', 'info': 'func client_close(cli *rpc.Client, Arg0 int) int'}, {'word': 'client_cursor_type_pkg(', 'abbr': 'func client_cursor_type_pkg(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)', 'info': 'func client_cursor_type_pkg(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)'}, {'word': 'client_drop_cache(', 'abbr': 'func client_drop_cache(cli *rpc.Client, Arg0 int) int', 'info': 'func client_drop_cache(cli *rpc.Client, Arg0 int) int'}, {'word': 'client_highlight(', 'abbr': 'func client_highlight(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)', 'info': 'func client_highlight(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)'}, {'word': 'client_set(', 'abbr': 'func client_set(cli *rpc.Client, Arg0, Arg1 string) string', 'info': 'func client_set(cli *rpc.Client, Arg0, Arg1 string) string'}, {'word': 'client_status(', 'abbr': 'func client_status(cli *rpc.Client, Arg0 int) string', 'info': 'func client_status(cli *rpc.Client, Arg0 int) string'}]]`}, 67 | {"godit", `6,,7 68 | func client_auto_complete(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int),,client_auto_complete( 69 | func client_close(cli *rpc.Client, Arg0 int) int,,client_close( 70 | func client_cursor_type_pkg(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string),,client_cursor_type_pkg( 71 | func client_drop_cache(cli *rpc.Client, Arg0 int) int,,client_drop_cache( 72 | func client_highlight(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int),,client_highlight( 73 | func client_set(cli *rpc.Client, Arg0, Arg1 string) string,,client_set( 74 | func client_status(cli *rpc.Client, Arg0 int) string,,client_status( 75 | `}, 76 | {"emacs", ` 77 | client_auto_complete,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int) 78 | client_close,,func(cli *rpc.Client, Arg0 int) int 79 | client_cursor_type_pkg,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string) 80 | client_drop_cache,,func(cli *rpc.Client, Arg0 int) int 81 | client_highlight,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int) 82 | client_set,,func(cli *rpc.Client, Arg0, Arg1 string) string 83 | client_status,,func(cli *rpc.Client, Arg0 int) string 84 | `[1:]}, {"sexp", ` 85 | ((func "client_auto_complete" "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int)" "gocode")(func "client_close" "func(cli *rpc.Client, Arg0 int) int" "gocode")(func "client_cursor_type_pkg" "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string)" "gocode")(func "client_drop_cache" "func(cli *rpc.Client, Arg0 int) int" "gocode")(func "client_highlight" "func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int)" "gocode")(func "client_set" "func(cli *rpc.Client, Arg0, Arg1 string) string" "gocode")(func "client_status" "func(cli *rpc.Client, Arg0 int) string" "gocode"))`[1:]}, 86 | {"csv", ` 87 | func,,client_auto_complete,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int, Arg3 gocode_env) (c []candidate, d int),,gocode 88 | func,,client_close,,func(cli *rpc.Client, Arg0 int) int,,gocode 89 | func,,client_cursor_type_pkg,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 int) (typ, pkg string),,gocode 90 | func,,client_drop_cache,,func(cli *rpc.Client, Arg0 int) int,,gocode 91 | func,,client_highlight,,func(cli *rpc.Client, Arg0 []byte, Arg1 string, Arg2 gocode_env) (c []highlight_range, d int),,gocode 92 | func,,client_set,,func(cli *rpc.Client, Arg0, Arg1 string) string,,gocode 93 | func,,client_status,,func(cli *rpc.Client, Arg0 int) string,,gocode 94 | `[1:]}, 95 | } 96 | 97 | for _, test := range tests { 98 | var out bytes.Buffer 99 | suggest.Formatters[test.name](&out, candidates, num) 100 | 101 | if got := out.String(); got != test.want { 102 | t.Errorf("Format %s:\nGot:\n%q\nWant:\n%q\n", test.name, got, test.want) 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /internal/suggest/stdlib.go: -------------------------------------------------------------------------------- 1 | package suggest 2 | 3 | var knownPackageIdents = map[string]string{ 4 | "adler32": "hash/adler32", 5 | "aes": "crypto/aes", 6 | "ascii85": "encoding/ascii85", 7 | "asn1": "encoding/asn1", 8 | "ast": "go/ast", 9 | "atomic": "sync/atomic", 10 | "base32": "encoding/base32", 11 | "base64": "encoding/base64", 12 | "big": "math/big", 13 | "binary": "encoding/binary", 14 | "bufio": "bufio", 15 | "build": "go/build", 16 | "bytes": "bytes", 17 | "bzip2": "compress/bzip2", 18 | "cgi": "net/http/cgi", 19 | "cgo": "runtime/cgo", 20 | "cipher": "crypto/cipher", 21 | "cmplx": "math/cmplx", 22 | "color": "image/color", 23 | "constant": "go/constant", 24 | "context": "context", 25 | "cookiejar": "net/http/cookiejar", 26 | "crc32": "hash/crc32", 27 | "crc64": "hash/crc64", 28 | "crypto": "crypto", 29 | "csv": "encoding/csv", 30 | "debug": "runtime/debug", 31 | "des": "crypto/des", 32 | "doc": "go/doc", 33 | "draw": "image/draw", 34 | "driver": "database/sql/driver", 35 | "dsa": "crypto/dsa", 36 | "dwarf": "debug/dwarf", 37 | "ecdsa": "crypto/ecdsa", 38 | "elf": "debug/elf", 39 | "elliptic": "crypto/elliptic", 40 | "encoding": "encoding", 41 | "errors": "errors", 42 | "exec": "os/exec", 43 | "expvar": "expvar", 44 | "fcgi": "net/http/fcgi", 45 | "filepath": "path/filepath", 46 | "flag": "flag", 47 | "flate": "compress/flate", 48 | "fmt": "fmt", 49 | "fnv": "hash/fnv", 50 | "format": "go/format", 51 | "gif": "image/gif", 52 | "gob": "encoding/gob", 53 | "gosym": "debug/gosym", 54 | "gzip": "compress/gzip", 55 | "hash": "hash", 56 | "heap": "container/heap", 57 | "hex": "encoding/hex", 58 | "hmac": "crypto/hmac", 59 | "hpack": "vendor/golang_org/x/net/http2/hpack", 60 | "html": "html", 61 | "http": "net/http", 62 | "httplex": "vendor/golang_org/x/net/lex/httplex", 63 | "httptest": "net/http/httptest", 64 | "httptrace": "net/http/httptrace", 65 | "httputil": "net/http/httputil", 66 | "image": "image", 67 | "importer": "go/importer", 68 | "io": "io", 69 | "iotest": "testing/iotest", 70 | "ioutil": "io/ioutil", 71 | "jpeg": "image/jpeg", 72 | "json": "encoding/json", 73 | "jsonrpc": "net/rpc/jsonrpc", 74 | "list": "container/list", 75 | "log": "log", 76 | "lzw": "compress/lzw", 77 | "macho": "debug/macho", 78 | "mail": "net/mail", 79 | "math": "math", 80 | "md5": "crypto/md5", 81 | "mime": "mime", 82 | "multipart": "mime/multipart", 83 | "net": "net", 84 | "os": "os", 85 | "palette": "image/color/palette", 86 | "parse": "text/template/parse", 87 | "parser": "go/parser", 88 | "path": "path", 89 | "pe": "debug/pe", 90 | "pem": "encoding/pem", 91 | "pkix": "crypto/x509/pkix", 92 | "plan9obj": "debug/plan9obj", 93 | "png": "image/png", 94 | "pprof": "net/http/pprof", 95 | "printer": "go/printer", 96 | "quick": "testing/quick", 97 | "quotedprintable": "mime/quotedprintable", 98 | "race": "runtime/race", 99 | "rand": "math/rand", 100 | "rc4": "crypto/rc4", 101 | "reflect": "reflect", 102 | "regexp": "regexp", 103 | "ring": "container/ring", 104 | "rpc": "net/rpc", 105 | "rsa": "crypto/rsa", 106 | "runtime": "runtime", 107 | "scanner": "text/scanner", 108 | "sha1": "crypto/sha1", 109 | "sha256": "crypto/sha256", 110 | "sha512": "crypto/sha512", 111 | "signal": "os/signal", 112 | "smtp": "net/smtp", 113 | "sort": "sort", 114 | "sql": "database/sql", 115 | "strconv": "strconv", 116 | "strings": "strings", 117 | "subtle": "crypto/subtle", 118 | "suffixarray": "index/suffixarray", 119 | "sync": "sync", 120 | "syntax": "regexp/syntax", 121 | "syscall": "syscall", 122 | "syslog": "log/syslog", 123 | "tabwriter": "text/tabwriter", 124 | "tar": "archive/tar", 125 | "template": "html/template", 126 | "testing": "testing", 127 | "textproto": "net/textproto", 128 | "time": "time", 129 | "tls": "crypto/tls", 130 | "token": "go/token", 131 | "trace": "runtime/trace", 132 | "types": "go/types", 133 | "unicode": "unicode", 134 | "url": "net/url", 135 | "user": "os/user", 136 | "utf16": "unicode/utf16", 137 | "utf8": "unicode/utf8", 138 | "x509": "crypto/x509", 139 | "xml": "encoding/xml", 140 | "zip": "archive/zip", 141 | "zlib": "compress/zlib", 142 | //"scanner": "go/scanner", // DUP: prefer text/scanner 143 | //"template": "text/template", // DUP: prefer html/template 144 | //"pprof": "runtime/pprof", // DUP: prefer net/http/pprof 145 | //"rand": "crypto/rand", // DUP: prefer math/rand 146 | } 147 | -------------------------------------------------------------------------------- /internal/suggest/suggest.go: -------------------------------------------------------------------------------- 1 | package suggest 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/ast" 7 | "go/parser" 8 | "go/scanner" 9 | "go/token" 10 | "go/types" 11 | "io/ioutil" 12 | "os" 13 | "path/filepath" 14 | "strings" 15 | "sync" 16 | "time" 17 | 18 | "github.com/mdempsky/gocode/internal/lookdot" 19 | ) 20 | 21 | type Config struct { 22 | Importer types.Importer 23 | Logf func(fmt string, args ...interface{}) 24 | Builtin bool 25 | IgnoreCase bool 26 | UnimportedPackages bool 27 | } 28 | 29 | var cache = struct { 30 | lock sync.Mutex 31 | files map[string]fileCacheEntry 32 | fset *token.FileSet 33 | }{ 34 | files: make(map[string]fileCacheEntry), 35 | fset: token.NewFileSet(), 36 | } 37 | 38 | type fileCacheEntry struct { 39 | file *ast.File 40 | mtime time.Time 41 | } 42 | 43 | // Suggest returns a list of suggestion candidates and the length of 44 | // the text that should be replaced, if any. 45 | func (c *Config) Suggest(filename string, data []byte, cursor int) ([]Candidate, int) { 46 | if cursor < 0 { 47 | return nil, 0 48 | } 49 | 50 | fset, pos, pkg, imports := c.analyzePackage(filename, data, cursor) 51 | if pkg == nil { 52 | c.Logf("no package found for %s", filename) 53 | return nil, 0 54 | } 55 | scope := pkg.Scope().Innermost(pos) 56 | 57 | ctx, expr, partial := deduceCursorContext(data, cursor) 58 | b := candidateCollector{ 59 | localpkg: pkg, 60 | imports: imports, 61 | partial: partial, 62 | filter: objectFilters[partial], 63 | builtin: ctx != selectContext && c.Builtin, 64 | ignoreCase: c.IgnoreCase, 65 | } 66 | 67 | switch ctx { 68 | case emptyResultsContext: 69 | // don't show results in certain cases 70 | return nil, 0 71 | 72 | case selectContext: 73 | tv, _ := types.Eval(fset, pkg, pos, expr) 74 | if lookdot.Walk(&tv, b.appendObject) { 75 | break 76 | } 77 | 78 | _, obj := scope.LookupParent(expr, pos) 79 | if pkgName, isPkg := obj.(*types.PkgName); isPkg { 80 | c.packageCandidates(pkgName.Imported(), &b) 81 | break 82 | } 83 | if !c.UnimportedPackages { 84 | return nil, 0 85 | } 86 | pkg := c.resolveKnownPackageIdent(expr) 87 | if pkg == nil { 88 | return nil, 0 89 | } 90 | c.packageCandidates(pkg, &b) 91 | 92 | case compositeLiteralContext: 93 | tv, _ := types.Eval(fset, pkg, pos, expr) 94 | if tv.IsType() { 95 | if _, isStruct := tv.Type.Underlying().(*types.Struct); isStruct { 96 | c.fieldNameCandidates(tv.Type, &b) 97 | break 98 | } 99 | } 100 | fallthrough 101 | case unknownContext: 102 | c.scopeCandidates(scope, pos, &b) 103 | } 104 | 105 | res := b.getCandidates() 106 | if len(res) == 0 { 107 | return nil, 0 108 | } 109 | return res, len(partial) 110 | } 111 | 112 | func (c *Config) parseOtherFile(filename string) *ast.File { 113 | entry := cache.files[filename] 114 | 115 | fi, err := os.Stat(filename) 116 | if err != nil { 117 | // TODO(mdempsky): How to handle this cleanly? 118 | panic(err) 119 | } 120 | 121 | if entry.mtime != fi.ModTime() { 122 | file, err := parser.ParseFile(cache.fset, filename, nil, 0) 123 | if err != nil { 124 | c.logParseError(fmt.Sprintf("Error parsing %q", filename), err) 125 | } 126 | trimAST(file, token.NoPos) 127 | 128 | entry = fileCacheEntry{file, fi.ModTime()} 129 | cache.files[filename] = entry 130 | } 131 | 132 | return entry.file 133 | } 134 | 135 | func (c *Config) analyzePackage(filename string, data []byte, cursor int) (*token.FileSet, token.Pos, *types.Package, []*ast.ImportSpec) { 136 | cache.lock.Lock() 137 | defer cache.lock.Unlock() 138 | 139 | // Reset every 1GB of files so fset doesn't overflow. 140 | if cache.fset.Base() >= 1e9 { 141 | cache.fset = token.NewFileSet() 142 | cache.files = make(map[string]fileCacheEntry) 143 | } 144 | 145 | // Delete random files to keep the cache at most 100 entries. 146 | for k := range cache.files { 147 | if len(cache.files) <= 100 { 148 | break 149 | } 150 | delete(cache.files, k) 151 | } 152 | 153 | // If we're in trailing white space at the end of a scope, 154 | // sometimes go/types doesn't recognize that variables should 155 | // still be in scope there. 156 | filesemi := bytes.Join([][]byte{data[:cursor], []byte(";"), data[cursor:]}, nil) 157 | 158 | fileAST, err := parser.ParseFile(cache.fset, filename, filesemi, parser.AllErrors) 159 | if err != nil { 160 | c.logParseError("Error parsing input file (outer block)", err) 161 | } 162 | astPos := fileAST.Pos() 163 | if astPos == 0 { 164 | return nil, token.NoPos, nil, nil 165 | } 166 | pos := cache.fset.File(astPos).Pos(cursor) 167 | trimAST(fileAST, pos) 168 | 169 | files := []*ast.File{fileAST} 170 | for _, otherName := range c.findOtherPackageFiles(filename, fileAST.Name.Name) { 171 | files = append(files, c.parseOtherFile(otherName)) 172 | } 173 | 174 | cfg := types.Config{ 175 | Importer: c.Importer, 176 | Error: func(err error) {}, 177 | } 178 | pkg, _ := cfg.Check("", cache.fset, files, nil) 179 | 180 | return cache.fset, pos, pkg, fileAST.Imports 181 | } 182 | 183 | // trimAST clears any part of the AST not relevant to type checking 184 | // expressions at pos. 185 | func trimAST(file *ast.File, pos token.Pos) { 186 | ast.Inspect(file, func(n ast.Node) bool { 187 | if n == nil { 188 | return false 189 | } 190 | if pos < n.Pos() || pos >= n.End() { 191 | switch n := n.(type) { 192 | case *ast.FuncDecl: 193 | n.Body = nil 194 | case *ast.BlockStmt: 195 | n.List = nil 196 | case *ast.CaseClause: 197 | n.Body = nil 198 | case *ast.CommClause: 199 | n.Body = nil 200 | case *ast.CompositeLit: 201 | // Leave elts in place for [...]T 202 | // array literals, because they can 203 | // affect the expression's type. 204 | if !isEllipsisArray(n.Type) { 205 | n.Elts = nil 206 | } 207 | } 208 | } 209 | return true 210 | }) 211 | } 212 | 213 | func isEllipsisArray(n ast.Expr) bool { 214 | at, ok := n.(*ast.ArrayType) 215 | if !ok { 216 | return false 217 | } 218 | _, ok = at.Len.(*ast.Ellipsis) 219 | return ok 220 | } 221 | 222 | func (c *Config) fieldNameCandidates(typ types.Type, b *candidateCollector) { 223 | s := typ.Underlying().(*types.Struct) 224 | for i, n := 0, s.NumFields(); i < n; i++ { 225 | b.appendObject(s.Field(i)) 226 | } 227 | } 228 | 229 | func (c *Config) packageCandidates(pkg *types.Package, b *candidateCollector) { 230 | c.scopeCandidates(pkg.Scope(), token.NoPos, b) 231 | } 232 | 233 | func (c *Config) scopeCandidates(scope *types.Scope, pos token.Pos, b *candidateCollector) { 234 | seen := make(map[string]bool) 235 | for scope != nil { 236 | for _, name := range scope.Names() { 237 | if seen[name] { 238 | continue 239 | } 240 | seen[name] = true 241 | _, obj := scope.LookupParent(name, pos) 242 | if obj != nil { 243 | b.appendObject(obj) 244 | } 245 | } 246 | scope = scope.Parent() 247 | } 248 | } 249 | 250 | func (c *Config) logParseError(intro string, err error) { 251 | if c.Logf == nil { 252 | return 253 | } 254 | if el, ok := err.(scanner.ErrorList); ok { 255 | c.Logf("%s:", intro) 256 | for _, er := range el { 257 | c.Logf(" %s", er) 258 | } 259 | } else { 260 | c.Logf("%s: %s", intro, err) 261 | } 262 | } 263 | 264 | func (c *Config) findOtherPackageFiles(filename, pkgName string) []string { 265 | if filename == "" { 266 | return nil 267 | } 268 | 269 | dir, file := filepath.Split(filename) 270 | dents, err := ioutil.ReadDir(dir) 271 | if err != nil { 272 | panic(err) 273 | } 274 | isTestFile := strings.HasSuffix(file, "_test.go") 275 | 276 | // TODO(mdempsky): Use go/build.(*Context).MatchFile or 277 | // something to properly handle build tags? 278 | var out []string 279 | for _, dent := range dents { 280 | name := dent.Name() 281 | if strings.HasPrefix(name, ".") || strings.HasPrefix(name, "_") { 282 | continue 283 | } 284 | if name == file || !strings.HasSuffix(name, ".go") { 285 | continue 286 | } 287 | if !isTestFile && strings.HasSuffix(name, "_test.go") { 288 | continue 289 | } 290 | 291 | abspath := filepath.Join(dir, name) 292 | if pkgNameFor(abspath) == pkgName { 293 | out = append(out, abspath) 294 | } 295 | } 296 | 297 | return out 298 | } 299 | 300 | func (c *Config) resolveKnownPackageIdent(pkgName string) *types.Package { 301 | pkgName, ok := knownPackageIdents[pkgName] 302 | if !ok { 303 | return nil 304 | } 305 | pkg, _ := c.Importer.Import(pkgName) 306 | return pkg 307 | } 308 | 309 | func pkgNameFor(filename string) string { 310 | file, _ := parser.ParseFile(token.NewFileSet(), filename, nil, parser.PackageClauseOnly) 311 | if file == nil { 312 | return "" 313 | } 314 | return file.Name.Name 315 | } 316 | -------------------------------------------------------------------------------- /internal/suggest/suggest_test.go: -------------------------------------------------------------------------------- 1 | package suggest_test 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "flag" 7 | "go/importer" 8 | "io/ioutil" 9 | "os" 10 | "path/filepath" 11 | "runtime" 12 | "strings" 13 | "testing" 14 | 15 | "github.com/mdempsky/gocode/internal/suggest" 16 | ) 17 | 18 | var testDirFlag = flag.String("testdir", "", "specify a directory to run the test on") 19 | 20 | func TestRegress(t *testing.T) { 21 | testDirs, err := filepath.Glob("testdata/test.*") 22 | if err != nil { 23 | t.Fatal(err) 24 | } 25 | if *testDirFlag != "" { 26 | t.Run(*testDirFlag, func(t *testing.T) { 27 | testRegress(t, "testdata/test."+*testDirFlag) 28 | }) 29 | } else { 30 | for _, testDir := range testDirs { 31 | // Skip test.0011 for Go <= 1.11 because a method was added to reflect.Value. 32 | // TODO(rstambler): Change this when Go 1.12 comes out. 33 | if !strings.HasPrefix(runtime.Version(), "devel") && strings.HasSuffix(testDir, "test.0011") { 34 | continue 35 | } 36 | testDir := testDir // capture 37 | name := strings.TrimPrefix(testDir, "testdata/") 38 | t.Run(name, func(t *testing.T) { 39 | t.Parallel() 40 | testRegress(t, testDir) 41 | }) 42 | } 43 | } 44 | } 45 | 46 | func testRegress(t *testing.T, testDir string) { 47 | testDir, err := filepath.Abs(testDir) 48 | if err != nil { 49 | t.Errorf("Abs failed: %v", err) 50 | return 51 | } 52 | 53 | filename := filepath.Join(testDir, "test.go.in") 54 | data, err := ioutil.ReadFile(filename) 55 | if err != nil { 56 | t.Errorf("ReadFile failed: %v", err) 57 | return 58 | } 59 | 60 | cursor := bytes.IndexByte(data, '@') 61 | if cursor < 0 { 62 | t.Errorf("Missing @") 63 | return 64 | } 65 | data = append(data[:cursor], data[cursor+1:]...) 66 | 67 | cfg := suggest.Config{ 68 | Importer: importer.Default(), 69 | Logf: func(string, ...interface{}) {}, 70 | } 71 | cfg.Logf = func(string, ...interface{}) {} 72 | if testing.Verbose() { 73 | cfg.Logf = t.Logf 74 | } 75 | if cfgJSON, err := os.Open(filepath.Join(testDir, "config.json")); err == nil { 76 | if err := json.NewDecoder(cfgJSON).Decode(&cfg); err != nil { 77 | t.Errorf("Decode failed: %v", err) 78 | return 79 | } 80 | } else if !os.IsNotExist(err) { 81 | t.Errorf("Open failed: %v", err) 82 | return 83 | } 84 | candidates, prefixLen := cfg.Suggest(filename, data, cursor) 85 | 86 | var out bytes.Buffer 87 | suggest.NiceFormat(&out, candidates, prefixLen) 88 | want, _ := ioutil.ReadFile(filepath.Join(testDir, "out.expected")) 89 | if got := out.Bytes(); !bytes.Equal(got, want) { 90 | t.Errorf("%s:\nGot:\n%s\nWant:\n%s\n", testDir, got, want) 91 | return 92 | } 93 | } 94 | 95 | func contains(haystack []string, needle string) bool { 96 | for _, x := range haystack { 97 | if needle == x { 98 | return true 99 | } 100 | } 101 | return false 102 | } 103 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0001/out.expected: -------------------------------------------------------------------------------- 1 | Found 25 candidates: 2 | func Errorf(format string, a ...interface{}) error 3 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) 4 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) 5 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) 6 | func Fscan(r io.Reader, a ...interface{}) (n int, err error) 7 | func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) 8 | func Fscanln(r io.Reader, a ...interface{}) (n int, err error) 9 | func Print(a ...interface{}) (n int, err error) 10 | func Printf(format string, a ...interface{}) (n int, err error) 11 | func Println(a ...interface{}) (n int, err error) 12 | func Scan(a ...interface{}) (n int, err error) 13 | func Scanf(format string, a ...interface{}) (n int, err error) 14 | func Scanln(a ...interface{}) (n int, err error) 15 | func Sprint(a ...interface{}) string 16 | func Sprintf(format string, a ...interface{}) string 17 | func Sprintln(a ...interface{}) string 18 | func Sscan(str string, a ...interface{}) (n int, err error) 19 | func Sscanf(str string, format string, a ...interface{}) (n int, err error) 20 | func Sscanln(str string, a ...interface{}) (n int, err error) 21 | type Formatter interface 22 | type GoStringer interface 23 | type ScanState interface 24 | type Scanner interface 25 | type State interface 26 | type Stringer interface 27 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0001/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | fmt.@ 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0002/out.expected: -------------------------------------------------------------------------------- 1 | Found 5 candidates: 2 | func main() 3 | package os 4 | var key string 5 | var test map[string]invalid type 6 | var value invalid type 7 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0002/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "os" 4 | 5 | var test map[string]os.Error 6 | 7 | func main() { 8 | for key, value := range test { 9 | @ 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0003/out.expected: -------------------------------------------------------------------------------- 1 | Found 7 candidates: 2 | func End() token.Pos 3 | func IsExported() bool 4 | func Pos() token.Pos 5 | func String() string 6 | var Name string 7 | var NamePos token.Pos 8 | var Obj *ast.Object 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0003/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go/ast" 5 | "io" 6 | ) 7 | 8 | func PrettyPrintTypeExpr(out io.Writer, e ast.Expr) { 9 | switch t := e.(type) { 10 | case *ast.StarExpr: 11 | fmt.Fprintf(out, "*") 12 | PrettyPrintTypeExpr(out, t.X) 13 | case *ast.Ident: 14 | // ast.Ident type decl as a reminder (note embedded type): 15 | // 16 | // type Ident struct { 17 | // token.Position // identifier position 18 | // Obj *Object // denoted object 19 | // } 20 | // 21 | // Correct type inference in complex type switch statements + 22 | // support for type embedding 23 | fmt.Fprintf(out, t.Name()) 24 | t.@ 25 | case *ast.ArrayType: 26 | fmt.Fprintf(out, "[]") 27 | PrettyPrintTypeExpr(out, t.Elt) 28 | case *ast.SelectorExpr: 29 | PrettyPrintTypeExpr(out, t.X) 30 | fmt.Fprintf(out, ".%s", t.Sel.Name()) 31 | case *ast.FuncType: 32 | // SKIP THIS FOR DEMO 33 | case *ast.MapType: 34 | fmt.Fprintf(out, "map[") 35 | PrettyPrintTypeExpr(out, t.Key) 36 | fmt.Fprintf(out, "]") 37 | PrettyPrintTypeExpr(out, t.Value) 38 | case *ast.InterfaceType: 39 | fmt.Fprintf(out, "interface{}") 40 | case *ast.Ellipsis: 41 | fmt.Fprintf(out, "...") 42 | PrettyPrintTypeExpr(out, t.Elt) 43 | case *ast.StructType: 44 | fmt.Fprintf(out, "struct") 45 | case *ast.ChanType: 46 | switch t.Dir { 47 | case ast.RECV: 48 | fmt.Fprintf(out, "<-chan ") 49 | case ast.SEND: 50 | fmt.Fprintf(out, "chan<- ") 51 | case ast.SEND | ast.RECV: 52 | fmt.Fprintf(out, "chan ") 53 | } 54 | PrettyPrintTypeExpr(out, t.Value) 55 | default: 56 | panic("OMGWTFBBQ!") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0004/out.expected: -------------------------------------------------------------------------------- 1 | Found 6 candidates: 2 | func PrettyPrintTypeExpr(out io.Writer, e ast.Expr) 3 | package ast 4 | package io 5 | var e ast.Expr 6 | var out io.Writer 7 | var t ast.Expr 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0004/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "go/ast" 5 | "io" 6 | ) 7 | 8 | func PrettyPrintTypeExpr(out io.Writer, e ast.Expr) { 9 | switch t := e.(type) { 10 | case *ast.StarExpr: 11 | fmt.Fprintf(out, "*") 12 | PrettyPrintTypeExpr(out, t.X) 13 | case *ast.Ident: 14 | // ast.Ident type decl as a reminder (note embedded type): 15 | // 16 | // type Ident struct { 17 | // token.Position // identifier position 18 | // Obj *Object // denoted object 19 | // } 20 | // 21 | // Correct type inference in complex type switch statements + 22 | // support for type embedding 23 | fmt.Fprintf(out, t.Name()) 24 | case *ast.ArrayType: 25 | fmt.Fprintf(out, "[]") 26 | PrettyPrintTypeExpr(out, t.Elt) 27 | case *ast.SelectorExpr: 28 | PrettyPrintTypeExpr(out, t.X) 29 | fmt.Fprintf(out, ".%s", t.Sel.Name()) 30 | case *ast.FuncType: 31 | // SKIP THIS FOR DEMO 32 | case *ast.MapType: 33 | fmt.Fprintf(out, "map[") 34 | PrettyPrintTypeExpr(out, t.Key) 35 | fmt.Fprintf(out, "]") 36 | PrettyPrintTypeExpr(out, t.Value) 37 | case *ast.InterfaceType: 38 | fmt.Fprintf(out, "interface{}") 39 | case *ast.Ellipsis: 40 | fmt.Fprintf(out, "...") 41 | PrettyPrintTypeExpr(out, t.Elt) 42 | case *ast.StructType: 43 | fmt.Fprintf(out, "struct") 44 | case *ast.ChanType: 45 | switch t.Dir { 46 | case ast.RECV: 47 | fmt.Fprintf(out, "<-chan ") 48 | case ast.SEND: 49 | fmt.Fprintf(out, "chan<- ") 50 | case ast.SEND | ast.RECV: 51 | fmt.Fprintf(out, "chan ") 52 | } 53 | PrettyPrintTypeExpr(out, t.Value) 54 | default: 55 | @ 56 | panic("OMGWTFBBQ!") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0005/b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // this is a file 'a.go' 4 | 5 | import ( 6 | superos "os" 7 | ) 8 | 9 | func B() superos.Error { 10 | return nil 11 | } 12 | 13 | // notice how changing type of a return function in one file, 14 | // the inferred type of a variable in another file changes also 15 | 16 | func (t *Tester) SetC() { 17 | t.c = 31337 18 | } 19 | 20 | func (t *Tester) SetD() { 21 | t.d = 31337 22 | } 23 | 24 | // support for multifile packages, including correct namespace handling 25 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0005/out.expected: -------------------------------------------------------------------------------- 1 | Found 5 candidates: 2 | func A() invalid type 3 | func B() invalid type 4 | package localos 5 | type Tester struct 6 | var test invalid type 7 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0005/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // this is a file 'a.go' 4 | 5 | import ( 6 | localos "os" 7 | ) 8 | 9 | func A() localos.Error { 10 | return nil 11 | } 12 | 13 | // B() is defined in file 'b.go' 14 | var test = B() 15 | 16 | type Tester struct { 17 | a, b, c, d int 18 | } 19 | 20 | func (t *Tester) SetA() { 21 | t.a = 31337 22 | } 23 | 24 | func (t *Tester) SetB() { 25 | t.b = 31337 26 | } 27 | 28 | // methods SetC and SetD are defined in 'b.go' 29 | 30 | func _() {} 31 | 32 | @ 33 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0006/b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // this is a file 'a.go' 4 | 5 | import ( 6 | superos "os" 7 | ) 8 | 9 | func B() superos.Error { 10 | return nil 11 | } 12 | 13 | // notice how changing type of a return function in one file, 14 | // the inferred type of a variable in another file changes also 15 | 16 | func (t *Tester) SetC() { 17 | t.c = 31337 18 | } 19 | 20 | func (t *Tester) SetD() { 21 | t.d = 31337 22 | } 23 | 24 | // support for multifile packages, including correct namespace handling 25 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0006/out.expected: -------------------------------------------------------------------------------- 1 | Nothing to complete. 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0006/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // this is a file 'a.go' 4 | 5 | import ( 6 | localos "os" 7 | ) 8 | 9 | func A() localos.Error { 10 | return nil 11 | } 12 | 13 | // B() is defined in file 'b.go' 14 | var test = B() 15 | 16 | type Tester struct { 17 | a, b, c, d int 18 | } 19 | 20 | func (t *Tester) SetA() { 21 | t.a = 31337 22 | } 23 | 24 | func (t *Tester) SetB() { 25 | t.b = 31337 26 | } 27 | 28 | Tester.@ 29 | 30 | // methods SetC and SetD are defined in 'b.go' 31 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0007/out.expected: -------------------------------------------------------------------------------- 1 | Found 5 candidates: 2 | var ForkLock sync.RWMutex 3 | var SocketDisableIPv6 bool 4 | var Stderr int 5 | var Stdin int 6 | var Stdout int 7 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0007/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "syscall" 4 | 5 | func main() { 6 | syscall.var@ 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0008/out.expected: -------------------------------------------------------------------------------- 1 | Found 6 candidates: 2 | func Lock() 3 | func Unlock() 4 | var Mutex sync.Mutex 5 | var data map[string][]string 6 | var path string 7 | var time int64 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0008/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var hosts struct { 6 | sync.Mutex 7 | data map[string][]string 8 | time int64 9 | path string 10 | } 11 | 12 | hosts.@ 13 | 14 | func main() { 15 | } 16 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0009/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | func Lock() 3 | func Unlock() 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0009/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | var hosts struct { 6 | sync.Mutex 7 | data map[string][]string 8 | time int64 9 | path string 10 | } 11 | 12 | hosts.Mutex.@ 13 | 14 | func main() { 15 | } 16 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0010/out.expected: -------------------------------------------------------------------------------- 1 | Found 7 candidates: 2 | func End() token.Pos 3 | func Pos() token.Pos 4 | var Comment *ast.CommentGroup 5 | var Doc *ast.CommentGroup 6 | var Names []*ast.Ident 7 | var Type ast.Expr 8 | var Values []ast.Expr 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0010/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "go/ast" 4 | 5 | func main() { 6 | var gd *ast.GenDecl 7 | v := gd.Specs[0].(*ast.ValueSpec) 8 | v.@ 9 | } 10 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0011/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | func Error() string 3 | var Kind reflect.Kind 4 | var Method string 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0011/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "reflect" 4 | 5 | func main() { 6 | var test reflect.ValueError 7 | test.@ 8 | } 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0012/out.expected: -------------------------------------------------------------------------------- 1 | Found 6 candidates: 2 | func main() 3 | type MyMap map[string]int 4 | var key string 5 | var m MyMap 6 | var value int 7 | var z int 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0012/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type MyMap map[string]int 4 | 5 | func main() { 6 | var m MyMap 7 | for key, value := range m { 8 | z := m["15"] 9 | @ 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0013/out.expected: -------------------------------------------------------------------------------- 1 | Found 24 candidates: 2 | func main() 3 | var a int 4 | var add int 5 | var and int 6 | var andnot int 7 | var b int 8 | var c bool 9 | var d bool 10 | var div int 11 | var eq bool 12 | var geq bool 13 | var greater bool 14 | var leq bool 15 | var less bool 16 | var logand bool 17 | var logor bool 18 | var mul int 19 | var neq bool 20 | var or int 21 | var rem int 22 | var shl int 23 | var shr int 24 | var sub int 25 | var xor int 26 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0013/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var a, b int 5 | add := a + b 6 | sub := a - b 7 | mul := a * b 8 | div := a / b 9 | or := a | b 10 | xor := a ^ b 11 | rem := a % b 12 | shr := a >> uint(b) 13 | shl := a << uint(b) 14 | and := a & b 15 | andnot := a &^ b 16 | 17 | eq := a == b 18 | neq := a != b 19 | less := a < b 20 | leq := a <= b 21 | greater := a > b 22 | geq := a >= b 23 | 24 | var c, d bool 25 | logor := c || d 26 | logand := c && d 27 | @ 28 | } 29 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0014/out.expected: -------------------------------------------------------------------------------- 1 | Found 11 candidates: 2 | func main() 3 | type MyPtrInt *int 4 | var a *int 5 | var aa int 6 | var b int 7 | var bb *MyPtrInt 8 | var c *int 9 | var d **int 10 | var megaptr **int 11 | var superint int 12 | var typeptr MyPtrInt 13 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0014/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type MyPtrInt *int 4 | 5 | func main() { 6 | var megaptr **int 7 | a := *megaptr 8 | b := *a 9 | 10 | var superint int 11 | c := &superint 12 | d := &c 13 | 14 | var typeptr MyPtrInt 15 | aa := *typeptr 16 | bb := &typeptr 17 | @ 18 | } 19 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0015/out.expected: -------------------------------------------------------------------------------- 1 | Found 9 candidates: 2 | func main() 3 | var a int 4 | var arro bool 5 | var b bool 6 | var c chan bool 7 | var uadd int 8 | var unot bool 9 | var usub int 10 | var uxor invalid type 11 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0015/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var a int 5 | var b bool 6 | var c chan bool 7 | uadd := +a 8 | usub := -a 9 | unot := !b 10 | uxor := ^b 11 | arro := <-c 12 | @ 13 | } 14 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0016/out.expected: -------------------------------------------------------------------------------- 1 | Found 4 candidates: 2 | func main() 3 | var a int 4 | var b string 5 | var d bool 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0016/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var a int 5 | for { 6 | var b string 7 | if a == len(b) { 8 | var c bool 9 | } else { 10 | var d bool 11 | @ 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0017/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | func a(a int, b int, c int) int 3 | func b(a string, b string, c string) string 4 | func main() 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0017/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func a(a, b, c int) int { 4 | return 123 5 | } 6 | 7 | func main() { 8 | @ 9 | } 10 | 11 | func b(a, b, c string) string { 12 | return "abc" 13 | } 14 | 15 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0018/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | func Bark() 3 | var Legs int 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0018/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // just a dummy example 8 | type Dog struct { 9 | Legs int 10 | } 11 | 12 | func (d *Dog) Bark() { 13 | fmt.Printf("Bark!\n") 14 | } 15 | 16 | // another one 17 | type Test struct { 18 | // map of slices of pointer to a *Dog 19 | MoreTests map[string][]**Dog 20 | } 21 | 22 | func (t *Test) GetMe() *Test { 23 | return t 24 | } 25 | 26 | func main() { 27 | t := new(Test) 28 | (*t.GetMe().MoreTests["blabla"][10]).@ 29 | } 30 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0019/out.expected: -------------------------------------------------------------------------------- 1 | Found 15 candidates: 2 | const BestCompression untyped int 3 | const BestSpeed untyped int 4 | const DefaultCompression untyped int 5 | const HuffmanOnly untyped int 6 | const NoCompression untyped int 7 | func NewReader(r io.Reader) (io.ReadCloser, error) 8 | func NewReaderDict(r io.Reader, dict []byte) (io.ReadCloser, error) 9 | func NewWriter(w io.Writer) *zlib.Writer 10 | func NewWriterLevel(w io.Writer, level int) (*zlib.Writer, error) 11 | func NewWriterLevelDict(w io.Writer, level int, dict []byte) (*zlib.Writer, error) 12 | type Resetter interface 13 | type Writer struct 14 | var ErrChecksum error 15 | var ErrDictionary error 16 | var ErrHeader error 17 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0019/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "compress/zlib" 4 | 5 | func main() { 6 | var b map[int]zlib.@ 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0020/out.expected: -------------------------------------------------------------------------------- 1 | Found 4 candidates: 2 | func Lock() 3 | func Unlock() 4 | var Dummy Dummy 5 | var Mutex sync.Mutex 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0020/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "sync" 4 | 5 | type Dummy struct { 6 | sync.Mutex 7 | } 8 | 9 | type Bar struct { 10 | Dummy 11 | } 12 | 13 | func (b *Bar) Lock() { 14 | } 15 | 16 | func (b *Bar) Unlock() { 17 | } 18 | 19 | func main() { 20 | var b Bar 21 | b.@ 22 | } 23 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0021/out.expected: -------------------------------------------------------------------------------- 1 | Found 7 candidates: 2 | func End() token.Pos 3 | func IsExported() bool 4 | func Pos() token.Pos 5 | func String() string 6 | var Name string 7 | var NamePos token.Pos 8 | var Obj *ast.Object 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0021/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "go/ast" 4 | 5 | func main() { 6 | var test *ast.ImportSpec 7 | test.Name.@ 8 | } 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0022/out.expected: -------------------------------------------------------------------------------- 1 | Found 5 candidates: 2 | func getMap() map[string]invalid type 3 | func main() 4 | package os 5 | var key string 6 | var value invalid type 7 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0022/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "os" 4 | 5 | func main() { 6 | for key, value := range getMap() { 7 | @ 8 | } 9 | } 10 | 11 | func getMap() map[string]os.Error { 12 | // simply to trick type inference 13 | return nil 14 | } 15 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0023/out.expected: -------------------------------------------------------------------------------- 1 | Found 25 candidates: 2 | func Errorf(format string, a ...interface{}) error 3 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) 4 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) 5 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) 6 | func Fscan(r io.Reader, a ...interface{}) (n int, err error) 7 | func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) 8 | func Fscanln(r io.Reader, a ...interface{}) (n int, err error) 9 | func Print(a ...interface{}) (n int, err error) 10 | func Printf(format string, a ...interface{}) (n int, err error) 11 | func Println(a ...interface{}) (n int, err error) 12 | func Scan(a ...interface{}) (n int, err error) 13 | func Scanf(format string, a ...interface{}) (n int, err error) 14 | func Scanln(a ...interface{}) (n int, err error) 15 | func Sprint(a ...interface{}) string 16 | func Sprintf(format string, a ...interface{}) string 17 | func Sprintln(a ...interface{}) string 18 | func Sscan(str string, a ...interface{}) (n int, err error) 19 | func Sscanf(str string, format string, a ...interface{}) (n int, err error) 20 | func Sscanln(str string, a ...interface{}) (n int, err error) 21 | type Formatter interface 22 | type GoStringer interface 23 | type ScanState interface 24 | type Scanner interface 25 | type State interface 26 | type Stringer interface 27 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0023/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Simple check for unicode awareness 4 | 5 | import ъ "fmt" 6 | 7 | func main() { 8 | ъ.@ 9 | } 10 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0024/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | var a int 3 | var b int 4 | var c int 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0024/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type X struct { 4 | a,b,c int 5 | } 6 | 7 | func main() { 8 | var X *X 9 | X.@ 10 | } 11 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0025/out.expected: -------------------------------------------------------------------------------- 1 | Found 4 candidates: 2 | func Alignof(x Type) uintptr 3 | func Offsetof(x Type) uintptr 4 | func Sizeof(x Type) uintptr 5 | type Pointer unsafe.Pointer 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0025/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "unsafe" 4 | 5 | func main() { 6 | unsafe.@ 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0026/out.expected: -------------------------------------------------------------------------------- 1 | Found 7 candidates: 2 | func main() 3 | var A struct 4 | var B struct 5 | var C struct 6 | var a int 7 | var d int 8 | var g int 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0026/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var A = struct { a, b, c int }{1,2,3} 4 | var B struct { d, e, f int } 5 | 6 | func main() { 7 | C := struct { g, h, i int }{1,2,3} 8 | 9 | a := A.a 10 | d := B.d 11 | g := C.g 12 | @ 13 | } 14 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0027/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | func String() 3 | var name string 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0027/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type file struct { 4 | name string 5 | } 6 | 7 | func (file *file) String() { 8 | file.@ 9 | return file.name 10 | } 11 | 12 | func main() { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0028/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | func PowerOfTwo() int 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0028/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type X int 4 | 5 | func (x X) PowerOfTwo() int { 6 | return int(x * x) 7 | } 8 | 9 | func main() { 10 | a := X(56) 11 | i := a + 67 12 | j := ^i 13 | j.@ 14 | } 15 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0029/out.expected: -------------------------------------------------------------------------------- 1 | Found 27 candidates: 2 | func Errorf(format string, a ...interface{}) error 3 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) 4 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) 5 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) 6 | func Fscan(r io.Reader, a ...interface{}) (n int, err error) 7 | func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) 8 | func Fscanln(r io.Reader, a ...interface{}) (n int, err error) 9 | func Print(a ...interface{}) (n int, err error) 10 | func Printf(format string, a ...interface{}) (n int, err error) 11 | func Println(a ...interface{}) (n int, err error) 12 | func Scan(a ...interface{}) (n int, err error) 13 | func Scanf(format string, a ...interface{}) (n int, err error) 14 | func Scanln(a ...interface{}) (n int, err error) 15 | func Sprint(a ...interface{}) string 16 | func Sprintf(format string, a ...interface{}) string 17 | func Sprintln(a ...interface{}) string 18 | func Sscan(str string, a ...interface{}) (n int, err error) 19 | func Sscanf(str string, format string, a ...interface{}) (n int, err error) 20 | func Sscanln(str string, a ...interface{}) (n int, err error) 21 | func main() 22 | type Formatter interface 23 | type GoStringer interface 24 | type ScanState interface 25 | type Scanner interface 26 | type State interface 27 | type Stringer interface 28 | var a fmt.Formatter 29 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0029/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import . "fmt" 4 | 5 | func main() { 6 | var a Formatter 7 | @ 8 | } 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0030/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | var F1 int 3 | var F2 int 4 | var F3 int 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0030/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type A struct { 4 | F1, F2, F3 int 5 | } 6 | 7 | type B A 8 | 9 | func main() { 10 | var b B 11 | b.@ 12 | } 13 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0031/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | func main() 3 | var c int 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0031/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var c chan int 5 | select { 6 | case c := <-c: 7 | _ = c 8 | @ 9 | default: 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0032/out.expected: -------------------------------------------------------------------------------- 1 | Found 124 candidates: 2 | func main() 3 | package adler32 4 | package aes 5 | package ascii85 6 | package asn1 7 | package ast 8 | package atomic 9 | package base32 10 | package base64 11 | package big 12 | package binary 13 | package bufio 14 | package build 15 | package bytes 16 | package bzip2 17 | package cgi 18 | package cgo 19 | package cipher 20 | package cmplx 21 | package color 22 | package crc32 23 | package crc64 24 | package crypto 25 | package csv 26 | package debug 27 | package des 28 | package doc 29 | package draw 30 | package driver 31 | package dsa 32 | package dwarf 33 | package ecdsa 34 | package elf 35 | package elliptic 36 | package errors 37 | package exec 38 | package expvar 39 | package fcgi 40 | package filepath 41 | package flag 42 | package flate 43 | package fmt 44 | package fnv 45 | package gif 46 | package gob 47 | package gosym 48 | package gzip 49 | package hash 50 | package heap 51 | package hex 52 | package hmac 53 | package html 54 | package http 55 | package httptest 56 | package httputil 57 | package image 58 | package io 59 | package iotest 60 | package ioutil 61 | package jpeg 62 | package json 63 | package jsonrpc 64 | package list 65 | package log 66 | package lzw 67 | package macho 68 | package mail 69 | package math 70 | package md5 71 | package mime 72 | package multipart 73 | package net 74 | package os 75 | package parse 76 | package parser 77 | package path 78 | package pe 79 | package pem 80 | package pkix 81 | package png 82 | package pprof 83 | package printer 84 | package quick 85 | package rand 86 | package rc4 87 | package reflect 88 | package regexp 89 | package ring 90 | package rpc 91 | package rsa 92 | package runtime 93 | package scanner 94 | package sha1 95 | package sha256 96 | package sha512 97 | package signal 98 | package smtp 99 | package sort 100 | package sql 101 | package strconv 102 | package strings 103 | package subtle 104 | package suffixarray 105 | package sync 106 | package syntax 107 | package syscall 108 | package syslog 109 | package tabwriter 110 | package tar 111 | package template 112 | package testing 113 | package textproto 114 | package time 115 | package tls 116 | package token 117 | package unicode 118 | package url 119 | package user 120 | package utf16 121 | package utf8 122 | package x509 123 | package xml 124 | package zip 125 | package zlib 126 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0032/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "archive/tar" 5 | "archive/zip" 6 | "bufio" 7 | "bytes" 8 | "compress/bzip2" 9 | "compress/flate" 10 | "compress/gzip" 11 | "compress/lzw" 12 | "compress/zlib" 13 | "container/heap" 14 | "container/list" 15 | "container/ring" 16 | "crypto" 17 | "crypto/aes" 18 | "crypto/cipher" 19 | "crypto/des" 20 | "crypto/dsa" 21 | "crypto/ecdsa" 22 | "crypto/elliptic" 23 | "crypto/hmac" 24 | "crypto/md5" 25 | "crypto/rand" 26 | "crypto/rc4" 27 | "crypto/rsa" 28 | "crypto/sha1" 29 | "crypto/sha256" 30 | "crypto/sha512" 31 | "crypto/subtle" 32 | "crypto/tls" 33 | "crypto/x509" 34 | "crypto/x509/pkix" 35 | "database/sql" 36 | "database/sql/driver" 37 | "debug/dwarf" 38 | "debug/elf" 39 | "debug/gosym" 40 | "debug/macho" 41 | "debug/pe" 42 | "encoding/ascii85" 43 | "encoding/asn1" 44 | "encoding/base32" 45 | "encoding/base64" 46 | "encoding/binary" 47 | "encoding/csv" 48 | "encoding/gob" 49 | "encoding/hex" 50 | "encoding/json" 51 | "encoding/pem" 52 | "encoding/xml" 53 | "errors" 54 | "expvar" 55 | "flag" 56 | "fmt" 57 | "go/ast" 58 | "go/build" 59 | "go/doc" 60 | "go/parser" 61 | "go/printer" 62 | "go/scanner" 63 | "go/token" 64 | "hash" 65 | "hash/adler32" 66 | "hash/crc32" 67 | "hash/crc64" 68 | "hash/fnv" 69 | "html" 70 | "html/template" 71 | "image" 72 | "image/color" 73 | "image/draw" 74 | "image/gif" 75 | "image/jpeg" 76 | "image/png" 77 | "index/suffixarray" 78 | "io" 79 | "io/ioutil" 80 | "log" 81 | "log/syslog" 82 | "math" 83 | "math/big" 84 | "math/cmplx" 85 | "math/rand" 86 | "mime" 87 | "mime/multipart" 88 | "net" 89 | "net/http" 90 | "net/http/cgi" 91 | "net/http/fcgi" 92 | "net/http/httptest" 93 | "net/http/httputil" 94 | "net/http/pprof" 95 | "net/mail" 96 | "net/rpc" 97 | "net/rpc/jsonrpc" 98 | "net/smtp" 99 | "net/textproto" 100 | "net/url" 101 | "os" 102 | "os/exec" 103 | "os/signal" 104 | "os/user" 105 | "path" 106 | "path/filepath" 107 | "reflect" 108 | "regexp" 109 | "regexp/syntax" 110 | "runtime" 111 | "runtime/cgo" 112 | "runtime/debug" 113 | "runtime/pprof" 114 | "sort" 115 | "strconv" 116 | "strings" 117 | "sync" 118 | "sync/atomic" 119 | "syscall" 120 | "testing" 121 | "testing/iotest" 122 | "testing/quick" 123 | "text/scanner" 124 | "text/tabwriter" 125 | "text/template" 126 | "text/template/parse" 127 | "time" 128 | "unicode" 129 | "unicode/utf16" 130 | "unicode/utf8" 131 | ) 132 | 133 | func main() { 134 | 135 | @} 136 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0033/out.expected: -------------------------------------------------------------------------------- 1 | Found 6 candidates: 2 | func testEllipsis(dummies ...*Dummy) 3 | type Dummy struct 4 | var d *Dummy 5 | var dummies []*Dummy 6 | var i int 7 | var x *Dummy 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0033/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Dummy struct { 4 | a, b, c int 5 | } 6 | 7 | func testEllipsis(dummies ...*Dummy) { 8 | for i, d := range dummies { 9 | x := dummies[0] 10 | @ 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0034/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | func At(x int, y int) color.Color 3 | func Bounds() image.Rectangle 4 | func ColorModel() color.Model 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0034/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "image/png" 4 | 5 | func test() { 6 | img, err := png.Decode(nil) 7 | img.@ 8 | } 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0035/out.expected: -------------------------------------------------------------------------------- 1 | Found 5 candidates: 2 | func main() 3 | var err error 4 | var offset int 5 | var r rune 6 | var s string 7 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0035/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | var err error 5 | s := err.Error() 6 | for offset, r := range s { 7 | @ 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0036/out.expected: -------------------------------------------------------------------------------- 1 | Found 21 candidates: 2 | func Close() error 3 | func DWARF() (*dwarf.Data, error) 4 | func DynString(tag elf.DynTag) ([]string, error) 5 | func DynamicSymbols() ([]elf.Symbol, error) 6 | func ImportedLibraries() ([]string, error) 7 | func ImportedSymbols() ([]elf.ImportedSymbol, error) 8 | func Section(name string) *elf.Section 9 | func SectionByType(typ elf.SectionType) *elf.Section 10 | func Symbols() ([]elf.Symbol, error) 11 | var ABIVersion uint8 12 | var ByteOrder binary.ByteOrder 13 | var Class elf.Class 14 | var Data elf.Data 15 | var Entry uint64 16 | var FileHeader elf.FileHeader 17 | var Machine elf.Machine 18 | var OSABI elf.OSABI 19 | var Progs []*elf.Prog 20 | var Sections []*elf.Section 21 | var Type elf.Type 22 | var Version elf.Version 23 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0036/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "debug/elf" 4 | 5 | func main() { 6 | var f elf.File 7 | f.@ 8 | } -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0037/out.expected: -------------------------------------------------------------------------------- 1 | Found 7 candidates: 2 | func main() 3 | type Array [5]int 4 | var a Array 5 | var s []string 6 | var s1 []string 7 | var s2 []int 8 | var s3 invalid type 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0037/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Array [5]int 4 | 5 | func main() { 6 | var s []string 7 | var a Array 8 | s1 := append(s[:], "123") 9 | s2 := a[0:100500] 10 | s3 := append() 11 | @ 12 | } 13 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0038/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | func main() 3 | var x int 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0038/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | if y := 20; y == 0 { 5 | } 6 | if x := 10; x == 0 { 7 | } else if 8 | @} 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0039/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | func main() 3 | var z int 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0039/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | z := 5 5 | if y := 20; y == 0 { 6 | } 7 | if x := 10; x == 0 { 8 | } 9 | 10 | @} 11 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0040/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | func create_foo() Foo 3 | type Foo struct 4 | var t Foo 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0040/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Foo struct {} 4 | 5 | func (f *Foo) Bar() {} 6 | 7 | func create_foo() Foo { 8 | t := Foo{} 9 | 10 | @} 11 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0041/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | var Xa int 3 | var Xb int 4 | var Xy Y 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0041/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type X struct { 4 | Xa int 5 | Xb int 6 | Xy Y 7 | } 8 | 9 | type Y struct { 10 | Ya int 11 | Yb int 12 | } 13 | 14 | func main() { 15 | x := X{ 16 | Xy: Y{ 17 | Ya: 1, 18 | }, 19 | 20 | @} 21 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0042/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | var Ya int 3 | var Yb int 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0042/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type X struct { 4 | Xa int 5 | Xb int 6 | Xy Y 7 | } 8 | 9 | type Y struct { 10 | Ya int 11 | Yb int 12 | } 13 | 14 | func main() { 15 | x := X{ 16 | Xy: Y{ 17 | 18 | @} 19 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0043/out.expected: -------------------------------------------------------------------------------- 1 | Found 4 candidates: 2 | func foo() 3 | var Xa int 4 | var Xb int 5 | var Xy Y 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0043/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type X struct { 4 | Xa int 5 | Xb int 6 | Xy Y 7 | } 8 | 9 | type Y struct { 10 | Ya int 11 | Yb int 12 | } 13 | 14 | func (x X) foo() { 15 | return 16 | } 17 | 18 | func main() { 19 | x := X{ 20 | Xa: 1, 21 | Xb: 2, 22 | } 23 | y := Y{ 24 | Ya: x. 25 | @} 26 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0044/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | var Xa int 3 | var Xb int 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0044/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type X struct { 4 | Xa int 5 | Xb int 6 | } 7 | 8 | func (x X) foo() { 9 | return 10 | } 11 | 12 | func main() { 13 | x := X{ 14 | 15 | @} 16 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0045/out.expected: -------------------------------------------------------------------------------- 1 | Found 25 candidates: 2 | func Errorf(format string, a ...interface{}) error 3 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) 4 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) 5 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) 6 | func Fscan(r io.Reader, a ...interface{}) (n int, err error) 7 | func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) 8 | func Fscanln(r io.Reader, a ...interface{}) (n int, err error) 9 | func Print(a ...interface{}) (n int, err error) 10 | func Printf(format string, a ...interface{}) (n int, err error) 11 | func Println(a ...interface{}) (n int, err error) 12 | func Scan(a ...interface{}) (n int, err error) 13 | func Scanf(format string, a ...interface{}) (n int, err error) 14 | func Scanln(a ...interface{}) (n int, err error) 15 | func Sprint(a ...interface{}) string 16 | func Sprintf(format string, a ...interface{}) string 17 | func Sprintln(a ...interface{}) string 18 | func Sscan(str string, a ...interface{}) (n int, err error) 19 | func Sscanf(str string, format string, a ...interface{}) (n int, err error) 20 | func Sscanln(str string, a ...interface{}) (n int, err error) 21 | type Formatter interface 22 | type GoStringer interface 23 | type ScanState interface 24 | type Scanner interface 25 | type State interface 26 | type Stringer interface 27 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0045/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | if fmt. 7 | @} 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0046/out.expected: -------------------------------------------------------------------------------- 1 | Found 25 candidates: 2 | func Errorf(format string, a ...interface{}) error 3 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) 4 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) 5 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) 6 | func Fscan(r io.Reader, a ...interface{}) (n int, err error) 7 | func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) 8 | func Fscanln(r io.Reader, a ...interface{}) (n int, err error) 9 | func Print(a ...interface{}) (n int, err error) 10 | func Printf(format string, a ...interface{}) (n int, err error) 11 | func Println(a ...interface{}) (n int, err error) 12 | func Scan(a ...interface{}) (n int, err error) 13 | func Scanf(format string, a ...interface{}) (n int, err error) 14 | func Scanln(a ...interface{}) (n int, err error) 15 | func Sprint(a ...interface{}) string 16 | func Sprintf(format string, a ...interface{}) string 17 | func Sprintln(a ...interface{}) string 18 | func Sscan(str string, a ...interface{}) (n int, err error) 19 | func Sscanf(str string, format string, a ...interface{}) (n int, err error) 20 | func Sscanln(str string, a ...interface{}) (n int, err error) 21 | type Formatter interface 22 | type GoStringer interface 23 | type ScanState interface 24 | type Scanner interface 25 | type State interface 26 | type Stringer interface 27 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0046/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | x := fmt. 7 | @} 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0047/out.expected: -------------------------------------------------------------------------------- 1 | Nothing to complete. 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0047/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | type T struct{ X } 4 | 5 | var _ = T.x.@ 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0048/out.expected: -------------------------------------------------------------------------------- 1 | Found 7 candidates: 2 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) 3 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) 4 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) 5 | func Fscan(r io.Reader, a ...interface{}) (n int, err error) 6 | func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) 7 | func Fscanln(r io.Reader, a ...interface{}) (n int, err error) 8 | type Formatter interface 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0048/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | x := fmt.F@pr 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0049/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | var i int 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0049/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | func t(a struct { 4 | i int 5 | }) { 6 | a.@ 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0050/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | var i int 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0050/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | func t(a *struct { 4 | i int 5 | }) { 6 | a.@ 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0051/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | var NumApples int 3 | var NumBananas int 4 | var NumOranges int 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0051/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Item struct { 4 | Quantity int 5 | Name string 6 | } 7 | 8 | type Config struct { 9 | NumApples int 10 | NumOranges int 11 | NumBananas int 12 | } 13 | 14 | func main() { 15 | var cfg Config 16 | items := []Item{ 17 | {cfg.NumApples, "apple"}, 18 | {cfg.NumOranges, "orange"}, 19 | {cfg.@NumApples, "banana"}, 20 | } 21 | 22 | var a int 23 | var b int 24 | var c int 25 | var d int 26 | var e int 27 | var f int 28 | var g int 29 | var h int 30 | var i int 31 | var j int 32 | } 33 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0052/out.expected: -------------------------------------------------------------------------------- 1 | Found 3 candidates: 2 | var a int 3 | var b int 4 | var c int 5 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0052/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Dummy struct { 4 | a, b, c int 5 | } 6 | 7 | func testEllipsis(dummies ...*Dummy) { 8 | for _, d := range dummies { 9 | d.@ 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0053/b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // this is a file 'a.go' 4 | 5 | import ( 6 | superos "os" 7 | ) 8 | 9 | func B() superos.Error { 10 | return nil 11 | } 12 | 13 | // notice how changing type of a return function in one file, 14 | // the inferred type of a variable in another file changes also 15 | 16 | func (t Tester) SetC() { 17 | t.c = 31337 18 | } 19 | 20 | func (t Tester) SetD() { 21 | t.d = 31337 22 | } 23 | 24 | // support for multifile packages, including correct namespace handling 25 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0053/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | func SetC() 3 | func SetD() 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0053/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // this is a file 'a.go' 4 | 5 | import ( 6 | localos "os" 7 | ) 8 | 9 | func A() localos.Error { 10 | return nil 11 | } 12 | 13 | // B() is defined in file 'b.go' 14 | var test = B() 15 | 16 | type Tester struct { 17 | a, b, c, d int 18 | } 19 | 20 | func (t *Tester) SetA() { 21 | t.a = 31337 22 | } 23 | 24 | func (t *Tester) SetB() { 25 | t.b = 31337 26 | } 27 | 28 | Tester.@ 29 | 30 | // methods SetC and SetD are defined in 'b.go' 31 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0054/b.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // this is a file 'a.go' 4 | 5 | import ( 6 | superos "os" 7 | ) 8 | 9 | func B() superos.Error { 10 | return nil 11 | } 12 | 13 | // notice how changing type of a return function in one file, 14 | // the inferred type of a variable in another file changes also 15 | 16 | func (t Tester) SetC() { 17 | t.c = 31337 18 | } 19 | 20 | func (t Tester) SetD() { 21 | t.d = 31337 22 | } 23 | 24 | // support for multifile packages, including correct namespace handling 25 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0054/out.expected: -------------------------------------------------------------------------------- 1 | Found 4 candidates: 2 | func SetA() 3 | func SetB() 4 | func SetC() 5 | func SetD() 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0054/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // this is a file 'a.go' 4 | 5 | import ( 6 | localos "os" 7 | ) 8 | 9 | func A() localos.Error { 10 | return nil 11 | } 12 | 13 | // B() is defined in file 'b.go' 14 | var test = B() 15 | 16 | type Tester struct { 17 | a, b, c, d int 18 | } 19 | 20 | func (t *Tester) SetA() { 21 | t.a = 31337 22 | } 23 | 24 | func (t *Tester) SetB() { 25 | t.b = 31337 26 | } 27 | 28 | (*Tester).@ 29 | 30 | // methods SetC and SetD are defined in 'b.go' 31 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0055/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | var x int 3 | var y int 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0055/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | var _ = struct { 4 | x int 5 | y int 6 | }{ 7 | @ 8 | } 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0056/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | var L sync.Locker 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0056/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | import "sync" 4 | 5 | var _ = sync.Cond{@ 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0057/out.expected: -------------------------------------------------------------------------------- 1 | Nothing to complete. 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0057/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | var _ = (*[]int).@ 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0058/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | var str func(myint) string 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0058/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | type myint int 4 | 5 | func (i myint) String() string { 6 | return "int" 7 | } 8 | 9 | func main() { 10 | str := myint.String 11 | st@ 12 | } 13 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0059/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | func B() 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0059/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | type A int 4 | type B []A 5 | 6 | func (A) A() {} 7 | func (B) B() {} 8 | 9 | func test(x B) { 10 | for _, x := range x.@ { 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0060/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | func M2() 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0060/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | type S1 struct { *S2 } 4 | type S2 struct { *S1 } 5 | 6 | func (*S1) M1() {} 7 | func (*S2) M2() {} 8 | 9 | var _ = (S1).@ 10 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0061/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | var buf int 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0061/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | import "bytes" 4 | 5 | type S struct { buf int } 6 | type T struct { S; bytes.Buffer } 7 | 8 | func _(t T) { 9 | _ = t.b@ 10 | } 11 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0062/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | type S struct 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0062/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | type S struct{ x int } 4 | 5 | var _ = []S{ @ } 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0063/out.expected: -------------------------------------------------------------------------------- 1 | Nothing to complete. 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0063/test.go.in: -------------------------------------------------------------------------------- 1 | pa@ 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0064/config.json: -------------------------------------------------------------------------------- 1 | {"Builtin": true} 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0064/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | type string string 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0064/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | func f() { 4 | var s str@ 5 | } 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0065/config.json: -------------------------------------------------------------------------------- 1 | {"Builtin": true} 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0065/out.expected: -------------------------------------------------------------------------------- 1 | Found 25 candidates: 2 | func Errorf(format string, a ...interface{}) error 3 | func Fprint(w banana.Writer, a ...interface{}) (n int, err error) 4 | func Fprintf(w banana.Writer, format string, a ...interface{}) (n int, err error) 5 | func Fprintln(w banana.Writer, a ...interface{}) (n int, err error) 6 | func Fscan(r banana.Reader, a ...interface{}) (n int, err error) 7 | func Fscanf(r banana.Reader, format string, a ...interface{}) (n int, err error) 8 | func Fscanln(r banana.Reader, a ...interface{}) (n int, err error) 9 | func Print(a ...interface{}) (n int, err error) 10 | func Printf(format string, a ...interface{}) (n int, err error) 11 | func Println(a ...interface{}) (n int, err error) 12 | func Scan(a ...interface{}) (n int, err error) 13 | func Scanf(format string, a ...interface{}) (n int, err error) 14 | func Scanln(a ...interface{}) (n int, err error) 15 | func Sprint(a ...interface{}) string 16 | func Sprintf(format string, a ...interface{}) string 17 | func Sprintln(a ...interface{}) string 18 | func Sscan(str string, a ...interface{}) (n int, err error) 19 | func Sscanf(str string, format string, a ...interface{}) (n int, err error) 20 | func Sscanln(str string, a ...interface{}) (n int, err error) 21 | type Formatter interface 22 | type GoStringer interface 23 | type ScanState interface 24 | type Scanner interface 25 | type State interface 26 | type Stringer interface 27 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0065/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | import ( 4 | "fmt" 5 | banana "io" 6 | ) 7 | 8 | var _ = fmt.@ 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0066/config.json: -------------------------------------------------------------------------------- 1 | {"IgnoreCase": false} 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0066/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | func FuncX() 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0066/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | func FuncX() {} 4 | func funcY() {} 5 | 6 | func f() { 7 | Fun@ 8 | } 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0067/config.json: -------------------------------------------------------------------------------- 1 | {"IgnoreCase": false} 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0067/out.expected: -------------------------------------------------------------------------------- 1 | Found 1 candidates: 2 | func funcY() 3 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0067/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | func FuncX() {} 4 | func funcY() {} 5 | 6 | func f() { 7 | fun@ 8 | } 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0068/config.json: -------------------------------------------------------------------------------- 1 | {"IgnoreCase": true} 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0068/out.expected: -------------------------------------------------------------------------------- 1 | Found 2 candidates: 2 | func FuncX() 3 | func funcY() 4 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0068/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | func FuncX() {} 4 | func funcY() {} 5 | 6 | func f() { 7 | fun@ 8 | } 9 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0069/config.json: -------------------------------------------------------------------------------- 1 | {"IgnoreCase": true} 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0069/out.expected: -------------------------------------------------------------------------------- 1 | Nothing to complete. 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0069/test.go.in: -------------------------------------------------------------------------------- 1 | package p 2 | 3 | var x int 4 | 5 | func _() { 6 | 10.@ 7 | } -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0070/config.json: -------------------------------------------------------------------------------- 1 | {"UnimportedPackages": true} 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0070/out.expected: -------------------------------------------------------------------------------- 1 | Found 25 candidates: 2 | func Errorf(format string, a ...interface{}) error 3 | func Fprint(w io.Writer, a ...interface{}) (n int, err error) 4 | func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) 5 | func Fprintln(w io.Writer, a ...interface{}) (n int, err error) 6 | func Fscan(r io.Reader, a ...interface{}) (n int, err error) 7 | func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error) 8 | func Fscanln(r io.Reader, a ...interface{}) (n int, err error) 9 | func Print(a ...interface{}) (n int, err error) 10 | func Printf(format string, a ...interface{}) (n int, err error) 11 | func Println(a ...interface{}) (n int, err error) 12 | func Scan(a ...interface{}) (n int, err error) 13 | func Scanf(format string, a ...interface{}) (n int, err error) 14 | func Scanln(a ...interface{}) (n int, err error) 15 | func Sprint(a ...interface{}) string 16 | func Sprintf(format string, a ...interface{}) string 17 | func Sprintln(a ...interface{}) string 18 | func Sscan(str string, a ...interface{}) (n int, err error) 19 | func Sscanf(str string, format string, a ...interface{}) (n int, err error) 20 | func Sscanln(str string, a ...interface{}) (n int, err error) 21 | type Formatter interface 22 | type GoStringer interface 23 | type ScanState interface 24 | type Scanner interface 25 | type State interface 26 | type Stringer interface 27 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0070/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | fmt.@ 5 | } 6 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0071/out.expected: -------------------------------------------------------------------------------- 1 | Nothing to complete. 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0071/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // fmt.@ 6 | func main() { 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0072/out.expected: -------------------------------------------------------------------------------- 1 | Nothing to complete. 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0072/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /* fmt.@ 6 | func main() { 7 | } 8 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0073/out.expected: -------------------------------------------------------------------------------- 1 | Nothing to complete. 2 | -------------------------------------------------------------------------------- /internal/suggest/testdata/test.0073/test.go.in: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | /* 6 | * fmt.@ 7 | */ 8 | func main() { 9 | } 10 | -------------------------------------------------------------------------------- /nvim/autoload/gocomplete.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_gocode') 2 | finish 3 | endif 4 | let g:loaded_gocode = 1 5 | 6 | fu! s:gocodeCurrentBuffer() 7 | let buf = getline(1, '$') 8 | if &encoding != 'utf-8' 9 | let buf = map(buf, 'iconv(v:val, &encoding, "utf-8")') 10 | endif 11 | if &l:fileformat == 'dos' 12 | " XXX: line2byte() depend on 'fileformat' option. 13 | " so if fileformat is 'dos', 'buf' must include '\r'. 14 | let buf = map(buf, 'v:val."\r"') 15 | endif 16 | let file = tempname() 17 | call writefile(buf, file) 18 | return file 19 | endf 20 | 21 | let s:vim_system = get(g:, 'gocomplete#system_function', 'system') 22 | 23 | fu! s:system(str, ...) 24 | return call(s:vim_system, [a:str] + a:000) 25 | endf 26 | 27 | fu! s:gocodeShellescape(arg) 28 | try 29 | let ssl_save = &shellslash 30 | set noshellslash 31 | return shellescape(a:arg) 32 | finally 33 | let &shellslash = ssl_save 34 | endtry 35 | endf 36 | 37 | fu! s:gocodeCommand(cmd, preargs, args) 38 | for i in range(0, len(a:args) - 1) 39 | let a:args[i] = s:gocodeShellescape(a:args[i]) 40 | endfor 41 | for i in range(0, len(a:preargs) - 1) 42 | let a:preargs[i] = s:gocodeShellescape(a:preargs[i]) 43 | endfor 44 | let result = s:system(printf('gocode %s %s %s', join(a:preargs), a:cmd, join(a:args))) 45 | if v:shell_error != 0 46 | return "[\"0\", []]" 47 | else 48 | if &encoding != 'utf-8' 49 | let result = iconv(result, 'utf-8', &encoding) 50 | endif 51 | return result 52 | endif 53 | endf 54 | 55 | fu! s:gocodeCurrentBufferOpt(filename) 56 | return '-in=' . a:filename 57 | endf 58 | 59 | fu! s:gocodeCursor() 60 | if &encoding != 'utf-8' 61 | let c = col('.') 62 | let buf = line('.') == 1 ? "" : (join(getline(1, line('.')-1), "\n") . "\n") 63 | let buf .= c == 1 ? "" : getline('.')[:c-2] 64 | return printf('%d', len(iconv(buf, &encoding, "utf-8"))) 65 | endif 66 | return printf('%d', line2byte(line('.')) + (col('.')-2)) 67 | endf 68 | 69 | fu! s:gocodeAutocomplete() 70 | let filename = s:gocodeCurrentBuffer() 71 | let result = s:gocodeCommand('autocomplete', 72 | \ [s:gocodeCurrentBufferOpt(filename), '-f=vim'], 73 | \ [expand('%:p'), s:gocodeCursor()]) 74 | call delete(filename) 75 | return result 76 | endf 77 | 78 | fu! gocomplete#Complete(findstart, base) 79 | "findstart = 1 when we need to get the text length 80 | if a:findstart == 1 81 | execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete() 82 | return col('.') - g:gocomplete_completions[0] - 1 83 | "findstart = 0 when we need to return the list of completions 84 | else 85 | return g:gocomplete_completions[1] 86 | endif 87 | endf 88 | -------------------------------------------------------------------------------- /nvim/ftplugin/go/gocomplete.vim: -------------------------------------------------------------------------------- 1 | setlocal omnifunc=gocomplete#Complete 2 | -------------------------------------------------------------------------------- /nvim/pathogen_update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | if [ -z $XDG_CONFIG_HOME ]; then 3 | XDG_CONFIG_HOME="$HOME/.config" 4 | fi 5 | mkdir -p "$XDG_CONFIG_HOME/nvim/bundle/gocode/autoload" 6 | mkdir -p "$XDG_CONFIG_HOME/nvim/bundle/gocode/ftplugin/go" 7 | cp "${0%/*}/autoload/gocomplete.vim" "$XDG_CONFIG_HOME/nvim/bundle/gocode/autoload" 8 | cp "${0%/*}/ftplugin/go/gocomplete.vim" "$XDG_CONFIG_HOME/nvim/bundle/gocode/ftplugin/go" 9 | -------------------------------------------------------------------------------- /nvim/symlink.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd "${0%/*}" 3 | ROOTDIR=`pwd` 4 | mkdir -p "$HOME/.config/nvim/autoload" 5 | mkdir -p "$HOME/.config/nvim/ftplugin/go" 6 | ln -s "$ROOTDIR/autoload/gocomplete.vim" "$HOME/.config/nvim/autoload/" 7 | ln -s "$ROOTDIR/ftplugin/go/gocomplete.vim" "$HOME/.config/nvim/ftplugin/go/" 8 | -------------------------------------------------------------------------------- /nvim/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p "$HOME/.config/nvim/autoload" 3 | mkdir -p "$HOME/.config/nvim/ftplugin/go" 4 | cp "${0%/*}/autoload/gocomplete.vim" "$HOME/.config/nvim/autoload" 5 | cp "${0%/*}/ftplugin/go/gocomplete.vim" "$HOME/.config/nvim/ftplugin/go" 6 | -------------------------------------------------------------------------------- /os_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | package main 4 | 5 | import ( 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | ) 10 | 11 | const defaultSocketType = "unix" 12 | 13 | // Full path of the current executable 14 | func get_executable_filename() string { 15 | // try readlink first 16 | path, err := os.Readlink("/proc/self/exe") 17 | if err == nil { 18 | return path 19 | } 20 | // use argv[0] 21 | path = os.Args[0] 22 | if !filepath.IsAbs(path) { 23 | cwd, _ := os.Getwd() 24 | path = filepath.Join(cwd, path) 25 | } 26 | if fileExists(path) { 27 | return path 28 | } 29 | // Fallback : use "gocode" and assume we are in the PATH... 30 | path, err = exec.LookPath("gocode") 31 | if err == nil { 32 | return path 33 | } 34 | return "" 35 | } 36 | -------------------------------------------------------------------------------- /os_windows.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "syscall" 6 | "unsafe" 7 | ) 8 | 9 | const defaultSocketType = "tcp" 10 | 11 | var ( 12 | kernel32 = syscall.NewLazyDLL("kernel32.dll") 13 | 14 | proc_get_module_file_name = kernel32.NewProc("GetModuleFileNameW") 15 | ) 16 | 17 | // Full path of the current executable 18 | func get_executable_filename() string { 19 | b := make([]uint16, syscall.MAX_PATH) 20 | ret, _, err := syscall.Syscall(proc_get_module_file_name.Addr(), 3, 21 | 0, uintptr(unsafe.Pointer(&b[0])), uintptr(len(b))) 22 | if int(ret) == 0 { 23 | panic(fmt.Sprintf("GetModuleFileNameW : err %d", int(err))) 24 | } 25 | return syscall.UTF16ToString(b) 26 | } 27 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "go/build" 7 | "go/importer" 8 | "log" 9 | "net" 10 | "net/rpc" 11 | "os" 12 | "os/signal" 13 | "runtime/debug" 14 | "time" 15 | 16 | "github.com/mdempsky/gocode/internal/cache" 17 | "github.com/mdempsky/gocode/internal/gbimporter" 18 | "github.com/mdempsky/gocode/internal/suggest" 19 | ) 20 | 21 | func doServer(cache bool) { 22 | addr := *g_addr 23 | if *g_sock == "unix" { 24 | addr = getSocketPath() 25 | } 26 | 27 | lis, err := net.Listen(*g_sock, addr) 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | sigs := make(chan os.Signal) 33 | signal.Notify(sigs, os.Interrupt) 34 | go func() { 35 | <-sigs 36 | exitServer() 37 | }() 38 | 39 | if err = rpc.Register(&Server{ 40 | cache: cache, 41 | }); err != nil { 42 | log.Fatal(err) 43 | } 44 | rpc.Accept(lis) 45 | } 46 | 47 | func exitServer() { 48 | if *g_sock == "unix" { 49 | _ = os.Remove(getSocketPath()) 50 | } 51 | os.Exit(0) 52 | } 53 | 54 | type Server struct { 55 | cache bool 56 | } 57 | 58 | type AutoCompleteRequest struct { 59 | Filename string 60 | Data []byte 61 | Cursor int 62 | Context cache.PackedContext 63 | Source bool 64 | Builtin bool 65 | IgnoreCase bool 66 | UnimportedPackages bool 67 | FallbackToSource bool 68 | } 69 | 70 | type AutoCompleteReply struct { 71 | Candidates []suggest.Candidate 72 | Len int 73 | } 74 | 75 | func (s *Server) AutoComplete(req *AutoCompleteRequest, res *AutoCompleteReply) error { 76 | defer func() { 77 | if err := recover(); err != nil { 78 | fmt.Printf("panic: %s\n\n", err) 79 | debug.PrintStack() 80 | 81 | res.Candidates = []suggest.Candidate{ 82 | {Class: "PANIC", Name: "PANIC", Type: "PANIC"}, 83 | } 84 | } 85 | }() 86 | if *g_debug { 87 | var buf bytes.Buffer 88 | log.Printf("Got autocompletion request for '%s'\n", req.Filename) 89 | log.Printf("Cursor at: %d\n", req.Cursor) 90 | buf.WriteString("-------------------------------------------------------\n") 91 | buf.Write(req.Data[:req.Cursor]) 92 | buf.WriteString("#") 93 | buf.Write(req.Data[req.Cursor:]) 94 | log.Print(buf.String()) 95 | log.Println("-------------------------------------------------------") 96 | } 97 | now := time.Now() 98 | cfg := suggest.Config{ 99 | Builtin: req.Builtin, 100 | IgnoreCase: req.IgnoreCase, 101 | UnimportedPackages: req.UnimportedPackages, 102 | Logf: func(string, ...interface{}) {}, 103 | } 104 | cfg.Logf = func(string, ...interface{}) {} 105 | if *g_debug { 106 | cfg.Logf = log.Printf 107 | } 108 | // TODO(rstambler): Figure out why this happens sometimes. 109 | if req.Context.GOPATH == "" || req.Context.GOROOT == "" { 110 | req.Context = cache.PackContext(&build.Default) 111 | } 112 | if req.Source { 113 | cfg.Importer = gbimporter.New(&req.Context, req.Filename, importer.For("source", nil), func(s string, args ...interface{}) { 114 | cfg.Logf("source: "+s, args...) 115 | }) 116 | } else if s.cache { 117 | cache.Mu.Lock() 118 | defer cache.Mu.Unlock() 119 | cfg.Importer = cache.NewImporter(&req.Context, req.Filename, req.FallbackToSource, func(s string, args ...interface{}) { 120 | cfg.Logf("cache: "+s, args...) 121 | }) 122 | } else { 123 | cfg.Importer = gbimporter.New(&req.Context, req.Filename, importer.Default(), func(s string, args ...interface{}) { 124 | cfg.Logf("gbimporter: "+s, args...) 125 | }) 126 | } 127 | 128 | candidates, d := cfg.Suggest(req.Filename, req.Data, req.Cursor) 129 | elapsed := time.Since(now) 130 | if *g_debug { 131 | log.Printf("Elapsed duration: %v\n", elapsed) 132 | log.Printf("Offset: %d\n", res.Len) 133 | log.Printf("Number of candidates found: %d\n", len(candidates)) 134 | log.Printf("Candidates are:\n") 135 | for _, c := range candidates { 136 | log.Printf(" %s\n", c.String()) 137 | } 138 | log.Println("=======================================================") 139 | } 140 | res.Candidates, res.Len = candidates, d 141 | return nil 142 | } 143 | 144 | type ExitRequest struct{} 145 | type ExitReply struct{} 146 | 147 | func (s *Server) Exit(req *ExitRequest, res *ExitReply) error { 148 | go func() { 149 | time.Sleep(time.Second) 150 | exitServer() 151 | }() 152 | return nil 153 | } 154 | -------------------------------------------------------------------------------- /subl3/README.md: -------------------------------------------------------------------------------- 1 | # Sublime Text 3 plugin 2 | 3 | If you want full Go experience in sublime, use [GoSublime](https://github.com/DisposaBoy/GoSublime). 4 | 5 | This plugin is written by me (nsf) as a result of frustration with GoSublime. I wanted something simpler and plugin has to use system version of the gocode. As you may know, GoSublime uses a fork of gocode, which is integrated into its own daemon called MarGo (as far as I know). 6 | 7 | The plugin does: 8 | 9 | 1. Basic gocode autocompletion. I did my best presenting the results to the user. Maybe it's possible to improve that part a bit, not quite sure yet. For functions I use "{**class**} {**name**}\t{**returns**}", for everything else "{**class**} {**name**}\t{**type**}". In addition to that functions are properly augmented with argument snippets. 10 | 11 | 2. Gofmt on save. Here I use difflib to calculate differences, which results in line by line changes. GoSublime uses [google-diff-match-patch](https://code.google.com/archive/p/google-diff-match-patch/) library, which calculates changes on finer granularity, maybe I should switch to that lib as well. 12 | 13 | I don't have any big plans on this plugin. The reason why I moved to sublime in the first place is, again, frustration, but with Atom (too slow). We'll see how it goes. 14 | 15 | Oh yes, this plugin uses GoSublime syntax files for Go. Well, they use a fork of gocode, I'll use the fork of their syntax files, fair deal. 16 | -------------------------------------------------------------------------------- /subl3/gocode.py: -------------------------------------------------------------------------------- 1 | import sublime, sublime_plugin, subprocess, difflib 2 | 3 | # go to balanced pair, e.g.: 4 | # ((abc(def))) 5 | # ^ 6 | # \--------->^ 7 | # 8 | # returns -1 on failure 9 | def skip_to_balanced_pair(str, i, open, close): 10 | count = 1 11 | i += 1 12 | while i < len(str): 13 | if str[i] == open: 14 | count += 1 15 | elif str[i] == close: 16 | count -= 1 17 | 18 | if count == 0: 19 | break 20 | i += 1 21 | if i >= len(str): 22 | return -1 23 | return i 24 | 25 | # split balanced parens string using comma as separator 26 | # e.g.: "ab, (1, 2), cd" -> ["ab", "(1, 2)", "cd"] 27 | # filters out empty strings 28 | def split_balanced(s): 29 | out = [] 30 | i = 0 31 | beg = 0 32 | while i < len(s): 33 | if s[i] == ',': 34 | out.append(s[beg:i].strip()) 35 | beg = i+1 36 | i += 1 37 | elif s[i] == '(': 38 | i = skip_to_balanced_pair(s, i, "(", ")") 39 | if i == -1: 40 | i = len(s) 41 | else: 42 | i += 1 43 | 44 | out.append(s[beg:i].strip()) 45 | return list(filter(bool, out)) 46 | 47 | 48 | def extract_arguments_and_returns(sig): 49 | sig = sig.strip() 50 | if not sig.startswith("func"): 51 | return [], [] 52 | 53 | # find first pair of parens, these are arguments 54 | beg = sig.find("(") 55 | if beg == -1: 56 | return [], [] 57 | end = skip_to_balanced_pair(sig, beg, "(", ")") 58 | if end == -1: 59 | return [], [] 60 | args = split_balanced(sig[beg+1:end]) 61 | 62 | # find the rest of the string, these are returns 63 | sig = sig[end+1:].strip() 64 | sig = sig[1:-1] if sig.startswith("(") and sig.endswith(")") else sig 65 | returns = split_balanced(sig) 66 | 67 | return args, returns 68 | 69 | # takes gocode's candidate and returns sublime's hint and subj 70 | def hint_and_subj(cls, name, type): 71 | subj = name 72 | if cls == "func": 73 | hint = cls + " " + name 74 | args, returns = extract_arguments_and_returns(type) 75 | if returns: 76 | hint += "\t" + ", ".join(returns) 77 | if args: 78 | sargs = [] 79 | for i, a in enumerate(args): 80 | ea = a.replace("{", "\\{").replace("}", "\\}") 81 | sargs.append("${{{0}:{1}}}".format(i+1, ea)) 82 | subj += "(" + ", ".join(sargs) + ")" 83 | else: 84 | subj += "()" 85 | else: 86 | hint = cls + " " + name + "\t" + type 87 | return hint, subj 88 | 89 | def diff_sanity_check(a, b): 90 | if a != b: 91 | raise Exception("diff sanity check mismatch\n-%s\n+%s" % (a, b)) 92 | 93 | class GocodeGofmtCommand(sublime_plugin.TextCommand): 94 | def run(self, edit): 95 | view = self.view 96 | src = view.substr(sublime.Region(0, view.size())) 97 | gofmt = subprocess.Popen(["gofmt"], 98 | stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 99 | sout, serr = gofmt.communicate(src.encode()) 100 | if gofmt.returncode != 0: 101 | print(serr.decode(), end="") 102 | return 103 | 104 | newsrc = sout.decode() 105 | diff = difflib.ndiff(src.splitlines(), newsrc.splitlines()) 106 | i = 0 107 | for line in diff: 108 | if line.startswith("?"): # skip hint lines 109 | continue 110 | 111 | l = (len(line)-2)+1 112 | if line.startswith("-"): 113 | diff_sanity_check(view.substr(sublime.Region(i, i+l-1)), line[2:]) 114 | view.erase(edit, sublime.Region(i, i+l)) 115 | elif line.startswith("+"): 116 | view.insert(edit, i, line[2:]+"\n") 117 | i += l 118 | else: 119 | diff_sanity_check(view.substr(sublime.Region(i, i+l-1)), line[2:]) 120 | i += l 121 | 122 | class Gocode(sublime_plugin.EventListener): 123 | def on_query_completions(self, view, prefix, locations): 124 | loc = locations[0] 125 | if not view.match_selector(loc, "source.go"): 126 | return None 127 | 128 | src = view.substr(sublime.Region(0, view.size())) 129 | filename = view.file_name() 130 | cloc = "c{0}".format(loc) 131 | gocode = subprocess.Popen(["gocode", "-f=csv", "autocomplete", filename, cloc], 132 | stdin=subprocess.PIPE, stdout=subprocess.PIPE) 133 | out = gocode.communicate(src.encode())[0].decode() 134 | 135 | result = [] 136 | for line in filter(bool, out.split("\n")): 137 | arg = line.split(",,") 138 | hint, subj = hint_and_subj(arg[0], arg[1], arg[2]) 139 | result.append([hint, subj]) 140 | 141 | return (result, sublime.INHIBIT_WORD_COMPLETIONS) 142 | 143 | def on_pre_save(self, view): 144 | if not view.match_selector(0, "source.go"): 145 | return 146 | view.run_command('gocode_gofmt') 147 | -------------------------------------------------------------------------------- /subl3/syntax/GoSublime-Go.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | comment 6 | Based on work from github.com/frou/GoFeather and github.com/DisposaBoy/GoSublime 7 | fileTypes 8 | 9 | go 10 | 11 | firstLineMatch 12 | -[*]-( Mode:)? Go -[*]- 13 | keyEquivalent 14 | ^~G 15 | name 16 | GoSublime: Go 17 | patterns 18 | 19 | 20 | begin 21 | /\* 22 | end 23 | \*/ 24 | name 25 | comment.block.go 26 | 27 | 28 | begin 29 | // 30 | end 31 | \z 32 | name 33 | comment.line.double-slash.go 34 | 35 | 36 | begin 37 | " 38 | beginCaptures 39 | 40 | 0 41 | 42 | name 43 | punctuation.definition.string.begin.go 44 | 45 | 46 | end 47 | " 48 | endCaptures 49 | 50 | 0 51 | 52 | name 53 | punctuation.definition.string.end.go 54 | 55 | 56 | name 57 | string.quoted.double.go 58 | patterns 59 | 60 | 61 | include 62 | #string_placeholder 63 | 64 | 65 | include 66 | #string_escaped_char 67 | 68 | 69 | 70 | 71 | begin 72 | ` 73 | beginCaptures 74 | 75 | 0 76 | 77 | name 78 | punctuation.definition.string.begin.go 79 | 80 | 81 | end 82 | ` 83 | endCaptures 84 | 85 | 0 86 | 87 | name 88 | punctuation.definition.string.end.go 89 | 90 | 91 | name 92 | string.quoted.raw.go 93 | patterns 94 | 95 | 96 | include 97 | #string_placeholder 98 | 99 | 100 | include 101 | source.gotemplate 102 | 103 | 104 | 105 | 106 | match 107 | \b(true|false|nil|iota)\b 108 | name 109 | constant.language.go 110 | 111 | 112 | match 113 | \b((\d+\.(\d+)?([eE][+-]?\d+)?|\d+[eE][+-]?\d+|\.\d+([eE][+-]?\d+)?)i?)\b 114 | name 115 | constant.numeric.floating-point.go 116 | 117 | 118 | match 119 | \b(\d+i|0[xX][0-9A-Fa-f]+|0[0-7]*|[1-9][0-9]*)\b 120 | name 121 | constant.numeric.integer.go 122 | 123 | 124 | match 125 | '(?:[^'\\]|\\(?:\\|[abfnrtv']|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|[0-7]{3}))' 126 | name 127 | constant.other.rune.go 128 | 129 | 130 | match 131 | \b(bool|byte|complex(64|128)|error|float(32|64)|rune|string|u?int(8|16|32|64)?|uintptr)\b 132 | name 133 | storage.type.go 134 | 135 | 136 | comment 137 | A subset of keyword.other.go for flow control keywords. 138 | match 139 | \b(break|case|continue|default|defer|else|for|go|goto|if|range|return|select|switch)\b 140 | name 141 | keyword.control.go 142 | 143 | 144 | match 145 | \b(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b 146 | name 147 | keyword.other.go 148 | 149 | 150 | captures 151 | 152 | 0 153 | 154 | name 155 | variable.other.go 156 | 157 | 1 158 | 159 | name 160 | keyword.operator.initialize.go 161 | 162 | 163 | comment 164 | This matches the 'x := 0' style of variable declaration. 165 | match 166 | (?:[[:alpha:]_][[:alnum:]_]*)(?:,\s+[[:alpha:]_][[:alnum:]_]*)*\s*(:=) 167 | name 168 | meta.initialization.short.go 169 | 170 | 171 | match 172 | (?<=(\Afunc|...\))\s)\b(\w+)\b(?=\() 173 | name 174 | entity.name.function.go 175 | 176 | 177 | match 178 | (?<=(\sfunc|....\))\s)\b(\w+)\b(?=\() 179 | name 180 | entity.name.function.go 181 | 182 | 183 | match 184 | (?<=\Atype\s)\b(\w+)\b 185 | name 186 | entity.name.type.go 187 | 188 | 189 | match 190 | (?<=\stype\s)\b(\w+)\b 191 | name 192 | entity.name.type.go 193 | 194 | 195 | match 196 | \b(append|cap|close|complex|copy|delete|imag|len|make|new|panic|print|println|real|recover)\b 197 | name 198 | support.function.builtin.go 199 | 200 | 201 | match 202 | \b(\w+)\b(?=\() 203 | name 204 | support.function.go 205 | 206 | 207 | match 208 | (<-) 209 | name 210 | keyword.operator.channel.go 211 | 212 | 213 | match 214 | (==|!=|<|<=|>|>=) 215 | name 216 | keyword.operator.comparison.go 217 | 218 | 219 | match 220 | (&&|[|]{2}|!) 221 | name 222 | keyword.operator.logical.go 223 | 224 | 225 | match 226 | ([+]{2}) 227 | name 228 | keyword.operator.increment.go 229 | 230 | 231 | match 232 | (--) 233 | name 234 | keyword.decrement.go 235 | 236 | 237 | match 238 | (=|(?:[+]|-|[|]|^|[*]|/|%|<<|>>|&|&^)=) 239 | name 240 | keyword.operator.assignment.go 241 | 242 | 243 | match 244 | ([+]|-|[*]|/|%|&|[|]|^|&^|<<|>>) 245 | name 246 | keyword.operator.arithmetic.go 247 | 248 | 249 | match 250 | (;) 251 | name 252 | keyword.operator.semi-colon.go 253 | 254 | 255 | match 256 | (,) 257 | name 258 | punctuation.definition.comma.go 259 | 260 | 261 | match 262 | ([.]) 263 | name 264 | punctuation.definition.dot.go 265 | 266 | 267 | match 268 | (:) 269 | name 270 | punctuation.definition.colon.go 271 | 272 | 273 | match 274 | (\[|\]|{|}|\(|\)) 275 | name 276 | punctuation.definition.bracket.go 277 | 278 | 279 | repository 280 | 281 | string_escaped_char 282 | 283 | patterns 284 | 285 | 286 | match 287 | \\(\\|[abfnrtv'"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|[0-7]{3}) 288 | name 289 | constant.character.escape.go 290 | 291 | 292 | match 293 | \\. 294 | name 295 | invalid.illegal.unknown-escape.go 296 | 297 | 298 | 299 | string_placeholder 300 | 301 | patterns 302 | 303 | 304 | match 305 | (?x)% 306 | (\d+\$)? # field (argument #) 307 | [#0\- +']* # flags 308 | [,;:_]? # separator character (AltiVec) 309 | ((-?\d+)|\*(-?\d+\$)?)? # minimum field width 310 | (\.((-?\d+)|\*(-?\d+\$)?)?)? # precision 311 | [diouxXDOUeEfFgGaAcCsSqpnvtTbyYhHmMzZ%] # conversion type 312 | 313 | name 314 | constant.other.placeholder.go 315 | 316 | 317 | 318 | 319 | scopeName 320 | source.go 321 | uuid 322 | 1caaa75c-b61d-11e2-ba93-138feb5db969 323 | 324 | 325 | -------------------------------------------------------------------------------- /subl3/syntax/GoSublime-Go.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "Based on work from github.com/frou/GoFeather and github.com/DisposaBoy/GoSublime", 3 | "fileTypes": [ 4 | "go" 5 | ], 6 | "firstLineMatch": "-[*]-( Mode:)? Go -[*]-", 7 | "keyEquivalent": "^~G", 8 | "name": "GoSublime: Go", 9 | "patterns": [ 10 | { 11 | "begin": "/\\*", 12 | "end": "\\*/", 13 | "name": "comment.block.go" 14 | }, 15 | { 16 | "begin": "//", 17 | "end": "\\z", 18 | "name": "comment.line.double-slash.go" 19 | }, 20 | { 21 | "begin": "\"", 22 | "beginCaptures": { 23 | "0": { 24 | "name": "punctuation.definition.string.begin.go" 25 | } 26 | }, 27 | "end": "\"", 28 | "endCaptures": { 29 | "0": { 30 | "name": "punctuation.definition.string.end.go" 31 | } 32 | }, 33 | "name": "string.quoted.double.go", 34 | "patterns": [ 35 | { 36 | "include": "#string_placeholder" 37 | }, 38 | { 39 | "include": "#string_escaped_char" 40 | } 41 | ] 42 | }, 43 | { 44 | "begin": "`", 45 | "beginCaptures": { 46 | "0": { 47 | "name": "punctuation.definition.string.begin.go" 48 | } 49 | }, 50 | "end": "`", 51 | "endCaptures": { 52 | "0": { 53 | "name": "punctuation.definition.string.end.go" 54 | } 55 | }, 56 | "name": "string.quoted.raw.go", 57 | "patterns": [ 58 | { 59 | "include": "#string_placeholder" 60 | }, 61 | { 62 | "include": "source.gotemplate" 63 | } 64 | ] 65 | }, 66 | { 67 | "match": "\\b(true|false|nil|iota)\\b", 68 | "name": "constant.language.go" 69 | }, 70 | { 71 | "match": "\\b((\\d+\\.(\\d+)?([eE][+-]?\\d+)?|\\d+[eE][+-]?\\d+|\\.\\d+([eE][+-]?\\d+)?)i?)\\b", 72 | "name": "constant.numeric.floating-point.go" 73 | }, 74 | { 75 | "match": "\\b(\\d+i|0[xX][0-9A-Fa-f]+|0[0-7]*|[1-9][0-9]*)\\b", 76 | "name": "constant.numeric.integer.go" 77 | }, 78 | { 79 | "name": "constant.other.rune.go", 80 | "match": "'(?:[^'\\\\]|\\\\(?:\\\\|[abfnrtv']|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|[0-7]{3}))'" 81 | }, 82 | { 83 | "match": "\\b(bool|byte|complex(64|128)|error|float(32|64)|rune|string|u?int(8|16|32|64)?|uintptr)\\b", 84 | "name": "storage.type.go" 85 | }, 86 | { 87 | "comment": "A subset of keyword.other.go for flow control keywords.", 88 | "match": "\\b(break|case|continue|default|defer|else|for|go|goto|if|range|return|select|switch)\\b", 89 | "name": "keyword.control.go" 90 | }, 91 | { 92 | "match": "\\b(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)\\b", 93 | "name": "keyword.other.go" 94 | }, 95 | { 96 | "captures": { 97 | "0": { 98 | "name": "variable.other.go" 99 | }, 100 | "1": { 101 | "name": "keyword.operator.initialize.go" 102 | } 103 | }, 104 | "comment": "This matches the 'x := 0' style of variable declaration.", 105 | "match": "(?:[[:alpha:]_][[:alnum:]_]*)(?:,\\s+[[:alpha:]_][[:alnum:]_]*)*\\s*(:=)", 106 | "name": "meta.initialization.short.go" 107 | }, 108 | { 109 | "match": "(?<=(\\Afunc|...\\))\\s)\\b(\\w+)\\b(?=\\()", 110 | "name": "entity.name.function.go" 111 | }, 112 | { 113 | "match": "(?<=(\\sfunc|....\\))\\s)\\b(\\w+)\\b(?=\\()", 114 | "name": "entity.name.function.go" 115 | }, 116 | { 117 | "match": "(?<=\\Atype\\s)\\b(\\w+)\\b", 118 | "name": "entity.name.type.go" 119 | }, 120 | { 121 | "match": "(?<=\\stype\\s)\\b(\\w+)\\b", 122 | "name": "entity.name.type.go" 123 | }, 124 | { 125 | "match": "\\b(append|cap|close|complex|copy|delete|imag|len|make|new|panic|print|println|real|recover)\\b", 126 | "name": "support.function.builtin.go" 127 | }, 128 | { 129 | "match": "\\b(\\w+)\\b(?=\\()", 130 | "name": "support.function.go" 131 | }, 132 | { 133 | "match": "(<-)", 134 | "name": "keyword.operator.channel.go" 135 | }, 136 | { 137 | "match": "(==|!=|<|<=|>|>=)", 138 | "name": "keyword.operator.comparison.go" 139 | }, 140 | { 141 | "match": "(&&|[|]{2}|!)", 142 | "name": "keyword.operator.logical.go" 143 | }, 144 | { 145 | "match": "([+]{2})", 146 | "name": "keyword.operator.increment.go" 147 | }, 148 | { 149 | "match": "(--)", 150 | "name": "keyword.decrement.go" 151 | }, 152 | { 153 | "match": "(=|(?:[+]|-|[|]|^|[*]|/|%|<<|>>|&|&^)=)", 154 | "name": "keyword.operator.assignment.go" 155 | }, 156 | { 157 | "match": "([+]|-|[*]|/|%|&|[|]|^|&^|<<|>>)", 158 | "name": "keyword.operator.arithmetic.go" 159 | }, 160 | { 161 | "match": "(;)", 162 | "name": "keyword.operator.semi-colon.go" 163 | }, 164 | { 165 | "match": "(,)", 166 | "name": "punctuation.definition.comma.go" 167 | }, 168 | { 169 | "match": "([.])", 170 | "name": "punctuation.definition.dot.go" 171 | }, 172 | { 173 | "match": "(:)", 174 | "name": "punctuation.definition.colon.go" 175 | }, 176 | { 177 | "match": "(\\[|\\]|{|}|\\(|\\))", 178 | "name": "punctuation.definition.bracket.go" 179 | } 180 | ], 181 | // note: keep this in sync with GoTemplate 182 | "repository": { 183 | "string_escaped_char": { 184 | "patterns": [ 185 | { 186 | // note: keep this in sync with constant.other.rune.go 187 | "match": "\\\\(\\\\|[abfnrtv'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|[0-7]{3})", 188 | "name": "constant.character.escape.go" 189 | }, 190 | { 191 | "match": "\\\\.", 192 | "name": "invalid.illegal.unknown-escape.go" 193 | } 194 | ] 195 | }, 196 | "string_placeholder": { 197 | "patterns": [ 198 | { 199 | "match": "(?x)%\n (\\d+\\$)? # field (argument #)\n [#0\\- +']* # flags\n [,;:_]? # separator character (AltiVec)\n ((-?\\d+)|\\*(-?\\d+\\$)?)? # minimum field width\n (\\.((-?\\d+)|\\*(-?\\d+\\$)?)?)? # precision\n [diouxXDOUeEfFgGaAcCsSqpnvtTbyYhHmMzZ%] # conversion type\n ", 200 | "name": "constant.other.placeholder.go" 201 | } 202 | ] 203 | } 204 | }, 205 | "scopeName": "source.go", 206 | "uuid": "1caaa75c-b61d-11e2-ba93-138feb5db969" 207 | } 208 | -------------------------------------------------------------------------------- /subl3/syntax/GoSublime-HTML.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | gohtml 8 | 9 | name 10 | GoSublime: HTML 11 | patterns 12 | 13 | 14 | include 15 | text.html.basic 16 | 17 | 18 | include 19 | source.gotemplate 20 | 21 | 22 | scopeName 23 | text.html.gohtml 24 | uuid 25 | 78c486ec-0101-11e2-a85a-00262d996a67 26 | 27 | 28 | -------------------------------------------------------------------------------- /subl3/syntax/GoSublime-HTML.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GoSublime: HTML", 3 | "uuid": "78c486ec-0101-11e2-a85a-00262d996a67", 4 | "scopeName": "text.html.gohtml", 5 | "fileTypes": ["gohtml"], 6 | 7 | "patterns": [ 8 | { 9 | "include": "text.html.basic", 10 | }, 11 | { 12 | "include": "source.gotemplate", 13 | }, 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /subl3/syntax/GoSublime-Template.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | foldingStartMarker 6 | \{\{\s*(?:if|with|range)\b 7 | foldingStopMarker 8 | \{\{\s*(?:else|end)\b 9 | name 10 | GoSublime: Template 11 | patterns 12 | 13 | 14 | begin 15 | \{\{ 16 | beginCaptures 17 | 18 | 0 19 | 20 | name 21 | punctuation.section.embedded.begin.gotemplate 22 | 23 | 24 | end 25 | \}\} 26 | endCaptures 27 | 28 | 0 29 | 30 | name 31 | punctuation.section.embedded.end.gotemplate 32 | 33 | 34 | patterns 35 | 36 | 37 | match 38 | := 39 | name 40 | keyword.operator.initialize.gotemplate 41 | 42 | 43 | match 44 | \| 45 | name 46 | keyword.operator.pipe.gotemplate 47 | 48 | 49 | match 50 | [.$][\w]* 51 | name 52 | variable.other.gotemplate 53 | 54 | 55 | match 56 | \b(if|else|range|template|with|end|nil|with|define)\b 57 | name 58 | keyword.control.gotemplate 59 | 60 | 61 | match 62 | \b(and|call|html|index|js|len|not|or|print|printf|println|urlquery|eq|ne|lt|le|gt|ge)\b 63 | name 64 | support.function.builtin.gotemplate 65 | 66 | 67 | begin 68 | /\* 69 | end 70 | \*/ 71 | name 72 | comment.block.gotemplate 73 | 74 | 75 | begin 76 | " 77 | beginCaptures 78 | 79 | 0 80 | 81 | name 82 | punctuation.definition.string.begin.gotemplate 83 | 84 | 85 | end 86 | " 87 | endCaptures 88 | 89 | 0 90 | 91 | name 92 | punctuation.definition.string.end.gotemplate 93 | 94 | 95 | name 96 | string.quoted.double.gotemplate 97 | patterns 98 | 99 | 100 | include 101 | #string_placeholder 102 | 103 | 104 | include 105 | #string_escaped_char 106 | 107 | 108 | 109 | 110 | begin 111 | ` 112 | beginCaptures 113 | 114 | 0 115 | 116 | name 117 | punctuation.definition.string.begin.gotemplate 118 | 119 | 120 | end 121 | ` 122 | endCaptures 123 | 124 | 0 125 | 126 | name 127 | punctuation.definition.string.end.gotemplate 128 | 129 | 130 | name 131 | string.quoted.raw.gotemplate 132 | patterns 133 | 134 | 135 | include 136 | #string_placeholder 137 | 138 | 139 | 140 | 141 | 142 | 143 | repository 144 | 145 | string_escaped_char 146 | 147 | patterns 148 | 149 | 150 | match 151 | \\(\\|[abfnrtv'"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|[0-7]{3}) 152 | name 153 | constant.character.escape.gotemplate 154 | 155 | 156 | match 157 | \\. 158 | name 159 | invalid.illegal.unknown-escape.gotemplate 160 | 161 | 162 | 163 | string_placeholder 164 | 165 | patterns 166 | 167 | 168 | match 169 | (?x)% 170 | (\d+\$)? # field (argument #) 171 | [#0\- +']* # flags 172 | [,;:_]? # separator character (AltiVec) 173 | ((-?\d+)|\*(-?\d+\$)?)? # minimum field width 174 | (\.((-?\d+)|\*(-?\d+\$)?)?)? # precision 175 | [diouxXDOUeEfFgGaAcCsSqpnvtTbyYhHmMzZ%] # conversion type 176 | 177 | name 178 | constant.other.placeholder.gotemplate 179 | 180 | 181 | match 182 | % 183 | name 184 | invalid.illegal.placeholder.gotemplate 185 | 186 | 187 | 188 | 189 | scopeName 190 | source.gotemplate 191 | uuid 192 | 43606de8-c638-11e2-b661-6711676f99ca 193 | 194 | 195 | -------------------------------------------------------------------------------- /subl3/syntax/GoSublime-Template.tmLanguage.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GoSublime: Template", 3 | "uuid": "43606de8-c638-11e2-b661-6711676f99ca", 4 | "scopeName": "source.gotemplate", 5 | 6 | "foldingStartMarker": "\\{\\{\\s*(?:if|with|range)\\b", 7 | "foldingStopMarker": "\\{\\{\\s*(?:else|end)\\b", 8 | 9 | "patterns": [ 10 | { 11 | "begin": "\\{\\{", 12 | "beginCaptures": { 13 | "0": { 14 | "name": "punctuation.section.embedded.begin.gotemplate" 15 | } 16 | }, 17 | 18 | "end": "\\}\\}", 19 | "endCaptures": { 20 | "0": { 21 | "name": "punctuation.section.embedded.end.gotemplate" 22 | } 23 | }, 24 | 25 | "patterns": [ 26 | { 27 | "match": ":=", 28 | "name": "keyword.operator.initialize.gotemplate" 29 | }, 30 | { 31 | "match": "\\|", 32 | "name": "keyword.operator.pipe.gotemplate" 33 | }, 34 | { 35 | "match": "[.$][\\w]*", 36 | "name": "variable.other.gotemplate" 37 | }, 38 | { 39 | "match": "\\b(if|else|range|template|with|end|nil|with|define)\\b", 40 | "name": "keyword.control.gotemplate" 41 | }, 42 | { 43 | "match": "\\b(and|call|html|index|js|len|not|or|print|printf|println|urlquery|eq|ne|lt|le|gt|ge)\\b", 44 | "name": "support.function.builtin.gotemplate" 45 | }, 46 | { 47 | "begin": "/\\*", 48 | "end": "\\*/", 49 | "name": "comment.block.gotemplate" 50 | }, 51 | { 52 | "begin": "\"", 53 | "beginCaptures": { 54 | "0": { 55 | "name": "punctuation.definition.string.begin.gotemplate" 56 | } 57 | }, 58 | "end": "\"", 59 | "endCaptures": { 60 | "0": { 61 | "name": "punctuation.definition.string.end.gotemplate" 62 | } 63 | }, 64 | "name": "string.quoted.double.gotemplate", 65 | "patterns": [ 66 | { 67 | "include": "#string_placeholder" 68 | }, 69 | { 70 | "include": "#string_escaped_char" 71 | } 72 | ] 73 | }, 74 | { 75 | "begin": "`", 76 | "beginCaptures": { 77 | "0": { 78 | "name": "punctuation.definition.string.begin.gotemplate" 79 | } 80 | }, 81 | "end": "`", 82 | "endCaptures": { 83 | "0": { 84 | "name": "punctuation.definition.string.end.gotemplate" 85 | } 86 | }, 87 | "name": "string.quoted.raw.gotemplate", 88 | "patterns": [ 89 | { 90 | "include": "#string_placeholder" 91 | } 92 | ] 93 | } 94 | ], 95 | } 96 | ], 97 | "repository": { 98 | "string_escaped_char": { 99 | "patterns": [ 100 | { 101 | // note: keep this in sync with constant.other.rune.go 102 | "match": "\\\\(\\\\|[abfnrtv'\"]|x[0-9a-fA-F]{2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8}|[0-7]{3})", 103 | "name": "constant.character.escape.gotemplate" 104 | }, 105 | { 106 | "match": "\\\\.", 107 | "name": "invalid.illegal.unknown-escape.gotemplate" 108 | } 109 | ] 110 | }, 111 | "string_placeholder": { 112 | "patterns": [ 113 | { 114 | "match": "(?x)%\n (\\d+\\$)? # field (argument #)\n [#0\\- +']* # flags\n [,;:_]? # separator character (AltiVec)\n ((-?\\d+)|\\*(-?\\d+\\$)?)? # minimum field width\n (\\.((-?\\d+)|\\*(-?\\d+\\$)?)?)? # precision\n [diouxXDOUeEfFgGaAcCsSqpnvtTbyYhHmMzZ%] # conversion type\n ", 115 | "name": "constant.other.placeholder.gotemplate" 116 | }, 117 | { 118 | "match": "%", 119 | "name": "invalid.illegal.placeholder.gotemplate" 120 | } 121 | ] 122 | } 123 | }, 124 | } 125 | -------------------------------------------------------------------------------- /subl3/syntax/LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 The GoSublime Authors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "unicode/utf8" 6 | ) 7 | 8 | func fileExists(filename string) bool { 9 | _, err := os.Stat(filename) 10 | if err != nil { 11 | return false 12 | } 13 | return true 14 | } 15 | 16 | func runeToByteOffset(s []byte, offset_c int) (offset_b int) { 17 | for offset_b = 0; offset_c > 0 && offset_b < len(s); offset_b++ { 18 | if utf8.RuneStart(s[offset_b]) { 19 | offset_c-- 20 | } 21 | } 22 | return offset_b 23 | } 24 | -------------------------------------------------------------------------------- /vim/autoload/gocomplete.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_gocode') 2 | finish 3 | endif 4 | let g:loaded_gocode = 1 5 | 6 | fu! s:gocodeCurrentBuffer() 7 | let buf = getline(1, '$') 8 | if &encoding != 'utf-8' 9 | let buf = map(buf, 'iconv(v:val, &encoding, "utf-8")') 10 | endif 11 | if &l:fileformat == 'dos' 12 | " XXX: line2byte() depend on 'fileformat' option. 13 | " so if fileformat is 'dos', 'buf' must include '\r'. 14 | let buf = map(buf, 'v:val."\r"') 15 | endif 16 | let file = tempname() 17 | call writefile(buf, file) 18 | return file 19 | endf 20 | 21 | let s:vim_system = get(g:, 'gocomplete#system_function', 'system') 22 | 23 | fu! s:system(str, ...) 24 | return call(s:vim_system, [a:str] + a:000) 25 | endf 26 | 27 | fu! s:gocodeShellescape(arg) 28 | try 29 | let ssl_save = &shellslash 30 | set noshellslash 31 | return shellescape(a:arg) 32 | finally 33 | let &shellslash = ssl_save 34 | endtry 35 | endf 36 | 37 | fu! s:gocodeCommand(cmd, preargs, args) 38 | for i in range(0, len(a:args) - 1) 39 | let a:args[i] = s:gocodeShellescape(a:args[i]) 40 | endfor 41 | for i in range(0, len(a:preargs) - 1) 42 | let a:preargs[i] = s:gocodeShellescape(a:preargs[i]) 43 | endfor 44 | let result = s:system(printf('gocode %s %s %s', join(a:preargs), a:cmd, join(a:args))) 45 | if v:shell_error != 0 46 | return "[\"0\", []]" 47 | else 48 | if &encoding != 'utf-8' 49 | let result = iconv(result, 'utf-8', &encoding) 50 | endif 51 | return result 52 | endif 53 | endf 54 | 55 | fu! s:gocodeCurrentBufferOpt(filename) 56 | return '-in=' . a:filename 57 | endf 58 | 59 | fu! s:gocodeCursor() 60 | if &encoding != 'utf-8' 61 | let c = col('.') 62 | let buf = line('.') == 1 ? "" : (join(getline(1, line('.')-1), "\n") . "\n") 63 | let buf .= c == 1 ? "" : getline('.')[:c-2] 64 | return printf('%d', len(iconv(buf, &encoding, "utf-8"))) 65 | endif 66 | return printf('%d', line2byte(line('.')) + (col('.')-2)) 67 | endf 68 | 69 | fu! s:gocodeAutocomplete() 70 | let filename = s:gocodeCurrentBuffer() 71 | let result = s:gocodeCommand('autocomplete', 72 | \ [s:gocodeCurrentBufferOpt(filename), '-f=vim'], 73 | \ [expand('%:p'), s:gocodeCursor()]) 74 | call delete(filename) 75 | return result 76 | endf 77 | 78 | fu! gocomplete#Complete(findstart, base) 79 | "findstart = 1 when we need to get the text length 80 | if a:findstart == 1 81 | execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete() 82 | return col('.') - g:gocomplete_completions[0] - 1 83 | "findstart = 0 when we need to return the list of completions 84 | else 85 | return g:gocomplete_completions[1] 86 | endif 87 | endf 88 | -------------------------------------------------------------------------------- /vim/ftplugin/go/gocomplete.vim: -------------------------------------------------------------------------------- 1 | setlocal omnifunc=gocomplete#Complete 2 | -------------------------------------------------------------------------------- /vim/pathogen_update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p "$HOME/.vim/bundle/gocode/autoload" 3 | mkdir -p "$HOME/.vim/bundle/gocode/ftplugin/go" 4 | cp "${0%/*}/autoload/gocomplete.vim" "$HOME/.vim/bundle/gocode/autoload" 5 | cp "${0%/*}/ftplugin/go/gocomplete.vim" "$HOME/.vim/bundle/gocode/ftplugin/go" 6 | -------------------------------------------------------------------------------- /vim/symlink.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | cd "${0%/*}" 3 | ROOTDIR=`pwd` 4 | mkdir -p "$HOME/.vim/autoload" 5 | mkdir -p "$HOME/.vim/ftplugin/go" 6 | ln -sf "$ROOTDIR/autoload/gocomplete.vim" "$HOME/.vim/autoload/" 7 | ln -sf "$ROOTDIR/ftplugin/go/gocomplete.vim" "$HOME/.vim/ftplugin/go/" 8 | -------------------------------------------------------------------------------- /vim/update.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | mkdir -p "$HOME/.vim/autoload" 3 | mkdir -p "$HOME/.vim/ftplugin/go" 4 | cp "${0%/*}/autoload/gocomplete.vim" "$HOME/.vim/autoload" 5 | cp "${0%/*}/ftplugin/go/gocomplete.vim" "$HOME/.vim/ftplugin/go" 6 | --------------------------------------------------------------------------------