├── CHANGELOG ├── Context.sublime-menu ├── Default.sublime-commands ├── ExampleProject.sublime-project ├── LICENSE.md ├── README.md ├── dependencies.json ├── golang.sublime-settings ├── gotools_doc.py ├── gotools_format.py ├── gotools_goto_def.py ├── gotools_guru.py ├── gotools_lint.py ├── gotools_rename.py ├── gotools_suggestions.py ├── gotools_util.py └── messages.json /CHANGELOG: -------------------------------------------------------------------------------- 1 | *** Golang Tools Integration *** 2 | 3 | 2016/11/03 v1.0.10 4 | + make it easier to use guru -- Thanks @jostyee 5 | 1. make "build_packages" to be optional 6 | 2. add "guru_use_current_package" for auto-config 7 | 8 | 2016/10/10 v1.0.9 9 | + Add alternative gocode for better autocomplete performance -- Thanks @jostyee 10 | 1. go get -u -v github.com/mdempsky/gocode 11 | 2(optional). Set the Golang config: "gocode_client_mode": true 12 | 13 | 2016/09/15 v1.0.8 14 | + Merge 2 PRs for better Re-name feature and README fix 15 | 16 | 2016/05/28 v1.0.7 17 | + Fix an issue: #issue13 18 | 19 | 2016/04/25 v1.0.6 20 | + Add "-srcdir" arguments for goimports -- Thanks @ottob 21 | + Replce "oracle" with "guru" since oracle is deprecated -- Thanks @ottob 22 | + Add the check for the installation of dependent go tools(goimports, guru, gorename...) 23 | !!! Users must install guru through "go get golang.org/x/tools/cmd/guru" 24 | 25 | 2016/04/20 v1.0.5 26 | + Add error messages for oracle and gorename 27 | + Add Tips for auto-completion configuration to README 28 | 29 | 2016/04/17 v1.0.4 30 | + Add messages.json for showing the CHANGELOG autmatically 31 | + Add the "GOPATH/PATH" setting notice into README 32 | 33 | 2016/04/16 v1.0.3 34 | + Use golangconfig to replace the gotools_settings 35 | + Improve the performance, especially for auto-completion 36 | - Remove the Gotools's Package-Settings menu item: replaced by the unified Golang settings 37 | !!! Users must migrate the global and project-specific settings 38 | Please refer to the README and "ExamplePorject.sublime-project", "golang.sublime-settings" 39 | 40 | 2016/04/10 v1.0.2 41 | + Godoc support 42 | 43 | 2016/04/10 v1.0.1 44 | + Improve the auto-completion 45 | 46 | 2016/04/05 v1.0.0 47 | + Golint/govet support 48 | - Remove syntax 49 | - Remove build system 50 | -------------------------------------------------------------------------------- /Context.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "command": "gotools_goto_def", 4 | "caption": "GoTools: Go to definition" 5 | }, 6 | { 7 | "command": "gotools_doc", 8 | "caption": "GoTools: Doc" 9 | }, 10 | { 11 | "command": "gotools_rename", 12 | "caption": "GoTools: Rename" 13 | }, 14 | { 15 | "command": "gotools_guru", 16 | "caption": "GoTools: Guru: Callers", 17 | "args": { 18 | "command": "callers" 19 | } 20 | } 21 | ] 22 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "GoTools: Go to definition", 4 | "command": "gotools_goto_def" 5 | }, 6 | { 7 | "caption": "GoTools: Format", 8 | "command": "gotools_format" 9 | }, 10 | { 11 | "caption": "GoTools: Lint", 12 | "command": "gotools_lint" 13 | }, 14 | { 15 | "caption": "GoTools: Doc", 16 | "command": "gotools_doc" 17 | }, 18 | { 19 | "caption": "GoTools: Rename", 20 | "command": "gotools_rename" 21 | }, 22 | { 23 | "caption": "GoTools: Guru: Callers", 24 | "command": "gotools_guru", 25 | "args": { 26 | "command": "callers" 27 | } 28 | }, 29 | { 30 | "caption": "GoTools: Guru: Callees", 31 | "command": "gotools_guru", 32 | "args": { 33 | "command": "callees" 34 | } 35 | }, 36 | { 37 | "caption": "GoTools: Guru: Callstack", 38 | "command": "gotools_guru", 39 | "args": { 40 | "command": "callstack" 41 | } 42 | }, 43 | { 44 | "caption": "GoTools: Guru: Describe", 45 | "command": "gotools_guru", 46 | "args": { 47 | "command": "describe" 48 | } 49 | }, 50 | { 51 | "caption": "GoTools: Guru: Freevars", 52 | "command": "gotools_guru", 53 | "args": { 54 | "command": "freevars" 55 | } 56 | }, 57 | { 58 | "caption": "GoTools: Guru: Implements", 59 | "command": "gotools_guru", 60 | "args": { 61 | "command": "implements" 62 | } 63 | }, 64 | { 65 | "caption": "GoTools: Guru: Peers", 66 | "command": "gotools_guru", 67 | "args": { 68 | "command": "peers" 69 | } 70 | }, 71 | { 72 | "caption": "GoTools: Guru: Referrers", 73 | "command": "gotools_guru", 74 | "args": { 75 | "command": "referrers" 76 | } 77 | } 78 | ] 79 | -------------------------------------------------------------------------------- /ExampleProject.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "C:\\Users\\jsmith\\projects\\myproj" 5 | } 6 | ], 7 | 8 | "settings": { 9 | "golang": { 10 | 11 | // A list of GOPATH for this project 12 | // refer to github.com/golang/sublime-config 13 | "GOPATH": "C:\\Users\\jsmith\\go", 14 | 15 | // A list of PATH for this project 16 | // refer to github.com/golang/sublime-config 17 | "PATH": "C:\\Users\\jsmith\\go\\bin", 18 | 19 | // The root package (or namespace) of a project. 20 | "project_package": "github.com/some/project", 21 | 22 | // A list of sub-packages relative to project_packages to be included in 23 | // builds. 24 | "build_packages": ["cmd/myprogram"] 25 | 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Dan Mace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # I've changed my IDE choice to JetBrains' Gogland. 2 | 3 | # Golang Tools Integration 4 | 5 | It's a fork version of [GoTools](https://github.com/ironcladlou/GoTools) by ironcladlou. I create this project because ironcladlou has decided to abandon GoTools project, and I want to use this plugin together with [Golang Build](https://github.com/golang/sublime-build) (by wbond) ([discuss-1]( 6 | https://github.com/ironcladlou/GoTools/issues/121); [discuss-2](https://github.com/ironcladlou/GoTools/issues/128)). 7 | 8 | ### Features inherited from GoTools: 9 | - Auto-Completion: with gocode 10 | - Auto-Format: with gofmt or goimports 11 | - Go-to Definition: with guru 12 | - Rename: with gorename 13 | 14 | ### Features removed(replaced) from GoTools: 15 | - Go syntax highlight: replaced by sublime's native support 16 | - Go build & test system: replaced by "[Golang Build](https://github.com/golang/sublime-build)" (search in package-control) 17 | - GoTools Settings: replaced by "[golangconfig](https://github.com/golang/sublime-config)" 18 | - godef support: only use guru for "Go to definition" 19 | 20 | ### Features added: 21 | - Auto-Lint: with golint or govet 22 | - Doc tips over selected text or cursor: with godoc 23 | 24 | ### Usage 25 | - **Step 1**: Install golang and go tools by yourself: gocode, goimports, guru, gorename, golint, and make sure the `$GOPATH/bin` is added into `$PATH` (Or you can set them in Golang's global "Settings - User" later). 26 | 27 | ```bash 28 | go get -u -v github.com/nsf/gocode 29 | # OR mdempsky/gocode for better performance 30 | go get -u -v github.com/mdempsky/gocode 31 | 32 | go get -u -v github.com/golang/lint/golint 33 | go get -u -v golang.org/x/tools/cmd/guru 34 | go get -u -v golang.org/x/tools/cmd/goimports 35 | go get -u -v golang.org/x/tools/cmd/gorename 36 | ``` 37 | 38 | - **Step 2**: Search and install "Golang Tools Integration" from package control. 39 | - **Step 3(optional)**: Configure the Settings for `golang` and your project following the `golang.sublime-settings` and `ExampleProject.sublime-project`. Typically, the full features of 'guru' need use the configuration of the project. 40 | 41 | ### Tips 42 | - If you want to trigger auto-completion after ".", you can add below into Settings - Syntax specific - User (a.k.a. User/Go.sublime-settings): 43 | 44 | ```json 45 | { 46 | "auto_complete_triggers": [{"selector": "source.go - string - comment - constant.numeric", "characters": "."}] 47 | } 48 | ``` 49 | 50 | - If you want to ignore auto-completion when in comments, constant strings, and numbers, you can add below into Settings - Syntax specific - User (a.k.a. User/Go.sublime-settings): 51 | 52 | ```json 53 | { 54 | "auto_complete_selector": "meta.tag - punctuation.definition.tag.begin, source - comment - string - constant.numeric" 55 | } 56 | ``` 57 | 58 | ------------------ 59 | 60 | **Introduction below comes from GoTools project** 61 | 62 | # GoTools 63 | 64 | GoTools is a [Go programming language](http://www.golang.org) plugin for [Sublime Text 3](http://www.sublimetext.com) inspired by [vim-go](https://github.com/fatih/vim-go). Rather than attempting to reinvent various supporting IDE components, it provides integration with existing community-supported tools. 65 | 66 | ## Features 67 | 68 | * Jump to symbol/declaration using [guru](https://godoc.org/golang.org/x/tools/cmd/guru) 69 | * Format and syntax check on save, including gutter marks (using [gofmt](https://golang.org/cmd/gofmt/)) 70 | * Autocompletion (using [gocode](https://github.com/nsf/gocode)) 71 | * Build and test integration 72 | * Source analysis (using [guru](https://godoc.org/golang.org/x/tools/cmd/guru)) 73 | * Identifier renaming (using [gorename](https://godoc.org/golang.org/x/tools/cmd/gorename)) 74 | * Improved syntax support (borrowed from [GoSublime](https://github.com/DisposaBoy/GoSublime)) 75 | 76 | ### Prerequisites 77 | 78 | GoTools will attempt to find all external Go tools (`guru`, `gofmt`, `gocode`, etc.) using `GOPATH` and `GOROOT` (not `PATH`). If you don't have these binaries, use `go get` to install them: 79 | 80 | go get -u -v github.com/nsf/gocode 81 | go get -u -v golang.org/x/tools/cmd/goimports 82 | go get -u -v golang.org/x/tools/cmd/guru 83 | go get -u -v golang.org/x/tools/cmd/gorename 84 | 85 | GoTools is only tested with Go 1.4. Note that `gofmt` is now included with the Go distribution, and any `gofmt` installed to `GOPATH` is likely from an old Go version and should probably be removed. 86 | 87 | ### Installing 88 | 89 | The easiest way to install GoTools is to use [Package Control](https://packagecontrol.io). Simply install Package Control, and then install the "GoTools" package using `Package Control: Install Package` from the command palette. 90 | 91 | If you want to install GoTools manually, download [the latest release](https://github.com/ironcladlou/GoTools/releases) and extract it to `~/.config/sublime-text-3/Packages/GoTools` on Linux, or `~/Library/Application\ Support/Sublime\ Text\ 3/Packages/GoTools` on OSX. 92 | 93 | ### Configuring GoTools 94 | 95 | Create a GoTools settings file through the Sublime Text preferences menu at `Package Settings -> GoTools -> Settings -> User`. 96 | 97 | [Default settings](GoTools.sublime-settings) are provided and can be accessed through the Sublime Text preferences menu at `Package Settings -> GoTools -> Settings - Default`. Each option is documented in the settings file itself. 98 | 99 | ### Configuring Your Project 100 | 101 | Create a `GoTools` settings key in a Sublime Text `.sublime-project` file (through the menu at `Project -> Edit Project`). 102 | 103 | A documented [example project file](ExampleProject.sublime-project) is provided. 104 | 105 | ## Using GoTools 106 | 107 | **NOTE:** Most GoTools commands are available via the Sublime Text command palette. Open the palette when viewing a Go source file and search for "GoTools" to see what's available. 108 | 109 | Many of the build commands are also available via the context menu. 110 | 111 | #### Format on Save 112 | 113 | GoTools will format Go source buffers each time they're saved. To disable automatic formatting, set `format_on_save` in your [GoTools settings](GoTools.sublime-settings). 114 | 115 | Here's an example key binding which formats a source file when `++f` is pressed: 116 | 117 | ```json 118 | {"keys": ["ctrl+alt+f"], "command": "gotools_format"} 119 | ``` 120 | 121 | By default [gofmt](https://golang.org/cmd/gofmt/) is used for formatting. To change the backend, set `format_backend` in your [GoTools settings](GoTools.sublime-settings). [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) is also available, as well as the option to first run goimports, then gofmt. This third option is useful when you want the automatic import resolution as well as the simplification (`-s`) feature from gofmt at the same time. 122 | 123 | #### Go to Definition 124 | 125 | GoTools provides a `gotools_goto_def` Sublime Text command which will jump to the symbol definition at the cursor. 126 | 127 | Here's an example key binding which will go to a definition when `` is pressed: 128 | 129 | ```json 130 | {"keys": ["ctrl+g"], "command": "gotools_goto_def"} 131 | ``` 132 | 133 | Here's an example `sublime-mousemap` entry which will go to a definition using `+`: 134 | 135 | ```json 136 | {"button": "button1", "count": 1, "modifiers": ["ctrl"], "command": "gotools_goto_def"} 137 | ``` 138 | 139 | #### Autocomplete 140 | 141 | GoTools integrates the Sublime Text autocompletion engine with [gocode](https://github.com/nsf/gocode). 142 | 143 | Here's an example key binding which autocompletes when `+` is pressed: 144 | 145 | ```json 146 | {"keys": ["ctrl+space"], "command": "auto_complete"} 147 | ``` 148 | 149 | When suggestions are available, a specially formatted suggestion list will appear, including type information for each suggestion. 150 | 151 | To disable autocompletion integration, set `autocomplete` in your [GoTools settings](GoTools.sublime-settings). 152 | 153 | #### Builds 154 | 155 | GoTools integrates the Sublime Text build system with `go build`. 156 | 157 | Activate the GoTools build system from the Sublime Text menu by selecting it from `Tools -> Build System`. If the build system is set to `Automatic`, GoTools will be automatically used for builds when editing Go source files. 158 | 159 | There are several ways to perform a build: 160 | 161 | * From the Sublime Text menu at `Tools -> Build` 162 | * A key bound to the `build` command 163 | * The command palette, as `Build: Build` 164 | 165 | A "Clean Build" command variant is also provided which recursively deletes all `GOPATH/pkg` directory contents prior to executing the build as usual. 166 | 167 | Build results are placed in the Sublime Text build output panel which can be toggled with a command such as: 168 | 169 | ```json 170 | { "keys" : ["ctrl+m"], "command" : "show_panel" , "args" : {"panel": "output.exec", "toggle": true}}, 171 | ``` 172 | 173 | Here's an example key binding which runs a build when `+b` is pressed: 174 | 175 | ```json 176 | { "keys": ["ctrl+b"], "command": "build" }, 177 | ``` 178 | 179 | Here's an example key binding which runs "Clean Build" when `++b` is pressed: 180 | 181 | ```json 182 | { "keys": ["ctrl+alt+b"], "command": "build", "args": {"variant": "Clean Build"}}, 183 | ``` 184 | 185 | #### Tests 186 | 187 | GoTools integrates the Sublime Text build system with `go test`. 188 | 189 | GoTools attempts to "do what you mean" depending on context. For instance, when using "Run Test at Cursor" in a test file which requires an `integration` Go build tag, GoTools will notice this and automatically add `-tags integration` to the test execution. 190 | 191 | The following GoTools build variants are available: 192 | 193 | Variant | Description 194 | --------------------------|------------- 195 | Run Tests | Discovers test packages based on the `project_package` and `test_packages` settings relative to the project `gopath` and executes them. 196 | Run Test at Cursor | Runs a single test method at or surrounding the cursor. 197 | Run Current Package Tests | Runs tests for the package containing the current file. 198 | Run Tagged Tests | Like "Run Tests" but for the packages specified in the `tagged_packages` setting. 199 | Run Last Test | Runs the last test variant that was executed. 200 | 201 | Test results are placed in the built-in Sublime Text build output panel which can be toggled with a command such as: 202 | 203 | ```json 204 | { "keys" : ["ctrl+m"], "command" : "show_panel" , "args" : {"panel": "output.exec", "toggle": true}}, 205 | ``` 206 | 207 | Here's an example key binding which runs the test at the cursor when `++t` is pressed: 208 | 209 | ```json 210 | { "keys": ["ctrl+alt+t"], "command": "build", "args": {"variant": "Run Test at Cursor"}}, 211 | ``` 212 | 213 | Replace `variant` in the command with any variant name from the preceding table for other bindings. 214 | 215 | #### Oracle Analysis (experimental) 216 | 217 | GoTools integrates Sublime Text with [guru](https://godoc.org/golang.org/x/tools/cmd/guru). Oracle is invoked with the `gotools_guru` Sublime Text command. 218 | 219 | Here's an example which runs the guru "implements" command when `` is pressed: 220 | 221 | ```json 222 | { "keys" : ["ctrl+alt+i"], "command" : "gotools_guru" , "args" : {"command": "implements"}}, 223 | ``` 224 | 225 | The following guru operations are supported as arguments to the `gotools_guru` command: 226 | 227 | Command | Notes 228 | -------------|------ 229 | callers | Slow on large codebases. 230 | callees | Slow on large codebases. 231 | callstack | Slow on large codebases. 232 | describe | 233 | freevars | Requires a selection. 234 | implements | 235 | peers | 236 | referrers | 237 | 238 | Oracle results are placed in a Sublime Text output panel which can be toggled with a command such as: 239 | 240 | ```json 241 | { "keys" : ["ctrl+m"], "command" : "show_panel" , "args" : {"panel": "output.gotools_guru", "toggle": true}}, 242 | ``` 243 | 244 | #### Rename (experimental) 245 | 246 | GoTools provides a `gotools_rename` command supported by [gorename](https://godoc.org/golang.org/x/tools/cmd/gorename) which supports type-safe renaming of identifiers. 247 | 248 | When the `gotools_rename` command is executed, an input panel labeled `Go rename:` will appear. Rename results are placed in a Sublime Text output panel which can be toggled with a command such as: 249 | 250 | ```json 251 | { "keys" : ["ctrl+m"], "command" : "show_panel" , "args" : {"panel": "output.gotools_rename", "toggle": true}}, 252 | ``` 253 | 254 | **Important**: The `gorename` tool writes files in-place with no option for a dry-run. Changes might be destructive, and the tool is known to have bugs. 255 | 256 | 257 | ### Gocode Caveats 258 | 259 | **Important**: Using gocode support will modify the `lib-path` setting in the gocode daemon. The change will affect all clients, including other Sublime Text sessions, Vim instances, etc. Don't use this setting if you're concerned about interoperability with other tools which integrate with gocode. 260 | 261 | Some projects make use of a dependency isolation tool such as [Godep](https://github.com/tools/godep), and many projects use some sort of custom build script. Additionally, gocode uses a client/server architecture, and at present relies on a global server-side setting to resolve Go package paths for suggestion computation. By default, gocode will only search `GOROOT` and `GOPATH/pkg` for packages, which may be insufficient if the project compiles source to multiple `GOPATH` entries (such as `Godeps/_workspace/pkg`). 262 | 263 | With such a project, to get the best suggestions from gocode, it's necessary to configure the gocode daemon prior to client suggestion requests to inform gocode about the locations of compiled packages for the project. 264 | 265 | GoTools will infer the correct gocode `lib-path` by constructing a path which incorporates all project `GOPATH` entries. 266 | 267 | ### GoSublime Caveats 268 | 269 | Installing GoTools alongside GoSublime isn't tested or supported, so YMMV. 270 | -------------------------------------------------------------------------------- /dependencies.json: -------------------------------------------------------------------------------- 1 | { 2 | "*": { 3 | "*": [ 4 | "shellenv", 5 | "golangconfig" 6 | ] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /golang.sublime-settings: -------------------------------------------------------------------------------- 1 | { 2 | 3 | // Format source files each time they're saved. 4 | "format_on_save": true, 5 | 6 | // A formatting backend (must be either 'gofmt', 'goimports' or 'both'). 7 | // The 'both' option will first run 'goimports' then 'gofmt' 8 | "format_backend": "gofmt", 9 | 10 | // Lint source files each time they're saved. 11 | "lint_on_save": true, 12 | 13 | // A lintting backend (must be either 'govet' or 'golint' or 'both'). 14 | // The 'both' option will first run 'go vet' then 'golint' 15 | "lint_backend": "golint", 16 | 17 | // Enable gocode autocompletion. 18 | "autocomplete": true, 19 | 20 | // Enable GoTools debugging output to the Sublime console. 21 | "debug_enabled": false, 22 | 23 | // Enable gocode client mode, github.com/mdempsky/gocode needed 24 | "gocode_client_mode": false, 25 | 26 | // adds to the guru_scope the current package of the the working file 27 | "guru_use_current_package": true 28 | } 29 | -------------------------------------------------------------------------------- /gotools_doc.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import os 4 | 5 | from .gotools_util import Buffers 6 | from .gotools_util import GoBuffers 7 | from .gotools_util import Logger 8 | from .gotools_util import ToolRunner 9 | 10 | class GotoolsDocCommand(sublime_plugin.TextCommand): 11 | def is_enabled(self): 12 | return GoBuffers.is_go_source(self.view) 13 | 14 | def run(self, edit): 15 | command = "go" 16 | args = ["doc"] 17 | 18 | if self.view.sel()[0].size() > 0: 19 | args.append(self.view.substr(self.view.sel()[0])) 20 | else: 21 | args.append(self.view.substr(self.view.word(self.view.sel()[0].begin()))) 22 | 23 | stdout, stderr, rc = ToolRunner.run(self.view, command, args) 24 | output_view = self.view.window().create_output_panel('godoc-output') 25 | 26 | if rc == 0: 27 | output_view.run_command('append', {'characters': stdout}) 28 | else: 29 | output_view.run_command('append', {'characters': stderr}) 30 | 31 | self.view.window().run_command("show_panel", {"panel": "output.godoc-output"}) 32 | -------------------------------------------------------------------------------- /gotools_format.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import re 4 | import os 5 | import golangconfig 6 | 7 | from .gotools_util import Buffers 8 | from .gotools_util import GoBuffers 9 | from .gotools_util import Logger 10 | from .gotools_util import ToolRunner 11 | 12 | class GotoolsFormatOnSave(sublime_plugin.EventListener): 13 | def on_pre_save(self, view): 14 | if not GoBuffers.is_go_source(view): return 15 | if not golangconfig.setting_value("format_on_save")[0]: return 16 | view.run_command('gotools_format') 17 | 18 | class GotoolsFormat(sublime_plugin.TextCommand): 19 | def is_enabled(self): 20 | return GoBuffers.is_go_source(self.view) 21 | 22 | def run(self, edit): 23 | command = "" 24 | args = [] 25 | if golangconfig.setting_value("format_backend")[0] == "gofmt": 26 | command = "gofmt" 27 | args = ["-e", "-s"] 28 | elif golangconfig.setting_value("format_backend")[0] in ["goimports", "both"] : 29 | command = "goimports" 30 | args = ["-e"] 31 | 32 | stdout, stderr, rc = ToolRunner.run(self.view, command, args, stdin=Buffers.buffer_text(self.view)) 33 | 34 | # Clear previous syntax error marks 35 | self.view.erase_regions("mark") 36 | 37 | if rc == 2: 38 | # Show syntax errors and bail 39 | self.show_syntax_errors(stderr) 40 | Logger.err = True 41 | return 42 | 43 | if rc != 0: 44 | # Ermmm... 45 | Logger.log("unknown gofmt error (" + str(rc) + ") stderr:\n" + stderr) 46 | return 47 | 48 | if golangconfig.setting_value("format_backend")[0] == "both": 49 | command = "gofmt" 50 | args = ["-e", "-s"] 51 | stdout, stderr, rc = ToolRunner.run(self.view, command, args, stdin=stdout.encode('utf-8')) 52 | 53 | # Clear previous syntax error marks 54 | self.view.erase_regions("mark") 55 | 56 | if rc == 2: 57 | # Show syntax errors and bail 58 | self.show_syntax_errors(stderr) 59 | Logger.err = True 60 | return 61 | 62 | if rc != 0: 63 | # Ermmm... 64 | Logger.log("unknown gofmt error (" + str(rc) + ") stderr:\n" + stderr) 65 | return 66 | 67 | # Everything's good, hide the syntax error panel 68 | self.view.window().run_command("hide_panel", {"panel": "output.gotools_syntax_errors"}) 69 | 70 | # Remember the viewport position. When replacing the buffer, Sublime likes to jitter the 71 | # viewport around for some reason. 72 | self.prev_viewport_pos = self.view.viewport_position() 73 | 74 | # Replace the buffer with gofmt output. 75 | self.view.replace(edit, sublime.Region(0, self.view.size()), stdout) 76 | 77 | # Restore the viewport on the main GUI thread (which is the only way this works). 78 | sublime.set_timeout(self.restore_viewport, 0) 79 | 80 | Logger.err = False 81 | 82 | def restore_viewport(self): 83 | self.view.set_viewport_position(self.prev_viewport_pos, False) 84 | 85 | # Display an output panel containing the syntax errors, and set gutter marks for each error. 86 | def show_syntax_errors(self, stderr): 87 | output_view = self.view.window().create_output_panel('gotools_syntax_errors') 88 | output_view.set_scratch(True) 89 | output_view.settings().set("result_file_regex","^(.*):(\d+):(\d+):(.*)$") 90 | output_view.run_command("select_all") 91 | output_view.run_command("right_delete") 92 | 93 | syntax_output = stderr.replace("", self.view.file_name()) 94 | output_view.run_command('append', {'characters': syntax_output}) 95 | self.view.window().run_command("show_panel", {"panel": "output.gotools_syntax_errors"}) 96 | 97 | marks = [] 98 | for error in stderr.splitlines(): 99 | match = re.match("((?:[a-zA-Z]:)?.*):(\d+):(\d+):", error) 100 | if not match or not match.group(2): 101 | Logger.log("skipping unrecognizable error:\n" + error + "\nmatch:" + str(match)) 102 | continue 103 | 104 | row = int(match.group(2)) 105 | pt = self.view.text_point(row-1, 0) 106 | Logger.log("adding mark at row " + str(row)) 107 | marks.append(sublime.Region(pt)) 108 | 109 | if len(marks) > 0: 110 | self.view.add_regions("mark", marks, "mark", "dot", sublime.DRAW_STIPPLED_UNDERLINE | sublime.PERSISTENT) 111 | -------------------------------------------------------------------------------- /gotools_goto_def.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import os 4 | import json 5 | 6 | from .gotools_util import Buffers 7 | from .gotools_util import GoBuffers 8 | from .gotools_util import Logger 9 | from .gotools_util import ToolRunner 10 | 11 | class GotoolsGotoDef(sublime_plugin.TextCommand): 12 | def is_enabled(self): 13 | return GoBuffers.is_go_source(self.view) 14 | 15 | # Capture mouse events so users can click on a definition. 16 | def want_event(self): 17 | return True 18 | 19 | def run(self, edit, event=None): 20 | filename, row, col, offset, offset_end = Buffers.location_at_cursor(self.view) 21 | 22 | try: 23 | file, row, col = self.get_guru_location(filename, offset) 24 | except Exception as e: 25 | Logger.status(str(e)) 26 | return 27 | 28 | if not os.path.isfile(file): 29 | Logger.log("WARN: file indicated by guru not found: " + file) 30 | Logger.status("guru failed: Please enable debugging and check console log") 31 | return 32 | 33 | Logger.log("opening definition at " + file + ":" + str(row) + ":" + str(col)) 34 | w = self.view.window() 35 | new_view = w.open_file(file + ':' + str(row) + ':' + str(col), sublime.ENCODED_POSITION) 36 | group, index = w.get_view_index(new_view) 37 | if group != -1: 38 | w.focus_group(group) 39 | 40 | def get_guru_location(self, filename, offset): 41 | args = ["-json", "definition", filename+":#"+str(offset)] 42 | 43 | location, err, rc = ToolRunner.run(self.view, "guru", args) 44 | if rc != 0: 45 | raise Exception("no definition found") 46 | 47 | Logger.log("guru output:\n" + location.rstrip()) 48 | 49 | # cut anything prior to the first path separator 50 | location = json.loads(location.rstrip())['objpos'].rsplit(":", 2) 51 | 52 | if len(location) != 3: 53 | raise Exception("no definition found") 54 | 55 | file = location[0] 56 | row = int(location[1]) 57 | col = int(location[2]) 58 | 59 | return [file, row, col] 60 | -------------------------------------------------------------------------------- /gotools_guru.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import os 4 | import golangconfig 5 | 6 | from .gotools_util import Buffers 7 | from .gotools_util import GoBuffers 8 | from .gotools_util import Logger 9 | from .gotools_util import ToolRunner 10 | 11 | class GotoolsGuruCommand(sublime_plugin.TextCommand): 12 | def is_enabled(self): 13 | return GoBuffers.is_go_source(self.view) 14 | 15 | def run(self, edit, command=None): 16 | if not command: 17 | Logger.log("command is required") 18 | return 19 | 20 | filename, row, col, offset, offset_end = Buffers.location_at_cursor(self.view) 21 | if command == "freevars": 22 | pos = filename+":#"+str(offset)+","+"#"+str(offset_end) 23 | else: 24 | pos = filename+":#"+str(offset) 25 | 26 | # Build up a package scope contaning all packages the user might have 27 | # configured. 28 | package_scope = [] 29 | project_package = golangconfig.setting_value("project_package", view=self.view)[0] 30 | if project_package: 31 | if not golangconfig.setting_value("build_packages")[0]: 32 | package_scope.append(project_package) 33 | else: 34 | for p in golangconfig.setting_value("build_packages", view=self.view)[0]: 35 | package_scope.append(os.path.join(project_package, p)) 36 | 37 | # add local package to guru scope 38 | if golangconfig.setting_value("guru_use_current_package")[0]: 39 | current_file_path = os.path.realpath(os.path.dirname(self.view.file_name())) 40 | toolpath, env = golangconfig.subprocess_info('guru', ['GOPATH', 'PATH'], view=self.view) 41 | GOPATH = os.path.realpath(env["GOPATH"]) 42 | GOPATH = os.path.join(GOPATH,"src") 43 | local_package = os.path.relpath(current_file_path, GOPATH) 44 | if sublime.platform() == 'windows': 45 | local_package = local_package.replace('\\', '/') 46 | Logger.status("GOPATH: "+GOPATH) 47 | Logger.status("local_package: "+local_package) 48 | package_scope.append(local_package) 49 | 50 | sublime.active_window().run_command("hide_panel", {"panel": "output.gotools_guru"}) 51 | self.do_plain_guru(command, pos, package_scope) 52 | 53 | def do_plain_guru(self, mode, pos, package_scope=[], regex="^(.*):(\d+):(\d+):(.*)$"): 54 | Logger.status("running guru "+mode+"...") 55 | args = [] 56 | if len(package_scope) > 0: 57 | args = ["-scope", ",".join(package_scope)] 58 | 59 | args = args + [mode, pos] 60 | output, err, rc = ToolRunner.run(self.view, "guru", args, timeout=60) 61 | Logger.log("guru "+mode+" output: " + output.rstrip()) 62 | 63 | if rc != 0: 64 | print("GoTools: Guru error:\n%s" % err) 65 | Logger.status("guru call failed (" + str(rc) +")") 66 | return 67 | Logger.status("guru "+mode+" finished") 68 | 69 | panel = self.view.window().create_output_panel('gotools_guru') 70 | panel.set_scratch(True) 71 | panel.settings().set("result_file_regex", regex) 72 | panel.run_command("select_all") 73 | panel.run_command("right_delete") 74 | panel.run_command('append', {'characters': output}) 75 | self.view.window().run_command("show_panel", {"panel": "output.gotools_guru"}) 76 | -------------------------------------------------------------------------------- /gotools_lint.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import re 4 | import os 5 | import golangconfig 6 | 7 | from .gotools_util import Buffers 8 | from .gotools_util import GoBuffers 9 | from .gotools_util import Logger 10 | from .gotools_util import ToolRunner 11 | 12 | class GotoolsLintOnSave(sublime_plugin.EventListener): 13 | def on_post_save(self, view): 14 | if not GoBuffers.is_go_source(view): return 15 | if not golangconfig.setting_value("lint_on_save")[0]: return 16 | if Logger.err: return 17 | view.run_command('gotools_lint') 18 | 19 | class GotoolsLint(sublime_plugin.TextCommand): 20 | def is_enabled(self): 21 | return GoBuffers.is_go_source(self.view) 22 | 23 | def run(self, edit): 24 | if golangconfig.setting_value("lint_backend")[0] == "golint": 25 | self.run_golint() 26 | elif golangconfig.setting_value("lint_backend")[0] == "govet": 27 | self.run_govet() 28 | elif golangconfig.setting_value("lint_backend")[0] == "both": 29 | rc = self.run_govet() 30 | if rc != 1: 31 | self.run_golint() 32 | else: 33 | sublime.error_message("Must choose a linter: govet or golint or both") 34 | return 35 | 36 | def run_govet(self): 37 | command = "go" 38 | args = ["vet"] 39 | 40 | stdout, stderr, rc = ToolRunner.run(self.view, command, args, timeout=60, cwd=os.path.dirname(self.view.file_name())) 41 | 42 | # Clear previous syntax error marks 43 | self.view.erase_regions("mark") 44 | 45 | if rc == 1: 46 | # Show syntax errors and bail 47 | self.show_syntax_errors(stderr, "^(.*?):(\d+):*(\d*):(.*)$") 48 | elif rc != 0: 49 | # Ermmm... 50 | Logger.log("unknown govet error (" + str(rc) + ") stderr:\n" + stderr) 51 | else: 52 | # Everything's good, hide the syntax error panel 53 | self.view.window().run_command("hide_panel", {"panel": "output.gotools_syntax_errors"}) 54 | 55 | return rc 56 | 57 | def run_golint(self): 58 | command = "golint" 59 | args = [self.view.file_name()] 60 | 61 | stdout, stderr, rc = ToolRunner.run(self.view, command, args, timeout=60) 62 | 63 | # Clear previous syntax error marks 64 | self.view.erase_regions("mark") 65 | 66 | if rc != 0: 67 | # Ermmm... 68 | Logger.log("unknown golint error (" + str(rc) + ") stderr:\n" + stderr) 69 | return 70 | 71 | if stdout != "": 72 | # Show syntax errors and bail 73 | self.show_syntax_errors(stdout, "^(.*):(\d+):(\d+):(.*)$") 74 | else: 75 | # Everything's good, hide the syntax error panel 76 | self.view.window().run_command("hide_panel", {"panel": "output.gotools_syntax_errors"}) 77 | 78 | # Display an output panel containing the syntax errors, and set gutter marks for each error. 79 | def show_syntax_errors(self, stderr, file_regex,): 80 | output_view = self.view.window().create_output_panel('gotools_syntax_errors') 81 | output_view.set_scratch(True) 82 | output_view.settings().set("result_file_regex", file_regex) 83 | output_view.run_command("select_all") 84 | output_view.run_command("right_delete") 85 | 86 | marks = [] 87 | for error in stderr.splitlines(): 88 | match = re.match(file_regex, error) 89 | if not match or not match.group(2): 90 | Logger.log("skipping unrecognizable error: " + error) 91 | continue 92 | 93 | syntax_output = error.replace(match.group(1), self.view.file_name()) 94 | output_view.run_command('append', {'characters': syntax_output}) 95 | 96 | row = int(match.group(2)) 97 | pt = self.view.text_point(row-1, 0) 98 | Logger.log("adding mark at row " + str(row)) 99 | marks.append(sublime.Region(pt)) 100 | 101 | self.view.window().run_command("show_panel", {"panel": "output.gotools_syntax_errors"}) 102 | 103 | if len(marks) > 0: 104 | self.view.add_regions("mark", marks, "mark", "dot", sublime.DRAW_STIPPLED_UNDERLINE | sublime.PERSISTENT) 105 | -------------------------------------------------------------------------------- /gotools_rename.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import os 4 | 5 | from .gotools_util import Buffers 6 | from .gotools_util import GoBuffers 7 | from .gotools_util import Logger 8 | from .gotools_util import ToolRunner 9 | 10 | class GotoolsRenameCommand(sublime_plugin.TextCommand): 11 | def is_enabled(self): 12 | return GoBuffers.is_go_source(self.view) 13 | 14 | def run(self, edit): 15 | initial_text = self.view.substr(self.view.word(self.view.sel()[0].begin())) 16 | self.view.window().show_input_panel("Go rename:", initial_text, self.do_rename, None, None) 17 | 18 | def do_rename(self, name): 19 | filename, _row, _col, offset, _offset_end = Buffers.location_at_cursor(self.view) 20 | args = [ 21 | "-offset", "{file}:#{offset}".format(file=filename, offset=offset), 22 | "-to", name, 23 | "-v" 24 | ] 25 | output, err, exit = ToolRunner.run(self.view, "gorename", args, timeout=15) 26 | 27 | if exit != 0: 28 | print("GoTools: Gorename error:\n%s" % err) 29 | Logger.status("rename failed ({0}): {1}".format(exit, err)) 30 | return 31 | Logger.status("renamed symbol to {name}".format(name=name)) 32 | 33 | panel = self.view.window().create_output_panel('gotools_rename') 34 | panel.set_scratch(True) 35 | # TODO: gorename isn't emitting line numbers, so to get clickable 36 | # referenced we'd need to process each line to append ':N' to make the 37 | # sublime regex work properly (line number is a required capture group). 38 | panel.settings().set("result_file_regex", "^\t(.*\.go)$") 39 | panel.run_command("select_all") 40 | panel.run_command("right_delete") 41 | panel.run_command('append', {'characters': err}) 42 | self.view.window().run_command("show_panel", {"panel": "output.gotools_rename"}) 43 | -------------------------------------------------------------------------------- /gotools_suggestions.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import sublime_plugin 3 | import json 4 | import os 5 | import golangconfig 6 | 7 | from .gotools_util import Buffers 8 | from .gotools_util import GoBuffers 9 | from .gotools_util import Logger 10 | from .gotools_util import ToolRunner 11 | 12 | class GotoolsSuggestions(sublime_plugin.EventListener): 13 | CLASS_SYMBOLS = { 14 | "func": "ƒ", 15 | "var": "ν", 16 | "type": "ʈ", 17 | "package": "ρ" 18 | } 19 | 20 | def on_query_completions(self, view, prefix, locations): 21 | if not GoBuffers.is_go_source(view): return 22 | if not golangconfig.setting_value("autocomplete")[0]: return 23 | 24 | gocodeFlag = ["-f=json", "-sock=none"] if golangconfig.setting_value("gocode_client_mode")[0] else ["-f=json"] 25 | suggestionsJsonStr, stderr, rc = ToolRunner.run(view, "gocode", gocodeFlag + ["autocomplete", view.file_name(), str(locations[0])], stdin=Buffers.buffer_text(view)) 26 | 27 | suggestionsJson = json.loads(suggestionsJsonStr) 28 | 29 | Logger.log("DEBUG: gocode output: " + suggestionsJsonStr) 30 | 31 | if rc != 0: 32 | Logger.status("no completions found: " + str(e)) 33 | return [] 34 | 35 | if len(suggestionsJson) > 0: 36 | return ([GotoolsSuggestions.build_suggestion(j) for j in suggestionsJson[1]], sublime.INHIBIT_WORD_COMPLETIONS) 37 | else: 38 | return [] 39 | 40 | @staticmethod 41 | def build_suggestion(json): 42 | label = '{0: <30.30} {1: <40.40} {2}'.format( 43 | json["name"], 44 | json["type"], 45 | GotoolsSuggestions.CLASS_SYMBOLS.get(json["class"], "?")) 46 | return (label, json["name"]) 47 | -------------------------------------------------------------------------------- /gotools_util.py: -------------------------------------------------------------------------------- 1 | import sublime 2 | import os 3 | import re 4 | import platform 5 | import subprocess 6 | import time 7 | import golangconfig 8 | 9 | def plugin_loaded(): 10 | # check if the dependent gotools have been installed 11 | tools = ["guru", "golint", "gocode", "gorename", "goimports"] 12 | 13 | missed_tools = "" 14 | for t in tools: 15 | if golangconfig.executable_path(t)[0] == None: 16 | missed_tools += '"%s" ' % t 17 | 18 | if missed_tools != "": 19 | print("\nGoTools Warning: %scan't be found in executable path.\nPlease \"go get\" them (Refer to README.md).\n" % missed_tools) 20 | 21 | class Buffers(): 22 | @staticmethod 23 | def offset_at_row_col(view, row, col): 24 | point = view.text_point(row, col) 25 | select_region = sublime.Region(0, point) 26 | string_region = view.substr(select_region) 27 | buffer_region = bytearray(string_region, encoding="utf8") 28 | offset = len(buffer_region) 29 | return offset 30 | 31 | @staticmethod 32 | def buffer_text(view): 33 | file_text = sublime.Region(0, view.size()) 34 | return view.substr(file_text).encode('utf-8') 35 | 36 | @staticmethod 37 | def offset_at_cursor(view): 38 | begin_row, begin_col = view.rowcol(view.sel()[0].begin()) 39 | end_row, end_col = view.rowcol(view.sel()[0].end()) 40 | 41 | return (Buffers.offset_at_row_col(view, begin_row, begin_col), Buffers.offset_at_row_col(view, end_row, end_col)) 42 | 43 | @staticmethod 44 | def location_at_cursor(view): 45 | row, col = view.rowcol(view.sel()[0].begin()) 46 | offsets = Buffers.offset_at_cursor(view) 47 | return (view.file_name(), row, col, offsets[0], offsets[1]) 48 | 49 | @staticmethod 50 | def location_for_event(view, event): 51 | pt = view.window_to_text((event["x"], event["y"])) 52 | row, col = view.rowcol(pt) 53 | offset = Buffers.offset_at_row_col(view, row, col) 54 | return (view.file_name(), row, col, offset) 55 | 56 | class GoBuffers(): 57 | @staticmethod 58 | def func_name_at_cursor(view): 59 | func_regions = view.find_by_selector('meta.function') 60 | 61 | func_name = "" 62 | for r in func_regions: 63 | if r.contains(Buffers.offset_at_cursor(view)[0]): 64 | lines = view.substr(r).splitlines() 65 | match = re.match('func.*(Test.+)\(', lines[0]) 66 | if match and match.group(1): 67 | func_name = match.group(1) 68 | break 69 | 70 | return func_name 71 | 72 | @staticmethod 73 | def is_go_source(view): 74 | return view.score_selector(0, 'source.go') != 0 75 | 76 | class Logger(): 77 | err = False 78 | @staticmethod 79 | def log(msg): 80 | if golangconfig.setting_value('debug_enabled')[0]: 81 | print("GoTools: DEBUG: {0}".format(msg)) 82 | 83 | @staticmethod 84 | def error(msg): 85 | print("GoTools: ERROR: {0}".format(msg)) 86 | 87 | @staticmethod 88 | def status(msg): 89 | sublime.status_message("GoTools: " + msg) 90 | 91 | class ToolRunner(): 92 | @staticmethod 93 | def run(view, tool, args=[], stdin=None, timeout=5, cwd=None): 94 | 95 | toolpath, env = golangconfig.subprocess_info(tool, ['GOPATH', 'PATH'], view=view) 96 | cmd = [toolpath] + args 97 | try: 98 | Logger.log("spawning process...") 99 | Logger.log("\tcommand: " + " ".join(cmd)) 100 | # Logger.log("\tenvironment: " + str(env)) 101 | 102 | # Hide popups on Windows 103 | si = None 104 | if platform.system() == "Windows": 105 | si = subprocess.STARTUPINFO() 106 | si.dwFlags |= subprocess.STARTF_USESHOWWINDOW 107 | 108 | start = time.time() 109 | p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, startupinfo=si, cwd=cwd) 110 | stdout, stderr = p.communicate(input=stdin, timeout=timeout) 111 | p.wait(timeout=timeout) 112 | elapsed = round(time.time() - start) 113 | Logger.log("process returned ({0}) in {1} seconds".format(str(p.returncode), str(elapsed))) 114 | stderr = stderr.decode("utf-8") 115 | if len(stderr) > 0: 116 | Logger.log("stderr:\n{0}".format(stderr)) 117 | return stdout.decode("utf-8"), stderr, p.returncode 118 | except subprocess.CalledProcessError as e: 119 | raise 120 | -------------------------------------------------------------------------------- /messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "install": "README.md", 3 | "1.0.1": "CHANGELOG", 4 | "1.0.2": "CHANGELOG", 5 | "1.0.3": "CHANGELOG", 6 | "1.0.4": "CHANGELOG", 7 | "1.0.5": "CHANGELOG", 8 | "1.0.6": "CHANGELOG", 9 | "1.0.7": "CHANGELOG", 10 | "1.0.8": "CHANGELOG", 11 | "1.0.9": "CHANGELOG" 12 | } 13 | --------------------------------------------------------------------------------