Skip to content

Commit f38e348

Browse files
committed
src/goLanguageServer: propagate go.buildFlags,buildTags to gopls
They are passed as gopls.buildFlags only if gopls.buildFlags isn't already explicitly set - users have to use JSON-based UI and add the fields (including []). If go.buildFlags has -tags, go.buildTags is ignored, which is consistent with the extension's build behavior coded in goBuild (goBuild.ts). For #155 Change-Id: I03d5f5eb58d049454fb4a66d902a7daea4a1269f Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/273146 Trust: Hyang-Ah Hana Kim <hyangah@gmail.com> Trust: Suzy Mueller <suzmue@golang.org> Run-TryBot: Hyang-Ah Hana Kim <hyangah@gmail.com> TryBot-Result: kokoro <noreply+kokoro@google.com> Reviewed-by: Suzy Mueller <suzmue@golang.org>
1 parent dbc8da9 commit f38e348

File tree

6 files changed

+184
-61
lines changed

6 files changed

+184
-61
lines changed

docs/settings.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ Default: `false`
7979

8080
### `go.buildFlags`
8181

82-
Flags to `go build`/`go test` used during build-on-save or running tests. (e.g. ["-ldflags='-s'"])
82+
Flags to `go build`/`go test` used during build-on-save or running tests. (e.g. ["-ldflags='-s'"]) This is propagated to the language server if `gopls.buildFlags` is not specified.
8383

8484
### `go.buildOnSave`
8585

@@ -91,7 +91,7 @@ Default: `package`
9191

9292
### `go.buildTags`
9393

94-
The Go build tags to use for all commands, that support a `-tags '...'` argument. When running tests, go.testTags will be used instead if it was set.
94+
The Go build tags to use for all commands, that support a `-tags '...'` argument. When running tests, go.testTags will be used instead if it was set. This is propagated to the language server if `gopls.buildFlags` is not specified.
9595

9696
Default: ``
9797

@@ -617,6 +617,8 @@ buildFlags is the set of flags passed on to the build system when invoked.
617617
It is applied to queries like `go list`, which is used when discovering files.
618618
The most common use is to set `-tags`.
619619

620+
If unspecified, values of `go.buildFlags, go.buildTags` will be propagated.
621+
620622

