├── .editorconfig ├── .github ├── issue_template.md ├── locker.yml └── needs_more_info.yml ├── .gitignore ├── .travis.yml ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── CHANGELOG.md ├── Go-latest.vsix ├── LICENSE ├── README.md ├── README_old.md ├── go-commands.md ├── images ├── goIcon.png ├── gutter-green.svg └── gutter-red.svg ├── package-lock.json ├── package.json ├── scripts └── terminateProcess.sh ├── snippets └── go.json ├── src-ignored ├── debugAdapter │ ├── .vscode │ │ └── launch.json │ ├── Readme.md │ └── goDebug.ts ├── goBrowsePackage.ts ├── goCheck.ts ├── goCover.ts ├── goDebugConfiguration.ts ├── goImpl.ts ├── goLiveErrors.ts └── goTest.ts ├── src-vscode-mock ├── activate.ts ├── cli.ts ├── commands.ts ├── config.spec.ts ├── config.ts ├── debug.ts ├── diagnostics.ts ├── languages.ts ├── logger.ts ├── lsp-client.ts ├── lsp-connection.ts ├── lsp-server.spec.ts ├── lsp-server.ts ├── mock-error.ts ├── providers.ts ├── snippet-proposal-provider.ts ├── test-utils.ts ├── text-document.ts ├── text-editor.ts ├── thenable.ts ├── types.ts ├── utils.ts ├── vscode-extension-telemetry.ts ├── vscode.ts ├── window.ts └── workspace.ts ├── src ├── diffUtils.ts ├── goBaseCodelens.ts ├── goBuild.ts ├── goCodeAction.ts ├── goDeclaration.ts ├── goExtraInfo.ts ├── goFillStruct.ts ├── goFormat.ts ├── goGenerateTests.ts ├── goGetPackage.ts ├── goImplementations.ts ├── goImport.ts ├── goInstall.ts ├── goInstallTools.ts ├── goLint.ts ├── goMain.ts ├── goMode.ts ├── goModifytags.ts ├── goOutline.ts ├── goPackages.ts ├── goPath.ts ├── goPlayground.ts ├── goReferences.ts ├── goReferencesCodelens.ts ├── goRename.ts ├── goRunTestCodelens.ts ├── goSignature.ts ├── goStatus.ts ├── goSuggest.ts ├── goSymbol.ts ├── goVet.ts ├── testUtils.ts └── util.ts ├── test-lsp └── .gitignore ├── test ├── fixtures │ ├── buildTags │ │ └── hello.go │ ├── completions │ │ └── unimportedPkgs.go │ ├── diffTestData │ │ ├── file1.go │ │ └── file2.go │ ├── errorsTest │ │ └── errors.go │ ├── fillStruct │ │ ├── golden_1.go │ │ ├── golden_2.go │ │ ├── input_1.go │ │ └── input_2.go │ ├── generatetests │ │ └── generatetests.go │ ├── gogetdocTestData │ │ └── test.go │ ├── importTest │ │ ├── groupImports.go │ │ ├── noimports.go │ │ └── singleImports.go │ ├── linterTest │ │ ├── linter_1.go │ │ └── linter_2.go │ ├── sample_test.go │ └── test.go ├── go.test.ts └── index.ts ├── thirdpartynotices.txt ├── tsconfig.json ├── tslint.json ├── typings ├── diff.d.ts ├── json-rpc2.d.ts └── lodash.d.ts └── vscode-api.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Tab indentation 7 | [*] 8 | indent_style = tab 9 | indent_size = 4 10 | 11 | # The indent size used in the `package.json` file cannot be changed 12 | # https://github.com/npm/npm/pull/3180#issuecomment-16336516 13 | [{.travis.yml,npm-shrinkwrap.json,package.json}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | If you have a question, please ask it on https://gitter.im/Microsoft/vscode-go or on the VSCode channel in Gophers Slack(https://invite.slack.golangbridge.org/) 2 | 3 | If you have issues around GOPATH, read https://github.com/Microsoft/vscode-go/wiki/GOPATH-in-the-VS-Code-Go-extension first. 4 | 5 | If you have issues debugging your Go programs 6 | - read https://github.com/Microsoft/vscode-go/wiki/Debugging-Go-code-using-VS-Code first. 7 | - set `"trace": "verbose"` and share the resulting logs in the debug console 8 | 9 | If this is a bug report, please share 10 | - a sample code which can be used to reproduce the issue 11 | - any Go related settings you might have added/changed 12 | 13 | Steps to Reproduce: 14 | 15 | 1. 16 | 2. 17 | 18 | 19 | -------------------------------------------------------------------------------- /.github/locker.yml: -------------------------------------------------------------------------------- 1 | { 2 | daysAfterClose: 45, 3 | daysSinceLastUpdate: 3, 4 | perform: true 5 | } -------------------------------------------------------------------------------- /.github/needs_more_info.yml: -------------------------------------------------------------------------------- 1 | { 2 | daysUntilClose: 7, 3 | needsMoreInfoLabel: 'needs more info', 4 | perform: true, 5 | closeComment: 'This issue has been closed automatically because it needs more information and has not had recent activity. Thank you for your contributions.' 6 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test 4 | .DS_Store 5 | yarn-error.log 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7.x 5 | - 1.8.x 6 | - 1.9.x 7 | - 1.10.x 8 | - tip 9 | 10 | sudo: false 11 | 12 | os: 13 | - osx 14 | - linux 15 | 16 | before_install: 17 | - if [ $TRAVIS_OS_NAME == "linux" ]; then 18 | export DISPLAY=:99.0; 19 | sh -e /etc/init.d/xvfb start; 20 | sudo apt-get update && sudo apt-get install -y libsecret-1-0; 21 | fi 22 | 23 | install: 24 | - TRAVIS_NODE_VERSION="6"; 25 | # Clear out whatever version of NVM Travis has as it is old. 26 | - rm -rf ~/.nvm; 27 | # Grab NVM. 28 | - git clone https://github.com/creationix/nvm.git ~/.nvm; 29 | # Checkout the latest stable tag. 30 | # Note that you can just hardcode a preferred version here. 31 | - (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`); 32 | # Install the desired version of Node 33 | - source ~/.nvm/nvm.sh; 34 | - nvm install $TRAVIS_NODE_VERSION; 35 | - npm install 36 | - npm run vscode:prepublish 37 | - go get -u -v github.com/nsf/gocode 38 | - go get -u -v github.com/rogpeppe/godef 39 | - if [[ "$(go version)" =~ "go version go1.5" ]]; then echo hello; else go get -u -v github.com/zmb3/gogetdoc; fi 40 | - if [[ "$(go version)" =~ "go version go1.5" ]]; then echo cannot get golint; else go get -u -v github.com/golang/lint/golint; fi 41 | - go get -u -v github.com/ramya-rao-a/go-outline 42 | - go get -u -v sourcegraph.com/sqs/goreturns 43 | - go get -u -v golang.org/x/tools/cmd/gorename 44 | - go get -u -v github.com/uudashr/gopkgs/cmd/gopkgs 45 | - go get -u -v github.com/acroca/go-symbols 46 | - go get -u -v github.com/cweill/gotests/... 47 | - go get -u -v github.com/haya14busa/goplay/cmd/goplay 48 | - go get -u -v github.com/davidrjenni/reftools/cmd/fillstruct 49 | - GO15VENDOREXPERIMENT=1 50 | - if [[ "$(go version)" =~ "go version go1.5" ]]; then echo skipping gometalinter; else go get -u -v github.com/alecthomas/gometalinter; fi 51 | - if [[ "$(go version)" =~ "go version go1.5" ]]; then echo skipping gometalinter; else gometalinter --install; fi 52 | 53 | script: 54 | - npm run lint 55 | - npm test --silent 56 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Mocha Tests", 8 | "program": "${workspaceFolder}/node_modules/mocha/bin/_mocha", 9 | "args": [ 10 | "--timeout", 11 | "999999", 12 | "--colors", 13 | "--reporter", 14 | "spec", 15 | "./out/**/*.spec.js" 16 | ], 17 | "internalConsoleOptions": "openOnSessionStart", 18 | }, 19 | { 20 | "type": "node", 21 | "request": "attach", 22 | "name": "Attach", 23 | "port": 9229 24 | }, 25 | { 26 | "name": "Launch Extension", 27 | "type": "extensionHost", 28 | "request": "launch", 29 | // path to VSCode executable 30 | "runtimeExecutable": "${execPath}", 31 | "args": [ "--extensionDevelopmentPath=${workspaceRoot}" ], 32 | "stopOnEntry": false, 33 | "sourceMaps": true, 34 | "outFiles": ["${workspaceRoot}/out/**/*.js"], 35 | "preLaunchTask": "npm" 36 | }, 37 | { 38 | "name": "Launch as server", 39 | "type": "node2", 40 | "request": "launch", 41 | "program": "${workspaceRoot}/out/src/debugAdapter/goDebug.js", 42 | "args": [ "--server=4712" ], 43 | "sourceMaps": true, 44 | "outFiles": ["${workspaceRoot}/out/**/*.js"] 45 | }, 46 | { 47 | "name": "Launch Tests", 48 | "type": "extensionHost", 49 | "request": "launch", 50 | "runtimeExecutable": "${execPath}", 51 | // the workspace path should be GOPATH 52 | "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test", "env.GOPATH" ], 53 | "stopOnEntry": false, 54 | "sourceMaps": true, 55 | "outFiles": ["${workspaceRoot}/out/**/*.js"], 56 | "preLaunchTask": "npm" 57 | } 58 | ], 59 | "compounds": [ 60 | { 61 | "name": "Extension + Debug server", 62 | "configurations": ["Launch Extension", "Launch as server"] 63 | } 64 | ] 65 | } 66 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false, 5 | "typings": false 6 | }, 7 | "search.exclude": { 8 | "**/node_modules": true, 9 | "**/bower_components": true, 10 | "out/": true 11 | }, 12 | "editor.insertSpaces": false 13 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // Available variables which can be used inside of strings. 2 | // ${workspaceRoot}: the root folder of the team 3 | // ${file}: the current opened file 4 | // ${fileBasename}: the current opened file's basename 5 | // ${fileDirname}: the current opened file's dirname 6 | // ${fileExtname}: the current opened file's extension 7 | // ${cwd}: the current working directory of the spawned process 8 | 9 | // A task runner that calls the Typescript compiler (tsc) and 10 | // compiles the extension. 11 | { 12 | "version": "0.1.0", 13 | 14 | // we want to run npm 15 | "command": "npm", 16 | 17 | // the command is a shell script 18 | "isShellCommand": true, 19 | 20 | // show the output window only if unrecognized errors occur. 21 | "showOutput": "silent", 22 | 23 | // we run the custom script "compile" as defined in package.json 24 | "args": ["run", "compile"], 25 | 26 | // The tsc compiler is started in watching mode 27 | "isBackground": true, 28 | 29 | // use the standard tsc in watch mode problem matcher to find compile problems in the output. 30 | "problemMatcher": "$tsc-watch" 31 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | src/**/* 2 | typings/**/* 3 | .vscode/**/* 4 | tsconfig.json 5 | .gitignore 6 | node_modules/fs-extra -------------------------------------------------------------------------------- /Go-latest.vsix: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theia-ide/go-language-server/efa486bc607cc80540fb3910f3f1e45058faff52/Go-latest.vsix -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | vscode-go 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) Microsoft Corporation 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Go Language Server based on the Go Extension for Visual Studio Code 2 | 3 | Wraps the VSCode Go extension from Microsoft into a language server, such that 4 | its functionality can be reused with other LSP-aware clients. 5 | 6 | In the first iteration we will mock VSCode APIs or simulate their behavior 7 | backed by an LSP. We will maintain this as a fork of the original repository 8 | such that we can easily pick up incoming improvements of that by just rebasing. 9 | Once we got more confidence, we'd probably refactor the VSCode specific parts 10 | away. 11 | 12 | [Original README.md](README_old.MD). 13 | 14 | ## Mismatches and Challenges 15 | 16 | - There is no such thing as the `activeTextEditor` in LSP. For services that 17 | have a `TextDocumentItem`, we set it before calling the service impl, but for 18 | other services, e.g. `executeCommand` we cannot make sure that they are 19 | performed on the correct document. 20 | - We have to use/mock/adapt a lot of global variables 21 | 22 | ## Prerequisites 23 | 24 | Make sure the `go` command is available from your path and that the GOPATH 25 | environment variable points to where your go packages are installed. Some go 26 | packages are necessary: 27 | 28 | ```shell 29 | go get -u -v github.com/nsf/gocode 30 | go get -u -v github.com/uudashr/gopkgs/cmd/gopkgs 31 | go get -u -v github.com/ramya-rao-a/go-outline 32 | go get -u -v github.com/acroca/go-symbols 33 | go get -u -v golang.org/x/tools/cmd/guru 34 | go get -u -v golang.org/x/tools/cmd/gorename 35 | go get -u -v github.com/fatih/gomodifytags 36 | go get -u -v github.com/haya14busa/goplay/cmd/goplay 37 | go get -u -v github.com/josharian/impl 38 | go get -u -v github.com/tylerb/gotype-live 39 | go get -u -v github.com/rogpeppe/godef 40 | go get -u -v golang.org/x/tools/cmd/godoc 41 | go get -u -v github.com/zmb3/gogetdoc 42 | go get -u -v golang.org/x/tools/cmd/goimports 43 | go get -u -v sourcegraph.com/sqs/goreturns 44 | go get -u -v github.com/golang/lint/golint 45 | go get -u -v github.com/cweill/gotests/... 46 | go get -u -v github.com/alecthomas/gometalinter 47 | go get -u -v honnef.co/go/tools/... 48 | go get -u -v github.com/sourcegraph/go-langserver 49 | go get -u -v github.com/derekparker/delve/cmd/dlv 50 | go get -u -v github.com/davidrjenni/reftools/cmd/fillstruc 51 | ``` 52 | 53 | Note: this list comes from 54 | [here](https://github.com/theia-ide/go-language-server/blob/master/src/goInstallTools.ts#L20-L42). 55 | 56 | ## Build and usage 57 | 58 | 1. Run `npm install` to install the package dependencies. 59 | 2. Run `npm run compile` to compile the Typescript to Javascript. 60 | 3. Run the server with `node ./out/src-vscode-mock/cli.js --stdio`. 61 | -------------------------------------------------------------------------------- /go-commands.md: -------------------------------------------------------------------------------- 1 | # State of command migration 2 | 3 | |command|title|description|status/comment| 4 | |----|----|-----|----| 5 | | `go.gopath` | Current GOPATH | See the currently set GOPATH. | ready |  6 | | `go.add.tags` | Add Tags To Struct Fields | Add tags configured in go.addTags setting to selected struct using gomodifytags | ready | 7 | | `go.remove.tags` | Remove Tags From Struct Fields | Remove tags configured in go.removeTags setting from selected struct using gomodifytags | ready | 8 | | `go.impl.cursor` | Generate Interface Stubs | Generates method stub for implementing the provided interface and inserts at the cursor. | missing: cannot prompt for interface name | 9 | | `go.test.cursor` | Test Function At Cursor | Runs a unit test at the cursor. | missing: depends on goCover, decorations not supported | 10 | | `go.benchmark.cursor` | Benchmark Function At Cursor | Runs a benchmark at the cursor. | missing: depends on goCover, decorations not supported | 11 | | `go.test.package` | Test Package | Runs all unit tests in the package of the current file. | missing: depends on goCover, decorations not supported | 12 | | `go.test.file` | Test File | Runs all unit tests in the current file. | missing: depends on goCover, decorations not supported | 13 | | `go.test.workspace` | Test All Packages In Workspace | Runs all unit tests from all packages in the current workspace. | missing: depends on goCover, decorations not supported | 14 | | `go.test.previous` | Test Previous | Re-runs the last executed test. | missing: depends on goCover, decorations not supported | 15 | | `go.test.coverage` | Toggle Test Coverage In Current Package | Displays test coverage in the current package. | missing: depends on goCover, decorations not supported | 16 | | `go.tests.showOutput` ||| 17 | | `go.import.add` | Add Import | Add an import declaration | ready | 18 | | `go.tools.install` | Install/Update Tools | install/update the required go packages | ready | 19 | | `go.browse.packages` | Browse Packages | Browse packages and Go files inside the packages. | missing: cannot open editors | 20 | | `go.test.generate.package` | Generate Unit Tests For Package | Generates unit tests for the current package | ready | 21 | | `go.test.generate.file` | Generate Unit Tests For File | Generates unit tests for the current file | ready | 22 | | `go.test.generate.function` | Generate Unit Tests For Function | Generates unit tests for the selected function in the current file | ready | 23 | | `go.toggle.test.file` | Toggle Test File | Toggles between file in current active editor and the corresponding test file. | missing: cannot open editors | 24 | | `go.debug.startSession`||| 25 | | `go.show.commands` | Show All Commands... | Shows all commands from the Go extension in the quick pick | [display bug](https://github.com/theia-ide/theia-go-extension/issues/4)| 26 | | `go.get.package` | Get Package | Run `go get -v` on the package on the current line. | test when oputput channel is available | 27 | | `go.playground` | Run on Go Playground | Upload the current selection or file to the Go Playground | untested | 28 | | `go.lint.package` | Lint Current Package | Run linter in the package of the current file. | ready | 29 | | `go.lint.workspace` | Lint Workspace | Run linter in the current workspace. | ready | 30 | | `go.vet.package` | Vet Current Package | Run go vet in the package of the current file. | ready | 31 | | `go.vet.workspace` | Vet Workspace | Run go vet in the current workspace. | ready | 32 | | `go.build.package` | Build Current Package | Build the package of the current file. | ready | 33 | | `go.build.workspace` | Build Workspace | Build the current workspace. | ready | 34 | | `go.install.package` | Install Current Package | Install the current package. | ready | 35 | | `go.promptforinstall`||| 36 | -------------------------------------------------------------------------------- /images/goIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theia-ide/go-language-server/efa486bc607cc80540fb3910f3f1e45058faff52/images/goIcon.png -------------------------------------------------------------------------------- /images/gutter-green.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /images/gutter-red.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /scripts/terminateProcess.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | terminateTree() { 4 | for cpid in $(/usr/bin/pgrep -P $1); do 5 | terminateTree $cpid 6 | done 7 | kill -9 $1 > /dev/null 2>&1 8 | } 9 | 10 | for pid in $*; do 11 | terminateTree $pid 12 | done 13 | -------------------------------------------------------------------------------- /src-ignored/debugAdapter/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "debug-debugger", 6 | "type": "node", 7 | "request": "launch", 8 | "program": "${workspaceRoot}/../../out/src/debugAdapter/goDebug.js", 9 | "stopOnEntry": false, 10 | "args": [ "--server=4711" ], 11 | "sourceMaps": true, 12 | "outDir": "${workspaceRoot}/../../out/src/debugAdapter" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /src-ignored/debugAdapter/Readme.md: -------------------------------------------------------------------------------- 1 | # Go Debug Adapter 2 | 3 | This code runs in a seperate Node process spawned by Code when launch the 'go' type in the Code debugger. 4 | 5 | ## Debugging the debugger 6 | 7 | The ideal setup involves three instances of Code: 8 | 9 | 1. Open the `vscode-go` folder in one instance. Choose the `Launch Extension` debug target and hit F5 to launch a second instance. 10 | 2. In the second instance, open the Go application you'd like to test against. In that instance, create a new Go debug target pointing at the program you want to debug, and add `"debugServer": 4711` in the root of the configuration. 11 | 3. Open another instance of Code on the `vscode-go/src/debugAdapter` folder. In that instance hit F5 to launch the debug adapter in server mode under the debugger. 12 | 4. Go back to the second instance and hit F5 to debug your Go code. Debuggers from the other two Code windows are attached to the Go debug adapter and the Go language integation respectively, so you can set breakpoints, step through code and inspect state as needed. -------------------------------------------------------------------------------- /src-ignored/goBrowsePackage.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | // [TypeFox] unused, as we can't open editors 9 | 10 | import vscode = require('vscode'); 11 | import cp = require('child_process'); 12 | import { getGoRuntimePath } from './goPath'; 13 | import path = require('path'); 14 | import { getAllPackages } from './goPackages'; 15 | import { getImportPath, getCurrentGoPath } from './util'; 16 | 17 | export function browsePackages() { 18 | let selectedText = ''; 19 | const editor = vscode.window.activeTextEditor; 20 | if (editor) { 21 | let selection = editor.selection; 22 | if (!selection.isEmpty) { 23 | // get selected text 24 | selectedText = editor.document.getText(selection); 25 | } else { 26 | // if selection is empty, then get the whole line the cursor is currently on. 27 | selectedText = editor.document.lineAt(selection.active.line).text; 28 | } 29 | selectedText = getImportPath(selectedText) || selectedText.trim(); 30 | } 31 | 32 | showPackageFiles(selectedText, true); 33 | } 34 | 35 | function showPackageFiles(pkg: string, showAllPkgsIfPkgNotFound: boolean) { 36 | const goRuntimePath = getGoRuntimePath(); 37 | if (!goRuntimePath) { 38 | return vscode.window.showErrorMessage('Could not locate Go path. Make sure you have Go installed'); 39 | } 40 | 41 | if (!pkg && showAllPkgsIfPkgNotFound) { 42 | return showPackageList(); 43 | } 44 | 45 | const env = Object.assign({}, process.env, { GOPATH: getCurrentGoPath() }); 46 | 47 | cp.execFile(goRuntimePath, ['list', '-f', '{{.Dir}}:{{.GoFiles}}:{{.TestGoFiles}}:{{.XTestGoFiles}}', pkg], { env }, (err, stdout, stderr) => { 48 | if (!stdout || stdout.indexOf(':') === -1) { 49 | if (showAllPkgsIfPkgNotFound) { 50 | return showPackageList(); 51 | } 52 | 53 | return; 54 | } 55 | 56 | let matches = stdout && stdout.match(/(.*):\[(.*)\]:\[(.*)\]:\[(.*)\]/); 57 | if (matches) { 58 | let dir = matches[1]; 59 | let files = matches[2] ? matches[2].split(' ') : []; 60 | let testfiles = matches[3] ? matches[3].split(' ') : []; 61 | let xtestfiles = matches[4] ? matches[4].split(' ') : []; 62 | files = files.concat(testfiles); 63 | files = files.concat(xtestfiles); 64 | vscode.window.showQuickPick(files, { placeHolder: `Below are Go files from ${pkg}` }).then(file => { 65 | // if user abandoned list, file will be null and path.join will error out. 66 | // therefore return. 67 | if (!file) return; 68 | 69 | vscode.workspace.openTextDocument(path.join(dir, file)).then(document => { 70 | vscode.window.showTextDocument(document); 71 | }); 72 | }); 73 | } 74 | }); 75 | } 76 | 77 | function showPackageList() { 78 | getAllPackages().then(pkgMap => { 79 | const pkgs: string[] = Array.from(pkgMap.keys()); 80 | if (pkgs.length === 0) { 81 | return vscode.window.showErrorMessage('Could not find packages. Ensure `gopkgs -format {{.Name}};{{.ImportPath}}` runs successfully.'); 82 | } 83 | 84 | vscode 85 | .window 86 | .showQuickPick(pkgs.sort(), { placeHolder: 'Select a package to browse' }) 87 | .then(pkgFromDropdown => { 88 | if (!pkgFromDropdown) return; 89 | showPackageFiles(pkgFromDropdown, false); 90 | }); 91 | }); 92 | } 93 | -------------------------------------------------------------------------------- /src-ignored/goCheck.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | // [TypeFox] unused as it relies on goCover (which needs text editor decorators) 9 | 10 | import vscode = require('vscode'); 11 | import path = require('path'); 12 | import os = require('os'); 13 | import { getGoRuntimePath } from './goPath'; 14 | import { getCoverage } from './goCover'; 15 | import { outputChannel, diagnosticsStatusBarItem } from './goStatus'; 16 | import { goTest } from './testUtils'; 17 | import { ICheckResult } from './util'; 18 | import { goLint } from './goLint'; 19 | import { goVet } from './goVet'; 20 | import { goBuild } from './goBuild'; 21 | 22 | let statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 23 | statusBarItem.command = 'go.test.showOutput'; 24 | 25 | export function removeTestStatus(e: vscode.TextDocumentChangeEvent) { 26 | if (e.document.isUntitled) { 27 | return; 28 | } 29 | statusBarItem.hide(); 30 | statusBarItem.text = ''; 31 | } 32 | 33 | export function notifyIfGeneratedFile(e: vscode.TextDocumentChangeEvent) { 34 | if (e.document.isUntitled || e.document.languageId !== 'go') { 35 | return; 36 | } 37 | 38 | if (e.document.lineAt(0).text.match(/^\/\/ Code generated .* DO NOT EDIT\.$/)) { 39 | vscode.window.showWarningMessage('This file seems to be generated. DO NOT EDIT.'); 40 | } 41 | } 42 | 43 | export function check(fileUri: vscode.Uri, goConfig: vscode.WorkspaceConfiguration): Promise { 44 | diagnosticsStatusBarItem.hide(); 45 | outputChannel.clear(); 46 | let runningToolsPromises = []; 47 | let cwd = path.dirname(fileUri.fsPath); 48 | let goRuntimePath = getGoRuntimePath(); 49 | 50 | if (!goRuntimePath) { 51 | vscode.window.showInformationMessage('Cannot find "go" binary. Update PATH or GOROOT appropriately'); 52 | return Promise.resolve([]); 53 | } 54 | 55 | let testPromise: Thenable; 56 | let tmpCoverPath; 57 | let runTest = () => { 58 | if (testPromise) { 59 | return testPromise; 60 | } 61 | 62 | let buildFlags = goConfig['testFlags'] || goConfig['buildFlags'] || []; 63 | 64 | let args = [...buildFlags]; 65 | if (goConfig['coverOnSave']) { 66 | tmpCoverPath = path.normalize(path.join(os.tmpdir(), 'go-code-cover')); 67 | args = ['-coverprofile=' + tmpCoverPath, ...buildFlags]; 68 | } 69 | 70 | testPromise = goTest({ 71 | goConfig: goConfig, 72 | dir: cwd, 73 | flags: args, 74 | background: true 75 | }); 76 | return testPromise; 77 | }; 78 | 79 | if (!!goConfig['buildOnSave'] && goConfig['buildOnSave'] !== 'off') { 80 | runningToolsPromises.push(goBuild(fileUri, goConfig, goConfig['buildOnSave'] === 'workspace')); 81 | } 82 | 83 | if (!!goConfig['testOnSave']) { 84 | statusBarItem.show(); 85 | statusBarItem.text = 'Tests Running'; 86 | runTest().then(success => { 87 | if (statusBarItem.text === '') { 88 | return; 89 | } 90 | if (success) { 91 | statusBarItem.text = 'Tests Passed'; 92 | } else { 93 | statusBarItem.text = 'Tests Failed'; 94 | } 95 | }); 96 | } 97 | 98 | if (!!goConfig['lintOnSave'] && goConfig['lintOnSave'] !== 'off') { 99 | runningToolsPromises.push(goLint(fileUri, goConfig, goConfig['lintOnSave'] === 'workspace')); 100 | } 101 | 102 | if (!!goConfig['vetOnSave'] && goConfig['vetOnSave'] !== 'off') { 103 | runningToolsPromises.push(goVet(fileUri, goConfig, goConfig['vetOnSave'] === 'workspace')); 104 | } 105 | 106 | if (!!goConfig['coverOnSave']) { 107 | runTest().then(success => { 108 | if (!success) { 109 | return []; 110 | } 111 | // FIXME: it's not obvious that tmpCoverPath comes from runTest() 112 | return getCoverage(tmpCoverPath); 113 | }); 114 | } 115 | 116 | return Promise.all(runningToolsPromises).then(function (resultSets) { 117 | return [].concat.apply([], resultSets); 118 | }); 119 | } 120 | -------------------------------------------------------------------------------- /src-ignored/goCover.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | *--------------------------------------------------------*/ 4 | 5 | 'use strict'; 6 | 7 | // [TypeFox] unused, as we can't map text editor decorators 8 | 9 | import vscode = require('vscode'); 10 | import path = require('path'); 11 | import os = require('os'); 12 | import fs = require('fs'); 13 | import { showTestOutput, goTest } from './testUtils'; 14 | import rl = require('readline'); 15 | 16 | export let coveredGutter; 17 | export let uncoveredGutter; 18 | 19 | let coveredHighLight = vscode.window.createTextEditorDecorationType({ 20 | // Green 21 | backgroundColor: 'rgba(64,128,64,0.5)', 22 | isWholeLine: false 23 | }); 24 | let uncoveredHighLight = vscode.window.createTextEditorDecorationType({ 25 | // Red 26 | backgroundColor: 'rgba(128,64,64,0.5)', 27 | isWholeLine: false 28 | }); 29 | let coverageFiles = {}; 30 | 31 | interface CoverageFile { 32 | filename: string; 33 | uncoveredRange: vscode.Range[]; 34 | coveredRange: vscode.Range[]; 35 | } 36 | 37 | function clearCoverage() { 38 | applyCoverage(true); 39 | coverageFiles = {}; 40 | } 41 | 42 | export function initGoCover(ctx: vscode.ExtensionContext) { 43 | coveredGutter = vscode.window.createTextEditorDecorationType({ 44 | // Gutter green 45 | gutterIconPath: ctx.asAbsolutePath('images/gutter-green.svg') 46 | }); 47 | uncoveredGutter = vscode.window.createTextEditorDecorationType({ 48 | // Gutter red 49 | gutterIconPath: ctx.asAbsolutePath('images/gutter-red.svg') 50 | }); 51 | } 52 | 53 | export function removeCodeCoverage(e: vscode.TextDocumentChangeEvent) { 54 | let editor = vscode.window.visibleTextEditors.find((value, index, obj) => { 55 | return value.document === e.document; 56 | }); 57 | if (!editor) { 58 | return; 59 | } 60 | for (let filename in coverageFiles) { 61 | let found = editor.document.uri.fsPath.endsWith(filename); 62 | // Check for file again if outside the $GOPATH. 63 | if (!found && filename.startsWith('_')) { 64 | found = editor.document.uri.fsPath.endsWith(filename.slice(1)); 65 | } 66 | if (found) { 67 | highlightCoverage(editor, coverageFiles[filename], true); 68 | delete coverageFiles[filename]; 69 | } 70 | } 71 | } 72 | 73 | export function toggleCoverageCurrentPackage() { 74 | let editor = vscode.window.activeTextEditor; 75 | if (!editor) { 76 | vscode.window.showInformationMessage('No editor is active.'); 77 | return; 78 | } 79 | 80 | // If current file has highlights, then remove coverage, else add coverage 81 | for (let filename in coverageFiles) { 82 | let found = editor.document.uri.fsPath.endsWith(filename); 83 | // Check for file again if outside the $GOPATH. 84 | if (!found && filename.startsWith('_')) { 85 | found = editor.document.uri.fsPath.endsWith(filename.slice(1)); 86 | } 87 | if (found) { 88 | clearCoverage(); 89 | return; 90 | } 91 | } 92 | 93 | let goConfig = vscode.workspace.getConfiguration('go', editor.document.uri); 94 | let cwd = path.dirname(editor.document.uri.fsPath); 95 | 96 | let buildFlags = goConfig['testFlags'] || goConfig['buildFlags'] || []; 97 | let tmpCoverPath = path.normalize(path.join(os.tmpdir(), 'go-code-cover')); 98 | let args = ['-coverprofile=' + tmpCoverPath, ...buildFlags]; 99 | return goTest({ 100 | goConfig: goConfig, 101 | dir: cwd, 102 | flags: args, 103 | background: true 104 | }).then(success => { 105 | if (!success) { 106 | showTestOutput(); 107 | return []; 108 | } 109 | return getCoverage(tmpCoverPath, true); 110 | }); 111 | } 112 | 113 | export function getCodeCoverage(editor: vscode.TextEditor) { 114 | if (!editor) { 115 | return; 116 | } 117 | for (let filename in coverageFiles) { 118 | if (editor.document.uri.fsPath.endsWith(filename)) { 119 | highlightCoverage(editor, coverageFiles[filename], false); 120 | } 121 | } 122 | } 123 | 124 | function applyCoverage(remove: boolean = false) { 125 | Object.keys(coverageFiles).forEach(filename => { 126 | let file = coverageFiles[filename]; 127 | // Highlight lines in current editor. 128 | vscode.window.visibleTextEditors.forEach((value, index, obj) => { 129 | let found = value.document.fileName.endsWith(filename); 130 | // Check for file again if outside the $GOPATH. 131 | if (!found && filename.startsWith('_')) { 132 | found = value.document.fileName.endsWith(filename.slice(1)); 133 | } 134 | if (found) { 135 | highlightCoverage(value, file, remove); 136 | } 137 | return found; 138 | }); 139 | }); 140 | } 141 | 142 | function highlightCoverage(editor: vscode.TextEditor, file: CoverageFile, remove: boolean) { 143 | let cfg = vscode.workspace.getConfiguration('go', editor.document.uri); 144 | let coverageOptions = cfg['coverageOptions']; 145 | let coverageDecorator = cfg['coverageDecorator']; 146 | 147 | editor.setDecorations(coveredGutter, []); 148 | editor.setDecorations(coveredHighLight, []); 149 | editor.setDecorations(uncoveredGutter, []); 150 | editor.setDecorations(uncoveredHighLight, []); 151 | 152 | if (remove) { 153 | return; 154 | } 155 | 156 | if (coverageOptions === 'showCoveredCodeOnly' || coverageOptions === 'showBothCoveredAndUncoveredCode') { 157 | editor.setDecorations(coverageDecorator === 'gutter' ? coveredGutter : coveredHighLight, file.coveredRange); 158 | } 159 | 160 | if (coverageOptions === 'showUncoveredCodeOnly' || coverageOptions === 'showBothCoveredAndUncoveredCode') { 161 | editor.setDecorations(coverageDecorator === 'gutter' ? uncoveredGutter : uncoveredHighLight, file.uncoveredRange); 162 | } 163 | } 164 | 165 | export function getCoverage(coverProfilePath: string, showErrOutput: boolean = false): Promise { 166 | return new Promise((resolve, reject) => { 167 | try { 168 | // Clear existing coverage files 169 | clearCoverage(); 170 | 171 | let lines = rl.createInterface({ 172 | input: fs.createReadStream(coverProfilePath), 173 | output: undefined 174 | }); 175 | 176 | lines.on('line', function (data: string) { 177 | // go test coverageprofile generates output: 178 | // filename:StartLine.StartColumn,EndLine.EndColumn Hits IsCovered 179 | // The first line will be "mode: set" which will be ignored 180 | let fileRange = data.match(/([^:]+)\:([\d]+)\.([\d]+)\,([\d]+)\.([\d]+)\s([\d]+)\s([\d]+)/); 181 | if (!fileRange) return; 182 | 183 | let coverage = coverageFiles[fileRange[1]] || { coveredRange: [], uncoveredRange: [] }; 184 | let range = new vscode.Range( 185 | // Start Line converted to zero based 186 | parseInt(fileRange[2]) - 1, 187 | // Start Column converted to zero based 188 | parseInt(fileRange[3]) - 1, 189 | // End Line converted to zero based 190 | parseInt(fileRange[4]) - 1, 191 | // End Column converted to zero based 192 | parseInt(fileRange[5]) - 1 193 | ); 194 | // If is Covered 195 | if (parseInt(fileRange[7]) === 1) { 196 | coverage.coveredRange.push({ range }); 197 | } 198 | // Not Covered 199 | else { 200 | coverage.uncoveredRange.push({ range }); 201 | } 202 | coverageFiles[fileRange[1]] = coverage; 203 | }); 204 | lines.on('close', function (data) { 205 | applyCoverage(); 206 | resolve([]); 207 | }); 208 | } catch (e) { 209 | vscode.window.showInformationMessage(e.msg); 210 | reject(e); 211 | } 212 | }); 213 | } 214 | -------------------------------------------------------------------------------- /src-ignored/goDebugConfiguration.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // [TypeFox] unused as we don't support debugging 4 | 5 | import vscode = require('vscode'); 6 | import { getCurrentGoPath } from './util'; 7 | 8 | export class GoDebugConfigurationProvider implements vscode.DebugConfigurationProvider { 9 | 10 | public provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, token?: vscode.CancellationToken): vscode.DebugConfiguration[] { 11 | return [ 12 | { 13 | 'name': 'Launch', 14 | 'type': 'go', 15 | 'request': 'launch', 16 | 'mode': 'debug', 17 | 'remotePath': '', 18 | 'port': 2345, 19 | 'host': '127.0.0.1', 20 | 'program': '${fileDirname}', 21 | 'env': {}, 22 | 'args': [], 23 | 'showLog': true 24 | } 25 | ]; 26 | } 27 | 28 | public resolveDebugConfiguration?(folder: vscode.WorkspaceFolder | undefined, debugConfiguration: vscode.DebugConfiguration, token?: vscode.CancellationToken): vscode.DebugConfiguration { 29 | 30 | const gopath = getCurrentGoPath(folder ? folder.uri : null); 31 | 32 | if (!debugConfiguration || !debugConfiguration.request) { // if 'request' is missing interpret this as a missing launch.json 33 | let activeEditor = vscode.window.activeTextEditor; 34 | if (!activeEditor || activeEditor.document.languageId !== 'go') { 35 | return; 36 | } 37 | 38 | return { 39 | 'name': 'Launch', 40 | 'type': 'go', 41 | 'request': 'launch', 42 | 'mode': 'debug', 43 | 'program': activeEditor.document.fileName, 44 | 'env': { 45 | 'GOPATH': gopath 46 | } 47 | }; 48 | } 49 | 50 | if (!debugConfiguration['env']) { 51 | debugConfiguration['env'] = { 'GOPATH': gopath }; 52 | } else if (!debugConfiguration['env']['GOPATH']) { 53 | debugConfiguration['env']['GOPATH'] = gopath; 54 | } 55 | 56 | return debugConfiguration; 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src-ignored/goImpl.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | // [TypeFox] unsused as we cannot prompt for the interface 9 | 10 | import vscode = require('vscode'); 11 | import cp = require('child_process'); 12 | import { getBinPath, getToolsEnvVars } from './util'; 13 | import { promptForMissingTool } from './goInstallTools'; 14 | 15 | interface GoImplInput { 16 | receiver: string; 17 | interface: string; 18 | } 19 | 20 | // Supports only passing interface, see TODO in implCursor to finish 21 | const inputRegex = /^(\w+\ \*?\w+\ )?([\w./]+)$/; 22 | 23 | export function implCursor() { 24 | let cursor = vscode.window.activeTextEditor.selection; 25 | return vscode.window.showInputBox({ 26 | placeHolder: 'f *File io.Closer', 27 | prompt: 'Enter receiver and interface to implement.' 28 | }).then(implInput => { 29 | if (typeof implInput === 'undefined') { 30 | return; 31 | } 32 | const matches = implInput.match(inputRegex); 33 | if (!matches) { 34 | vscode.window.showInformationMessage(`Not parsable input: ${implInput}`); 35 | return; 36 | } 37 | 38 | // TODO: automatically detect type name at cursor 39 | // if matches[1] is undefined then detect receiver type 40 | // take first character and use as receiver name 41 | 42 | let input: GoImplInput = { 43 | receiver: matches[1], 44 | interface: matches[2] 45 | }; 46 | 47 | runGoImpl(input, cursor.start); 48 | }); 49 | } 50 | 51 | function runGoImpl(input: GoImplInput, insertPos: vscode.Position) { 52 | let goimpl = getBinPath('impl'); 53 | let p = cp.execFile(goimpl, [input.receiver, input.interface], {env: getToolsEnvVars()}, (err, stdout, stderr) => { 54 | if (err && (err).code === 'ENOENT') { 55 | promptForMissingTool('impl'); 56 | return; 57 | } 58 | 59 | if (err) { 60 | vscode.window.showInformationMessage(`Cannot stub interface: ${stderr}`); 61 | return; 62 | } 63 | 64 | vscode.window.activeTextEditor.edit(editBuilder => { 65 | editBuilder.insert(insertPos, stdout); 66 | }); 67 | }); 68 | p.stdin.end(); 69 | } -------------------------------------------------------------------------------- /src-ignored/goLiveErrors.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // [TypeFox] not used in favor of goBuild 4 | import vscode = require('vscode'); 5 | import { getBinPath, getToolsEnvVars } from './util'; 6 | import cp = require('child_process'); 7 | import path = require('path'); 8 | import { promptForMissingTool } from './goInstallTools'; 9 | import { errorDiagnosticCollection } from './goMain'; 10 | 11 | // Interface for settings configuration for adding and removing tags 12 | interface GoLiveErrorsConfig { 13 | delay: number; 14 | enabled: boolean; 15 | } 16 | 17 | let runner; 18 | 19 | export function goLiveErrorsEnabled() { 20 | let goConfig = vscode.workspace.getConfiguration('go', vscode.window.activeTextEditor ? vscode.window.activeTextEditor.document.uri : null)['liveErrors']; 21 | if (goConfig === null || goConfig === undefined || !goConfig.enabled) { 22 | return false; 23 | } 24 | let files = vscode.workspace.getConfiguration('files'); 25 | let autoSave = files['autoSave']; 26 | let autoSaveDelay = files['autoSaveDelay']; 27 | if (autoSave !== null && autoSave !== undefined && 28 | autoSave === 'afterDelay' && autoSaveDelay < goConfig.delay * 1.5) { 29 | return false; 30 | } 31 | return goConfig.enabled; 32 | } 33 | 34 | // parseLiveFile runs the gotype command in live mode to check for any syntactic or 35 | // semantic errors and reports them immediately 36 | export function parseLiveFile(e: vscode.TextDocumentChangeEvent) { 37 | if (e.document.isUntitled) { 38 | return; 39 | } 40 | if (e.document.languageId !== 'go') { 41 | return; 42 | } 43 | if (!goLiveErrorsEnabled()) { 44 | return; 45 | } 46 | 47 | if (runner != null) { 48 | clearTimeout(runner); 49 | } 50 | runner = setTimeout(function(){ 51 | processFile(e); 52 | runner = null; 53 | }, vscode.workspace.getConfiguration('go', e.document.uri)['liveErrors']['delay']); 54 | } 55 | 56 | // processFile does the actual work once the timeout has fired 57 | function processFile(e: vscode.TextDocumentChangeEvent) { 58 | const gotypeLive = getBinPath('gotype-live'); 59 | if (!path.isAbsolute(gotypeLive)) { 60 | return promptForMissingTool('gotype-live'); 61 | } 62 | 63 | let fileContents = e.document.getText(); 64 | let fileName = e.document.fileName; 65 | let args = ['-e', '-a', '-lf=' + fileName, path.dirname(fileName)]; 66 | let env = getToolsEnvVars(); 67 | let p = cp.execFile(gotypeLive, args, {env}, (err, stdout, stderr) => { 68 | if (err && (err).code === 'ENOENT') { 69 | promptForMissingTool('gotype-live'); 70 | return; 71 | } 72 | 73 | errorDiagnosticCollection.clear(); 74 | 75 | if (err) { 76 | // we want to take the error path here because the command we are calling 77 | // returns a non-zero exit status if the checks fail 78 | let diagnosticMap: Map = new Map(); 79 | 80 | stderr.split('\n').forEach(error => { 81 | if (error === null || error.length === 0) { 82 | return; 83 | } 84 | // extract the line, column and error message from the gotype output 85 | let [_, file, line, column, message] = /^(.+):(\d+):(\d+):\s+(.+)/.exec(error); 86 | // get cannonical file path 87 | file = vscode.Uri.file(file).toString(); 88 | let range = new vscode.Range(+line - 1, +column, +line - 1, +column); 89 | let diagnostic = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error); 90 | 91 | let diagnostics = diagnosticMap.get(file); 92 | if (!diagnostics) { 93 | diagnostics = []; 94 | } 95 | diagnostics.push(diagnostic); 96 | diagnosticMap.set(file, diagnostics); 97 | }); 98 | 99 | diagnosticMap.forEach((diagnostics, file) => { 100 | errorDiagnosticCollection.set(vscode.Uri.parse(file), diagnostics); 101 | }); 102 | } 103 | }); 104 | p.stdin.end(fileContents); 105 | } 106 | -------------------------------------------------------------------------------- /src-ignored/goTest.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | // [TypeFox] unused as it relies on goCover (which needs text editor decorators) 9 | 10 | import path = require('path'); 11 | import vscode = require('vscode'); 12 | import os = require('os'); 13 | import { goTest, TestConfig, getTestFlags, getTestFunctions, getBenchmarkFunctions } from './testUtils'; 14 | import { getCoverage } from './goCover'; 15 | 16 | // lastTestConfig holds a reference to the last executed TestConfig which allows 17 | // the last test to be easily re-executed. 18 | let lastTestConfig: TestConfig; 19 | 20 | /** 21 | * Executes the unit test at the primary cursor using `go test`. Output 22 | * is sent to the 'Go' channel. 23 | * 24 | * @param goConfig Configuration for the Go extension. 25 | */ 26 | export function testAtCursor(goConfig: vscode.WorkspaceConfiguration, isBenchmark: boolean, args: any) { 27 | let editor = vscode.window.activeTextEditor; 28 | if (!editor) { 29 | vscode.window.showInformationMessage('No editor is active.'); 30 | return; 31 | } 32 | if (!editor.document.fileName.endsWith('_test.go')) { 33 | vscode.window.showInformationMessage('No tests found. Current file is not a test file.'); 34 | return; 35 | } 36 | 37 | const getFunctions = isBenchmark ? getBenchmarkFunctions : getTestFunctions; 38 | 39 | editor.document.save().then(() => { 40 | return getFunctions(editor.document, null).then(testFunctions => { 41 | let testFunctionName: string; 42 | 43 | // We use functionName if it was provided as argument 44 | // Otherwise find any test function containing the cursor. 45 | if (args && args.functionName) { 46 | testFunctionName = args.functionName; 47 | } else { 48 | for (let func of testFunctions) { 49 | let selection = editor.selection; 50 | if (selection && func.location.range.contains(selection.start)) { 51 | testFunctionName = func.name; 52 | break; 53 | } 54 | }; 55 | } 56 | 57 | if (!testFunctionName) { 58 | vscode.window.showInformationMessage('No test function found at cursor.'); 59 | return; 60 | } 61 | 62 | const testConfig = { 63 | goConfig: goConfig, 64 | dir: path.dirname(editor.document.fileName), 65 | flags: getTestFlags(goConfig, args), 66 | functions: [testFunctionName], 67 | isBenchmark: isBenchmark 68 | }; 69 | 70 | // Remember this config as the last executed test. 71 | lastTestConfig = testConfig; 72 | 73 | return goTest(testConfig); 74 | }); 75 | }).then(null, err => { 76 | console.error(err); 77 | }); 78 | } 79 | 80 | /** 81 | * Runs all tests in the package of the source of the active editor. 82 | * 83 | * @param goConfig Configuration for the Go extension. 84 | */ 85 | export function testCurrentPackage(goConfig: vscode.WorkspaceConfiguration, args: any) { 86 | let editor = vscode.window.activeTextEditor; 87 | if (!editor) { 88 | vscode.window.showInformationMessage('No editor is active.'); 89 | return; 90 | } 91 | 92 | let tmpCoverPath = ''; 93 | let testFlags = getTestFlags(goConfig, args) || []; 94 | if (goConfig['coverOnTestPackage'] === true) { 95 | tmpCoverPath = path.normalize(path.join(os.tmpdir(), 'go-code-cover')); 96 | testFlags.push('-coverprofile=' + tmpCoverPath); 97 | } 98 | 99 | const testConfig = { 100 | goConfig: goConfig, 101 | dir: path.dirname(editor.document.fileName), 102 | flags: testFlags, 103 | showTestCoverage: true 104 | }; 105 | // Remember this config as the last executed test. 106 | lastTestConfig = testConfig; 107 | 108 | goTest(testConfig).then(success => { 109 | if (success && tmpCoverPath) { 110 | return getCoverage(tmpCoverPath); 111 | } 112 | }, err => { 113 | console.log(err); 114 | }); 115 | } 116 | 117 | /** 118 | * Runs all tests from all directories in the workspace. 119 | * 120 | * @param goConfig Configuration for the Go extension. 121 | */ 122 | export function testWorkspace(goConfig: vscode.WorkspaceConfiguration, args: any) { 123 | let dir = vscode.workspace.rootPath; 124 | if (vscode.window.activeTextEditor && vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri)) { 125 | dir = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri).uri.fsPath; 126 | } 127 | if (!dir) { 128 | vscode.window.showInformationMessage('No workspace is open to run tests.'); 129 | return; 130 | } 131 | const testConfig = { 132 | goConfig: goConfig, 133 | dir: dir, 134 | flags: getTestFlags(goConfig, args), 135 | includeSubDirectories: true 136 | }; 137 | // Remember this config as the last executed test. 138 | lastTestConfig = testConfig; 139 | 140 | goTest(testConfig).then(null, err => { 141 | console.error(err); 142 | }); 143 | } 144 | 145 | /** 146 | * Runs all tests in the source of the active editor. 147 | * 148 | * @param goConfig Configuration for the Go extension. 149 | */ 150 | export function testCurrentFile(goConfig: vscode.WorkspaceConfiguration, args: string[]): Thenable { 151 | let editor = vscode.window.activeTextEditor; 152 | if (!editor) { 153 | vscode.window.showInformationMessage('No editor is active.'); 154 | return; 155 | } 156 | if (!editor.document.fileName.endsWith('_test.go')) { 157 | vscode.window.showInformationMessage('No tests found. Current file is not a test file.'); 158 | return; 159 | } 160 | 161 | return editor.document.save().then(() => { 162 | return getTestFunctions(editor.document, null).then(testFunctions => { 163 | const testConfig = { 164 | goConfig: goConfig, 165 | dir: path.dirname(editor.document.fileName), 166 | flags: getTestFlags(goConfig, args), 167 | functions: testFunctions.map(func => { return func.name; }) 168 | }; 169 | // Remember this config as the last executed test. 170 | lastTestConfig = testConfig; 171 | 172 | return goTest(testConfig); 173 | }); 174 | }).then(null, err => { 175 | console.error(err); 176 | return Promise.resolve(false); 177 | }); 178 | } 179 | 180 | /** 181 | * Runs the previously executed test. 182 | */ 183 | export function testPrevious() { 184 | if (!lastTestConfig) { 185 | vscode.window.showInformationMessage('No test has been recently executed.'); 186 | return; 187 | } 188 | goTest(lastTestConfig).then(null, err => { 189 | console.error(err); 190 | }); 191 | } 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /src-vscode-mock/activate.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { outputChannel } from '../src/goStatus'; 9 | import { installCurrentPackage } from '../src/goInstall'; 10 | import { commands } from './commands'; 11 | import { addImport } from '../src/goImport'; 12 | import { LspClient } from './lsp-client'; 13 | import { LspServer } from './lsp-server'; 14 | import { errorDiagnosticCollection, warningDiagnosticCollection } from '../src/goMain'; 15 | import { window } from './window'; 16 | import { Logger } from './logger'; 17 | import { lintCode } from '../src/goLint'; 18 | import { vetCode } from '../src/goVet'; 19 | import * as goGenerateTests from '../src/goGenerateTests'; 20 | import { goGetPackage } from '../src/goGetPackage'; 21 | import { addTags, removeTags } from '../src/goModifytags'; 22 | import { playgroundCommand } from '../src/goPlayground'; 23 | import { CommandConfig } from './config'; 24 | import { buildCode } from '../src/goBuild'; 25 | import { MessageType, workspace } from './vscode'; 26 | import { updateGoPathGoRootFromConfig, offerToInstallTools, installAllTools } from '../src/goInstallTools'; 27 | import { getCurrentGoPath, getToolsGopath } from '../src/util'; 28 | import { runFillStruct } from '../src/goFillStruct'; 29 | 30 | export async function activate(lspClient: LspClient, lspServer: LspServer, logger: Logger): Promise { 31 | outputChannel.lspClient = lspClient; 32 | window.lspClient = lspClient; 33 | 34 | console.log = logger.log.bind(logger); 35 | console.error = logger.error.bind(logger); 36 | console.warn = logger.warn.bind(logger); 37 | console.info = logger.info.bind(logger); 38 | 39 | errorDiagnosticCollection.onSet(lspClient.publishDiagnostics.bind(lspClient)); 40 | warningDiagnosticCollection.onSet(lspClient.publishDiagnostics.bind(lspClient)); 41 | 42 | commands.lspClient = lspClient; 43 | 44 | commands.registerCommand('go.gopath', () => { 45 | let gopath = getCurrentGoPath(); 46 | 47 | let wasInfered = workspace.getConfiguration('go', window.activeTextEditor ? window.activeTextEditor.document.uri : null)['inferGopath']; 48 | let root = workspace.rootPath; 49 | if (window.activeTextEditor && workspace.getWorkspaceFolder(window.activeTextEditor.document.uri)) { 50 | root = workspace.getWorkspaceFolder(window.activeTextEditor.document.uri).uri.fsPath; 51 | } 52 | 53 | // not only if it was configured, but if it was successful. 54 | if (wasInfered && root && root.indexOf(gopath) === 0) { 55 | const inferredFrom = window.activeTextEditor ? 'current folder' : 'workspace root'; 56 | window.showInformationMessage(`Current GOPATH is inferred from ${inferredFrom}: ${gopath}`); 57 | } else { 58 | window.showInformationMessage('Current GOPATH: ' + gopath); 59 | } 60 | if (getToolsGopath()) { 61 | window.showInformationMessage('toolsGopath: ' + getToolsGopath()); 62 | } 63 | }); 64 | 65 | commands.registerCommand('go.import.add', (arg: string) => { 66 | return addImport(typeof arg === 'string' ? arg : null); 67 | }); 68 | 69 | commands.registerCommand('go.install.package', installCurrentPackage); 70 | 71 | commands.registerCommand('go.lint.package', lintCode); 72 | commands.registerCommand('go.lint.workspace', () => lintCode(true)); 73 | 74 | commands.registerCommand('go.vet.package', vetCode); 75 | commands.registerCommand('go.vet.workspace', () => vetCode(true)); 76 | 77 | commands.registerCommand('go.test.generate.package', () => { 78 | goGenerateTests.generateTestCurrentPackage(); 79 | }); 80 | commands.registerCommand('go.test.generate.file', () => { 81 | goGenerateTests.generateTestCurrentFile(); 82 | }); 83 | commands.registerCommand('go.test.generate.function', () => { 84 | goGenerateTests.generateTestCurrentFunction(); 85 | }); 86 | // unmapped: 87 | // 'go.toggle.test.file': missing command 'open.file' 88 | 89 | commands.registerCommand('go.get.package', goGetPackage); 90 | 91 | commands.registerCommand('go.add.tags', (args) => { 92 | addTags(args); 93 | }); 94 | commands.registerCommand('go.remove.tags', (args) => { 95 | removeTags(args); 96 | }); 97 | 98 | commands.registerCommand('go.playground', playgroundCommand); 99 | 100 | commands.registerCommand('go.build.package', buildCode); 101 | 102 | commands.registerCommand('go.build.workspace', () => buildCode(true)); 103 | 104 | commands.registerCommand('workbench.action.openGlobalSettings', () => { 105 | lspClient.logMessage({ 106 | message: 'workbench.action.openGlobalSettings is called', 107 | type: MessageType.Warning 108 | }); 109 | }); 110 | commands.registerCommand('workbench.action.openWorkspaceSettings', () => { 111 | lspClient.logMessage({ 112 | message: 'workbench.action.openWorkspaceSettings is called', 113 | type: MessageType.Warning 114 | }); 115 | }); 116 | 117 | commands.registerCommand('go.tools.install', installAllTools); 118 | commands.registerCommand('go.fill.struct', () => { 119 | runFillStruct(window.activeTextEditor); 120 | }); 121 | 122 | return updateGoPathGoRootFromConfig().then(() => { 123 | lspClient.sendTelemetryEvent(workspace.getConfiguration('go')); 124 | offerToInstallTools(); 125 | }); 126 | } 127 | 128 | export function mockActivate(lspClient: LspClient) { 129 | window.lspClient = lspClient; 130 | commands.lspClient = lspClient; 131 | 132 | errorDiagnosticCollection.onSet(lspClient.publishDiagnostics.bind(lspClient)); 133 | warningDiagnosticCollection.onSet(lspClient.publishDiagnostics.bind(lspClient)); 134 | } -------------------------------------------------------------------------------- /src-vscode-mock/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | * Copyright (C) 2017 TypeFox and others. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 7 | */ 8 | 9 | import { Command } from 'commander'; 10 | import { createLspConnection } from './lsp-connection'; 11 | import * as lsp from 'vscode-languageserver'; 12 | 13 | const program = new Command('go-language-server') 14 | .version(require('../../package.json').version) 15 | .option('--stdio', 'use stdio') 16 | .option('--node-ipc', 'use node-ipc') 17 | .option('--log-level ', 'A number indicating the log level (4 = log, 3 = info, 2 = warn, 1 = error). Defaults to `2`.') 18 | .option('--socket ', 'use socket. example: --socket=5000') 19 | .option('--clientProcessId ', 'Provide client process id to the underlying language server. example: --clientProcessId=1234') 20 | .parse(process.argv); 21 | 22 | if (!(program.stdio || program.socket || program['node-ipc'])) { 23 | console.error('Connection type required (stdio, node-ipc, socket). Refer to --help for more details.'); 24 | process.exit(1); 25 | } 26 | 27 | let logLevel = lsp.MessageType.Warning; 28 | if (program.logLevel) { 29 | logLevel = parseInt(program.logLevel, 10); 30 | if (logLevel && (logLevel < 1 || logLevel > 4)) { 31 | console.error('Invalid `--log-level ' + logLevel + '`. Falling back to `info` level.'); 32 | logLevel = lsp.MessageType.Warning; 33 | } 34 | } 35 | 36 | createLspConnection({ 37 | goServerPath: program.goServerPath as string, 38 | goServerLogFile: program.goServerLogFile as string, 39 | goServerLogVerbosity: program.goServerLogVerbosity as string, 40 | showMessageLevel: logLevel as lsp.MessageType 41 | }).listen(); 42 | -------------------------------------------------------------------------------- /src-vscode-mock/commands.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { Thenable } from './thenable'; 9 | import { LspClient } from './lsp-client'; 10 | import * as lsp from 'vscode-languageserver'; 11 | import { generateUuid } from 'vscode-languageserver/lib/utils/uuid'; 12 | 13 | export class CommandRegistry { 14 | 15 | lspClient: LspClient; 16 | commandMap = new Map any>(); 17 | 18 | executeCommand(command: string, ...rest: any[]): Thenable { 19 | const func = this.commandMap.get(command); 20 | if (func) 21 | return func.apply(null, rest); 22 | else 23 | this.lspClient.logMessage({ 24 | message: 'No such command \'' + command + '\'', 25 | type: lsp.MessageType.Error 26 | }); 27 | } 28 | 29 | registerCommand(command: string, callback: (...args: any[]) => any, /*, thisArg?: any*/): any { 30 | this.commandMap.set(command, callback); 31 | const registrationParams = { 32 | registrations: [ 33 | { 34 | id: generateUuid(), 35 | method: 'workspace/executeCommand', 36 | registerOptions: { 37 | commands: [ command ] 38 | } 39 | } 40 | ] 41 | }; 42 | this.lspClient.registerCapability(registrationParams); 43 | // TODO implement deregistration 44 | } 45 | } 46 | 47 | export const commands = new CommandRegistry(); 48 | -------------------------------------------------------------------------------- /src-vscode-mock/config.spec.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { expect } from 'chai'; 9 | import { DefaultConfig } from './config'; 10 | 11 | describe('default config', () => { 12 | it('read property', () => { 13 | const config = DefaultConfig.instance; 14 | const property: string = config.get('buildOnSave'); 15 | expect(property).to.equal('package'); 16 | }); 17 | }); -------------------------------------------------------------------------------- /src-vscode-mock/config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { readFile, readFileSync } from 'fs-extra'; 9 | import { workspace, WorkspaceFolder } from './vscode'; 10 | import Uri from 'vscode-uri'; 11 | import { uriToStringUri } from './utils'; 12 | import { resolvePath } from '../src/util'; 13 | 14 | require('pkginfo')(module); 15 | 16 | export type Config = { 17 | readonly [key: string]: any 18 | }; 19 | 20 | export class FileBasedConfig { 21 | readonly [key: string]: any; 22 | 23 | constructor(path: string) { 24 | try { 25 | const data = readFileSync(path, 'UTF-8'); 26 | const elements = JSON.parse(data); 27 | for (let key in elements) { 28 | (this as any)[key] = elements[key]; 29 | } 30 | } catch (err) { 31 | console.log(err); 32 | // file read error 33 | } 34 | } 35 | } 36 | 37 | export class DefaultConfig implements Config { 38 | 39 | private constructor() { 40 | const config = module.exports.contributes.configuration.properties; 41 | for (let k in config) { 42 | const key = k.replace(/^go\./, ''); 43 | (this as any)[key] = config[k].default; 44 | } 45 | (this as any)['enableCodeLens'] = { 46 | runtest: false, 47 | references: true 48 | } as any; 49 | (this as any)['formatFlags'] = ''; 50 | } 51 | 52 | get(key: string): T { 53 | return this[key] as T; 54 | } 55 | 56 | static readonly instance = new DefaultConfig(); 57 | } 58 | 59 | export class CommandConfig { 60 | 61 | readonly map = new Map(); 62 | 63 | private constructor() { 64 | const commands = module.exports.contributes.commands; 65 | for (let command of commands)  { 66 | this.map.set(command.command, command.title); 67 | } 68 | } 69 | 70 | getTitle(commandId: string) { 71 | return this.map.get(commandId); 72 | } 73 | 74 | static readonly instance = new CommandConfig(); 75 | } 76 | -------------------------------------------------------------------------------- /src-vscode-mock/debug.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { mockFunction } from './mock-error'; 9 | 10 | export namespace debug { 11 | export const registerDebugConfigurationProvider = mockFunction; 12 | export const startDebugging = mockFunction; 13 | } -------------------------------------------------------------------------------- /src-vscode-mock/diagnostics.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { uriToStringUri } from './utils'; 9 | import * as lsp from 'vscode-languageserver'; 10 | import URI from 'vscode-uri'; 11 | import { Emitter } from 'vscode-jsonrpc'; 12 | 13 | export class Diagnostic implements lsp.Diagnostic { 14 | constructor(public readonly range: lsp.Range, public readonly message: string, public readonly severity: lsp.DiagnosticSeverity) { } 15 | } 16 | 17 | export class DiagnosticCollection { 18 | 19 | store = new Map(); 20 | private onSetEmitter = new Emitter(); 21 | onSet = this.onSetEmitter.event; 22 | 23 | constructor(readonly name: string) {} 24 | 25 | clear() { 26 | this.store.forEach((value, uri) => { 27 | this.onSetEmitter.fire({ 28 | diagnostics: [], 29 | uri: uriToStringUri(uri) 30 | }); 31 | }); 32 | this.store.clear(); 33 | } 34 | 35 | set(uri: URI, diagnostics: Diagnostic[]) { 36 | this.store.set(uri, diagnostics); 37 | this.onSetEmitter.fire({ 38 | diagnostics: diagnostics, 39 | uri: uriToStringUri(uri) 40 | }); 41 | } 42 | 43 | get(uri: URI) { 44 | return this.store.get(uri); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src-vscode-mock/languages.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { DiagnosticCollection } from './diagnostics'; 9 | import { mockFunction } from './mock-error'; 10 | import { TextDocument } from './text-document'; 11 | import { DocumentFilter } from './types'; 12 | 13 | export namespace languages { 14 | export const registerCodeActionsProvider = mockFunction; 15 | export const registerCodeLensProvider = mockFunction; 16 | export const registerCompletionItemProvider = mockFunction; 17 | export const registerDefinitionProvider = mockFunction; 18 | export const registerDocumentFormattingEditProvider = mockFunction; 19 | export const registerDocumentSymbolProvider = mockFunction; 20 | export const registerHoverProvider = mockFunction; 21 | export const registerImplementationProvider = mockFunction; 22 | export const registerReferenceProvider = mockFunction; 23 | export const registerRenameProvider = mockFunction; 24 | export const registerSignatureHelpProvider = mockFunction; 25 | export const registerWorkspaceSymbolProvider = mockFunction; 26 | export const setLanguageConfiguration = mockFunction; 27 | 28 | export function createDiagnosticCollection(name: string): DiagnosticCollection { 29 | return new DiagnosticCollection(name); 30 | } 31 | 32 | export function match(filter: DocumentFilter, document: TextDocument): boolean { 33 | return true; 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src-vscode-mock/logger.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { LspClient } from './lsp-client'; 9 | import * as lsp from 'vscode-languageserver'; 10 | 11 | /** 12 | * the logger type 13 | */ 14 | export interface Logger { 15 | error(...arg: any[]): void; 16 | warn(...arg: any[]): void; 17 | info(...arg: any[]): void; 18 | log(...arg: any[]): void; 19 | } 20 | 21 | export class LspClientLogger implements Logger { 22 | constructor(protected client: LspClient, protected level: lsp.MessageType) { } 23 | 24 | protected sendMessage(severity: lsp.MessageType, messageObjects: any[]): void { 25 | if (this.level >= severity) { 26 | let message = messageObjects.map( p => { 27 | if (typeof p === 'object') { 28 | return JSON.stringify(p); 29 | } else { 30 | return p; 31 | } 32 | }).join(' '); 33 | 34 | this.client.logMessage({ 35 | type: severity, 36 | message: message 37 | }); 38 | } 39 | } 40 | 41 | error(...arg: any[]): void { 42 | this.sendMessage(lsp.MessageType.Error, arg); 43 | } 44 | 45 | warn(...arg: any[]): void { 46 | this.sendMessage(lsp.MessageType.Warning, arg); 47 | } 48 | 49 | info(...arg: any[]): void { 50 | this.sendMessage(lsp.MessageType.Info, arg); 51 | } 52 | 53 | log(...arg: any[]): void { 54 | this.sendMessage(lsp.MessageType.Log, arg); 55 | } 56 | 57 | } 58 | 59 | export class ConsoleLogger implements Logger { 60 | 61 | constructor(private isLogEnabled?: boolean) {} 62 | 63 | error(...arg) { 64 | console.error(...arg); 65 | } 66 | warn(...arg) { 67 | console.warn(...arg); 68 | } 69 | info(...arg) { 70 | console.info(...arg); 71 | } 72 | log(...arg) { 73 | if (this.isLogEnabled) { 74 | console.log(...arg); 75 | } 76 | } 77 | } 78 | 79 | export class PrefixingLogger implements Logger { 80 | 81 | constructor(private logger: Logger, private prefix: string) { } 82 | 83 | error(...arg: any[]): void { 84 | this.logger.error(this.prefix, ...arg); 85 | } 86 | 87 | warn(...arg: any[]): void { 88 | this.logger.warn(this.prefix, ...arg); 89 | } 90 | 91 | info(...arg: any[]): void { 92 | this.logger.info(this.prefix, ...arg); 93 | } 94 | 95 | log(...arg: any[]): void { 96 | this.logger.log(this.prefix, ...arg); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src-vscode-mock/lsp-client.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import * as lsp from 'vscode-languageserver'; 9 | 10 | export interface LspClient { 11 | registerCapability(params: lsp.RegistrationParams): Promise; 12 | publishDiagnostics(args: lsp.PublishDiagnosticsParams): void; 13 | sendTelemetryEvent(args: any): void; 14 | showMessage(args: lsp.ShowMessageParams): void; 15 | logMessage(args: lsp.LogMessageParams): void; 16 | showMessageRequest(args: lsp.ShowMessageRequestParams): Promise; 17 | applyWorkspaceEdit(args: lsp.ApplyWorkspaceEditParams): Promise; 18 | showInformationMessage(msg: string, ...options: string[]): Promise; 19 | } 20 | 21 | export class LspClientImpl implements LspClient { 22 | constructor(protected connection: lsp.IConnection) { 23 | } 24 | 25 | publishDiagnostics(args: lsp.PublishDiagnosticsParams): void { 26 | this.connection.sendNotification(lsp.PublishDiagnosticsNotification.type, args); 27 | } 28 | 29 | showMessage(args: lsp.ShowMessageParams): void { 30 | this.connection.sendNotification(lsp.ShowMessageNotification.type, args); 31 | } 32 | 33 | logMessage(args: lsp.LogMessageParams): void { 34 | this.connection.sendNotification(lsp.LogMessageNotification.type, args); 35 | } 36 | 37 | sendTelemetryEvent(args: any): void { 38 | this.connection.sendNotification(lsp.TelemetryEventNotification.type, args); 39 | } 40 | 41 | async showMessageRequest(args: lsp.ShowMessageRequestParams): Promise { 42 | return this.connection.sendRequest(lsp.ShowMessageRequest.type, args); 43 | } 44 | 45 | async applyWorkspaceEdit(args: lsp.ApplyWorkspaceEditParams): Promise { 46 | return this.connection.sendRequest(lsp.ApplyWorkspaceEditRequest.type, args); 47 | } 48 | 49 | async showInformationMessage(msg: string, ...options: string[]): Promise { 50 | if (!options) { 51 | this.showMessageRequest({ 52 | message: msg, 53 | type: 3 54 | }); 55 | return undefined; 56 | } else { 57 | const selected = await this.showMessageRequest({ 58 | message: msg, 59 | type: 3, 60 | actions: options.map(title => { return { title }; }) 61 | }); 62 | if (selected) 63 | return selected.title; 64 | else 65 | return undefined; 66 | } 67 | } 68 | 69 | async registerCapability(args: lsp.RegistrationParams): Promise { 70 | return this.connection.sendRequest(lsp.RegistrationRequest.type, args); 71 | } 72 | } -------------------------------------------------------------------------------- /src-vscode-mock/lsp-connection.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import * as lsp from 'vscode-languageserver'; 9 | 10 | import { LspClientLogger, PrefixingLogger, Logger } from './logger'; 11 | import { LspServer } from './lsp-server'; 12 | import { LspClientImpl } from './lsp-client'; 13 | import { activate } from './activate'; 14 | 15 | export interface IServerOptions { 16 | goServerPath: string; 17 | goServerLogFile?: string; 18 | goServerLogVerbosity?: string; 19 | showMessageLevel: lsp.MessageType; 20 | } 21 | 22 | export function createLspConnection(options: IServerOptions): lsp.IConnection { 23 | const connection = lsp.createConnection(); 24 | const lspClient = new LspClientImpl(connection); 25 | let logger: Logger = new LspClientLogger(lspClient, options.showMessageLevel); 26 | logger = new PrefixingLogger(logger, '[lspserver]'); 27 | const server: LspServer = new LspServer({ 28 | logger, 29 | lspClient 30 | }); 31 | 32 | connection.onInitialize(server.initialize.bind(server)); 33 | connection.onInitialized(server.initialized.bind(server)); 34 | connection.onDidOpenTextDocument(server.didOpenTextDocument.bind(server)); 35 | connection.onDidSaveTextDocument(server.didSaveTextDocument.bind(server)); 36 | connection.onDidCloseTextDocument(server.didCloseTextDocument.bind(server)); 37 | connection.onDidChangeTextDocument(server.didChangeTextDocument.bind(server)); 38 | 39 | connection.onCodeAction(server.codeAction.bind(server)); 40 | connection.onCodeLens(server.codeLens.bind(server)); 41 | connection.onCodeLensResolve(server.codeLensResolve.bind(server)); 42 | connection.onCompletion(server.completion.bind(server)); 43 | connection.onDefinition(server.definition.bind(server)); 44 | connection.onDocumentFormatting(server.documentFormatting.bind(server)); 45 | connection.onDocumentHighlight(server.documentHighlight.bind(server)); 46 | connection.onDocumentSymbol(server.documentSymbol.bind(server)); 47 | connection.onExecuteCommand(server.executeCommand.bind(server)); 48 | connection.onHover(server.hover.bind(server)); 49 | connection.onReferences(server.references.bind(server)); 50 | connection.onRenameRequest(server.rename.bind(server)); 51 | connection.onSignatureHelp(server.signatureHelp.bind(server)); 52 | connection.onWorkspaceSymbol(server.workspaceSymbol.bind(server)); 53 | 54 | return connection; 55 | } 56 | -------------------------------------------------------------------------------- /src-vscode-mock/mock-error.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | export function mockError() { 9 | throw new Error('Mock should never be called'); 10 | } 11 | 12 | export function mockFunction(...args: any[]): any { 13 | mockError(); 14 | } -------------------------------------------------------------------------------- /src-vscode-mock/providers.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | export interface CodeActionProvider {} 9 | 10 | export interface CodeLensProvider {} 11 | 12 | export interface CompletionItemProvider {} 13 | 14 | export interface DefinitionProvider {} 15 | 16 | export interface DocumentFormattingEditProvider {} 17 | 18 | export interface DocumentSymbolProvider {} 19 | 20 | export interface HoverProvider {} 21 | 22 | export interface ImplementationProvider {} 23 | 24 | export interface RenameProvider {} 25 | 26 | export interface ReferenceProvider {} 27 | 28 | export interface SignatureHelpProvider {} 29 | 30 | export interface WorkspaceSymbolProvider {} 31 | -------------------------------------------------------------------------------- /src-vscode-mock/snippet-proposal-provider.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { readFileSync } from 'fs'; 9 | import * as path from 'path'; 10 | import { CompletionItem } from './types'; 11 | import * as lsp from 'vscode-languageserver'; 12 | import { Logger } from './logger'; 13 | 14 | export class SnippetProposalProvider { 15 | 16 | readonly proposals: CompletionItem[] = []; 17 | 18 | constructor(logger: Logger) { 19 | const snippetPath = path.resolve(__dirname, '..', '..', 'snippets', 'go.json'); 20 | try { 21 | const data = readFileSync(snippetPath, 'UTF-8'); 22 | const snippets = JSON.parse(data)['.source.go']; 23 | for (let description in snippets) { 24 | const snippet = snippets[description]; 25 | const item = new CompletionItem(snippet.prefix, lsp.CompletionItemKind.Snippet); 26 | item.insertText = snippet.body; 27 | item.insertTextFormat = lsp.InsertTextFormat.Snippet; 28 | item.detail = description; 29 | item.sortText = 'b'; 30 | this.proposals.push(item); 31 | } 32 | } catch (err) { 33 | logger.error(this, 'Error reading snippets', err); 34 | } 35 | } 36 | } 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src-vscode-mock/test-utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import * as path from 'path'; 9 | import * as fs from 'fs'; 10 | import { pathToUri } from './utils'; 11 | 12 | export function uri( 13 | suffix: string): string { 14 | const resolved = this.testFilePath(suffix); 15 | return pathToUri(resolved); 16 | } 17 | 18 | export function testWorkspacePath() { 19 | return path.resolve(__dirname, '..', '..' , 'test-lsp'); 20 | } 21 | 22 | export function testFilePath(suffix: string): string { 23 | return path.resolve(testWorkspacePath(), suffix); 24 | } 25 | 26 | export function writeContents(path: string, contents: string) { 27 | fs.writeFileSync(path, contents, 'utf-8'); 28 | } 29 | 30 | export function readContents(path: string): string { 31 | return fs.readFileSync(path, 'utf-8').toString(); 32 | } 33 | 34 | export function unlink(path: string) { 35 | return fs.unlinkSync(path); 36 | } -------------------------------------------------------------------------------- /src-vscode-mock/text-document.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import * as lsp from 'vscode-languageserver'; 9 | import URI from 'vscode-uri'; 10 | 11 | export interface Line { 12 | text: string; 13 | } 14 | 15 | export function applyEdits(before: string, edits: lsp.TextEdit[]): string { 16 | const sorted = edits.sort((a, b) => { 17 | if (a.range.start.line === b.range.start.line) { 18 | return a.range.start.character - b.range.start.character; 19 | } 20 | return a.range.start.line - b.range.start.line; 21 | }); 22 | const doc = lsp.TextDocument.create('', '', 0, before); 23 | let currentDoc = ''; 24 | let offset = 0; 25 | for (const edit of sorted) { 26 | const startOffset = doc.offsetAt(edit.range.start); 27 | currentDoc += before.substr(offset, startOffset - offset) + edit.newText; 28 | offset = doc.offsetAt(edit.range.end); 29 | } 30 | return currentDoc + before.substr(offset); 31 | } 32 | 33 | export class TextDocument { 34 | 35 | public readonly uri: URI; 36 | text: string; 37 | version: number = 0; 38 | lastAccessed: number = new Date().getTime(); 39 | 40 | constructor(doc: lsp.TextDocumentItem) { 41 | this.text = doc.text; 42 | this.uri = URI.parse(doc.uri); 43 | if (lsp.VersionedTextDocumentIdentifier.is(doc)) { 44 | this.version = doc.version; 45 | } 46 | } 47 | 48 | private get lines() { 49 | return this.text.split('\n'); 50 | } 51 | 52 | get lineCount(): number { 53 | return this.lines.length; 54 | } 55 | 56 | get languageId() { 57 | return 'go'; 58 | } 59 | 60 | getText(range?: lsp.Range)  { 61 | if (!range) 62 | return this.text; 63 | const offset = this.offsetAt(range.start); 64 | const length = this.offsetAt(range.end) - offset; 65 | return this.text.substr(offset, length); 66 | } 67 | 68 | getWordRangeAtPosition(position: lsp.Position): lsp.Range |  undefined { 69 | const lines = this.lines; 70 | const line = Math.min(lines.length - 1, Math.max(0, position.line)); 71 | const lineText = lines[line]; 72 | const character = Math.min(lineText.length - 1, Math.max(0, position.character)); 73 | let startChar = character; 74 | while (startChar > 0 && !/\s/.test(lineText.charAt(startChar - 1))) 75 | --startChar; 76 | let endChar = character; 77 | while (endChar < lineText.length - 1 && !/\s/.test(lineText.charAt(endChar))) 78 | ++endChar; 79 | if (startChar === endChar) 80 | return undefined; 81 | else 82 | return lsp.Range.create(line, startChar, line, endChar); 83 | } 84 | 85 | lineAt(line: number): Line { 86 | return { 87 | text: this.lines[line] 88 | }; 89 | } 90 | 91 | getPosition(offset: number): lsp.Position { 92 | if (offset > this.text.length) { 93 | throw new Error('offset ' + offset + ' is out of bounds. Document length was ' + this.text.length); 94 | } 95 | const lines = this.lines; 96 | let currentOffSet = 0; 97 | for (let i = 0; i < lines.length; i++) { 98 | const l = lines[i]; 99 | if (currentOffSet + l.length > offset) { 100 | return { 101 | line: i, 102 | character: offset - currentOffSet 103 | }; 104 | } else { 105 | currentOffSet += l.length + 1; 106 | } 107 | } 108 | return { 109 | line: lines.length - 1, 110 | character: lines[lines.length - 1].length 111 | }; 112 | } 113 | 114 | positionAt(offset: number): lsp.Position { 115 | return this.getPosition(offset); 116 | } 117 | 118 | offsetAt(position: lsp.Position): number { 119 | const lines = this.text.split('\n'); 120 | let currentOffSet = 0; 121 | for (let i = 0; i < lines.length; i++) { 122 | const l = lines[i]; 123 | if (position.line === i) { 124 | if (l.length < position.character) { 125 | throw new Error(`Position ${JSON.stringify(position)} is out of range. Line [${i}] only has length ${l.length}.`); 126 | } 127 | return currentOffSet + position.character; 128 | } else { 129 | currentOffSet += l.length + 1; 130 | } 131 | } 132 | throw new Error(`Position ${JSON.stringify(position)} is out of range. Document only has ${lines.length} lines.`); 133 | } 134 | 135 | apply(contentChanges: lsp.TextDocumentContentChangeEvent[], version: number) { 136 | this.applyEdits(contentChanges.map(e => { 137 | const range = e.range || lsp.Range.create(lsp.Position.create(0, 0), this.getPosition(e.rangeLength || 0)); 138 | return lsp.TextEdit.replace(range, e.text); 139 | })); 140 | this.version = version; 141 | } 142 | 143 | applyEdits(edits: lsp.TextEdit[]) { 144 | this.text = applyEdits(this.text, edits); 145 | this.lastAccessed = new Date().getTime(); 146 | } 147 | 148 | async save(): Promise { 149 | // TODO sync with disc? 150 | } 151 | 152 | get fileName(): string { 153 | return this.uri.fsPath; 154 | } 155 | 156 | get isUntitled(): boolean { 157 | return false; 158 | } 159 | 160 | get isDirty(): boolean { 161 | return false; 162 | } 163 | } 164 | 165 | export class TextDocumentChangeEvent { 166 | document: TextDocument; 167 | } -------------------------------------------------------------------------------- /src-vscode-mock/text-editor.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { LspClient } from './lsp-client'; 9 | import { TextDocument } from './text-document'; 10 | import { Position, Selection, Range, WorkspaceEdit } from './types'; 11 | import * as lsp from 'vscode-languageserver'; 12 | import { TextEdit } from 'vscode-languageserver'; 13 | 14 | export class TextEditor { 15 | 16 | selection: Selection; 17 | 18 | constructor(readonly document: TextDocument, readonly lspClient: LspClient) { 19 | this.selection = new Selection(new Range(0, 0, 0, 0)); 20 | } 21 | 22 | async edit(editBuilder: (edit: TextEditorEdit) => void) { 23 | const textEditorEdit = new TextEditorEdit(); 24 | editBuilder.call(null, textEditorEdit); 25 | const workspaceEdit = new WorkspaceEdit(); 26 | workspaceEdit.set(this.document.uri, textEditorEdit.textEdits); 27 | this.lspClient.applyWorkspaceEdit({ edit: workspaceEdit }); 28 | } 29 | 30 | viewColumn(): number { 31 | return 1; 32 | } 33 | } 34 | 35 | export class TextEditorEdit { 36 | 37 | public textEdits: lsp.TextEdit[] = []; 38 | 39 | replace(location: Position | Range /*| Selection*/, newText: string): void { 40 | if (lsp.Position.is(location)) 41 | this.textEdits.push(TextEdit.replace(new Range(location, location), newText)); 42 | else 43 | this.textEdits.push(TextEdit.replace(location, newText)); 44 | } 45 | 46 | insert(location: Position, newText: string): void  { 47 | this.textEdits.push(TextEdit.insert(location, newText)); 48 | 49 | } 50 | 51 | delete(location: Range /*| Selection*/): void { 52 | this.textEdits.push(TextEdit.del(location)); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src-vscode-mock/thenable.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | export interface Thenable { 9 | then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; 10 | then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; 11 | } -------------------------------------------------------------------------------- /src-vscode-mock/types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { TextDocument } from './text-document'; 9 | import { uriToStringUri } from './utils'; 10 | import { window } from './window'; 11 | import * as lsp from 'vscode-languageserver'; 12 | import URI from 'vscode-uri'; 13 | import { TextDocumentIdentifier } from 'vscode-languageserver'; 14 | 15 | export interface CodeActionContext extends lsp.CodeActionContext { } 16 | 17 | export class CodeLens implements lsp.CodeLens { 18 | data?: { 19 | textDocument: TextDocumentIdentifier, 20 | }; 21 | 22 | document?: TextDocument; 23 | 24 | constructor(public range: lsp.Range, public command?: Command) { 25 | } 26 | } 27 | 28 | export interface Command extends lsp.Command { } 29 | 30 | export class CompletionItem implements lsp.CompletionItem { 31 | detail?: string; 32 | documentation?: string; 33 | sortText?: string; 34 | filterText?: string; 35 | insertText?: string; 36 | insertTextFormat?: lsp.InsertTextFormat; 37 | textEdit?: lsp.TextEdit; 38 | additionalTextEdits?: lsp.TextEdit[]; 39 | commitCharacters?: string[]; 40 | command?: lsp.Command; 41 | data?: any; 42 | 43 | constructor(public label: string, public kind?: lsp.CompletionItemKind) { } 44 | } 45 | 46 | export type Definition = Location | Location[] | null; 47 | 48 | export interface DocumentFilter { 49 | language: string; 50 | scheme: string; 51 | } 52 | 53 | export class ExtensionContext { 54 | subscriptions: any[] = []; 55 | globalState: GlobalState; 56 | } 57 | 58 | export class FormattingOptions { } 59 | 60 | export class GlobalState { 61 | 62 | get(key: string, defaultValue?: T): T | undefined { 63 | return undefined; 64 | } 65 | 66 | update(key: string, value: any): Thenable { 67 | return undefined; 68 | } 69 | } 70 | 71 | export class Hover implements lsp.Hover { 72 | constructor(public readonly contents: lsp.MarkedString | lsp.MarkedString[], public readonly range?: lsp.Range) { } 73 | } 74 | 75 | export namespace InsertTextFormat { 76 | export const PlainText = 1; 77 | export const Snippet = 2; 78 | } 79 | 80 | export class Location implements lsp.Location { 81 | uri: string; 82 | range: lsp.Range; 83 | 84 | constructor(range: lsp.Range, uri: string) 85 | constructor(uri: URI, range: lsp.Range) 86 | constructor(uri: URI, position: lsp.Position) 87 | constructor(public readonly rangeOrUri: lsp.Range | URI, public readonly uriRangeOrPosition: string | lsp.Range | lsp.Position) { 88 | if (lsp.Range.is(rangeOrUri)) 89 | this.range = rangeOrUri; 90 | else 91 | this.uri = uriToStringUri(rangeOrUri); 92 | if (lsp.Range.is(uriRangeOrPosition)) 93 | this.range = uriRangeOrPosition; 94 | else if (lsp.Position.is(uriRangeOrPosition)) 95 | this.range = new Range(uriRangeOrPosition, uriRangeOrPosition); 96 | else 97 | this.uri = uriRangeOrPosition; 98 | } 99 | } 100 | 101 | export type MarkedString = lsp.MarkedString; 102 | 103 | export class ParameterInformation implements lsp.ParameterInformation { 104 | constructor(public readonly label: string, public readonly documentation?: string) { } 105 | } 106 | 107 | export class Position implements lsp.Position { 108 | constructor(public readonly line: number, public readonly character: number) { } 109 | } 110 | 111 | export type ProviderResult = T | undefined | null | Thenable; 112 | 113 | export class Range implements lsp.Range { 114 | readonly start: lsp.Position; 115 | readonly end: lsp.Position; 116 | 117 | constructor(range: lsp.Range) 118 | constructor(start: lsp.Position, end: lsp.Position) 119 | constructor(startLine: number, startCharacter: number, endLine: number, endCharacter: number) 120 | constructor(first: lsp.Position | number | lsp.Range, second?: lsp.Position | number, 121 | endLine: number = -1, endCharacter: number = -1) { 122 | if (lsp.Range.is(first)) { 123 | this.start = first.start; 124 | this.end = first.end; 125 | } else if (lsp.Position.is(first) && lsp.Position.is(second)) { 126 | this.start = first as Position; 127 | this.end = second as Position; 128 | } else { 129 | this.start = new Position(first as number, second as number); 130 | this.end = new Position(endLine, endCharacter); 131 | } 132 | } 133 | 134 | static contains(range: lsp.Range, position: lsp.Position): boolean { 135 | return (range.start.line < position.line 136 | || (range.start.line === position.line && range.start.character <= position.character)) 137 | && (range.end.line > position.line 138 | || (range.end.line === position.line && range.end.character >= position.character)); 139 | } 140 | 141 | isEmpty(): boolean { 142 | return this.start.line === this.end.line 143 | && this.start.character === this.end.character; 144 | } 145 | } 146 | 147 | export class Selection extends Range { 148 | constructor(range: Range) { 149 | super(range); 150 | } 151 | 152 | get active(): lsp.Position { 153 | return this.start; 154 | } 155 | } 156 | 157 | export class SignatureHelp implements lsp.SignatureHelp { 158 | signatures: SignatureInformation[]; 159 | activeSignature: number | null; 160 | activeParameter: number | null; 161 | } 162 | 163 | export class SignatureInformation implements lsp.SignatureInformation { 164 | parameters: ParameterInformation[] = []; 165 | 166 | constructor(public readonly label: string, public readonly documentation?: string/* | MarkdownString*/) { } 167 | } 168 | 169 | export class StatusBarItem { 170 | text: string; 171 | tooltip: string | undefined; 172 | color: string /*| ThemeColor*/ | undefined; 173 | command: string | undefined; 174 | show(): void { }; 175 | hide(): void { }; 176 | dispose(): void { }; 177 | 178 | constructor(public readonly alignment: StatusBarAlignment, public readonly priority: number) { } 179 | } 180 | 181 | export enum StatusBarAlignment { 182 | Left = 1, 183 | Right = 2 184 | } 185 | 186 | export class SymbolInformation implements lsp.SymbolInformation { 187 | uri: string; 188 | location: lsp.Location | undefined; 189 | 190 | constructor(public readonly name: string, 191 | public readonly kind: lsp.SymbolKind, 192 | public readonly range: lsp.Range, 193 | uri: URI | undefined, 194 | public readonly containerName?: string) { 195 | if (uri) 196 | this.location = new Location(range, uriToStringUri(uri)); 197 | else 198 | this.location = new Location(range, uriToStringUri(window.activeTextEditor.document.uri)); 199 | } 200 | } 201 | 202 | export class TextEdit implements lsp.TextEdit { 203 | constructor(public readonly range: lsp.Range, public readonly newText: string) { } 204 | 205 | static insert(position: Position, text: string) { 206 | return new TextEdit(new Range(position, position), text); 207 | } 208 | 209 | static delete(range: Range) { 210 | return new TextEdit(range, ''); 211 | } 212 | 213 | static replace(range: Range, text: string) { 214 | return new TextEdit(range, text); 215 | } 216 | } 217 | 218 | 219 | export class WorkspaceEdit implements lsp.WorkspaceEdit { 220 | changes: { 221 | [uri: string]: TextEdit[]; 222 | } = {}; 223 | 224 | set(uri: URI, edits: TextEdit[]) { 225 | this.changes[uriToStringUri(uri)] = edits; 226 | } 227 | 228 | add(uri: URI, edit: TextEdit) { 229 | const existing = this.changes[uriToStringUri(uri)]; 230 | if (existing) 231 | existing.push(edit); 232 | else 233 | this.set(uri, [edit]); 234 | } 235 | 236 | insert(uri: URI, position: Position, text: string) { 237 | this.add(uri, new TextEdit(new Range(position, position), text)); 238 | } 239 | 240 | delete(uri: URI, range: Range) { 241 | this.add(uri, new TextEdit(range, '')); 242 | } 243 | 244 | replace(uri: URI, range: Range, text: string) { 245 | this.add(uri, new TextEdit(range, text)); 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /src-vscode-mock/utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import URI from 'vscode-uri'; 9 | 10 | export class Deferred { 11 | 12 | constructor(private operation: string, timeout?: number) { 13 | setTimeout(() => { 14 | this.reject(new Error(this.operation + ' timeout')); 15 | }, timeout || 20000); 16 | } 17 | 18 | resolve: (value?: T) => void; 19 | reject: (err?: any) => void; 20 | 21 | promise = new Promise((resolve, reject) => { 22 | this.resolve = resolve; 23 | this.reject = reject; 24 | }); 25 | } 26 | 27 | export function getTsserverExecutable(): string { 28 | return isWindows() ? 'tsserver.cmd' : 'tsserver'; 29 | } 30 | 31 | export function isWindows(): boolean { 32 | return /^win/.test(process.platform); 33 | } 34 | 35 | export function uriToPath(stringUri: string): string { 36 | const uri = URI.parse(stringUri); 37 | return uri.fsPath; 38 | } 39 | 40 | export function pathToUri(p: string): string { 41 | return 'file://' + (isWindows() ? '/' + p.replace(/\//g, '/') : p); 42 | } 43 | 44 | export function uriToStringUri(uri: URI) { 45 | return pathToUri(uri.fsPath); 46 | } -------------------------------------------------------------------------------- /src-vscode-mock/vscode-extension-telemetry.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { window } from './window'; 9 | 10 | export default class TelemetryReporter { 11 | constructor(...args: any[]) { 12 | } 13 | 14 | sendTelemetryEvent(...args: any[]) { 15 | window.lspClient.sendTelemetryEvent(args); 16 | } 17 | 18 | async dispose() { 19 | // no-op 20 | } 21 | } -------------------------------------------------------------------------------- /src-vscode-mock/vscode.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | /** 9 | * Mocks VSCode API or replaces it by LSP 10 | */ 11 | 12 | import Uri from 'vscode-uri'; 13 | 14 | export * from './commands'; 15 | export * from './debug'; 16 | export * from './diagnostics'; 17 | export * from './languages'; 18 | export * from './providers'; 19 | export * from './text-document'; 20 | export * from './text-editor'; 21 | export * from './types'; 22 | export * from './window'; 23 | export * from './workspace'; 24 | 25 | export { Uri } 26 | export { MessageType } from 'vscode-languageserver-protocol'; 27 | export { DiagnosticSeverity, CompletionItemKind, SymbolKind } from 'vscode-languageserver-types'; 28 | export { Disposable, Event, Emitter as EventEmitter, CancellationToken, CancellationTokenSource } from 'vscode-languageserver' 29 | -------------------------------------------------------------------------------- /src-vscode-mock/window.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { LspClient } from './lsp-client'; 9 | import { StatusBarAlignment, StatusBarItem } from './types'; 10 | import { TextEditor } from './text-editor'; 11 | import { Thenable } from './thenable'; 12 | import { Event } from 'vscode-jsonrpc'; 13 | import * as lsp from 'vscode-languageserver'; 14 | 15 | class Window { 16 | activeTextEditor: TextEditor | undefined; 17 | 18 | visibleTextEditors: TextEditor[] = []; 19 | 20 | lspClient: LspClient; 21 | 22 | createOutputChannel(name: string): OutputChannel { 23 | return new OutputChannel(name); 24 | } 25 | 26 | createStatusBarItem(alignment?: StatusBarAlignment, priority?: number) { 27 | return new StatusBarItem(alignment, priority); 28 | } 29 | 30 | onDidChangeActiveTextEditor: Event; 31 | 32 | showInformationMessage(message: string, ...items: T[]): Thenable { 33 | if (items) { 34 | const choices = items.map(item => typeof item === 'string' ? item as string : (item as any).title); 35 | return this.lspClient.showInformationMessage(message, ...choices).then(selection => { 36 | return items.find(item => item as any === selection || (item as any).title === selection) as T; 37 | }); 38 | } else { 39 | return this.lspClient.showInformationMessage(message).then(result => undefined); 40 | } 41 | } 42 | 43 | showQuickPick(items: string[] /*| Thenable/*, options?: QuickPickOptions, token?: CancellationToken*/): Thenable { 44 | return this.lspClient.showInformationMessage('', ...items).then(selection => { 45 | return items.find(item => item === selection); 46 | }); 47 | } 48 | 49 | showErrorMessage(message: string) { 50 | this.lspClient.showMessage({ 51 | message: message, 52 | type: lsp.MessageType.Error 53 | }); 54 | } 55 | } 56 | 57 | export const window = new Window(); 58 | 59 | export class OutputChannel { 60 | 61 | lspClient: LspClient; 62 | 63 | constructor(readonly name: string) {} 64 | 65 | append(value: string): void { 66 | if (this.lspClient) { 67 | this.lspClient.logMessage({ 68 | message: value, 69 | type: lsp.MessageType.Info 70 | }); 71 | } 72 | } 73 | 74 | appendLine(value: string): void { 75 | this.append(value); 76 | } 77 | 78 | clear(): void { 79 | // TODO: implement 80 | } 81 | 82 | show(preserveFocus?: boolean): void { 83 | // TODO: implement 84 | } 85 | 86 | hide(): void { 87 | // TODO: implement 88 | } 89 | 90 | dispose(): void { 91 | // TODO: implement 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src-vscode-mock/workspace.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 TypeFox and others. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 5 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 6 | */ 7 | 8 | import { WorkspaceEdit } from './types'; 9 | import Uri from 'vscode-uri'; 10 | import { TextDocument } from './text-document'; 11 | import { uriToStringUri } from './utils'; 12 | import { LspClient } from './lsp-client'; 13 | import { Config, DefaultConfig, FileBasedConfig } from './config'; 14 | import * as os from 'os'; 15 | import * as path from 'path'; 16 | 17 | export class WorkspaceConfiguration implements Config { 18 | 19 | constructor(private defaultValues: Config, private globalValues: Config, private workspaceValues: Config, private workspaceFolderValues: Config) { 20 | const mergedValues = { ...defaultValues, ...globalValues, ...workspaceValues, ...workspaceFolderValues }; 21 | for (let key in mergedValues) 22 | (this as any)[key] = mergedValues[key]; 23 | } 24 | 25 | get(section: string): T | undefined { 26 | return this[section] as T; 27 | } 28 | 29 | inspect(section: string): { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, workspaceFolderValue?: T } | undefined { 30 | const value: T = this.get(section); 31 | if (value) { 32 | return { 33 | key: section, 34 | defaultValue: this.defaultValues.get(section), 35 | globalValue: this.globalValues.get(section), 36 | workspaceValue: this.workspaceValues.get(section), 37 | workspaceFolderValue: this.workspaceFolderValues.get(section) 38 | }; 39 | } 40 | } 41 | } 42 | 43 | export class WorkspacConfigurationProvider { 44 | 45 | private defaultConfig = DefaultConfig.instance; 46 | private folder2config = new Map(); 47 | private path2config = new Map(); 48 | 49 | get(section: string, uri: Uri| undefined): WorkspaceConfiguration { 50 | const workspaceFolder = this.getWorkspaceFolder(uri); 51 | if (workspaceFolder) { 52 | const config = this.folder2config.get(workspaceFolder); 53 | if (config)  54 | return config; 55 | const workspaceConfiguration = new WorkspaceConfiguration( 56 | this.defaultConfig, 57 | this.getConfig(path.join(os.homedir(), '.go')), 58 | this.getConfig(workspace.rootPath), 59 | this.getConfig(workspaceFolder.uri.fsPath)); 60 | this.folder2config.set(workspaceFolder, workspaceConfiguration); 61 | return workspaceConfiguration; 62 | } 63 | return new WorkspaceConfiguration( 64 | this.defaultConfig, 65 | this.getConfig(path.join(os.homedir(), '.go')), 66 | this.getConfig(workspace.rootPath), 67 | {}); 68 | } 69 | 70 | private getWorkspaceFolder(uri: Uri | undefined) { 71 | if (uri) 72 | return workspace.getWorkspaceFolder(uri); 73 | else if (workspace.workspaceFolders.length === 1) 74 | return workspace.workspaceFolders[0]; 75 | else 76 | return undefined; 77 | } 78 | 79 | public isConfigFile(uri: Uri): boolean { 80 | return this.path2config.get(uri.fsPath) !== undefined; 81 | } 82 | 83 | private getConfig(folder: string) { 84 | if (!folder) 85 | return {}; 86 | const configPath = path.join(folder, 'go.json'); 87 | const cached = this.path2config.get(configPath); 88 | if (!cached) { 89 | const config = new FileBasedConfig(configPath); 90 | this.path2config.set(configPath, config); 91 | return config; 92 | } else { 93 | return cached; 94 | } 95 | } 96 | } 97 | 98 | export class WorkspaceFolder { 99 | constructor(public readonly uri: Uri) { } 100 | } 101 | 102 | class Workspace { 103 | 104 | workspaceConfigurationProvider = new WorkspacConfigurationProvider(); 105 | 106 | workspaceFolders: WorkspaceFolder[] = []; 107 | rootPath: string | undefined; 108 | lspClient: LspClient; 109 | 110 | getConfiguration(section?: string, resource?: Uri): WorkspaceConfiguration { 111 | return this.workspaceConfigurationProvider.get(section, resource); 112 | } 113 | 114 | getWorkspaceFolder(uri: Uri): WorkspaceFolder { 115 | const filePath = uriToStringUri(uri); 116 | return this.workspaceFolders.find(f => { 117 | const rawPath = uriToStringUri(f.uri); 118 | const path = (rawPath.endsWith('/')) ? rawPath : rawPath + '/'; 119 | if (filePath.startsWith(path) ||  filePath === rawPath) { 120 | return true; 121 | } 122 | }); 123 | } 124 | 125 | applyEdit(edit: WorkspaceEdit): void { 126 | this.lspClient.applyWorkspaceEdit({ edit : edit }); 127 | } 128 | 129 | async saveAll(includeUntitled?: boolean): Promise { 130 | return true; 131 | } 132 | } 133 | 134 | export const workspace = new Workspace(); 135 | -------------------------------------------------------------------------------- /src/diffUtils.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | import { Position, Range, TextEdit, Uri, WorkspaceEdit, TextEditorEdit } from '../src-vscode-mock/vscode'; 7 | import { getBinPathFromEnvVar } from './goPath'; 8 | import jsDiff = require('diff'); 9 | 10 | let diffToolAvailable: boolean = null; 11 | 12 | export function isDiffToolAvailable(): boolean { 13 | if (diffToolAvailable == null) { 14 | const envPath = process.env['PATH'] || (process.platform === 'win32' ? process.env['Path'] : null); 15 | diffToolAvailable = getBinPathFromEnvVar('diff', envPath, false) != null; 16 | } 17 | return diffToolAvailable; 18 | } 19 | 20 | export enum EditTypes { EDIT_DELETE, EDIT_INSERT, EDIT_REPLACE }; 21 | 22 | export class Edit { 23 | action: number; 24 | start: Position; 25 | end: Position; 26 | text: string; 27 | 28 | constructor(action: number, start: Position) { 29 | this.action = action; 30 | this.start = start; 31 | this.text = ''; 32 | } 33 | 34 | // Creates TextEdit for current Edit 35 | apply(): TextEdit { 36 | switch (this.action) { 37 | case EditTypes.EDIT_INSERT: 38 | return TextEdit.insert(this.start, this.text); 39 | 40 | case EditTypes.EDIT_DELETE: 41 | return TextEdit.delete(new Range(this.start, this.end)); 42 | 43 | case EditTypes.EDIT_REPLACE: 44 | return TextEdit.replace(new Range(this.start, this.end), this.text); 45 | } 46 | } 47 | 48 | // Applies Edit using given TextEditorEdit 49 | applyUsingTextEditorEdit(editBuilder: TextEditorEdit): void { 50 | switch (this.action) { 51 | case EditTypes.EDIT_INSERT: 52 | editBuilder.insert(this.start, this.text); 53 | break; 54 | 55 | case EditTypes.EDIT_DELETE: 56 | editBuilder.delete(new Range(this.start, this.end)); 57 | break; 58 | 59 | case EditTypes.EDIT_REPLACE: 60 | editBuilder.replace(new Range(this.start, this.end), this.text); 61 | break; 62 | } 63 | } 64 | 65 | // Applies Edits to given WorkspaceEdit 66 | applyUsingWorkspaceEdit(workspaceEdit: WorkspaceEdit, fileUri: Uri): void { 67 | switch (this.action) { 68 | case EditTypes.EDIT_INSERT: 69 | workspaceEdit.insert(fileUri, this.start, this.text); 70 | break; 71 | 72 | case EditTypes.EDIT_DELETE: 73 | workspaceEdit.delete(fileUri, new Range(this.start, this.end)); 74 | break; 75 | 76 | case EditTypes.EDIT_REPLACE: 77 | workspaceEdit.replace(fileUri, new Range(this.start, this.end), this.text); 78 | break; 79 | } 80 | } 81 | } 82 | 83 | export interface FilePatch { 84 | fileName: string; 85 | edits: Edit[]; 86 | } 87 | 88 | /** 89 | * Uses diff module to parse given array of IUniDiff objects and returns edits for files 90 | * 91 | * @param diffOutput jsDiff.IUniDiff[] 92 | * 93 | * @returns Array of FilePatch objects, one for each file 94 | */ 95 | function parseUniDiffs(diffOutput: jsDiff.IUniDiff[]): FilePatch[] { 96 | let filePatches: FilePatch[] = []; 97 | diffOutput.forEach((uniDiff: jsDiff.IUniDiff) => { 98 | let edit: Edit = null; 99 | let edits: Edit[] = []; 100 | uniDiff.hunks.forEach((hunk: jsDiff.IHunk) => { 101 | let startLine = hunk.oldStart; 102 | hunk.lines.forEach((line) => { 103 | switch (line.substr(0, 1)) { 104 | case '-': 105 | edit = new Edit(EditTypes.EDIT_DELETE, new Position(startLine - 1, 0)); 106 | edit.end = new Position(startLine, 0); 107 | edits.push(edit); 108 | startLine++; 109 | break; 110 | case '+': 111 | edit = new Edit(EditTypes.EDIT_INSERT, new Position(startLine - 1, 0)); 112 | edit.text += line.substr(1) + '\n'; 113 | edits.push(edit); 114 | break; 115 | case ' ': 116 | startLine++; 117 | break; 118 | } 119 | }); 120 | }); 121 | 122 | let fileName = uniDiff.oldFileName; 123 | filePatches.push({ fileName, edits }); 124 | }); 125 | 126 | return filePatches; 127 | 128 | } 129 | 130 | /** 131 | * Returns a FilePatch object by generating diffs between given oldStr and newStr using the diff module 132 | * 133 | * @param fileName string: Name of the file to which edits should be applied 134 | * @param oldStr string 135 | * @param newStr string 136 | * 137 | * @returns A single FilePatch object 138 | */ 139 | export function getEdits(fileName: string, oldStr: string, newStr: string): FilePatch { 140 | if (process.platform === 'win32') { 141 | oldStr = oldStr.split('\r\n').join('\n'); 142 | newStr = newStr.split('\r\n').join('\n'); 143 | } 144 | let unifiedDiffs: jsDiff.IUniDiff = jsDiff.structuredPatch(fileName, fileName, oldStr, newStr, '', ''); 145 | let filePatches: FilePatch[] = parseUniDiffs([unifiedDiffs]); 146 | return filePatches[0]; 147 | } 148 | 149 | /** 150 | * Uses diff module to parse given diff string and returns edits for files 151 | * 152 | * @param diffStr : Diff string in unified format. http://www.gnu.org/software/diffutils/manual/diffutils.html#Unified-Format 153 | * 154 | * @returns Array of FilePatch objects, one for each file 155 | */ 156 | export function getEditsFromUnifiedDiffStr(diffstr: string): FilePatch[] { 157 | let unifiedDiffs: jsDiff.IUniDiff[] = jsDiff.parsePatch(diffstr); 158 | let filePatches: FilePatch[] = parseUniDiffs(unifiedDiffs); 159 | return filePatches; 160 | } 161 | -------------------------------------------------------------------------------- /src/goBaseCodelens.ts: -------------------------------------------------------------------------------- 1 | 2 | import * as vscode from '../src-vscode-mock/vscode'; 3 | 4 | export abstract class GoBaseCodeLensProvider implements vscode.CodeLensProvider { 5 | protected enabled: boolean = true; 6 | private onDidChangeCodeLensesEmitter = new vscode.EventEmitter(); 7 | 8 | 9 | public get onDidChangeCodeLenses(): vscode.Event { 10 | return this.onDidChangeCodeLensesEmitter.event; 11 | } 12 | 13 | public setEnabled(enabled: false): void { 14 | if (this.enabled !== enabled) { 15 | this.enabled = enabled; 16 | // [TypeFox] 17 | // this.onDidChangeCodeLensesEmitter.fire(); 18 | this.onDidChangeCodeLensesEmitter.fire(undefined); 19 | } 20 | } 21 | 22 | provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.ProviderResult { 23 | return []; 24 | } 25 | 26 | } -------------------------------------------------------------------------------- /src/goBuild.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import * as vscode from '../src-vscode-mock/vscode'; 3 | import { getToolsEnvVars, runTool, ICheckResult, handleDiagnosticErrors, getWorkspaceFolderPath, getCurrentGoPath } from './util'; 4 | import { outputChannel } from './goStatus'; 5 | import os = require('os'); 6 | import { getNonVendorPackages } from './goPackages'; 7 | import { getTestFlags } from './testUtils'; 8 | import { getCurrentGoWorkspaceFromGOPATH } from './goPath'; 9 | import { diagnosticsStatusBarItem } from './goStatus'; 10 | /** 11 | * Builds current package or workspace. 12 | */ 13 | export function buildCode(buildWorkspace?: boolean) { 14 | let editor = vscode.window.activeTextEditor; 15 | if (!buildWorkspace) { 16 | if (!editor) { 17 | vscode.window.showInformationMessage('No editor is active, cannot find current package to build'); 18 | return; 19 | } 20 | if (editor.document.languageId !== 'go') { 21 | vscode.window.showInformationMessage('File in the active editor is not a Go file, cannot find current package to build'); 22 | return; 23 | } 24 | } 25 | 26 | let documentUri = editor ? editor.document.uri : null; 27 | let goConfig = vscode.workspace.getConfiguration('go', documentUri); 28 | 29 | outputChannel.clear(); // Ensures stale output from build on save is cleared 30 | diagnosticsStatusBarItem.show(); 31 | diagnosticsStatusBarItem.text = 'Building...'; 32 | 33 | goBuild(documentUri, goConfig, buildWorkspace) 34 | .then(errors => { 35 | handleDiagnosticErrors(editor ? editor.document : null, errors, vscode.DiagnosticSeverity.Error); 36 | diagnosticsStatusBarItem.hide(); 37 | }) 38 | .catch(err => { 39 | vscode.window.showInformationMessage('Error: ' + err); 40 | diagnosticsStatusBarItem.text = 'Build Failed'; 41 | }); 42 | } 43 | 44 | /** 45 | * Runs go build -i or go test -i and presents the output in the 'Go' channel and in the diagnostic collections. 46 | * 47 | * @param fileUri Document uri. 48 | * @param goConfig Configuration for the Go extension. 49 | * @param buildWorkspace If true builds code in all workspace. 50 | */ 51 | export function goBuild(fileUri: vscode.Uri, goConfig: vscode.WorkspaceConfiguration, buildWorkspace?: boolean): Promise { 52 | const currentWorkspace = getWorkspaceFolderPath(fileUri); 53 | const cwd = (buildWorkspace && currentWorkspace) ? currentWorkspace : path.dirname(fileUri.fsPath); 54 | if (!path.isAbsolute(cwd)) { 55 | return Promise.resolve([]); 56 | } 57 | 58 | const buildEnv = Object.assign({}, getToolsEnvVars()); 59 | const tmpPath = path.normalize(path.join(os.tmpdir(), 'go-code-check')); 60 | const isTestFile = fileUri && fileUri.fsPath.endsWith('_test.go'); 61 | 62 | const buildFlags: string[] = isTestFile ? getTestFlags(goConfig, null) : (Array.isArray(goConfig['buildFlags']) ? [...goConfig['buildFlags']] : []); 63 | // Remove the -i flag as it will be added later anyway 64 | if (buildFlags.indexOf('-i') > -1) { 65 | buildFlags.splice(buildFlags.indexOf('-i'), 1); 66 | } 67 | 68 | // If current file is a test file, then use `go test -c` instead of `go build` to find build errors 69 | let buildArgs: string[] = isTestFile ? ['test', '-c'] : ['build']; 70 | buildArgs.push('-i', '-o', tmpPath, ...buildFlags); 71 | if (goConfig['buildTags'] && buildFlags.indexOf('-tags') === -1) { 72 | buildArgs.push('-tags'); 73 | buildArgs.push(goConfig['buildTags']); 74 | } 75 | 76 | if (buildWorkspace && currentWorkspace && !isTestFile) { 77 | return getNonVendorPackages(currentWorkspace).then(pkgs => { 78 | let buildPromises = []; 79 | buildPromises = pkgs.map(pkgPath => { 80 | return runTool( 81 | buildArgs.concat(pkgPath), 82 | currentWorkspace, 83 | 'error', 84 | true, 85 | null, 86 | buildEnv, 87 | true 88 | ); 89 | }); 90 | return Promise.all(buildPromises).then((resultSets) => { 91 | let results: ICheckResult[] = [].concat.apply([], resultSets); 92 | // Filter duplicates 93 | return results.filter((results, index, self) => 94 | self.findIndex((t) => { 95 | return t.file === results.file && t.line === results.line && t.msg === results.msg && t.severity === results.severity; 96 | }) === index); 97 | }); 98 | }); 99 | } 100 | 101 | // Find the right importPath instead of directly using `.`. Fixes https://github.com/Microsoft/vscode-go/issues/846 102 | let currentGoWorkspace = getCurrentGoWorkspaceFromGOPATH(getCurrentGoPath(), cwd); 103 | let importPath = currentGoWorkspace ? cwd.substr(currentGoWorkspace.length + 1) : '.'; 104 | 105 | return runTool( 106 | buildArgs.concat(importPath), 107 | cwd, 108 | 'error', 109 | true, 110 | null, 111 | buildEnv, 112 | true 113 | ); 114 | 115 | 116 | 117 | 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/goCodeAction.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import { listPackages } from './goImport'; 10 | import * as _ from 'lodash'; 11 | 12 | export class GoCodeActionProvider implements vscode.CodeActionProvider { 13 | public provideCodeActions(document: vscode.TextDocument, range: vscode.Range, context: vscode.CodeActionContext, token: vscode.CancellationToken): Thenable { 14 | 15 | let promises = context.diagnostics.map(diag => { 16 | // When a name is not found but could refer to a package, offer to add import 17 | if (diag.message.indexOf('undefined: ') === 0) { 18 | let [_, name] = /^undefined: (\S*)/.exec(diag.message); 19 | return listPackages().then(packages => { 20 | let commands = packages 21 | .filter(pkg => pkg === name || pkg.endsWith('/' + name)) 22 | .map(pkg => { 23 | return { 24 | title: 'import "' + pkg + '"', 25 | command: 'go.import.add', 26 | arguments: [pkg] 27 | }; 28 | }); 29 | return commands; 30 | }); 31 | } 32 | return []; 33 | }); 34 | 35 | return Promise.all(promises).then(arrs => { 36 | let results = {}; 37 | for (let segment of arrs) { 38 | for (let item of segment) { 39 | results[item.title] = item; 40 | } 41 | } 42 | let ret = []; 43 | for (let title of Object.keys(results).sort()) { 44 | ret.push(results[title]); 45 | } 46 | return ret; 47 | }); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/goExtraInfo.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import { HoverProvider, Hover, MarkedString, TextDocument, Position, CancellationToken, WorkspaceConfiguration } from '../src-vscode-mock/vscode'; 10 | import { definitionLocation } from './goDeclaration'; 11 | 12 | export class GoHoverProvider implements HoverProvider { 13 | private goConfig = null; 14 | 15 | constructor(goConfig?: WorkspaceConfiguration) { 16 | this.goConfig = goConfig; 17 | } 18 | 19 | public provideHover(document: TextDocument, position: Position, token: CancellationToken): Thenable { 20 | if (!this.goConfig) { 21 | this.goConfig = vscode.workspace.getConfiguration('go', document.uri); 22 | } 23 | let goConfig = this.goConfig; 24 | 25 | // Temporary fix to fall back to godoc if guru is the set docsTool 26 | if (goConfig['docsTool'] === 'guru') { 27 | goConfig = Object.assign({}, goConfig, { 'docsTool': 'godoc' }); 28 | } 29 | return definitionLocation(document, position, goConfig, true, token).then(definitionInfo => { 30 | if (definitionInfo == null) return null; 31 | let lines = definitionInfo.declarationlines 32 | .filter(line => !line.startsWith('\t//') && line !== '') 33 | .map(line => line.replace(/\t/g, ' ')); 34 | let text; 35 | text = lines.join('\n').replace(/\n+$/, ''); 36 | let hoverTexts: MarkedString[] = []; 37 | hoverTexts.push({ language: 'go', value: text }); 38 | if (definitionInfo.doc != null) { 39 | hoverTexts.push(definitionInfo.doc); 40 | } 41 | let hover = new Hover(hoverTexts); 42 | return hover; 43 | }, () => { 44 | return null; 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/goFillStruct.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | *--------------------------------------------------------*/ 4 | 5 | 'use strict'; 6 | 7 | import * as vscode from '../src-vscode-mock/vscode'; 8 | import { byteOffsetAt, getBinPath, getFileArchive, getToolsEnvVars } from './util'; 9 | import cp = require('child_process'); 10 | import { promptForMissingTool } from './goInstallTools'; 11 | 12 | // Interface for the output from fillstruct 13 | interface GoFillStructOutput { 14 | start: number; 15 | end: number; 16 | code: string; 17 | } 18 | 19 | export function runFillStruct(editor: vscode.TextEditor): Promise { 20 | let args = getCommonArgs(editor); 21 | if (!args) { 22 | return Promise.reject('No args'); 23 | } 24 | 25 | return execFillStruct(editor, args); 26 | } 27 | 28 | function getCommonArgs(editor: vscode.TextEditor): string[] { 29 | if (!editor) { 30 | vscode.window.showInformationMessage('No editor is active.'); 31 | return; 32 | } 33 | if (!editor.document.fileName.endsWith('.go')) { 34 | vscode.window.showInformationMessage('Current file is not a Go file.'); 35 | return; 36 | } 37 | let args = ['-modified', '-file', editor.document.fileName]; 38 | if (editor.selection.isEmpty) { 39 | let offset = byteOffsetAt(editor.document, editor.selection.start); 40 | args.push('-offset'); 41 | args.push(offset.toString()); 42 | } else { 43 | args.push('-line'); 44 | args.push(`${editor.selection.start.line + 1}`); 45 | } 46 | return args; 47 | } 48 | 49 | function getTabsCount(editor: vscode.TextEditor): number { 50 | let startline = editor.selection.start.line; 51 | let tabs = editor.document.lineAt(startline).text.match('^\t*'); 52 | return tabs.length; 53 | } 54 | 55 | function execFillStruct(editor: vscode.TextEditor, args: string[]): Promise { 56 | let fillstruct = getBinPath('fillstruct'); 57 | let input = getFileArchive(editor.document); 58 | let tabsCount = getTabsCount(editor); 59 | 60 | return new Promise((resolve, reject) => { 61 | let p = cp.execFile(fillstruct, args, { env: getToolsEnvVars() }, (err, stdout, stderr) => { 62 | try { 63 | if (err && (err).code === 'ENOENT') { 64 | promptForMissingTool('fillstruct'); 65 | return reject(); 66 | } 67 | if (err) { 68 | vscode.window.showInformationMessage(`Cannot fill struct: ${stderr}`); 69 | return reject(); 70 | } 71 | 72 | let output = JSON.parse(stdout); 73 | 74 | if (output.length === 0) { 75 | vscode.window.showInformationMessage(`Got empty fillstruct output`); 76 | return reject(); 77 | } 78 | 79 | let indent = '\t'.repeat(tabsCount); 80 | 81 | editor.edit(editBuilder => { 82 | output.forEach((structToFill) => { 83 | const out = structToFill.code.replace(/\n/g, '\n' + indent); 84 | const rangeToReplace = new vscode.Range(editor.document.positionAt(structToFill.start), 85 | editor.document.positionAt(structToFill.end)); 86 | editBuilder.replace(rangeToReplace, out); 87 | }); 88 | }).then(() => resolve()); 89 | } catch (e) { 90 | reject(e); 91 | } 92 | }); 93 | p.stdin.end(input); 94 | }); 95 | } 96 | -------------------------------------------------------------------------------- /src/goFormat.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import cp = require('child_process'); 10 | import path = require('path'); 11 | import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools'; 12 | import { sendTelemetryEvent, getBinPath, getToolsEnvVars } from './util'; 13 | 14 | export class GoDocumentFormattingEditProvider implements vscode.DocumentFormattingEditProvider { 15 | 16 | public provideDocumentFormattingEdits(document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken): Thenable { 17 | let filename = document.fileName; 18 | let goConfig = vscode.workspace.getConfiguration('go', document.uri); 19 | let formatTool = goConfig['formatTool'] || 'goreturns'; 20 | let formatFlags = goConfig['formatFlags'].slice() || []; 21 | 22 | // We ignore the -w flag that updates file on disk because that would break undo feature 23 | if (formatFlags.indexOf('-w') > -1) { 24 | formatFlags.splice(formatFlags.indexOf('-w'), 1); 25 | } 26 | 27 | // Fix for https://github.com/Microsoft/vscode-go/issues/613 and https://github.com/Microsoft/vscode-go/issues/630 28 | if (formatTool === 'goimports' || formatTool === 'goreturns') { 29 | formatFlags.push('-srcdir', filename); 30 | } 31 | 32 | // Since goformat supports the style flag, set tabsize if user has not passed any flags 33 | /*if (formatTool === 'goformat' && formatFlags.length === 0 && options.insertSpaces) { 34 | formatFlags.push('-style=indent=' + options.tabSize); 35 | }*/ 36 | 37 | return this.runFormatter(formatTool, formatFlags, document).then(edits => edits, err => { 38 | if (err && err.startsWith('flag provided but not defined: -srcdir')) { 39 | promptForUpdatingTool(formatTool); 40 | return Promise.resolve([]); 41 | } 42 | if (err) { 43 | console.log(err); 44 | return Promise.reject('Check the console in dev tools to find errors when formatting.'); 45 | } 46 | }); 47 | } 48 | 49 | private runFormatter(formatTool: string, formatFlags: string[], document: vscode.TextDocument): Thenable { 50 | let formatCommandBinPath = getBinPath(formatTool); 51 | 52 | return new Promise((resolve, reject) => { 53 | if (!path.isAbsolute(formatCommandBinPath)) { 54 | promptForMissingTool(formatTool); 55 | return reject(); 56 | } 57 | 58 | let t0 = Date.now(); 59 | let env = getToolsEnvVars(); 60 | let stdout = ''; 61 | let stderr = ''; 62 | 63 | // Use spawn instead of exec to avoid maxBufferExceeded error 64 | const p = cp.spawn(formatCommandBinPath, formatFlags, { env }); 65 | p.stdout.setEncoding('utf8'); 66 | p.stdout.on('data', data => stdout += data); 67 | p.stderr.on('data', data => stderr += data); 68 | p.on('error', err => { 69 | if (err && (err).code === 'ENOENT') { 70 | promptForMissingTool(formatTool); 71 | return reject(); 72 | } 73 | }); 74 | p.on('close', code => { 75 | if (code !== 0) { 76 | return reject(stderr); 77 | } 78 | 79 | // Return the complete file content in the edit. 80 | // VS Code will calculate minimal edits to be applied 81 | const fileStart = new vscode.Position(0, 0); 82 | // [TypeFox] 83 | // const fileEnd = document.lineAt(document.lineCount - 1).range.end; 84 | const fileEnd = document.getPosition(document.text.length); 85 | const textEdits: vscode.TextEdit[] = [new vscode.TextEdit(new vscode.Range(fileStart, fileEnd), stdout)]; 86 | 87 | let timeTaken = Date.now() - t0; 88 | /* __GDPR__ 89 | "format" : { 90 | "tool" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, 91 | "timeTaken": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true } 92 | } 93 | */ 94 | sendTelemetryEvent('format', { tool: formatTool }, { timeTaken }); 95 | if (timeTaken > 750) { 96 | console.log(`Formatting took too long(${timeTaken}ms). Format On Save feature could be aborted.`); 97 | } 98 | return resolve(textEdits); 99 | }); 100 | p.stdin.end(document.getText()); 101 | }); 102 | } 103 | } 104 | 105 | // package main; import \"fmt\"; func main() {fmt.Print(\"Hello\")} 106 | // package main; import \"fmt\"; import \"math\"; func main() {fmt.Print(\"Hello\")} 107 | // package main; import \"fmt\"; import \"gopkg.in/Shopify/sarama.v1\"; func main() {fmt.Print(sarama.V0_10_0_0)} 108 | -------------------------------------------------------------------------------- /src/goGenerateTests.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import cp = require('child_process'); 9 | import path = require('path'); 10 | import * as vscode from '../src-vscode-mock/vscode'; 11 | 12 | import { getBinPath, getToolsEnvVars } from './util'; 13 | import { promptForMissingTool } from './goInstallTools'; 14 | import { GoDocumentSymbolProvider } from './goOutline'; 15 | 16 | const generatedWord = 'Generated '; 17 | 18 | /** 19 | * If current active editor has a Go file, returns the editor. 20 | */ 21 | function checkActiveEditor(): vscode.TextEditor { 22 | let editor = vscode.window.activeTextEditor; 23 | if (!editor) { 24 | vscode.window.showInformationMessage('Cannot generate unit tests. No editor selected.'); 25 | return; 26 | } 27 | if (!editor.document.fileName.endsWith('.go')) { 28 | vscode.window.showInformationMessage('Cannot generate unit tests. File in the editor is not a Go file.'); 29 | return; 30 | } 31 | if (editor.document.isDirty) { 32 | vscode.window.showInformationMessage('File has unsaved changes. Save and try again.'); 33 | return; 34 | } 35 | return editor; 36 | } 37 | 38 | /** 39 | * Toggles between file in current active editor and the corresponding test file. 40 | */ 41 | export function toggleTestFile(): void { 42 | let editor = vscode.window.activeTextEditor; 43 | if (!editor) { 44 | vscode.window.showInformationMessage('Cannot toggle test file. No editor selected.'); 45 | return; 46 | } 47 | let currentFilePath = editor.document.fileName; 48 | if (!currentFilePath.endsWith('.go')) { 49 | vscode.window.showInformationMessage('Cannot toggle test file. File in the editor is not a Go file.'); 50 | return; 51 | } 52 | let targetFilePath = ''; 53 | if (currentFilePath.endsWith('_test.go')) { 54 | targetFilePath = currentFilePath.substr(0, currentFilePath.lastIndexOf('_test.go')) + '.go'; 55 | } else { 56 | targetFilePath = currentFilePath.substr(0, currentFilePath.lastIndexOf('.go')) + '_test.go'; 57 | } 58 | for (let doc of vscode.window.visibleTextEditors) { 59 | if (doc.document.fileName === targetFilePath) { 60 | vscode.commands.executeCommand('vscode.open', vscode.Uri.file(targetFilePath), doc.viewColumn); 61 | return; 62 | } 63 | } 64 | vscode.commands.executeCommand('vscode.open', vscode.Uri.file(targetFilePath)); 65 | } 66 | 67 | export function generateTestCurrentPackage(): Thenable { 68 | let editor = checkActiveEditor(); 69 | if (!editor) { 70 | return; 71 | } 72 | let dir = path.dirname(editor.document.uri.fsPath); 73 | return generateTests({ dir: dir }); 74 | } 75 | 76 | export function generateTestCurrentFile(): Thenable { 77 | let editor = checkActiveEditor(); 78 | if (!editor) { 79 | return; 80 | } 81 | let file = editor.document.uri.fsPath; 82 | return generateTests({ dir: file }); 83 | } 84 | 85 | export function generateTestCurrentFunction(): Thenable { 86 | let editor = checkActiveEditor(); 87 | if (!editor) { 88 | return; 89 | } 90 | let file = editor.document.uri.fsPath; 91 | return getFunctions(editor.document).then(functions => { 92 | let currentFunction: vscode.SymbolInformation; 93 | for (let func of functions) { 94 | let selection = editor.selection; 95 | // [TypeFox] 96 | // if (selection && func.location.range.contains(selection.start)) { 97 | if (selection && vscode.Range.contains(func.location.range, selection.start)) { 98 | currentFunction = func; 99 | break; 100 | } 101 | }; 102 | if (!currentFunction) { 103 | vscode.window.showInformationMessage('No function found at cursor.'); 104 | return Promise.resolve(false); 105 | } 106 | let funcName = currentFunction.name; 107 | if (funcName.includes('.')) { 108 | funcName = funcName.split('.')[1]; 109 | } 110 | return generateTests({ dir: file, func: funcName }); 111 | }); 112 | } 113 | 114 | /** 115 | * Input to goTests. 116 | */ 117 | interface Config { 118 | /** 119 | * The working directory for `gotests`. 120 | */ 121 | dir: string; 122 | /** 123 | * Specific function names to generate tests squeleton. 124 | */ 125 | func?: string; 126 | } 127 | 128 | function generateTests(conf: Config): PromiseLike { 129 | return new Promise((resolve, reject) => { 130 | let cmd = getBinPath('gotests'); 131 | let args; 132 | if (conf.func) { 133 | args = ['-w', '-only', `^${conf.func}$`, conf.dir]; 134 | } else { 135 | args = ['-w', '-all', conf.dir]; 136 | } 137 | cp.execFile(cmd, args, {env: getToolsEnvVars()}, (err, stdout, stderr) => { 138 | try { 139 | if (err && (err).code === 'ENOENT') { 140 | promptForMissingTool('gotests'); 141 | return resolve(false); 142 | } 143 | if (err) { 144 | console.log(err); 145 | return reject('Cannot generate test due to errors'); 146 | } 147 | 148 | let message = stdout; 149 | let testsGenerated = false; 150 | 151 | // Expected stdout is of the format "Generated TestMain\nGenerated Testhello\n" 152 | if (stdout.startsWith(generatedWord)) { 153 | let lines = stdout.split('\n').filter(element => { 154 | return element.startsWith(generatedWord); 155 | }).map((element) => { 156 | return element.substr(generatedWord.length); 157 | }); 158 | message = `Generated ${lines.join(', ')}`; 159 | testsGenerated = true; 160 | } 161 | 162 | vscode.window.showInformationMessage(message); 163 | if (testsGenerated) { 164 | toggleTestFile(); 165 | } 166 | 167 | return resolve(true); 168 | } catch (e) { 169 | vscode.window.showInformationMessage(e.msg); 170 | reject(e); 171 | } 172 | }); 173 | }); 174 | } 175 | 176 | function getFunctions(doc: vscode.TextDocument): Thenable { 177 | let documentSymbolProvider = new GoDocumentSymbolProvider(); 178 | return documentSymbolProvider 179 | .provideDocumentSymbols(doc, null) 180 | .then(symbols => 181 | symbols.filter(sym => 182 | sym.kind === vscode.SymbolKind.Function) 183 | ); 184 | } 185 | -------------------------------------------------------------------------------- /src/goGetPackage.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as vscode from '../src-vscode-mock/vscode'; 4 | import cp = require('child_process'); 5 | import { getGoRuntimePath } from './goPath'; 6 | import { getImportPath, getCurrentGoPath } from './util'; 7 | import { outputChannel } from './goStatus'; 8 | 9 | export function goGetPackage() { 10 | const editor = vscode.window.activeTextEditor; 11 | const selection = editor.selection; 12 | const selectedText = editor.document.lineAt(selection.active.line).text; 13 | 14 | const importPath = getImportPath(selectedText); 15 | if (importPath === '') { 16 | vscode.window.showErrorMessage('No import path to get'); 17 | return; 18 | } 19 | 20 | const goRuntimePath = getGoRuntimePath(); 21 | if (!goRuntimePath) { 22 | return vscode.window.showErrorMessage('Could not locate Go binaries. Make sure you have Go installed'); 23 | } 24 | 25 | const env = Object.assign({}, process.env, { GOPATH: getCurrentGoPath() }); 26 | 27 | cp.execFile(goRuntimePath, ['get', '-v', importPath], { env }, (err, stdout, stderr) => { 28 | // go get -v uses stderr to write output regardless of success or failure 29 | if (stderr !== '') { 30 | outputChannel.show(); 31 | outputChannel.clear(); 32 | outputChannel.appendLine(stderr); 33 | 34 | return; 35 | } 36 | 37 | // go get -v doesn't write anything when the package already exists 38 | vscode.window.showInformationMessage(`Package already exists: ${importPath}`); 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /src/goImplementations.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as vscode from '../src-vscode-mock/vscode'; 4 | import cp = require('child_process'); 5 | import path = require('path'); 6 | import { byteOffsetAt, getBinPath, canonicalizeGOPATHPrefix } from './util'; 7 | import { promptForMissingTool } from './goInstallTools'; 8 | import { getToolsEnvVars } from './util'; 9 | import { getGoRuntimePath } from './goPath'; 10 | 11 | interface GoListOutput { 12 | Dir: string; 13 | ImportPath: string; 14 | } 15 | 16 | interface GuruImplementsRef { 17 | name: string; 18 | pos: string; 19 | kind: string; 20 | } 21 | 22 | interface GuruImplementsOutput { 23 | type: GuruImplementsRef; 24 | to: GuruImplementsRef[]; 25 | to_method: GuruImplementsRef[]; 26 | from: GuruImplementsRef[]; 27 | fromptr: GuruImplementsRef[]; 28 | } 29 | 30 | export class GoImplementationProvider implements vscode.ImplementationProvider { 31 | public provideImplementation(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): Thenable { 32 | // To keep `guru implements` fast we want to restrict the scope of the search to current workpsace 33 | // If no workpsace is open, then no-op 34 | let root = vscode.workspace.rootPath; 35 | if (vscode.workspace.getWorkspaceFolder(document.uri)) { 36 | root = vscode.workspace.getWorkspaceFolder(document.uri).uri.fsPath; 37 | } 38 | if (!root) { 39 | vscode.window.showInformationMessage('Cannot find implementations when there is no workspace open.'); 40 | return; 41 | } 42 | 43 | return new Promise((resolve, reject) => { 44 | if (token.isCancellationRequested) { 45 | return resolve(null); 46 | } 47 | let env = getToolsEnvVars(); 48 | let listProcess = cp.execFile(getGoRuntimePath(), ['list', '-e', '-json'], { cwd: root, env }, (err, stdout, stderr) => { 49 | if (err) { 50 | return reject(err); 51 | } 52 | let listOutput = JSON.parse(stdout.toString()); 53 | let scope = listOutput.ImportPath; 54 | let filename = canonicalizeGOPATHPrefix(document.fileName); 55 | let cwd = path.dirname(filename); 56 | let offset = byteOffsetAt(document, position); 57 | let goGuru = getBinPath('guru'); 58 | const buildTags = vscode.workspace.getConfiguration('go', document.uri)['buildTags']; 59 | let args = buildTags ? ['-tags', buildTags] : []; 60 | args.push('-scope', `${scope}/...`, '-json', 'implements', `${filename}:#${offset.toString()}`); 61 | 62 | let guruProcess = cp.execFile(goGuru, args, { env }, (err, stdout, stderr) => { 63 | if (err && (err).code === 'ENOENT') { 64 | promptForMissingTool('guru'); 65 | return resolve(null); 66 | } 67 | 68 | if (err) { 69 | return reject(err); 70 | } 71 | 72 | let guruOutput = JSON.parse(stdout.toString()); 73 | let results: vscode.Location[] = []; 74 | let addResults = list => { 75 | list.forEach(ref => { 76 | let match = /^(.*):(\d+):(\d+)/.exec(ref.pos); 77 | if (!match) return; 78 | let [_, file, lineStartStr, colStartStr] = match; 79 | let referenceResource = vscode.Uri.file(path.resolve(cwd, file)); 80 | let range = new vscode.Range( 81 | +lineStartStr - 1, +colStartStr - 1, +lineStartStr - 1, +colStartStr 82 | ); 83 | results.push(new vscode.Location(referenceResource, range)); 84 | }); 85 | }; 86 | 87 | // If we looked for implementation of method go to method implementations only 88 | if (guruOutput.to_method) { 89 | addResults(guruOutput.to_method); 90 | } else if (guruOutput.to) { 91 | addResults(guruOutput.to); 92 | } else if (guruOutput.from) { 93 | addResults(guruOutput.from); 94 | } else if (guruOutput.fromptr) { 95 | addResults(guruOutput.fromptr); 96 | } 97 | 98 | return resolve(results); 99 | }); 100 | token.onCancellationRequested(() => guruProcess.kill()); 101 | }); 102 | token.onCancellationRequested(() => listProcess.kill()); 103 | }); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/goImport.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import { Thenable } from '../src-vscode-mock/thenable'; 10 | import { parseFilePrelude } from './util'; 11 | import { documentSymbols } from './goOutline'; 12 | import { promptForMissingTool } from './goInstallTools'; 13 | import { getImportablePackages } from './goPackages'; 14 | 15 | const missingToolMsg = 'Missing tool: '; 16 | 17 | export function listPackages(excludeImportedPkgs: boolean = false): Thenable { 18 | let importsPromise = excludeImportedPkgs && vscode.window.activeTextEditor ? getImports(vscode.window.activeTextEditor.document) : Promise.resolve([]); 19 | let pkgsPromise = getImportablePackages(vscode.window.activeTextEditor.document.fileName); 20 | 21 | return Promise.all([pkgsPromise, importsPromise]).then(([pkgMap, importedPkgs]) => { 22 | importedPkgs.forEach(pkg => { 23 | pkgMap.delete(pkg); 24 | }); 25 | return Array.from(pkgMap.keys()).sort(); 26 | }); 27 | } 28 | 29 | /** 30 | * Returns the imported packages in the given file 31 | * 32 | * @param document TextDocument whose imports need to be returned 33 | * @returns Array of imported package paths wrapped in a promise 34 | */ 35 | function getImports(document: vscode.TextDocument): Promise { 36 | let options = { fileName: document.fileName, importsOnly: true, document }; 37 | return documentSymbols(options, null).then(symbols => { 38 | if (!symbols || !symbols[0] || !symbols[0].children) { 39 | return []; 40 | } 41 | // imports will be of the form { type: 'import', label: '"math"'} 42 | let imports = symbols[0].children.filter(x => x.type === 'import').map(x => x.label.substr(1, x.label.length - 2)); 43 | return imports; 44 | }); 45 | } 46 | 47 | function askUserForImport(): Thenable { 48 | return listPackages(true).then(packages => { 49 | return vscode.window.showQuickPick(packages); 50 | }, err => { 51 | if (typeof err === 'string' && err.startsWith(missingToolMsg)) { 52 | promptForMissingTool(err.substr(missingToolMsg.length)); 53 | } 54 | }); 55 | } 56 | 57 | export function getTextEditForAddImport(arg: string): vscode.TextEdit[] { 58 | // Import name wasn't provided 59 | if (arg === undefined) { 60 | return null; 61 | } 62 | 63 | let { imports, pkg } = parseFilePrelude(vscode.window.activeTextEditor.document.getText()); 64 | let multis = imports.filter(x => x.kind === 'multi'); 65 | if (multis.length > 0) { 66 | // There is a multiple import declaration, add to the last one 67 | const lastImportSection = multis[multis.length - 1]; 68 | if (lastImportSection.end === -1) { 69 | // For some reason there was an empty import section like `import ()` 70 | return [vscode.TextEdit.insert(new vscode.Position(lastImportSection.start + 1, 0), `import "${arg}"\n`)]; 71 | } 72 | // Add import at the start of the block so that goimports/goreturns can order them correctly 73 | return [vscode.TextEdit.insert(new vscode.Position(lastImportSection.start + 1, 0), '\t"' + arg + '"\n')]; 74 | } else if (imports.length > 0) { 75 | // There are some number of single line imports, which can just be collapsed into a block import. 76 | const edits = []; 77 | 78 | edits.push(vscode.TextEdit.insert(new vscode.Position(imports[0].start, 0), 'import (\n\t"' + arg + '"\n')); 79 | imports.forEach(element => { 80 | const currentLine = vscode.window.activeTextEditor.document.lineAt(element.start).text; 81 | const updatedLine = currentLine.replace(/^\s*import\s*/, '\t'); 82 | edits.push(vscode.TextEdit.replace(new vscode.Range(element.start, 0, element.start, currentLine.length), updatedLine)); 83 | }); 84 | edits.push(vscode.TextEdit.insert(new vscode.Position(imports[imports.length - 1].end + 1, 0), ')\n')); 85 | 86 | return edits; 87 | 88 | } else if (pkg && pkg.start >= 0) { 89 | // There are no import declarations, but there is a package declaration 90 | return [vscode.TextEdit.insert(new vscode.Position(pkg.start + 1, 0), '\nimport (\n\t"' + arg + '"\n)\n')]; 91 | } else { 92 | // There are no imports and no package declaration - give up 93 | return []; 94 | } 95 | } 96 | 97 | export function addImport(arg: string) { 98 | let p = arg ? Promise.resolve(arg) : askUserForImport(); 99 | p.then(imp => { 100 | let edits = getTextEditForAddImport(imp); 101 | if (edits && edits.length > 0) { 102 | const edit = new vscode.WorkspaceEdit(); 103 | edit.set(vscode.window.activeTextEditor.document.uri, edits); 104 | vscode.workspace.applyEdit(edit); 105 | } 106 | }); 107 | } 108 | -------------------------------------------------------------------------------- /src/goInstall.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import * as vscode from '../src-vscode-mock/vscode'; 3 | import { getToolsEnvVars, getCurrentGoPath } from './util'; 4 | import { outputChannel } from './goStatus'; 5 | import { getCurrentGoWorkspaceFromGOPATH, getGoRuntimePath } from './goPath'; 6 | import cp = require('child_process'); 7 | 8 | export function installCurrentPackage() { 9 | let editor = vscode.window.activeTextEditor; 10 | if (!editor) { 11 | vscode.window.showInformationMessage('No editor is active, cannot find current package to install'); 12 | return; 13 | } 14 | if (editor.document.languageId !== 'go') { 15 | vscode.window.showInformationMessage('File in the active editor is not a Go file, cannot find current package to install'); 16 | return; 17 | } 18 | 19 | let goRuntimePath = getGoRuntimePath(); 20 | if (!goRuntimePath) { 21 | vscode.window.showInformationMessage('Cannot find "go" binary. Update PATH or GOROOT appropriately'); 22 | return; 23 | } 24 | 25 | const env = Object.assign({}, getToolsEnvVars()); 26 | const cwd = path.dirname(editor.document.uri.fsPath); 27 | const goConfig = vscode.workspace.getConfiguration('go', editor.document.uri); 28 | const buildFlags = goConfig['buildFlags'] || []; 29 | const args = ['install', ...buildFlags]; 30 | 31 | if (goConfig['buildTags'] && buildFlags.indexOf('-tags') === -1) { 32 | args.push('-tags', goConfig['buildTags']); 33 | } 34 | 35 | // Find the right importPath instead of directly using `.`. Fixes https://github.com/Microsoft/vscode-go/issues/846 36 | const currentGoWorkspace = getCurrentGoWorkspaceFromGOPATH(getCurrentGoPath(), cwd); 37 | const importPath = currentGoWorkspace ? cwd.substr(currentGoWorkspace.length + 1) : '.'; 38 | args.push(importPath); 39 | 40 | outputChannel.clear(); 41 | outputChannel.show(); 42 | outputChannel.appendLine(`Installing ${importPath === '.' ? 'current package' : importPath}`); 43 | 44 | cp.execFile(goRuntimePath, args, { env, cwd }, (err, stdout, stderr) => { 45 | outputChannel.appendLine(err ? `Installation failed: ${stderr}` : `Installation successful`); 46 | }); 47 | } -------------------------------------------------------------------------------- /src/goLint.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import * as vscode from '../src-vscode-mock/vscode'; 3 | import { getToolsEnvVars, resolvePath, runTool, ICheckResult, handleDiagnosticErrors, getWorkspaceFolderPath } from './util'; 4 | import { outputChannel } from './goStatus'; 5 | import { diagnosticsStatusBarItem } from './goStatus'; 6 | /** 7 | * Runs linter in the current package or workspace. 8 | */ 9 | export function lintCode(lintWorkspace?: boolean) { 10 | let editor = vscode.window.activeTextEditor; 11 | if (!editor && !lintWorkspace) { 12 | vscode.window.showInformationMessage('No editor is active, cannot find current package to lint'); 13 | return; 14 | } 15 | if (editor.document.languageId !== 'go' && !lintWorkspace) { 16 | vscode.window.showInformationMessage('File in the active editor is not a Go file, cannot find current package to lint'); 17 | return; 18 | } 19 | 20 | let documentUri = editor ? editor.document.uri : null; 21 | let goConfig = vscode.workspace.getConfiguration('go', documentUri); 22 | 23 | outputChannel.clear(); // Ensures stale output from lint on save is cleared 24 | diagnosticsStatusBarItem.show(); 25 | diagnosticsStatusBarItem.text = 'Linting...'; 26 | 27 | goLint(documentUri, goConfig, lintWorkspace) 28 | .then(warnings => { 29 | handleDiagnosticErrors(editor ? editor.document : null, warnings, vscode.DiagnosticSeverity.Warning); 30 | diagnosticsStatusBarItem.hide(); 31 | }) 32 | .catch(err => { 33 | vscode.window.showInformationMessage('Error: ' + err); 34 | diagnosticsStatusBarItem.text = 'Linting Failed'; 35 | }); 36 | } 37 | 38 | /** 39 | * Runs linter and presents the output in the 'Go' channel and in the diagnostic collections. 40 | * 41 | * @param fileUri Document uri. 42 | * @param goConfig Configuration for the Go extension. 43 | * @param lintWorkspace If true runs linter in all workspace. 44 | */ 45 | export function goLint(fileUri: vscode.Uri, goConfig: vscode.WorkspaceConfiguration, lintWorkspace?: boolean): Promise { 46 | if (running) { 47 | tokenSource.cancel(); 48 | } 49 | 50 | const currentWorkspace = getWorkspaceFolderPath(fileUri); 51 | const cwd = (lintWorkspace && currentWorkspace) ? currentWorkspace : path.dirname(fileUri.fsPath); 52 | if (!path.isAbsolute(cwd)) { 53 | return Promise.resolve([]); 54 | } 55 | 56 | const lintTool = goConfig['lintTool'] || 'golint'; 57 | const lintFlags: string[] = goConfig['lintFlags'] || []; 58 | const lintEnv = Object.assign({}, getToolsEnvVars()); 59 | const args = []; 60 | const configFlag = '--config='; 61 | 62 | lintFlags.forEach(flag => { 63 | // --json is not a valid flag for golint and in gometalinter, it is used to print output in json which we dont want 64 | if (flag === '--json') { 65 | return; 66 | } 67 | if (flag.startsWith(configFlag)) { 68 | let configFilePath = flag.substr(configFlag.length); 69 | configFilePath = resolvePath(configFilePath); 70 | args.push(`${configFlag}${configFilePath}`); 71 | return; 72 | } 73 | args.push(flag); 74 | }); 75 | if (lintTool === 'gometalinter') { 76 | if (args.indexOf('--aggregate') === -1) { 77 | args.push('--aggregate'); 78 | } 79 | if (goConfig['toolsGopath']) { 80 | // gometalinter will expect its linters to be in the GOPATH 81 | // So add the toolsGopath to GOPATH 82 | lintEnv['GOPATH'] += path.delimiter + goConfig['toolsGopath']; 83 | } 84 | } 85 | 86 | if (lintWorkspace && currentWorkspace) { 87 | args.push('./...'); 88 | } 89 | 90 | running = true; 91 | const lintPromise = runTool( 92 | args, 93 | cwd, 94 | 'warning', 95 | false, 96 | lintTool, 97 | lintEnv, 98 | false, 99 | tokenSource.token 100 | ).then((result) => { 101 | running = false; 102 | return result; 103 | }); 104 | 105 | return lintPromise; 106 | } 107 | 108 | let tokenSource = new vscode.CancellationTokenSource(); 109 | let running = false; 110 | -------------------------------------------------------------------------------- /src/goMode.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | 10 | export const GO_MODE: vscode.DocumentFilter = { language: 'go', scheme: 'file' }; 11 | -------------------------------------------------------------------------------- /src/goModifytags.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import { byteOffsetAt, getBinPath, getFileArchive, getToolsEnvVars } from './util'; 10 | import cp = require('child_process'); 11 | import { promptForMissingTool } from './goInstallTools'; 12 | 13 | // Interface for the output from gomodifytags 14 | interface GomodifytagsOutput { 15 | start: number; 16 | end: number; 17 | lines: string[]; 18 | } 19 | 20 | // Interface for settings configuration for adding and removing tags 21 | interface GoTagsConfig { 22 | tags: string; 23 | options: string; 24 | promptForTags: boolean; 25 | } 26 | 27 | export function addTags(commandArgs: GoTagsConfig) { 28 | let args = getCommonArgs(); 29 | if (!args) { 30 | return; 31 | } 32 | 33 | getTagsAndOptions(vscode.workspace.getConfiguration('go', vscode.window.activeTextEditor.document.uri)['addTags'], commandArgs).then(([tags, options, transformValue]) => { 34 | if (!tags && !options) { 35 | return; 36 | } 37 | if (tags) { 38 | args.push('--add-tags'); 39 | args.push(tags); 40 | } 41 | if (options) { 42 | args.push('--add-options'); 43 | args.push(options); 44 | } 45 | if (transformValue) { 46 | args.push('--transform'); 47 | args.push(transformValue); 48 | } 49 | runGomodifytags(args); 50 | }); 51 | 52 | } 53 | 54 | export function removeTags(commandArgs: GoTagsConfig) { 55 | let args = getCommonArgs(); 56 | if (!args) { 57 | return; 58 | } 59 | 60 | getTagsAndOptions(vscode.workspace.getConfiguration('go', vscode.window.activeTextEditor.document.uri)['removeTags'], commandArgs).then(([tags, options]) => { 61 | if (!tags && !options) { 62 | args.push('--clear-tags'); 63 | args.push('--clear-options'); 64 | } 65 | if (tags) { 66 | args.push('--remove-tags'); 67 | args.push(tags); 68 | } 69 | if (options) { 70 | args.push('--remove-options'); 71 | args.push(options); 72 | } 73 | runGomodifytags(args); 74 | }); 75 | } 76 | 77 | function getCommonArgs(): string[] { 78 | let editor = vscode.window.activeTextEditor; 79 | if (!editor) { 80 | vscode.window.showInformationMessage('No editor is active.'); 81 | return; 82 | } 83 | if (!editor.document.fileName.endsWith('.go')) { 84 | vscode.window.showInformationMessage('Current file is not a Go file.'); 85 | return; 86 | } 87 | let args = ['-modified', '-file', editor.document.fileName, '-format', 'json']; 88 | if (editor.selection.start.line === editor.selection.end.line && editor.selection.start.character === editor.selection.end.character) { 89 | // Add tags to the whole struct 90 | let offset = byteOffsetAt(editor.document, editor.selection.start); 91 | args.push('-offset'); 92 | args.push(offset.toString()); 93 | } else if (editor.selection.start.line <= editor.selection.end.line) { 94 | // Add tags to selected lines 95 | args.push('-line'); 96 | args.push(`${editor.selection.start.line + 1},${editor.selection.end.line + 1}`); 97 | } 98 | 99 | return args; 100 | } 101 | 102 | function getTagsAndOptions(config: GoTagsConfig, commandArgs: GoTagsConfig): Thenable { 103 | let tags = commandArgs && commandArgs.hasOwnProperty('tags') ? commandArgs['tags'] : config['tags']; 104 | let options = commandArgs && commandArgs.hasOwnProperty('options') ? commandArgs['options'] : config['options']; 105 | let promptForTags = commandArgs && commandArgs.hasOwnProperty('promptForTags') ? commandArgs['promptForTags'] : config['promptForTags']; 106 | let transformValue = commandArgs && commandArgs.hasOwnProperty('transform') ? commandArgs['transform'] : config['transform']; 107 | 108 | // [TypeFox] 109 | // if (!promptForTags) { 110 | return Promise.resolve([tags, options, transformValue]); 111 | // [TypeFox] 112 | // } 113 | 114 | // return vscode.window.showInputBox({ 115 | // value: 'json', 116 | // prompt: 'Enter comma separated tag names' 117 | // }).then(inputTags => { 118 | // return vscode.window.showInputBox({ 119 | // value: 'json=omitempty,xml=cdata', 120 | // prompt: 'Enter comma separated options' 121 | // }).then(inputOptions => { 122 | // return [inputTags, inputOptions, transformValue]; 123 | // }); 124 | // }); 125 | } 126 | 127 | function runGomodifytags(args: string[]) { 128 | let gomodifytags = getBinPath('gomodifytags'); 129 | let editor = vscode.window.activeTextEditor; 130 | let input = getFileArchive(editor.document); 131 | let p = cp.execFile(gomodifytags, args, {env: getToolsEnvVars()}, (err, stdout, stderr) => { 132 | if (err && (err).code === 'ENOENT') { 133 | promptForMissingTool('gomodifytags'); 134 | return; 135 | } 136 | if (err) { 137 | vscode.window.showInformationMessage(`Cannot modify tags: ${stderr}`); 138 | return; 139 | } 140 | let output = JSON.parse(stdout); 141 | vscode.window.activeTextEditor.edit(editBuilder => { 142 | editBuilder.replace(new vscode.Range(output.start - 1, 0, output.end, 0), output.lines.join('\n') + '\n'); 143 | }); 144 | }); 145 | p.stdin.end(input); 146 | } -------------------------------------------------------------------------------- /src/goOutline.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import cp = require('child_process'); 10 | import { getBinPath, getFileArchive, getToolsEnvVars, killProcess } from './util'; 11 | import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools'; 12 | 13 | // Keep in sync with https://github.com/ramya-rao-a/go-outline 14 | export interface GoOutlineRange { 15 | start: number; 16 | end: number; 17 | } 18 | 19 | export interface GoOutlineDeclaration { 20 | label: string; 21 | type: string; 22 | receiverType?: string; 23 | icon?: string; // icon class or null to use the default images based on the type 24 | start: number; 25 | end: number; 26 | children?: GoOutlineDeclaration[]; 27 | signature?: GoOutlineRange; 28 | comment?: GoOutlineRange; 29 | } 30 | 31 | export interface GoOutlineOptions { 32 | /** 33 | * Path of the file for which outline is needed 34 | */ 35 | fileName: string; 36 | 37 | /** 38 | * If true, then the file will be parsed only till imports are collected 39 | */ 40 | importsOnly?: boolean; 41 | 42 | /** 43 | * Document to be parsed. If not provided, saved contents of the given fileName is used 44 | */ 45 | document?: vscode.TextDocument; 46 | } 47 | 48 | export function documentSymbols(options: GoOutlineOptions, token: vscode.CancellationToken): Promise { 49 | return new Promise((resolve, reject) => { 50 | let gooutline = getBinPath('go-outline'); 51 | let gooutlineFlags = ['-f', options.fileName]; 52 | if (options.importsOnly) { 53 | gooutlineFlags.push('-imports-only'); 54 | } 55 | if (options.document) { 56 | gooutlineFlags.push('-modified'); 57 | } 58 | 59 | let p: cp.ChildProcess; 60 | if (token) { 61 | token.onCancellationRequested(() => killProcess(p)); 62 | } 63 | 64 | // Spawn `go-outline` process 65 | p = cp.execFile(gooutline, gooutlineFlags, { env: getToolsEnvVars() }, (err, stdout, stderr) => { 66 | try { 67 | if (err && (err).code === 'ENOENT') { 68 | promptForMissingTool('go-outline'); 69 | } 70 | if (stderr && stderr.startsWith('flag provided but not defined: ')) { 71 | promptForUpdatingTool('go-outline'); 72 | if (stderr.startsWith('flag provided but not defined: -imports-only')) { 73 | options.importsOnly = false; 74 | } 75 | if (stderr.startsWith('flag provided but not defined: -modified')) { 76 | options.document = null; 77 | } 78 | p = null; 79 | return documentSymbols(options, token).then(results => { 80 | return resolve(results); 81 | }); 82 | } 83 | if (err) return resolve(null); 84 | let result = stdout.toString(); 85 | let decls = JSON.parse(result); 86 | return resolve(decls); 87 | } catch (e) { 88 | reject(e); 89 | } 90 | }); 91 | if (options.document) { 92 | p.stdin.end(getFileArchive(options.document)); 93 | } 94 | }); 95 | } 96 | 97 | export class GoDocumentSymbolProvider implements vscode.DocumentSymbolProvider { 98 | 99 | private goKindToCodeKind: { [key: string]: vscode.SymbolKind } = { 100 | 'package': vscode.SymbolKind.Package, 101 | 'import': vscode.SymbolKind.Namespace, 102 | 'variable': vscode.SymbolKind.Variable, 103 | 'type': vscode.SymbolKind.Interface, 104 | 'function': vscode.SymbolKind.Function 105 | }; 106 | 107 | private convertToCodeSymbols(document: vscode.TextDocument, decls: GoOutlineDeclaration[], symbols: vscode.SymbolInformation[], containerName: string): void { 108 | let gotoSymbolConfig = vscode.workspace.getConfiguration('go', document.uri)['gotoSymbol']; 109 | let includeImports = gotoSymbolConfig ? gotoSymbolConfig['includeImports'] : false; 110 | (decls || []).forEach(decl => { 111 | if (!includeImports && decl.type === 'import') return; 112 | let label = decl.label; 113 | if (decl.receiverType) { 114 | label = '(' + decl.receiverType + ').' + label; 115 | } 116 | 117 | let codeBuf = new Buffer(document.getText()); 118 | let start = codeBuf.slice(0, decl.start - 1).toString().length; 119 | let end = codeBuf.slice(0, decl.end - 1).toString().length; 120 | 121 | let symbolInfo = new vscode.SymbolInformation( 122 | label, 123 | this.goKindToCodeKind[decl.type], 124 | new vscode.Range(document.positionAt(start), document.positionAt(end)), 125 | undefined, 126 | containerName); 127 | symbols.push(symbolInfo); 128 | if (decl.children) { 129 | this.convertToCodeSymbols(document, decl.children, symbols, decl.label); 130 | } 131 | }); 132 | } 133 | 134 | public provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): Thenable { 135 | let options = { fileName: document.fileName, document: document }; 136 | return documentSymbols(options, token).then(decls => { 137 | let symbols: vscode.SymbolInformation[] = []; 138 | this.convertToCodeSymbols(document, decls, symbols, ''); 139 | return symbols; 140 | }); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/goPath.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | /** 9 | * This file is loaded by both the extension and debug adapter, so it cannot import 'vscode' 10 | */ 11 | import fs = require('fs'); 12 | import path = require('path'); 13 | import os = require('os'); 14 | 15 | let binPathCache: { [bin: string]: string; } = {}; 16 | let runtimePathCache: string = ''; 17 | export const envPath = process.env['PATH'] || (process.platform === 'win32' ? process.env['Path'] : null); 18 | 19 | export function getBinPathFromEnvVar(toolName: string, envVarValue: string, appendBinToPath: boolean): string { 20 | toolName = correctBinname(toolName); 21 | if (envVarValue) { 22 | let paths = envVarValue.split(path.delimiter); 23 | for (let i = 0; i < paths.length; i++) { 24 | let binpath = path.join(paths[i], appendBinToPath ? 'bin' : '', toolName); 25 | if (fileExists(binpath)) { 26 | binPathCache[toolName] = binpath; 27 | return binpath; 28 | } 29 | } 30 | } 31 | return null; 32 | } 33 | 34 | export function getBinPathWithPreferredGopath(binname: string, ...preferredGopaths) { 35 | if (binPathCache[correctBinname(binname)]) return binPathCache[correctBinname(binname)]; 36 | 37 | for (let i = 0; i < preferredGopaths.length; i++) { 38 | if (typeof preferredGopaths[i] === 'string') { 39 | // Search in the preferred GOPATH workspace's bin folder 40 | let pathFrompreferredGoPath = getBinPathFromEnvVar(binname, preferredGopaths[i], true); 41 | if (pathFrompreferredGoPath) { 42 | return pathFrompreferredGoPath; 43 | } 44 | } 45 | } 46 | 47 | // Then search PATH parts 48 | let pathFromPath = getBinPathFromEnvVar(binname, envPath, false); 49 | if (pathFromPath) { 50 | return pathFromPath; 51 | } 52 | 53 | // Finally check GOROOT just in case 54 | let pathFromGoRoot = getBinPathFromEnvVar(binname, process.env['GOROOT'], true); 55 | if (pathFromGoRoot) { 56 | return pathFromGoRoot; 57 | } 58 | 59 | // Else return the binary name directly (this will likely always fail downstream) 60 | return binname; 61 | } 62 | 63 | function correctBinname(binname: string) { 64 | if (process.platform === 'win32') 65 | return binname + '.exe'; 66 | else 67 | return binname; 68 | } 69 | 70 | /** 71 | * Returns Go runtime binary path. 72 | * 73 | * @return the path to the Go binary. 74 | */ 75 | export function getGoRuntimePath(): string { 76 | if (runtimePathCache) return runtimePathCache; 77 | let correctBinNameGo = correctBinname('go'); 78 | if (process.env['GOROOT']) { 79 | let runtimePathFromGoRoot = path.join(process.env['GOROOT'], 'bin', correctBinNameGo); 80 | if (fileExists(runtimePathFromGoRoot)) { 81 | runtimePathCache = runtimePathFromGoRoot; 82 | return runtimePathCache; 83 | } 84 | } 85 | 86 | if (envPath) { 87 | let pathparts = (envPath).split(path.delimiter); 88 | runtimePathCache = pathparts.map(dir => path.join(dir, correctBinNameGo)).filter(candidate => fileExists(candidate))[0]; 89 | } 90 | if (!runtimePathCache) { 91 | let defaultPathForGo = process.platform === 'win32' ? 'C:\\Go\\bin\\go.exe' : '/usr/local/go/bin/go'; 92 | if (fileExists(defaultPathForGo)) { 93 | runtimePathCache = defaultPathForGo; 94 | } 95 | } 96 | return runtimePathCache; 97 | } 98 | 99 | function fileExists(filePath: string): boolean { 100 | try { 101 | return fs.statSync(filePath).isFile(); 102 | } catch (e) { 103 | return false; 104 | } 105 | } 106 | 107 | export function clearCacheForTools() { 108 | binPathCache = {}; 109 | } 110 | 111 | /** 112 | * Exapnds ~ to homedir in non-Windows platform 113 | */ 114 | export function resolveHomeDir(inputPath: string): string { 115 | if (!inputPath || !inputPath.trim()) return inputPath; 116 | return inputPath.startsWith('~') ? path.join(os.homedir(), inputPath.substr(1)) : inputPath; 117 | } 118 | 119 | export function stripBOM(s: string): string { 120 | if (s && s[0] === '\uFEFF') { 121 | s = s.substr(1); 122 | } 123 | return s; 124 | } 125 | 126 | export function parseEnvFile(path: string): { [key: string]: string } { 127 | const env = {}; 128 | if (!path) { 129 | return env; 130 | } 131 | 132 | try { 133 | const buffer = stripBOM(fs.readFileSync(path, 'utf8')); 134 | buffer.split('\n').forEach(line => { 135 | const r = line.match(/^\s*([\w\.\-]+)\s*=\s*(.*)?\s*$/); 136 | if (r !== null) { 137 | let value = r[2] || ''; 138 | if (value.length > 0 && value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') { 139 | value = value.replace(/\\n/gm, '\n'); 140 | } 141 | env[r[1]] = value.replace(/(^['"]|['"]$)/g, ''); 142 | } 143 | }); 144 | return env; 145 | } catch (e) { 146 | throw (`Cannot load environment variables from file ${path}`); 147 | } 148 | } 149 | 150 | // Walks up given folder path to return the closest ancestor that has `src` as a child 151 | export function getInferredGopath(folderPath: string): string { 152 | if (!folderPath) { 153 | return; 154 | } 155 | 156 | let dirs = folderPath.toLowerCase().split(path.sep); 157 | 158 | // find src directory closest to given folder path 159 | let srcIdx = dirs.lastIndexOf('src'); 160 | if (srcIdx > 0) { 161 | return folderPath.substr(0, dirs.slice(0, srcIdx).join(path.sep).length); 162 | } 163 | } 164 | 165 | /** 166 | * Returns the workspace in the given Gopath to which given directory path belongs to 167 | * @param gopath string Current Gopath. Can be ; or : separated (as per os) to support multiple paths 168 | * @param currentFileDirPath string 169 | */ 170 | export function getCurrentGoWorkspaceFromGOPATH(gopath: string, currentFileDirPath: string): string { 171 | if (!gopath) { 172 | return; 173 | } 174 | let workspaces: string[] = gopath.split(path.delimiter); 175 | let currentWorkspace = ''; 176 | 177 | // Workaround for issue in https://github.com/Microsoft/vscode/issues/9448#issuecomment-244804026 178 | if (process.platform === 'win32') { 179 | currentFileDirPath = currentFileDirPath.substr(0, 1).toUpperCase() + currentFileDirPath.substr(1); 180 | } 181 | 182 | // Find current workspace by checking if current file is 183 | // under any of the workspaces in $GOPATH 184 | for (let i = 0; i < workspaces.length; i++) { 185 | let possibleCurrentWorkspace = path.join(workspaces[i], 'src'); 186 | if (currentFileDirPath.startsWith(possibleCurrentWorkspace)) { 187 | // In case of nested workspaces, (example: both /Users/me and /Users/me/src/a/b/c are in $GOPATH) 188 | // both parent & child workspace in the nested workspaces pair can make it inside the above if block 189 | // Therefore, the below check will take longer (more specific to current file) of the two 190 | if (possibleCurrentWorkspace.length > currentWorkspace.length) { 191 | currentWorkspace = possibleCurrentWorkspace; 192 | } 193 | } 194 | } 195 | return currentWorkspace; 196 | } -------------------------------------------------------------------------------- /src/goPlayground.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from '../src-vscode-mock/vscode'; 2 | import * as path from 'path'; 3 | import { execFile } from 'child_process'; 4 | import { outputChannel } from './goStatus'; 5 | import { getBinPath } from './util'; 6 | import { promptForMissingTool } from './goInstallTools'; 7 | 8 | const TOOL_CMD_NAME = 'goplay'; 9 | 10 | export const playgroundCommand = () => { 11 | const editor = vscode.window.activeTextEditor; 12 | if (!editor) { 13 | vscode.window.showInformationMessage('No editor is active.'); 14 | return; 15 | } 16 | 17 | const binaryLocation = getBinPath(TOOL_CMD_NAME); 18 | if (!path.isAbsolute(binaryLocation)) { 19 | return promptForMissingTool(TOOL_CMD_NAME); 20 | } 21 | 22 | outputChannel.clear(); 23 | outputChannel.show(); 24 | outputChannel.appendLine('Upload to the Go Playground in progress...\n'); 25 | 26 | const selection = editor.selection; 27 | const code = selection.isEmpty 28 | ? editor.document.getText() 29 | : editor.document.getText(selection); 30 | goPlay(code, vscode.workspace.getConfiguration('go', editor.document.uri).get('playground')).then(result => { 31 | outputChannel.append(result); 32 | }, (e: string) => { 33 | if (e) { 34 | outputChannel.append(e); 35 | } 36 | }); 37 | }; 38 | 39 | export function goPlay(code: string, goConfig: vscode.WorkspaceConfiguration): Thenable { 40 | const cliArgs = Object.keys(goConfig).map(key => `-${key}=${goConfig[key]}`); 41 | const binaryLocation = getBinPath(TOOL_CMD_NAME); 42 | 43 | return new Promise((resolve, reject) => { 44 | execFile(binaryLocation, [...cliArgs, '-'], (err, stdout, stderr) => { 45 | if (err && (err).code === 'ENOENT') { 46 | promptForMissingTool(TOOL_CMD_NAME); 47 | return reject(); 48 | } 49 | if (err) { 50 | return reject(`Upload to the Go Playground failed.\n${stdout || stderr || err.message}`); 51 | } 52 | return resolve( 53 | `Output from the Go Playground: 54 | ${stdout || stderr} 55 | Finished running tool: ${binaryLocation} ${cliArgs.join(' ')} -\n` 56 | ); 57 | }).stdin.end(code); 58 | }); 59 | } 60 | -------------------------------------------------------------------------------- /src/goReferences.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import cp = require('child_process'); 10 | import path = require('path'); 11 | import { getBinPath, byteOffsetAt, canonicalizeGOPATHPrefix, getFileArchive, getToolsEnvVars } from './util'; 12 | import { promptForMissingTool } from './goInstallTools'; 13 | 14 | export class GoReferenceProvider implements vscode.ReferenceProvider { 15 | 16 | public provideReferences(document: vscode.TextDocument, position: vscode.Position, options: { includeDeclaration: boolean }, token: vscode.CancellationToken): Thenable { 17 | return this.doFindReferences(document, position, options, token); 18 | } 19 | 20 | private doFindReferences(document: vscode.TextDocument, position: vscode.Position, options: { includeDeclaration: boolean }, token: vscode.CancellationToken): Thenable { 21 | return new Promise((resolve, reject) => { 22 | // get current word 23 | let wordRange = document.getWordRangeAtPosition(position); 24 | if (!wordRange) { 25 | return resolve([]); 26 | } 27 | 28 | let goGuru = getBinPath('guru'); 29 | if (!path.isAbsolute(goGuru)) { 30 | promptForMissingTool('guru'); 31 | return reject('Cannot find tool "guru" to find references.'); 32 | } 33 | 34 | let filename = canonicalizeGOPATHPrefix(document.fileName); 35 | let cwd = path.dirname(filename); 36 | let offset = byteOffsetAt(document, position); 37 | let env = getToolsEnvVars(); 38 | let buildTags = vscode.workspace.getConfiguration('go', document.uri)['buildTags']; 39 | let args = buildTags ? ['-tags', buildTags] : []; 40 | args.push('-modified', 'referrers', `${filename}:#${offset.toString()}`); 41 | 42 | let process = cp.execFile(goGuru, args, { env }, (err, stdout, stderr) => { 43 | try { 44 | if (err && (err).code === 'ENOENT') { 45 | promptForMissingTool('guru'); 46 | return reject('Cannot find tool "guru" to find references.'); 47 | } 48 | 49 | if (err && (err).killed !== true) { 50 | return reject(`Error running guru: ${err.message || stderr}`); 51 | } 52 | 53 | let lines = stdout.toString().split('\n'); 54 | let results: vscode.Location[] = []; 55 | for (let i = 0; i < lines.length; i++) { 56 | let match = /^(.*):(\d+)\.(\d+)-(\d+)\.(\d+):/.exec(lines[i]); 57 | if (!match) continue; 58 | let [_, file, lineStartStr, colStartStr, lineEndStr, colEndStr] = match; 59 | let referenceResource = vscode.Uri.file(path.resolve(cwd, file)); 60 | 61 | if (!options.includeDeclaration) { 62 | if (document.uri.fsPath === referenceResource.fsPath && 63 | position.line === Number(lineStartStr) - 1) { 64 | continue; 65 | } 66 | } 67 | 68 | let range = new vscode.Range( 69 | +lineStartStr - 1, +colStartStr - 1, +lineEndStr - 1, +colEndStr 70 | ); 71 | results.push(new vscode.Location(referenceResource, range)); 72 | } 73 | resolve(results); 74 | } catch (e) { 75 | reject(e); 76 | } 77 | }); 78 | process.stdin.end(getFileArchive(document)); 79 | 80 | token.onCancellationRequested(() => 81 | process.kill() 82 | ); 83 | }); 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/goReferencesCodelens.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as vscode from '../src-vscode-mock/vscode'; 4 | import { uriToStringUri } from '../src-vscode-mock/utils'; 5 | import { SymbolInformation, TextDocument, CancellationToken, CodeLens, Range, Command, Location, commands } from '../src-vscode-mock/vscode'; 6 | import { GoDocumentSymbolProvider } from './goOutline'; 7 | import { GoReferenceProvider } from './goReferences'; 8 | import { GoBaseCodeLensProvider } from './goBaseCodelens'; 9 | 10 | const methodRegex = /^func\s+\(\s*\w+\s+\*?\w+\s*\)\s+/; 11 | 12 | class ReferencesCodeLens extends CodeLens { 13 | constructor( 14 | public document: TextDocument, 15 | public symbol: SymbolInformation, 16 | range: Range 17 | ) { 18 | super(range); 19 | } 20 | } 21 | 22 | export class GoReferencesCodeLensProvider extends GoBaseCodeLensProvider { 23 | public provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable { 24 | if (!this.enabled) { 25 | return []; 26 | } 27 | let codeLensConfig = vscode.workspace.getConfiguration('go', document.uri).get('enableCodeLens'); 28 | let codelensEnabled = codeLensConfig ? codeLensConfig['references'] : false; 29 | if (!codelensEnabled) { 30 | return Promise.resolve([]); 31 | } 32 | 33 | return this.provideDocumentSymbols(document, token).then(symbols => { 34 | return symbols.map(symbol => { 35 | let position = symbol.location.range.start; 36 | 37 | // Add offset for functions as go-outline returns position at the keyword func instead of func name 38 | if (symbol.kind === vscode.SymbolKind.Function) { 39 | let funcDecl = document.lineAt(position.line).text.substr(position.character); 40 | let match = methodRegex.exec(funcDecl); 41 | // [TypeFox] 42 | // position = position.translate(0, match ? match[0].length : 5); 43 | position = new vscode.Position(position.line, position.character + (match ? match[0].length : 5)); 44 | } 45 | return new ReferencesCodeLens(document, symbol, new vscode.Range(position, position)); 46 | }); 47 | }); 48 | } 49 | 50 | public resolveCodeLens?(inputCodeLens: CodeLens, token: CancellationToken): CodeLens | Thenable { 51 | let codeLens = inputCodeLens as ReferencesCodeLens; 52 | 53 | if (token.isCancellationRequested) { 54 | return Promise.resolve(codeLens); 55 | } 56 | 57 | let options = { 58 | includeDeclaration: false 59 | }; 60 | let referenceProvider = new GoReferenceProvider(); 61 | return referenceProvider.provideReferences(codeLens.document, codeLens.range.start, options, token).then(references => { 62 | codeLens.command = { 63 | title: references.length === 1 64 | ? '1 reference' 65 | : references.length + ' references', 66 | // [TypeFox] 67 | // command: 'editor.action.showReferences', 68 | // arguments: [codeLens.document.uri, codeLens.range.start, references] 69 | command: 'go.editor.action.showReferences', 70 | arguments: [uriToStringUri(codeLens.document.uri), codeLens.range.start, references] 71 | }; 72 | return codeLens; 73 | }, err => { 74 | console.log(err); 75 | codeLens.command = { 76 | title: 'Error finding references', 77 | command: '' 78 | }; 79 | return codeLens; 80 | }); 81 | } 82 | 83 | private provideDocumentSymbols(document: TextDocument, token: CancellationToken): Thenable { 84 | let symbolProvider = new GoDocumentSymbolProvider(); 85 | let isTestFile = document.fileName.endsWith('_test.go'); 86 | return symbolProvider.provideDocumentSymbols(document, token).then(symbols => { 87 | return symbols.filter(symbol => { 88 | 89 | if (symbol.kind === vscode.SymbolKind.Interface) { 90 | return true; 91 | } 92 | 93 | if (symbol.kind === vscode.SymbolKind.Function) { 94 | if (isTestFile && (symbol.name.startsWith('Test') || symbol.name.startsWith('Example') || symbol.name.startsWith('Benchmark'))) { 95 | return false; 96 | } 97 | return true; 98 | } 99 | 100 | return false; 101 | } 102 | ); 103 | }); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/goRename.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import cp = require('child_process'); 10 | import { getBinPath, byteOffsetAt, canonicalizeGOPATHPrefix, getToolsEnvVars, killProcess } from './util'; 11 | import { getEditsFromUnifiedDiffStr, isDiffToolAvailable, FilePatch, Edit } from './diffUtils'; 12 | import { promptForMissingTool } from './goInstallTools'; 13 | 14 | export class GoRenameProvider implements vscode.RenameProvider { 15 | 16 | public provideRenameEdits(document: vscode.TextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Thenable { 17 | return vscode.workspace.saveAll(false).then(() => { 18 | return this.doRename(document, position, newName, token); 19 | }); 20 | } 21 | 22 | private doRename(document: vscode.TextDocument, position: vscode.Position, newName: string, token: vscode.CancellationToken): Thenable { 23 | return new Promise((resolve, reject) => { 24 | let filename = canonicalizeGOPATHPrefix(document.fileName); 25 | let range = document.getWordRangeAtPosition(position); 26 | let pos = range ? range.start : position; 27 | let offset = byteOffsetAt(document, pos); 28 | let env = getToolsEnvVars(); 29 | let gorename = getBinPath('gorename'); 30 | const buildTags = vscode.workspace.getConfiguration('go', document.uri)['buildTags'] ; 31 | let gorenameArgs = ['-offset', filename + ':#' + offset, '-to', newName]; 32 | if (buildTags) { 33 | gorenameArgs.push('-tags', buildTags); 34 | } 35 | let canRenameToolUseDiff = isDiffToolAvailable(); 36 | if (canRenameToolUseDiff) { 37 | gorenameArgs.push('-d'); 38 | } 39 | 40 | let p: cp.ChildProcess; 41 | if (token) { 42 | token.onCancellationRequested(() => killProcess(p)); 43 | } 44 | 45 | p = cp.execFile(gorename, gorenameArgs, {env}, (err, stdout, stderr) => { 46 | try { 47 | if (err && (err).code === 'ENOENT') { 48 | promptForMissingTool('gorename'); 49 | return resolve(null); 50 | } 51 | if (err) { 52 | let errMsg = stderr ? 'Rename failed: ' + stderr.replace(/\n/g, ' ') : 'Rename failed'; 53 | console.log(errMsg); 54 | return reject(errMsg); 55 | } 56 | 57 | let result = new vscode.WorkspaceEdit(); 58 | 59 | if (canRenameToolUseDiff) { 60 | let filePatches = getEditsFromUnifiedDiffStr(stdout); 61 | filePatches.forEach((filePatch: FilePatch) => { 62 | let fileUri = vscode.Uri.file(filePatch.fileName); 63 | filePatch.edits.forEach((edit: Edit) => { 64 | edit.applyUsingWorkspaceEdit(result, fileUri); 65 | }); 66 | }); 67 | } 68 | 69 | return resolve(result); 70 | } catch (e) { 71 | reject(e); 72 | } 73 | }); 74 | }); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/goRunTestCodelens.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import path = require('path'); 10 | import { TextDocument, CancellationToken, CodeLens, Command } from '../src-vscode-mock/vscode'; 11 | import { getTestFunctions, getBenchmarkFunctions, getTestFlags } from './testUtils'; 12 | import { GoDocumentSymbolProvider } from './goOutline'; 13 | import { getCurrentGoPath } from './util'; 14 | import { GoBaseCodeLensProvider } from './goBaseCodelens'; 15 | 16 | export class GoRunTestCodeLensProvider extends GoBaseCodeLensProvider { 17 | private readonly debugConfig: any = { 18 | 'name': 'Launch', 19 | 'type': 'go', 20 | 'request': 'launch', 21 | 'mode': 'test', 22 | 'env': { 23 | 'GOPATH': getCurrentGoPath() // Passing current GOPATH to Delve as it runs in another process 24 | } 25 | }; 26 | 27 | public provideCodeLenses(document: TextDocument, token: CancellationToken): CodeLens[] | Thenable { 28 | if (!this.enabled) { 29 | return []; 30 | } 31 | let config = vscode.workspace.getConfiguration('go', document.uri); 32 | let codeLensConfig = config.get('enableCodeLens'); 33 | let codelensEnabled = codeLensConfig ? codeLensConfig['runtest'] : false; 34 | if (!codelensEnabled || !document.fileName.endsWith('_test.go')) { 35 | return []; 36 | } 37 | 38 | return Promise.all([ 39 | this.getCodeLensForPackage(document, token), 40 | this.getCodeLensForFunctions(config, document, token) 41 | ]).then(([pkg, fns]) => { 42 | let res = []; 43 | if (pkg && Array.isArray(pkg)) { 44 | res = res.concat(pkg); 45 | } 46 | if (fns && Array.isArray(fns)) { 47 | res = res.concat(fns); 48 | } 49 | return res; 50 | }); 51 | } 52 | 53 | private getCodeLensForPackage(document: TextDocument, token: CancellationToken): Thenable { 54 | let documentSymbolProvider = new GoDocumentSymbolProvider(); 55 | return documentSymbolProvider.provideDocumentSymbols(document, token) 56 | .then(symbols => symbols.find(sym => sym.kind === vscode.SymbolKind.Package && !!sym.name)) 57 | .then(pkg => { 58 | if (pkg) { 59 | const range = pkg.location.range; 60 | return [ 61 | new CodeLens(range, { 62 | title: 'run package tests', 63 | command: 'go.test.package' 64 | }), 65 | new CodeLens(range, { 66 | title: 'run file tests', 67 | command: 'go.test.file' 68 | }) 69 | ]; 70 | } 71 | }); 72 | } 73 | 74 | private getCodeLensForFunctions(vsConfig: vscode.WorkspaceConfiguration, document: TextDocument, token: CancellationToken): Thenable { 75 | const codelens: CodeLens[] = []; 76 | 77 | const testPromise = getTestFunctions(document, token).then(testFunctions => { 78 | testFunctions.forEach(func => { 79 | let runTestCmd: Command = { 80 | title: 'run test', 81 | command: 'go.test.cursor', 82 | arguments: [{ functionName: func.name }] 83 | }; 84 | 85 | const args = ['-test.run', func.name]; 86 | const program = path.dirname(document.fileName); 87 | const env = Object.assign({}, this.debugConfig.env, vsConfig['testEnvVars']); 88 | const envFile = vsConfig['testEnvFile']; 89 | let buildFlags = getTestFlags(vsConfig, null); 90 | if (vsConfig['buildTags'] && buildFlags.indexOf('-tags') === -1) { 91 | buildFlags.push('-tags'); 92 | buildFlags.push(`${vsConfig['buildTags']}`); 93 | } 94 | 95 | let config = Object.assign({}, this.debugConfig, { args, program, env, envFile, buildFlags: buildFlags.map(x => `'${x}'`).join(' ') }); 96 | let debugTestCmd: Command = { 97 | title: 'debug test', 98 | command: 'go.debug.startSession', 99 | arguments: [config] 100 | }; 101 | 102 | codelens.push(new CodeLens(func.location.range, runTestCmd)); 103 | codelens.push(new CodeLens(func.location.range, debugTestCmd)); 104 | }); 105 | }); 106 | 107 | const benchmarkPromise = getBenchmarkFunctions(document, token).then(benchmarkFunctions => { 108 | benchmarkFunctions.forEach(func => { 109 | let runBenchmarkCmd: Command = { 110 | title: 'run benchmark', 111 | command: 'go.benchmark.cursor', 112 | arguments: [{ functionName: func.name }] 113 | }; 114 | 115 | codelens.push(new CodeLens(func.location.range, runBenchmarkCmd)); 116 | }); 117 | 118 | }); 119 | 120 | return Promise.all([testPromise, benchmarkPromise]).then(() => codelens); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/goSignature.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------------------------------------------- 2 | * Copyright (c) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import * as vscode from '../src-vscode-mock/vscode'; 9 | import { SignatureHelpProvider, SignatureHelp, SignatureInformation, ParameterInformation, TextDocument, Position, CancellationToken, WorkspaceConfiguration } from '../src-vscode-mock/vscode'; 10 | import { definitionLocation } from './goDeclaration'; 11 | import { parameters } from './util'; 12 | 13 | export class GoSignatureHelpProvider implements SignatureHelpProvider { 14 | private goConfig = null; 15 | 16 | constructor(goConfig?: WorkspaceConfiguration) { 17 | this.goConfig = goConfig; 18 | } 19 | 20 | public provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken): Promise { 21 | if (!this.goConfig) { 22 | this.goConfig = vscode.workspace.getConfiguration('go', document.uri); 23 | } 24 | 25 | let theCall = this.walkBackwardsToBeginningOfCall(document, position); 26 | if (theCall == null) { 27 | return Promise.resolve(null); 28 | } 29 | let callerPos = this.previousTokenPosition(document, theCall.openParen); 30 | // Temporary fix to fall back to godoc if guru is the set docsTool 31 | let goConfig = this.goConfig; 32 | if (goConfig['docsTool'] === 'guru') { 33 | goConfig = Object.assign({}, goConfig, { 'docsTool': 'godoc' }); 34 | } 35 | return definitionLocation(document, callerPos, goConfig, true, token).then(res => { 36 | if (!res) { 37 | // The definition was not found 38 | return null; 39 | } 40 | if (res.line === callerPos.line) { 41 | // This must be a function definition 42 | return null; 43 | } 44 | let declarationText: string = (res.declarationlines || []).join(' ').trim(); 45 | if (!declarationText) { 46 | return null; 47 | } 48 | let result = new SignatureHelp(); 49 | let sig: string; 50 | let si: SignatureInformation; 51 | if (res.toolUsed === 'godef') { 52 | // declaration is of the form "Add func(a int, b int) int" 53 | let nameEnd = declarationText.indexOf(' '); 54 | let sigStart = nameEnd + 5; // ' func' 55 | let funcName = declarationText.substring(0, nameEnd); 56 | sig = declarationText.substring(sigStart); 57 | si = new SignatureInformation(funcName + sig, res.doc); 58 | } else if (res.toolUsed === 'gogetdoc') { 59 | // declaration is of the form "func Add(a int, b int) int" 60 | declarationText = declarationText.substring(5); 61 | let funcNameStart = declarationText.indexOf(res.name + '('); // Find 'functionname(' to remove anything before it 62 | if (funcNameStart > 0) { 63 | declarationText = declarationText.substring(funcNameStart); 64 | } 65 | si = new SignatureInformation(declarationText, res.doc); 66 | sig = declarationText.substring(res.name.length); 67 | } 68 | 69 | si.parameters = parameters(sig).map(paramText => 70 | new ParameterInformation(paramText) 71 | ); 72 | result.signatures = [si]; 73 | result.activeSignature = 0; 74 | result.activeParameter = Math.min(theCall.commas.length, si.parameters.length - 1); 75 | return result; 76 | }, () => { 77 | return null; 78 | }); 79 | } 80 | 81 | private previousTokenPosition(document: TextDocument, position: Position): Position { 82 | while (position.character > 0) { 83 | let word = document.getWordRangeAtPosition(position); 84 | if (word) { 85 | return word.start; 86 | } 87 | // [TypeFox] 88 | // position = position.translate(0, -1); 89 | position = new Position(position.line, position.character - 1); 90 | } 91 | return null; 92 | } 93 | 94 | private walkBackwardsToBeginningOfCall(document: TextDocument, position: Position): { openParen: Position, commas: Position[] } { 95 | let parenBalance = 0; 96 | let commas = []; 97 | let maxLookupLines = 30; 98 | 99 | for (let line = position.line; line >= 0 && maxLookupLines >= 0; line--, maxLookupLines--) { 100 | let currentLine = document.lineAt(line).text; 101 | let characterPosition = document.lineAt(line).text.length - 1; 102 | 103 | if (line === position.line) { 104 | characterPosition = position.character; 105 | currentLine = currentLine.substring(0, position.character); 106 | } 107 | 108 | for (let char = characterPosition; char >= 0; char--) { 109 | switch (currentLine[char]) { 110 | case '(': 111 | parenBalance--; 112 | if (parenBalance < 0) { 113 | return { 114 | openParen: new Position(line, char), 115 | commas: commas 116 | }; 117 | } 118 | break; 119 | case ')': 120 | parenBalance++; 121 | break; 122 | case ',': 123 | if (parenBalance === 0) { 124 | commas.push(new Position(line, char)); 125 | } 126 | } 127 | } 128 | } 129 | return null; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/goStatus.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | 'use strict'; 7 | 8 | import { GO_MODE } from './goMode'; 9 | import * as vscode from '../src-vscode-mock/vscode'; 10 | 11 | export let outputChannel = vscode.window.createOutputChannel('Go'); 12 | 13 | export let diagnosticsStatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); 14 | 15 | let statusBarEntry: vscode.StatusBarItem; 16 | 17 | export function showHideStatus() { 18 | if (!statusBarEntry) { 19 | return; 20 | } 21 | if (!vscode.window.activeTextEditor) { 22 | statusBarEntry.hide(); 23 | return; 24 | } 25 | if (vscode.languages.match(GO_MODE, vscode.window.activeTextEditor.document)) { 26 | statusBarEntry.show(); 27 | return; 28 | } 29 | statusBarEntry.hide(); 30 | } 31 | 32 | export function hideGoStatus() { 33 | if (statusBarEntry) { 34 | statusBarEntry.dispose(); 35 | } 36 | } 37 | 38 | export function showGoStatus(message: string, command: string, tooltip?: string) { 39 | statusBarEntry = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, Number.MIN_VALUE); 40 | statusBarEntry.text = message; 41 | statusBarEntry.command = command; 42 | statusBarEntry.color = 'yellow'; 43 | statusBarEntry.tooltip = tooltip; 44 | statusBarEntry.show(); 45 | } 46 | -------------------------------------------------------------------------------- /src/goSymbol.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | *--------------------------------------------------------*/ 4 | 5 | 'use strict'; 6 | 7 | import * as vscode from '../src-vscode-mock/vscode'; 8 | import cp = require('child_process'); 9 | import { getBinPath, getToolsEnvVars, killProcess } from './util'; 10 | import { promptForMissingTool, promptForUpdatingTool } from './goInstallTools'; 11 | 12 | // Keep in sync with github.com/acroca/go-symbols' 13 | interface GoSymbolDeclaration { 14 | name: string; 15 | kind: string; 16 | package: string; 17 | path: string; 18 | line: number; 19 | character: number; 20 | } 21 | 22 | export class GoWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider { 23 | 24 | private goKindToCodeKind: { [key: string]: vscode.SymbolKind } = { 25 | 'package': vscode.SymbolKind.Package, 26 | 'import': vscode.SymbolKind.Namespace, 27 | 'var': vscode.SymbolKind.Variable, 28 | 'type': vscode.SymbolKind.Interface, 29 | 'func': vscode.SymbolKind.Function, 30 | 'const': vscode.SymbolKind.Constant, 31 | }; 32 | 33 | public provideWorkspaceSymbols(query: string, token: vscode.CancellationToken): Thenable { 34 | let convertToCodeSymbols = (decls: GoSymbolDeclaration[], symbols: vscode.SymbolInformation[]): void => { 35 | decls.forEach(decl => { 36 | let kind: vscode.SymbolKind; 37 | if (decl.kind !== '') { 38 | kind = this.goKindToCodeKind[decl.kind]; 39 | } 40 | let pos = new vscode.Position(decl.line, decl.character); 41 | let symbolInfo = new vscode.SymbolInformation( 42 | decl.name, 43 | kind, 44 | new vscode.Range(pos, pos), 45 | vscode.Uri.file(decl.path), 46 | ''); 47 | symbols.push(symbolInfo); 48 | }); 49 | }; 50 | let root = vscode.workspace.rootPath; 51 | if (vscode.window.activeTextEditor && vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri)) { 52 | root = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri).uri.fsPath; 53 | } 54 | if (!root) { 55 | vscode.window.showInformationMessage('No workspace is open to find symbols.'); 56 | return; 57 | } 58 | 59 | return getWorkspaceSymbols(root, query, token).then(results => { 60 | let symbols: vscode.SymbolInformation[] = []; 61 | convertToCodeSymbols(results, symbols); 62 | return symbols; 63 | }); 64 | } 65 | } 66 | 67 | export function getWorkspaceSymbols(workspacePath: string, query: string, token: vscode.CancellationToken, goConfig?: vscode.WorkspaceConfiguration, ignoreFolderFeatureOn: boolean = true): Thenable { 68 | if (!goConfig) { 69 | goConfig = vscode.workspace.getConfiguration('go', vscode.window.activeTextEditor ? vscode.window.activeTextEditor.document.uri : null); 70 | } 71 | let gotoSymbolConfig = goConfig['gotoSymbol']; 72 | let ignoreFolders: string[] = gotoSymbolConfig ? gotoSymbolConfig['ignoreFolders'] : []; 73 | let args = (ignoreFolderFeatureOn && ignoreFolders && ignoreFolders.length > 0) ? ['-ignore', ignoreFolders.join(',')] : []; 74 | args.push(workspacePath); 75 | args.push(query); 76 | let gosyms = getBinPath('go-symbols'); 77 | let env = getToolsEnvVars(); 78 | let p: cp.ChildProcess; 79 | if (token) { 80 | token.onCancellationRequested(() => killProcess(p)); 81 | } 82 | 83 | return new Promise((resolve, reject) => { 84 | p = cp.execFile(gosyms, args, { maxBuffer: 1024 * 1024, env }, (err, stdout, stderr) => { 85 | try { 86 | if (err && (err).code === 'ENOENT') { 87 | promptForMissingTool('go-symbols'); 88 | } 89 | if (err && stderr && stderr.startsWith('flag provided but not defined: -ignore')) { 90 | promptForUpdatingTool('go-symbols'); 91 | p = null; 92 | return getWorkspaceSymbols(workspacePath, query, token, goConfig, false).then(results => { 93 | return resolve(results); 94 | }); 95 | } 96 | if (err) return resolve(null); 97 | let result = stdout.toString(); 98 | let decls = JSON.parse(result); 99 | return resolve(decls); 100 | } catch (e) { 101 | reject(e); 102 | } 103 | }); 104 | }); 105 | } 106 | -------------------------------------------------------------------------------- /src/goVet.ts: -------------------------------------------------------------------------------- 1 | import path = require('path'); 2 | import * as vscode from '../src-vscode-mock/vscode'; 3 | import { getToolsEnvVars, runTool, ICheckResult, handleDiagnosticErrors, getWorkspaceFolderPath } from './util'; 4 | import { outputChannel } from './goStatus'; 5 | import { diagnosticsStatusBarItem } from './goStatus'; 6 | 7 | /** 8 | * Runs go vet in the current package or workspace. 9 | */ 10 | export function vetCode(vetWorkspace?: boolean) { 11 | let editor = vscode.window.activeTextEditor; 12 | if (!editor && !vetWorkspace) { 13 | vscode.window.showInformationMessage('No editor is active, cannot find current package to vet'); 14 | return; 15 | } 16 | if (editor.document.languageId !== 'go' && !vetWorkspace) { 17 | vscode.window.showInformationMessage('File in the active editor is not a Go file, cannot find current package to vet'); 18 | return; 19 | } 20 | 21 | let documentUri = editor ? editor.document.uri : null; 22 | let goConfig = vscode.workspace.getConfiguration('go', documentUri); 23 | 24 | outputChannel.clear(); // Ensures stale output from vet on save is cleared 25 | diagnosticsStatusBarItem.show(); 26 | diagnosticsStatusBarItem.text = 'Vetting...'; 27 | 28 | goVet(documentUri, goConfig, vetWorkspace) 29 | .then(warnings => { 30 | handleDiagnosticErrors(editor ? editor.document : null, warnings, vscode.DiagnosticSeverity.Warning); 31 | diagnosticsStatusBarItem.hide(); 32 | }) 33 | .catch(err => { 34 | vscode.window.showInformationMessage('Error: ' + err); 35 | diagnosticsStatusBarItem.text = 'Vetting Failed'; 36 | }); 37 | } 38 | 39 | /** 40 | * Runs go vet or go tool vet and presents the output in the 'Go' channel and in the diagnostic collections. 41 | * 42 | * @param fileUri Document uri. 43 | * @param goConfig Configuration for the Go extension. 44 | * @param vetWorkspace If true vets code in all workspace. 45 | */ 46 | export function goVet(fileUri: vscode.Uri, goConfig: vscode.WorkspaceConfiguration, vetWorkspace?: boolean): Promise { 47 | if (running) { 48 | tokenSource.cancel(); 49 | } 50 | 51 | const currentWorkspace = getWorkspaceFolderPath(fileUri); 52 | const cwd = (vetWorkspace && currentWorkspace) ? currentWorkspace : path.dirname(fileUri.fsPath); 53 | if (!path.isAbsolute(cwd)) { 54 | return Promise.resolve([]); 55 | } 56 | 57 | const vetFlags = goConfig['vetFlags'] || []; 58 | const vetEnv = Object.assign({}, getToolsEnvVars()); 59 | const vetArgs = vetFlags.length ? ['tool', 'vet', ...vetFlags, '.'] : ['vet', './...']; 60 | 61 | running = true; 62 | const vetPromise = runTool( 63 | vetArgs, 64 | cwd, 65 | 'warning', 66 | true, 67 | null, 68 | vetEnv, 69 | false, 70 | tokenSource.token 71 | ).then((result) => { 72 | running = false; 73 | return result; 74 | }); 75 | 76 | return vetPromise; 77 | } 78 | 79 | let tokenSource = new vscode.CancellationTokenSource(); 80 | let running = false; 81 | -------------------------------------------------------------------------------- /test-lsp/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/theia-ide/go-language-server/efa486bc607cc80540fb3910f3f1e45058faff52/test-lsp/.gitignore -------------------------------------------------------------------------------- /test/fixtures/buildTags/hello.go: -------------------------------------------------------------------------------- 1 | // +build randomtag 2 | 3 | package main 4 | 5 | import "fmt" 6 | 7 | func main() { 8 | fmt.Prinln("hello") 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/completions/unimportedPkgs.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func print(txt string) { 8 | fmt.Println(txt) 9 | } 10 | func main() { 11 | by 12 | math. 13 | print("Hello") 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/diffTestData/file1.go: -------------------------------------------------------------------------------- 1 | package file1 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | function main(){ 9 | hello:= sayHello("my friend"); 10 | 11 | 12 | bye:= sayBye("my friend"); 13 | randomNumber:= math.Abs(-1); 14 | 15 | 16 | 17 | 18 | fmt.Println(hello); 19 | } 20 | 21 | //Line 1 comments for sayHello 22 | //Line 2 comments for sayHello 23 | //Line 3 comments for sayHello 24 | function sayHello(string name) string { 25 | return "Hello " + name; 26 | } 27 | 28 | //Line 1 comments for sayBye 29 | //Line 2 comments for sayBye 30 | //Line 3 comments for sayBye 31 | function sayBye(string name) string { 32 | return "Bye " + name; 33 | } -------------------------------------------------------------------------------- /test/fixtures/diffTestData/file2.go: -------------------------------------------------------------------------------- 1 | // test new content 2 | // in the start of the file 3 | package file1 4 | 5 | // test single line addition 6 | import ( 7 | "fmt" 8 | 9 | "math" 10 | ) 11 | 12 | // Random comments for main 13 | // Random2 comments for main 14 | function main(){ 15 | hello:= sayHello("my friends"); 16 | bye:= sayBye("my friends"); 17 | randomNumber:= math.Abs(-1); 18 | 19 | fmt.Println(hello); 20 | } 21 | 22 | //Updated Line 1 comments for sayHello 23 | //Updated Line 2 comments for sayHello 24 | function sayHello(string name) string { 25 | return "Hello " + name; 26 | } 27 | 28 | //Updated Line 1 comments for sayBye 29 | //Updated Line 2 comments for sayBye 30 | //Updated Line 3 comments for sayBye 31 | //New Line 4 comments for sayBye 32 | function sayBye(string name) string { 33 | return "Byeee " + name; 34 | } -------------------------------------------------------------------------------- /test/fixtures/errorsTest/errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func Print2(txt string) { 8 | fmt.Println(txt) 9 | } 10 | func main2() { 11 | prin("Hello") 12 | } 13 | -------------------------------------------------------------------------------- /test/fixtures/fillStruct/golden_1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "time" 4 | 5 | type Struct struct { 6 | String string 7 | Number int 8 | Float float64 9 | Time time.Time 10 | } 11 | 12 | func main() { 13 | myStruct := Struct{ 14 | String: "", 15 | Number: 0, 16 | Float: 0.0, 17 | Time: time.Time{}, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/fixtures/fillStruct/golden_2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | func main() { 8 | _ = http.Client{ 9 | Transport: nil, 10 | CheckRedirect: nil, 11 | Jar: nil, 12 | Timeout: 0, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/fillStruct/input_1.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "time" 4 | 5 | type Struct struct { 6 | String string 7 | Number int 8 | Float float64 9 | Time time.Time 10 | } 11 | 12 | func main() { 13 | myStruct := Struct{} 14 | } 15 | -------------------------------------------------------------------------------- /test/fixtures/fillStruct/input_2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | func main() { 8 | _ = http.Client{} 9 | } 10 | -------------------------------------------------------------------------------- /test/fixtures/generatetests/generatetests.go: -------------------------------------------------------------------------------- 1 | package generatetests 2 | 3 | type obj struct {} 4 | 5 | func (o obj) String() string { 6 | return "" 7 | } 8 | 9 | func main() { 10 | fmt.Println("do nothing") 11 | } -------------------------------------------------------------------------------- /test/fixtures/gogetdocTestData/test.go: -------------------------------------------------------------------------------- 1 | package abc 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "net" 7 | ) 8 | 9 | // ABC is a struct, you coudnt use Goto Definition or Hover info on this before 10 | // Now you can due to gogetdoc 11 | type ABC struct { 12 | a int 13 | b int 14 | c int 15 | } 16 | 17 | // This is an unexported function so couldnt get this comment on hover :( 18 | // Not anymore!! gogetdoc to the rescue 19 | func print(txt string) { 20 | fmt.Println(txt) 21 | } 22 | 23 | func main() { 24 | print("Hello") 25 | } 26 | 27 | // Hello is a method on the struct ABC. Will signature help understand this correctly 28 | func (abcd *ABC) Hello(s string, exclaim bool) string { 29 | net.CIDRMask(10, 20) 30 | if exclaim { 31 | s = s + "!" 32 | } 33 | if abcd.a+abcd.b+abcd.c > 3 { 34 | return "Hello " + s 35 | } 36 | return "GoodBye " + s 37 | } 38 | 39 | // Greetings is an exported function. So all is good. 40 | func Greetings() string { 41 | xyz := ABC{1, 2, int(math.Abs(-1))} 42 | return xyz.Hello("World", false) 43 | } 44 | -------------------------------------------------------------------------------- /test/fixtures/importTest/groupImports.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | ) 7 | 8 | func one() { 9 | fmt.Print(math.Max(1, 2)) 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/importTest/noimports.go: -------------------------------------------------------------------------------- 1 | package hello 2 | -------------------------------------------------------------------------------- /test/fixtures/importTest/singleImports.go: -------------------------------------------------------------------------------- 1 | package hello 2 | 3 | import "fmt" 4 | import . "math" // comment 5 | 6 | func two() { 7 | fmt.Print(Max(1, 2)) 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/linterTest/linter_1.go: -------------------------------------------------------------------------------- 1 | package linterTest 2 | 3 | import "fmt" 4 | 5 | func ExportedFunc() { 6 | a := 10 7 | func() { 8 | a := 20 9 | }() 10 | 11 | fmt.Println("OUTER A: ", a) 12 | } 13 | -------------------------------------------------------------------------------- /test/fixtures/linterTest/linter_2.go: -------------------------------------------------------------------------------- 1 | package linterTest 2 | 3 | func secondFunc() error { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/sample_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "testing" 7 | ) 8 | 9 | func hello() { 10 | fmt.Println("Hello") 11 | } 12 | 13 | // TestMe 14 | func TestMe(t *testing.T) { 15 | if os.Getenv("dummyEnvVar") != "dummyEnvValue" { 16 | t.Errorf("Oops! Value for the variable is %q", os.Getenv("dummyEnvVar")) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtures/test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func print(txt string) { 8 | fmt.Println(txt) 9 | } 10 | func main() { 11 | print("Hello") 12 | } -------------------------------------------------------------------------------- /test/index.ts: -------------------------------------------------------------------------------- 1 | /*--------------------------------------------------------- 2 | * Copyright (C) Microsoft Corporation. All rights reserved. 3 | * Licensed under the MIT License. See License.txt in the project root for license information. 4 | *--------------------------------------------------------*/ 5 | 6 | // 7 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING 8 | // 9 | // This file is providing the test runner to use when running extension tests. 10 | // By default the test runner in use is Mocha based. 11 | // 12 | // You can provide your own test runner if you want to override it by exporting 13 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension 14 | // host can call to run the tests. The test runner is expected to use console.log 15 | // to report the results back to the caller. When the tests are finished, return 16 | // a possible error to the callback or null if none. 17 | 18 | let testRunner = require('vscode/lib/testrunner'); 19 | 20 | // You can directly control Mocha options by uncommenting the following lines 21 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info 22 | testRunner.configure({ 23 | ui: 'tdd', // the TDD UI is being used in extension.test.ts (suite, test, etc.) 24 | useColors: true, // colored output from test results 25 | timeout: 5000 26 | }); 27 | 28 | module.exports = testRunner; 29 | -------------------------------------------------------------------------------- /thirdpartynotices.txt: -------------------------------------------------------------------------------- 1 | THIRD-PARTY SOFTWARE NOTICES AND INFORMATION 2 | For TypeFox go-language-server 3 | 4 | This project incorporates material from the projects listed below. The original copyright notice 5 | and the license under which TypeFox received such material are set out below. TypeFox reserves 6 | all other rights not expressly granted, whether by implication, estoppel or otherwise. 7 | 8 | 1. vscode-go version 0.6.67 9 | 10 | The MIT License (MIT) 11 | 12 | Copyright (c) Microsoft Corporation 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | 32 | 33 | 34 | For Microsoft vscode-go 35 | 36 | This project incorporates material from the projects listed below. The original copyright notice 37 | and the license under which Microsoft received such material are set out below. Microsoft reserves 38 | all other rights not expressly granted, whether by implication, estoppel or otherwise. 39 | 40 | 41 | 1. json-rpc2 version 1.0.2 (https://github.com/pocesar/node-jsonrpc2) 42 | 43 | The MIT License 44 | 45 | Copyright (C) 2009 Eric Florenzano and 46 | Ryan Tomayko 47 | 48 | Permission is hereby granted, free of charge, to any person ob- 49 | taining a copy of this software and associated documentation 50 | files (the "Software"), to deal in the Software without restric- 51 | tion, including without limitation the rights to use, copy, modi- 52 | fy, merge, publish, distribute, sublicense, and/or sell copies of 53 | the Software, and to permit persons to whom the Software is fur- 54 | nished to do so, subject to the following conditions: 55 | 56 | The above copyright notice and this permission notice shall be 57 | included in all copies or substantial portions of the Software. 58 | 59 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 60 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 61 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN- 62 | FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 63 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 64 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 65 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 66 | SOFTWARE. 67 | 68 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "outDir": "out", 5 | "sourceMap": true, 6 | "target": "es6", 7 | "lib": [ 8 | "es2015" 9 | ] 10 | }, 11 | "include": [ 12 | "src", 13 | "src-vscode-mock" 14 | ], 15 | "exclude": [ 16 | "node_modules" 17 | ] 18 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "class-name": true, 4 | "comment-format": [ 5 | true, 6 | "check-space" 7 | ], 8 | "indent": [ 9 | true, 10 | "tabs" 11 | ], 12 | "no-duplicate-variable": true, 13 | "no-eval": true, 14 | "no-internal-module": true, 15 | "no-trailing-whitespace": true, 16 | "no-var-keyword": true, 17 | // "no-unused-variable": true, 18 | "one-line": [ 19 | true, 20 | "check-open-brace", 21 | "check-whitespace" 22 | ], 23 | "quotemark": [ 24 | true, 25 | "single" 26 | ], 27 | "semicolon": true, 28 | "triple-equals": [ 29 | true, 30 | "allow-null-check" 31 | ], 32 | "typedef-whitespace": [ 33 | true, 34 | { 35 | "call-signature": "nospace", 36 | "index-signature": "nospace", 37 | "parameter": "nospace", 38 | "property-declaration": "nospace", 39 | "variable-declaration": "nospace" 40 | } 41 | ], 42 | "variable-name": [ 43 | true, 44 | "ban-keywords" 45 | ], 46 | "whitespace": [ 47 | true, 48 | "check-branch", 49 | "check-decl", 50 | "check-operator", 51 | "check-separator", 52 | "check-type" 53 | ] 54 | } 55 | } -------------------------------------------------------------------------------- /typings/diff.d.ts: -------------------------------------------------------------------------------- 1 | // Type definitions for diff 2 | // Project: https://github.com/kpdecker/jsdiff 3 | // Definitions by: vvakame 4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 5 | 6 | declare namespace JsDiff { 7 | interface IDiffResult { 8 | value: string; 9 | count?: number; 10 | added?: boolean; 11 | removed?: boolean; 12 | } 13 | 14 | interface IBestPath { 15 | newPos: number; 16 | componenets: IDiffResult[]; 17 | } 18 | 19 | interface IHunk { 20 | oldStart: number; 21 | oldLines: number; 22 | newStart: number; 23 | newLines: number; 24 | lines: string[]; 25 | } 26 | 27 | interface IUniDiff { 28 | oldFileName: string; 29 | newFileName: string; 30 | oldHeader: string; 31 | newHeader: string; 32 | hunks: IHunk[]; 33 | } 34 | 35 | class Diff { 36 | ignoreWhitespace:boolean; 37 | 38 | constructor(ignoreWhitespace?:boolean); 39 | 40 | diff(oldString:string, newString:string):IDiffResult[]; 41 | 42 | pushComponent(components:IDiffResult[], value:string, added:boolean, removed:boolean):void; 43 | 44 | extractCommon(basePath:IBestPath, newString:string, oldString:string, diagonalPath:number):number; 45 | 46 | equals(left:string, right:string):boolean; 47 | 48 | join(left:string, right:string):string; 49 | 50 | tokenize(value:string):any; // return types are string or string[] 51 | } 52 | 53 | function diffChars(oldStr:string, newStr:string):IDiffResult[]; 54 | 55 | function diffWords(oldStr:string, newStr:string):IDiffResult[]; 56 | 57 | function diffWordsWithSpace(oldStr:string, newStr:string):IDiffResult[]; 58 | 59 | function diffJson(oldObj: Object, newObj: Object): IDiffResult[]; 60 | 61 | function diffLines(oldStr:string, newStr:string):IDiffResult[]; 62 | 63 | function diffCss(oldStr:string, newStr:string):IDiffResult[]; 64 | 65 | function createPatch(fileName: string, oldStr: string, newStr: string, oldHeader: string, newHeader: string, options?: {context: number}): string; 66 | 67 | function createTwoFilesPatch(oldFileName: string, newFileName: string, oldStr: string, newStr: string, oldHeader: string, newHeader: string, options?: {context: number}): string; 68 | 69 | function structuredPatch(oldFileName: string, newFileName: string, oldStr: string, newStr: string, oldHeader: string, newHeader: string, options?: {context: number}): IUniDiff; 70 | 71 | function applyPatch(oldStr: string, uniDiff: string | IUniDiff | IUniDiff[]): string; 72 | 73 | function applyPatches(uniDiff: IUniDiff[], options: { 74 | loadFile: (index: number, callback: (err: Error, data: string) => void) => void, 75 | patched: (index: number, content: string) => void, 76 | complete: (err?: Error) => void 77 | }): void; 78 | 79 | function parsePatch(diffStr: string, options?: {strict: boolean}): IUniDiff[]; 80 | 81 | function convertChangesToXML(changes:IDiffResult[]):string; 82 | 83 | function convertChangesToDMP(changes:IDiffResult[]):{0: number; 1:string;}[]; 84 | } 85 | 86 | declare module "diff" { 87 | export = JsDiff; 88 | } -------------------------------------------------------------------------------- /typings/json-rpc2.d.ts: -------------------------------------------------------------------------------- 1 | declare module "json-rpc2" { 2 | export interface RPCConnection { 3 | call(command: string, args: any[], callback: (err: Error, result:T) => void): void; 4 | } 5 | 6 | export class Client { 7 | static $create(port: number, addr: string): Client; 8 | connectSocket(callback: (err: Error, conn: RPCConnection) => void): void; 9 | } 10 | } -------------------------------------------------------------------------------- /vscode-api.txt: -------------------------------------------------------------------------------- 1 | grep -r -o -h -e 'vscode[\.\-][a-zA-Z0-9_\.\-]*' src | sort -u > vscode-api.txt 2 | 3 | vscode-debug-logger 4 | vscode-debugadapter 5 | vscode-debugprotocol 6 | vscode-extension-telemetry 7 | vscode-go 8 | vscode-go-debug.txt 9 | vscode-languageclient 10 | vscode.CancellationToken 11 | vscode.CancellationTokenSource 12 | vscode.CodeActionContext 13 | vscode.CodeActionProvider 14 | vscode.CodeLens 15 | vscode.CodeLensProvider 16 | vscode.Command 17 | vscode.CompletionItem 18 | vscode.CompletionItemKind 19 | vscode.CompletionItemKind.Field 20 | vscode.CompletionItemKind.Function 21 | vscode.CompletionItemKind.Keyword 22 | vscode.CompletionItemKind.Module 23 | vscode.CompletionItemKind.Property 24 | vscode.CompletionItemKind.Snippet 25 | vscode.CompletionItemProvider 26 | vscode.DebugConfiguration 27 | vscode.DebugConfigurationProvider 28 | vscode.Definition 29 | vscode.DefinitionProvider 30 | vscode.Diagnostic 31 | vscode.DiagnosticCollection 32 | vscode.DiagnosticSeverity 33 | vscode.DiagnosticSeverity.Error 34 | vscode.DiagnosticSeverity.Warning 35 | vscode.Disposable 36 | vscode.DocumentFilter 37 | vscode.DocumentFormattingEditProvider 38 | vscode.DocumentSymbolProvider 39 | vscode.Event 40 | vscode.EventEmitter 41 | vscode.ExtensionContext 42 | vscode.FormattingOptions 43 | vscode.ImplementationProvider 44 | vscode.Location 45 | vscode.Position 46 | vscode.ProviderResult 47 | vscode.Range 48 | vscode.ReferenceProvider 49 | vscode.RenameProvider 50 | vscode.SnippetString 51 | vscode.StatusBarAlignment.Left 52 | vscode.StatusBarAlignment.Right 53 | vscode.StatusBarItem 54 | vscode.SymbolInformation 55 | vscode.SymbolKind 56 | vscode.SymbolKind.Constant 57 | vscode.SymbolKind.Function 58 | vscode.SymbolKind.Interface 59 | vscode.SymbolKind.Namespace 60 | vscode.SymbolKind.Package 61 | vscode.SymbolKind.Variable 62 | vscode.TextDocument 63 | vscode.TextDocumentChangeEvent 64 | vscode.TextEdit 65 | vscode.TextEdit.insert 66 | vscode.TextEditor 67 | vscode.Uri 68 | vscode.Uri.file 69 | vscode.Uri.parse 70 | vscode.WorkspaceConfiguration 71 | vscode.WorkspaceEdit 72 | vscode.WorkspaceFolder 73 | vscode.WorkspaceSymbolProvider 74 | vscode.commands.executeCommand 75 | vscode.commands.registerCommand 76 | vscode.debug.registerDebugConfigurationProvider 77 | vscode.debug.startDebugging 78 | vscode.extensions.getExtension 79 | vscode.languages.createDiagnosticCollection 80 | vscode.languages.match 81 | vscode.languages.registerCodeActionsProvider 82 | vscode.languages.registerCodeLensProvider 83 | vscode.languages.registerCompletionItemProvider 84 | vscode.languages.registerDefinitionProvider 85 | vscode.languages.registerDocumentFormattingEditProvider 86 | vscode.languages.registerDocumentSymbolProvider 87 | vscode.languages.registerHoverProvider 88 | vscode.languages.registerImplementationProvider 89 | vscode.languages.registerReferenceProvider 90 | vscode.languages.registerRenameProvider 91 | vscode.languages.registerSignatureHelpProvider 92 | vscode.languages.registerWorkspaceSymbolProvider 93 | vscode.languages.setLanguageConfiguration 94 | vscode.open 95 | vscode.window.activeTextEditor 96 | vscode.window.activeTextEditor.document 97 | vscode.window.activeTextEditor.document.fileName 98 | vscode.window.activeTextEditor.document.getText 99 | vscode.window.activeTextEditor.document.languageId 100 | vscode.window.activeTextEditor.document.uri 101 | vscode.window.activeTextEditor.edit 102 | vscode.window.activeTextEditor.selection 103 | vscode.window.createOutputChannel 104 | vscode.window.createStatusBarItem 105 | vscode.window.createTextEditorDecorationType 106 | vscode.window.onDidChangeActiveTextEditor 107 | vscode.window.showErrorMessage 108 | vscode.window.showInformationMessage 109 | vscode.window.showInputBox 110 | vscode.window.showQuickPick 111 | vscode.window.showTextDocument 112 | vscode.window.visibleTextEditors.find 113 | vscode.window.visibleTextEditors.forEach 114 | vscode.workspace.applyEdit 115 | vscode.workspace.getConfiguration 116 | vscode.workspace.getWorkspaceFolder 117 | vscode.workspace.onDidChangeConfiguration 118 | vscode.workspace.onDidChangeTextDocument 119 | vscode.workspace.onDidSaveTextDocument 120 | vscode.workspace.openTextDocument 121 | vscode.workspace.rootPath 122 | vscode.workspace.saveAll 123 | vscode.workspace.workspaceFolders 124 | vscode.workspace.workspaceFolders.find 125 | vscode.workspace.workspaceFolders.length 126 | --------------------------------------------------------------------------------