src/goDocumentSymbols: refactor DocumentSymbolProvider to remove fallback code
This is an attempt to split out the code running go-outline
and the code that asks gopls for the document symbols.
This change also removes the fallback to go-outline if
the language server is enabled. This means that users of old gopls may
get worse results, since they cannot list packages.
This change affects identifying test functions.
Change-Id: I271db88ef4f5307791b7954f102b2f3aa859f38b
Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/403894
Reviewed-by: Jamal Carvalho <jamal@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Run-TryBot: Suzy Mueller <suzmue@golang.org>
diff --git a/src/goDocumentSymbols.ts b/src/goDocumentSymbols.ts
new file mode 100644
index 0000000..2da40ca
--- /dev/null
+++ b/src/goDocumentSymbols.ts
@@ -0,0 +1,102 @@
+/*---------------------------------------------------------
+ * Copyright 2022 The Go Authors. All rights reserved.
+ * Licensed under the MIT License. See LICENSE in the project root for license information.
+ *--------------------------------------------------------*/
+
+import vscode = require('vscode');
+import { ExecuteCommandParams, ExecuteCommandRequest } from 'vscode-languageserver-protocol';
+import { getGoConfig } from './config';
+import { goCtx } from './goMain';
+import { GoLegacyDocumentSymbolProvider } from './language/legacy/goOutline';
+
+export function GoDocumentSymbolProvider(
+ includeImports?: boolean
+): GoplsDocumentSymbolProvider | GoLegacyDocumentSymbolProvider {
+ const { latestConfig } = goCtx;
+ if (!latestConfig?.enabled) {
+ return new GoLegacyDocumentSymbolProvider(includeImports);
+ }
+ return new GoplsDocumentSymbolProvider(includeImports);
+}
+
+const GOPLS_LIST_IMPORTS = 'gopls.list_imports';
+export class GoplsDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
+ constructor(private includeImports?: boolean) {}
+
+ public async provideDocumentSymbols(document: vscode.TextDocument): Promise<vscode.DocumentSymbol[]> {
+ if (typeof this.includeImports !== 'boolean') {
+ const gotoSymbolConfig = getGoConfig(document.uri)['gotoSymbol'];
+ this.includeImports = gotoSymbolConfig ? gotoSymbolConfig['includeImports'] : false;
+ }
+ const { languageClient, serverInfo } = goCtx;
+ if (!languageClient) {
+ return [];
+ }
+
+ const symbols: vscode.DocumentSymbol[] | undefined = await vscode.commands.executeCommand(
+ 'vscode.executeDocumentSymbolProvider',
+ document.uri
+ );
+ if (!symbols || symbols.length === 0) {
+ return [];
+ }
+
+ // Stitch the results together to make the results look like
+ // go-outline.
+ let pkgDeclRng = new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0));
+ let pkgName = '';
+
+ // Try to find the package statement.
+ const text = document.getText();
+ const packageStatement = new RegExp('^[ \\t]*package[ \\t]*(\\S+)', 'm');
+ const match = packageStatement.exec(text);
+ if (match && match.length === 2) {
+ const packageDecl = match[0];
+ const start = text.indexOf(packageDecl);
+ pkgDeclRng = new vscode.Range(document.positionAt(start), document.positionAt(start + packageDecl.length));
+ pkgName = packageDecl[1];
+ }
+ const packageSymbol = new vscode.DocumentSymbol(
+ pkgName,
+ 'package',
+ vscode.SymbolKind.Package,
+ pkgDeclRng,
+ pkgDeclRng
+ );
+ packageSymbol.children = symbols;
+ if (this.includeImports && serverInfo?.Commands?.includes(GOPLS_LIST_IMPORTS)) {
+ try {
+ const imports = await listImports(document);
+ imports?.forEach((value) => {
+ packageSymbol.children.unshift(
+ new vscode.DocumentSymbol(
+ value.Path,
+ 'import',
+ vscode.SymbolKind.Namespace,
+ new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)),
+ new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0))
+ )
+ );
+ });
+ } catch (err) {
+ console.log('Failed to list imports: {err}');
+ }
+ }
+ return [packageSymbol];
+ }
+}
+
+async function listImports(document: vscode.TextDocument): Promise<{ Path: string; Name: string }[]> {
+ const { languageClient } = goCtx;
+ const uri = languageClient?.code2ProtocolConverter.asTextDocumentIdentifier(document).uri;
+ const params: ExecuteCommandParams = {
+ command: GOPLS_LIST_IMPORTS,
+ arguments: [
+ {
+ URI: uri
+ }
+ ]
+ };
+ const resp = await languageClient?.sendRequest(ExecuteCommandRequest.type, params);
+ return resp.Imports;
+}
diff --git a/src/goGenerateTests.ts b/src/goGenerateTests.ts
index 756dca8..e06354d 100644
--- a/src/goGenerateTests.ts
+++ b/src/goGenerateTests.ts
@@ -14,7 +14,7 @@
import { getGoConfig } from './config';
import { toolExecutionEnvironment } from './goEnv';
import { promptForMissingTool } from './goInstallTools';
-import { GoDocumentSymbolProvider } from './language/legacy/goOutline';
+import { GoDocumentSymbolProvider } from './goDocumentSymbols';
import { outputChannel } from './goStatus';
import { getBinPath } from './util';
@@ -223,7 +223,7 @@
}
async function getFunctions(doc: vscode.TextDocument): Promise<vscode.DocumentSymbol[]> {
- const documentSymbolProvider = new GoDocumentSymbolProvider();
+ const documentSymbolProvider = GoDocumentSymbolProvider();
const symbols = await documentSymbolProvider.provideDocumentSymbols(doc);
return symbols[0].children.filter((sym) =>
[vscode.SymbolKind.Function, vscode.SymbolKind.Method].includes(sym.kind)
diff --git a/src/goReferencesCodelens.ts b/src/goReferencesCodelens.ts
index b78a3a2..fc9cb67 100644
--- a/src/goReferencesCodelens.ts
+++ b/src/goReferencesCodelens.ts
@@ -10,7 +10,7 @@
import { CancellationToken, CodeLens, Range, TextDocument } from 'vscode';
import { getGoConfig } from './config';
import { GoBaseCodeLensProvider } from './goBaseCodelens';
-import { GoDocumentSymbolProvider } from './language/legacy/goOutline';
+import { GoDocumentSymbolProvider } from './goDocumentSymbols';
import { GoReferenceProvider } from './language/legacy/goReferences';
import { getBinPath } from './util';
import vscode = require('vscode');
@@ -89,7 +89,7 @@
document: TextDocument,
token: CancellationToken
): Promise<vscode.DocumentSymbol[]> {
- const symbolProvider = new GoDocumentSymbolProvider();
+ const symbolProvider = GoDocumentSymbolProvider();
const isTestFile = document.fileName.endsWith('_test.go');
const symbols = await symbolProvider.provideDocumentSymbols(document, token);
return symbols[0].children.filter((symbol) => {
diff --git a/src/goRunTestCodelens.ts b/src/goRunTestCodelens.ts
index 886fccf..77847ca 100644
--- a/src/goRunTestCodelens.ts
+++ b/src/goRunTestCodelens.ts
@@ -11,7 +11,7 @@
import { CancellationToken, CodeLens, TextDocument } from 'vscode';
import { getGoConfig } from './config';
import { GoBaseCodeLensProvider } from './goBaseCodelens';
-import { GoDocumentSymbolProvider } from './language/legacy/goOutline';
+import { GoDocumentSymbolProvider } from './goDocumentSymbols';
import { getBenchmarkFunctions, getTestFunctions } from './testUtils';
export class GoRunTestCodeLensProvider extends GoBaseCodeLensProvider {
@@ -36,7 +36,7 @@
}
private async getCodeLensForPackage(document: TextDocument, token: CancellationToken): Promise<CodeLens[]> {
- const documentSymbolProvider = new GoDocumentSymbolProvider();
+ const documentSymbolProvider = GoDocumentSymbolProvider();
const symbols = await documentSymbolProvider.provideDocumentSymbols(document, token);
if (!symbols || symbols.length === 0) {
return [];
diff --git a/src/goTest/explore.ts b/src/goTest/explore.ts
index 8477d50..412798e 100644
--- a/src/goTest/explore.ts
+++ b/src/goTest/explore.ts
@@ -18,7 +18,7 @@
WorkspaceFoldersChangeEvent
} from 'vscode';
import vscode = require('vscode');
-import { GoDocumentSymbolProvider } from '../language/legacy/goOutline';
+import { GoDocumentSymbolProvider } from '../goDocumentSymbols';
import { outputChannel } from '../goStatus';
import { dispose, disposeIfEmpty, findItem, GoTest, isInTest, Workspace } from './utils';
import { GoTestResolver, ProvideSymbols } from './resolve';
@@ -35,7 +35,7 @@
if (!isVscodeTestingAPIAvailable) throw new Error('VSCode Testing API is unavailable');
const ctrl = vscode.tests.createTestController('go', 'Go');
- const symProvider = new GoDocumentSymbolProvider(true);
+ const symProvider = GoDocumentSymbolProvider(true);
const inst = new this(workspace, ctrl, context.workspaceState, (doc, token) =>
symProvider.provideDocumentSymbols(doc, token)
);
diff --git a/src/language/legacy/goOutline.ts b/src/language/legacy/goOutline.ts
index 6552955..f8d4a70 100644
--- a/src/language/legacy/goOutline.ts
+++ b/src/language/legacy/goOutline.ts
@@ -8,11 +8,9 @@
import cp = require('child_process');
import vscode = require('vscode');
-import { ExecuteCommandParams, ExecuteCommandRequest } from 'vscode-languageserver-protocol';
import { getGoConfig } from '../../config';
import { toolExecutionEnvironment } from '../../goEnv';
import { promptForMissingTool, promptForUpdatingTool } from '../../goInstallTools';
-import { goCtx } from '../../goMain';
import { getBinPath, getFileArchive, makeMemoizedByteOffsetConverter } from '../../util';
import { killProcess } from '../../utils/processUtils';
@@ -193,9 +191,7 @@
return symbols;
}
-const GOPLS_LIST_IMPORTS = 'gopls.list_imports';
-
-export class GoDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
+export class GoLegacyDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
constructor(private includeImports?: boolean) {}
public async provideDocumentSymbols(
@@ -206,69 +202,6 @@
const gotoSymbolConfig = getGoConfig(document.uri)['gotoSymbol'];
this.includeImports = gotoSymbolConfig ? gotoSymbolConfig['includeImports'] : false;
}
-
- const { languageClient, serverInfo } = goCtx;
- // TODO(suzmue): Check the commands available instead of the version.
- if (languageClient && serverInfo?.Commands?.includes(GOPLS_LIST_IMPORTS)) {
- const symbols: vscode.DocumentSymbol[] | undefined = await vscode.commands.executeCommand(
- 'vscode.executeDocumentSymbolProvider',
- document.uri
- );
- if (!symbols || symbols.length === 0) {
- return [];
- }
-
- // Stitch the results together to make the results look like
- // go-outline.
- let pkgDeclRng = new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0));
- let pkgName = '';
-
- // Try to find the package statement.
- const text = document.getText();
- const packageStatement = new RegExp('^[ \\t]*package[ \\t]*(\\S+)', 'm');
- const match = packageStatement.exec(text);
- if (match && match.length === 2) {
- const packageDecl = match[0];
- const start = text.indexOf(packageDecl);
- pkgDeclRng = new vscode.Range(
- document.positionAt(start),
- document.positionAt(start + packageDecl.length)
- );
- pkgName = packageDecl[1];
- }
- const packageSymbol = new vscode.DocumentSymbol(
- pkgName,
- 'package',
- vscode.SymbolKind.Package,
- pkgDeclRng,
- pkgDeclRng
- );
- packageSymbol.children = symbols;
- if (this.includeImports) {
- try {
- const imports = await listImports(document);
- imports?.forEach((value) => {
- packageSymbol.children.unshift(
- new vscode.DocumentSymbol(
- value.Path,
- 'import',
- vscode.SymbolKind.Namespace,
- new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0)),
- new vscode.Range(new vscode.Position(0, 0), new vscode.Position(0, 0))
- )
- );
- });
- } catch (e) {
- // Fall back to use go-outline.
- return this.runGoOutline(document, token);
- }
- }
- return [packageSymbol];
- }
- return this.runGoOutline(document, token);
- }
-
- private runGoOutline(document: vscode.TextDocument, token?: vscode.CancellationToken) {
const options: GoOutlineOptions = {
fileName: document.fileName,
document,
@@ -277,18 +210,3 @@
return documentSymbols(options, token);
}
}
-
-async function listImports(document: vscode.TextDocument): Promise<{ Path: string; Name: string }[]> {
- const { languageClient } = goCtx;
- const uri = languageClient?.code2ProtocolConverter.asTextDocumentIdentifier(document).uri;
- const params: ExecuteCommandParams = {
- command: GOPLS_LIST_IMPORTS,
- arguments: [
- {
- URI: uri
- }
- ]
- };
- const resp = await languageClient?.sendRequest(ExecuteCommandRequest.type, params);
- return resp.Imports;
-}
diff --git a/src/language/registerDefaultProviders.ts b/src/language/registerDefaultProviders.ts
index a5380ac..86a1d20 100644
--- a/src/language/registerDefaultProviders.ts
+++ b/src/language/registerDefaultProviders.ts
@@ -13,7 +13,7 @@
import { GoImplementationProvider } from './legacy/goImplementations';
import { parseLiveFile } from './legacy/goLiveErrors';
import { GO_MODE } from '../goMode';
-import { GoDocumentSymbolProvider } from './legacy/goOutline';
+import { GoLegacyDocumentSymbolProvider } from './legacy/goOutline';
import { GoReferenceProvider } from './legacy/goReferences';
import { GoRenameProvider } from './legacy/goRename';
import { GoSignatureHelpProvider } from './legacy/goSignature';
@@ -32,7 +32,7 @@
this._disposables.push(vscode.languages.registerDefinitionProvider(GO_MODE, new GoDefinitionProvider()));
this._disposables.push(vscode.languages.registerReferenceProvider(GO_MODE, new GoReferenceProvider()));
this._disposables.push(
- vscode.languages.registerDocumentSymbolProvider(GO_MODE, new GoDocumentSymbolProvider())
+ vscode.languages.registerDocumentSymbolProvider(GO_MODE, new GoLegacyDocumentSymbolProvider())
);
this._disposables.push(vscode.languages.registerWorkspaceSymbolProvider(new GoWorkspaceSymbolProvider()));
this._disposables.push(
diff --git a/src/testUtils.ts b/src/testUtils.ts
index 1c7c2fd..8e8e42d 100644
--- a/src/testUtils.ts
+++ b/src/testUtils.ts
@@ -15,7 +15,7 @@
import { applyCodeCoverageToAllEditors } from './goCover';
import { toolExecutionEnvironment } from './goEnv';
import { getCurrentPackage } from './goModules';
-import { GoDocumentSymbolProvider } from './language/legacy/goOutline';
+import { GoDocumentSymbolProvider } from './goDocumentSymbols';
import { getNonVendorPackages } from './goPackages';
import { getBinPath, getCurrentGoPath, getTempFilePath, LineBuffer, resolvePath } from './util';
import { parseEnvFile } from './utils/envUtils';
@@ -144,7 +144,7 @@
doc: vscode.TextDocument,
token?: vscode.CancellationToken
): Promise<vscode.DocumentSymbol[] | undefined> {
- const documentSymbolProvider = new GoDocumentSymbolProvider(true);
+ const documentSymbolProvider = GoDocumentSymbolProvider(true);
const symbols = await documentSymbolProvider.provideDocumentSymbols(doc, token);
if (!symbols || symbols.length === 0) {
return;
@@ -226,7 +226,7 @@
doc: vscode.TextDocument,
token?: vscode.CancellationToken
): Promise<vscode.DocumentSymbol[] | undefined> {
- const documentSymbolProvider = new GoDocumentSymbolProvider();
+ const documentSymbolProvider = GoDocumentSymbolProvider();
const symbols = await documentSymbolProvider.provideDocumentSymbols(doc, token);
if (!symbols || symbols.length === 0) {
return;
diff --git a/test/integration/extension.test.ts b/test/integration/extension.test.ts
index 157825f..285dcb0 100644
--- a/test/integration/extension.test.ts
+++ b/test/integration/extension.test.ts
@@ -27,12 +27,8 @@
import { updateGoVarsFromConfig } from '../../src/goInstallTools';
import { buildLanguageServerConfig } from '../../src/language/goLanguageServer';
import { goLint } from '../../src/goLint';
-import {
- documentSymbols,
- GoDocumentSymbolProvider,
- GoOutlineImportsOptions
-} from '../../src/language/legacy/goOutline';
-import { getAllPackages } from '../../src/goPackages';
+import { documentSymbols, GoOutlineImportsOptions } from '../../src/language/legacy/goOutline';
+import { GoDocumentSymbolProvider } from '../../src/goDocumentSymbols';
import { goPlay } from '../../src/goPlayground';
import { GoSignatureHelpProvider } from '../../src/language/legacy/goSignature';
import { GoCompletionItemProvider } from '../../src/language/legacy/goSuggest';
@@ -734,7 +730,7 @@
test('Test Outline document symbols', async () => {
const uri = vscode.Uri.file(path.join(fixturePath, 'outlineTest', 'test.go'));
const document = await vscode.workspace.openTextDocument(uri);
- const symbolProvider = new GoDocumentSymbolProvider();
+ const symbolProvider = GoDocumentSymbolProvider();
const outlines = await symbolProvider.provideDocumentSymbols(document, dummyCancellationSource.token);
const packages = outlines.filter((x) => x.kind === vscode.SymbolKind.Package);