621623
#### `codelenses`
622624
codelenses overrides the enabled/disabled state of code lenses. See the "Code Lenses"
@@ -626,7 +628,7 @@ Example Usage:
626628
```json5
627629
"gopls": {
628630
...
629-
"codelenses": {
631+
"codelens": {
630632
"generate": false, // Don't show the `go generate` lens.
631633
"gc_details": true // Show a code lens toggling the display of gc's choices.
632634
}

package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -1222,13 +1222,13 @@
12221222
"type": "string"
12231223
},
12241224
"default": [],
1225-
"description": "Flags to `go build`/`go test` used during build-on-save or running tests. (e.g. [\"-ldflags='-s'\"])",
1225+
"description": "Flags to `go build`/`go test` used during build-on-save or running tests. (e.g. [\"-ldflags='-s'\"]) This is propagated to the language server if `gopls.buildFlags` is not specified.",
12261226
"scope": "resource"
12271227
},
12281228
"go.buildTags": {
12291229
"type": "string",
12301230
"default": "",
1231-
"description": "The Go build tags to use for all commands, that support a `-tags '...'` argument. When running tests, go.testTags will be used instead if it was set.",
1231+
"description": "The Go build tags to use for all commands, that support a `-tags '...'` argument. When running tests, go.testTags will be used instead if it was set. This is propagated to the language server if `gopls.buildFlags` is not specified.",
12321232
"scope": "resource"
12331233
},
12341234
"go.testTags": {
@@ -2068,7 +2068,7 @@
20682068
"properties": {
20692069
"buildFlags": {
20702070
"type": "array",
2071-
"markdownDescription": "buildFlags is the set of flags passed on to the build system when invoked.\nIt is applied to queries like `go list`, which is used when discovering files.\nThe most common use is to set `-tags`.\n",
2071+
"markdownDescription": "buildFlags is the set of flags passed on to the build system when invoked.\nIt is applied to queries like `go list`, which is used when discovering files.\nThe most common use is to set `-tags`.\n\nIf unspecified, values of `go.buildFlags, go.buildTags` will be propagated.\n",
20722072
"default": [],
20732073
"scope": "resource"
20742074
},
@@ -2130,7 +2130,7 @@
21302130
},
21312131
"codelenses": {
21322132
"type": "object",
2133-
"markdownDescription": "codelenses overrides the enabled/disabled state of code lenses. See the \"Code Lenses\"\nsection of settings.md for the list of supported lenses.\n\nExample Usage:\n```json5\n\"gopls\": {\n...\n \"codelenses\": {\n \"generate\": false, // Don't show the `go generate` lens.\n \"gc_details\": true // Show a code lens toggling the display of gc's choices.\n }\n...\n}\n```\n",
2133+
"markdownDescription": "codelenses overrides the enabled/disabled state of code lenses. See the \"Code Lenses\"\nsection of settings.md for the list of supported lenses.\n\nExample Usage:\n```json5\n\"gopls\": {\n...\n \"codelens\": {\n \"generate\": false, // Don't show the `go generate` lens.\n \"gc_details\": true // Show a code lens toggling the display of gc's choices.\n }\n...\n}\n```\n",
21342134
"default": {
21352135
"gc_details": false,
21362136
"generate": true,

src/goLanguageServer.ts

+52-27
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,7 @@ function buildLanguageClientOption(cfg: LanguageServerConfig): BuildLanguageClie
290290
// buildLanguageClient returns a language client built using the given language server config.
291291
// The returned language client need to be started before use.
292292
export async function buildLanguageClient(cfg: BuildLanguageClientOption): Promise<LanguageClient> {
293-
let goplsWorkspaceConfig = getGoplsConfig() as any;
294-
goplsWorkspaceConfig = filterDefaultConfigValues(goplsWorkspaceConfig, 'gopls', undefined);
295-
goplsWorkspaceConfig = await adjustGoplsWorkspaceConfiguration(cfg, goplsWorkspaceConfig);
293+
const goplsWorkspaceConfig = await adjustGoplsWorkspaceConfiguration(cfg, getGoplsConfig(), 'gopls', undefined);
296294
const c = new LanguageClient(
297295
'go', // id
298296
cfg.serverName, // name e.g. gopls
@@ -505,8 +503,7 @@ export async function buildLanguageClient(cfg: BuildLanguageClientOption): Promi
505503
const scopeUri = params.items[i].scopeUri;
506504
const resource = scopeUri ? vscode.Uri.parse(scopeUri) : undefined;
507505
const section = params.items[i].section;
508-
workspaceConfig = filterDefaultConfigValues(workspaceConfig, section, resource);
509-
workspaceConfig = await adjustGoplsWorkspaceConfiguration(cfg, workspaceConfig);
506+
workspaceConfig = await adjustGoplsWorkspaceConfiguration(cfg, workspaceConfig, section, resource);
510507
}
511508
ret.push(workspaceConfig);
512509
}
@@ -519,19 +516,15 @@ export async function buildLanguageClient(cfg: BuildLanguageClientOption): Promi
519516
return c;
520517
}
521518

522-
// filterDefaultConfigValues removes the entries filled based on the default values
519+
// filterGoplsDefaultConfigValues removes the entries filled based on the default values
523520
// and selects only those the user explicitly specifies in their settings.
524-
// This assumes workspaceConfig is a non-null(undefined) object type.
521+
// This returns a new object created based on the filtered properties of workspaceConfig.
525522
// Exported for testing.
526-
export function filterDefaultConfigValues(workspaceConfig: any, section: string, resource: vscode.Uri): any {
523+
export function filterGoplsDefaultConfigValues(workspaceConfig: any, resource: vscode.Uri): any {
527524
if (!workspaceConfig) {
528-
return workspaceConfig;
525+
workspaceConfig = {};
529526
}
530-
531-
const dot = section?.lastIndexOf('.') || -1;
532-
const sectionKey = dot >= 0 ? section.substr(0, dot) : section; // e.g. 'gopls'
533-
534-
const cfg = vscode.workspace.getConfiguration(sectionKey, resource);
527+
const cfg = getGoplsConfig(resource);
535528
const filtered = {} as { [key: string]: any };
536529
for (const [key, value] of Object.entries(workspaceConfig)) {
537530
if (typeof value === 'function') {
@@ -556,31 +549,63 @@ export function filterDefaultConfigValues(workspaceConfig: any, section: string,
556549
return filtered;
557550
}
558551

559-
// adjustGoplsWorkspaceConfiguration adds any extra options to the gopls
560-
// config. Right now, the only extra option is enabling experiments for the
561-
// Nightly extension.
562-
async function adjustGoplsWorkspaceConfiguration(cfg: LanguageServerConfig, config: any): Promise<any> {
563-
if (!config) {
564-
return config;
552+
// passGoConfigToGoplsConfigValues passes some of the relevant 'go.' settings to gopls settings.
553+
// This assumes `goplsWorkspaceConfig` is an output of filterGoplsDefaultConfigValues,
554+
// so it is modifiable and doesn't contain properties that are not explicitly set.
555+
// - go.buildTags and go.buildFlags are passed as gopls.buildFlags
556+
// if goplsWorkspaceConfig doesn't explicitly set it yet.
557+
// Exported for testing.
558+
export function passGoConfigToGoplsConfigValues(goplsWorkspaceConfig: any, goWorkspaceConfig: any): any {
559+
if (!goplsWorkspaceConfig) {
560+
goplsWorkspaceConfig = {};
561+
}
562+
563+
// If gopls.buildFlags is set, don't touch it.
564+
if (goplsWorkspaceConfig.buildFlags === undefined) {
565+
const buildFlags = [] as string[];
566+
if (goWorkspaceConfig?.buildFlags) {
567+
buildFlags.push(...goWorkspaceConfig?.buildFlags);
568+
}
569+
if (goWorkspaceConfig?.buildTags && buildFlags.indexOf('-tags') === -1) {
570+
buildFlags.push('-tags', goWorkspaceConfig?.buildTags);
571+
}
572+
if (buildFlags.length > 0) {
573+
goplsWorkspaceConfig.buildFlags = buildFlags;
574+
}
565575
}
576+
return goplsWorkspaceConfig;
577+
}
578+
579+
// adjustGoplsWorkspaceConfiguration filters unnecessary options and adds any necessary, additional
580+
// options to the gopls config. See filterGoplsDefaultConfigValues, passGoConfigToGoplsConfigValues.
581+
// If this is for the nightly extension, we also request to activate features under experiments.
582+
async function adjustGoplsWorkspaceConfiguration(cfg: LanguageServerConfig, workspaceConfig: any, section: string, resource: vscode.Uri): Promise<any> {
583+
// We process only gopls config
584+
if (section !== 'gopls') {
585+
return workspaceConfig;
586+
}
587+
588+
workspaceConfig = filterGoplsDefaultConfigValues(workspaceConfig, resource);
589+
// note: workspaceConfig is a modifiable, valid object.
590+
workspaceConfig = passGoConfigToGoplsConfigValues(workspaceConfig, getGoConfig(resource));
591+
566592
// Only modify the user's configurations for the Nightly.
567593
if (extensionId !== 'golang.go-nightly') {
568-
return config;
594+
return workspaceConfig;
569595
}
570596
// allExperiments is only available with gopls/v0.5.2 and above.
571597
const version = await getLocalGoplsVersion(cfg);
572598
if (!version) {
573-
return config;
599+
return workspaceConfig;
574600
}
575601
const sv = semver.parse(version, true);
576602
if (!sv || semver.lt(sv, 'v0.5.2')) {
577-
return config;
603+
return workspaceConfig;
578604
}
579-
const newConfig = Object.assign({}, config);
580-
if (!config['allExperiments']) {
581-
newConfig['allExperiments'] = true;
605+
if (!workspaceConfig['allExperiments']) {
606+
workspaceConfig['allExperiments'] = true;
582607
}
583-
return newConfig;
608+
return workspaceConfig;
584609
}
585610

586611
// createTestCodeLens adds the go.test.cursor and go.debug.cursor code lens

src/util.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -158,12 +158,12 @@ let toolsGopath: string;
158158

159159
// getGoConfig is declared as an exported const rather than a function, so it can be stubbbed in testing.
160160
export const getGoConfig = (uri?: vscode.Uri) => {
161-
return getConfig('go');
161+
return getConfig('go', uri);
162162
};
163163

164164
// getGoplsConfig returns the user's gopls configuration.
165-
export function getGoplsConfig() {
166-
return getConfig('gopls');
165+
export function getGoplsConfig(uri?: vscode.Uri) {
166+
return getConfig('gopls', uri);
167167
}
168168

169169
// getCheckForToolsUpdatesConfig returns go.toolsManagement.checkForUpdates configuration.

test/gopls/configuration.test.ts

+111-23
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,33 @@ import * as lsp from '../../src/goLanguageServer';
1212
import { getTool, Tool } from '../../src/goTools';
1313

1414
suite('gopls configuration tests', () => {
15-
test('configuration', async () => {
15+
test('filterGoplsDefaultConfigValues', async () => {
1616
const defaultGoplsConfig = vscode.workspace.getConfiguration('gopls');
1717
const defaultGoplsAnalysesConfig = vscode.workspace.getConfiguration('gopls.analyses');
1818

1919
interface TestCase {
2020
name: string;
2121
section: string;
22-
base: any;
22+
input: any;
2323
want: any;
2424
}
2525
const testCases: TestCase[] = [
2626
{
2727
name: 'user set no gopls settings',
2828
section: 'gopls',
29-
base: defaultGoplsConfig,
29+
input: defaultGoplsConfig,
3030
want: {}
3131
},
3232
{
3333
name: 'user set some gopls settings',
3434
section: 'gopls',
35-
base: defaultGoplsConfig,
35+
input: Object.assign({}, defaultGoplsConfig, {
36+
buildFlags: ['-something'],
37+
env: { foo: 'bar' },
38+
hoverKind: 'NoDocumentation',
39+
usePlaceholders: true,
40+
linkTarget: 'godoc.org'
41+
}),
3642
want: {
3743
buildFlags: ['-something'],
3844
env: { foo: 'bar' },
@@ -42,37 +48,119 @@ suite('gopls configuration tests', () => {
4248
},
4349
},
4450
{
45-
name: 'gopls asks analyses section, user set no analysis',
46-
section: 'gopls.analyses',
47-
base: defaultGoplsAnalysesConfig,
51+
name: 'user set extra gopls settings',
52+
section: 'gopls',
53+
input: Object.assign({}, defaultGoplsConfig, {
54+
undefinedGoplsSetting: true
55+
}),
56+
want: {
57+
undefinedGoplsSetting: true,
58+
},
59+
},
60+
{
61+
name: 'never returns undefined',
62+
section: 'undefined.section',
63+
input: undefined,
4864
want: {},
4965
},
66+
];
67+
testCases.map((tc: TestCase) => {
68+
const actual = lsp.filterGoplsDefaultConfigValues(tc.input, undefined);
69+
assert.deepStrictEqual(actual, tc.want, `Failed: ${tc.name}`);
70+
});
71+
});
72+
73+
test('passGoConfigToGoplsConfigValues', async () => {
74+
interface TestCase {
75+
name: string;
76+
goplsConfig: any;
77+
goConfig: any;
78+
want: any;
79+
}
80+
const testCases: TestCase[] = [
5081
{
51-
name: 'gopls asks analyses section, user set some',
52-
section: 'gopls.analyses',
53-
base: defaultGoplsAnalysesConfig,
54-
want: {
55-
coolAnalysis: true,
82+
name: 'undefined gopls, go configs result in an empty config',
83+
goplsConfig: undefined,
84+
goConfig: undefined,
85+
want: {}
86+
},
87+
{
88+
name: 'empty gopls, go configs result in an empty config',
89+
goplsConfig: {},
90+
goConfig: {},
91+
want: {}
92+
},
93+
{
94+
name: 'empty gopls, default go configs result in an empty config',
95+
goplsConfig: {},
96+
goConfig: {
97+
buildFlags: [],
98+
buildTags: '',
5699
},
100+
want: {}
57101
},
58102
{
59-
name: 'user set extra gopls settings',
60-
section: 'gopls',
61-
base: defaultGoplsConfig,
103+
name: 'pass go config buildFlags to gopls config',
104+
goplsConfig: {},
105+
goConfig: { buildFlags: ['-modfile', 'gopls.mod', '-tags', 'tag1,tag2', '-modcacherw'] },
106+
want: { buildFlags: ['-modfile', 'gopls.mod', '-tags', 'tag1,tag2', '-modcacherw']}
107+
},
108+
{
109+
name: 'pass go config buildTags to gopls config',
110+
goplsConfig: {},
111+
goConfig: { buildTags: 'tag1,tag2' },
112+
want: { buildFlags: ['-tags', 'tag1,tag2']}
113+
},
114+
{
115+
name: 'do not pass go config buildTags if buildFlags already have tags',
116+
goplsConfig: {},
117+
goConfig: {
118+
buildFlags: ['-tags', 'tag0'],
119+
buildTags: 'tag1,tag2'
120+
},
121+
want: { buildFlags: ['-tags', 'tag0']}
122+
},
123+
{
124+
name: 'do not mutate other gopls config but gopls.buildFlags',
125+
goplsConfig: {
126+
env: {GOPROXY: 'direct'}
127+
},
128+
goConfig: { buildFlags: ['-modfile', 'gopls.mod', '-tags', 'tag1,tag2', '-modcacherw'] },
62129
want: {
63-
undefinedGoplsSetting: true,
130+
env: { GOPROXY : 'direct' },
131+
buildFlags: ['-modfile', 'gopls.mod', '-tags', 'tag1,tag2', '-modcacherw']}
132+
},
133+
134+
{
135+
name: 'do not mutate misconfigured gopls.buildFlags',
136+
goplsConfig: {
137+
buildFlags: '-modfile gopls.mod', // misconfiguration
138+
},
139+
goConfig: {
140+
buildFlags: '-modfile go.mod -tags tag1 -modcacherw',
64141
},
142+
want: { buildFlags: '-modfile gopls.mod' }
65143
},
66144
{
67-
name: 'gopls asks undefined config section',
68-
section: 'undefined.section',
69-
base: undefined,
70-
want: {},
71-
}
145+
name: 'do not overwrite gopls config if it is explicitly set',
146+
goplsConfig: {
147+
env: {GOPROXY: 'direct'},
148+
buildFlags: [], // empty
149+
},
150+
goConfig: {
151+
// expect only non-conflicting flags (tags, modcacherw) passing.
152+
buildFlags: ['-modfile go.mod -tags tag1 -modcacherw'],
153+
buildTags: 'tag3',
154+
},
155+
want: {
156+
env: {GOPROXY: 'direct'},
157+
buildFlags: [],
158+
} // gopls.buildFlags untouched.
159+
},
160+
72161
];
73162
testCases.map((tc: TestCase) => {
74-
const input = Object.assign({}, tc.base, tc.want);
75-
const actual = lsp.filterDefaultConfigValues(input, tc.section, undefined);
163+
const actual = lsp.passGoConfigToGoplsConfigValues(tc.goplsConfig, tc.goConfig);
76164
assert.deepStrictEqual(actual, tc.want, `Failed: ${tc.name}`);
77165
});
78166
});

0 commit comments

Comments
 (0)