Skip to content

Commit 95a5893

Browse files
Merge branch 'topic/restart_cmds' into 'master'
Add commands to restart Ada/GPR language servers See merge request eng/ide/ada_language_server!1951
2 parents c0c9101 + 3d1b78b commit 95a5893

File tree

9 files changed

+173
-16
lines changed

9 files changed

+173
-16
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ section below it for the last release. -->
1919
been added, all available under the `File->New File...` menu
2020
* Add VS Code command `ada: GNATcoverage - Load an existing XML coverage report` for importing coverage reports
2121
* Support running GNATtest tests in coverage mode
22+
* Add command to restart the Ada Language Server instances
2223

2324
## 26.0.202412190
2425

integration/vscode/ada/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,11 @@
780780
}
781781
],
782782
"commands": [
783+
{
784+
"command": "ada.restartLanguageServers",
785+
"title": "Ada: Restart Language Servers",
786+
"when": "ADA_PROJECT_CONTEXT"
787+
},
783788
{
784789
"command": "ada.createNewAdaMainUnit",
785790
"title": "Ada: Create Main Unit",

integration/vscode/ada/src/ExtensionState.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import {
33
Disposable,
44
ExecuteCommandParams,
55
ExecuteCommandRequest,
6-
LanguageClient,
76
} from 'vscode-languageclient/node';
87
import { AdaCodeLensProvider } from './AdaCodeLensProvider';
9-
import { createClient } from './clients';
8+
import { AdaLanguageClient, createClient } from './clients';
109
import { AdaInitialDebugConfigProvider, initializeDebugging } from './debugConfigProvider';
1110
import { logger } from './extension';
1211
import { existsSync } from 'fs';
@@ -43,8 +42,8 @@ export type ALSSourceDirDescription = {
4342
* wherever needed.
4443
*/
4544
export class ExtensionState {
46-
public readonly adaClient: LanguageClient;
47-
public readonly gprClient: LanguageClient;
45+
public readonly adaClient: AdaLanguageClient;
46+
public readonly gprClient: AdaLanguageClient;
4847
public readonly context: vscode.ExtensionContext;
4948
public readonly dynamicDebugConfigProvider: {
5049
provideDebugConfigurations(

integration/vscode/ada/src/clients.ts

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ import {
99
import { logger } from './extension';
1010
import { logErrorAndThrow, setTerminalEnvironment } from './helpers';
1111

12-
class AdaLanguageClient extends LanguageClient {
12+
export class AdaLanguageClient extends LanguageClient {
13+
/**
14+
* Used to store the client's process environment.
15+
* Changes are taken into account when calling the
16+
* {@link vscode.LanguageClient.restart} function.
17+
*/
18+
public serverEnv: NodeJS.ProcessEnv = {};
19+
1320
/**
1421
* Override this function to avoid displaying popup notifications on LSP errors when
1522
* the 'showNotificationsOnErrors' setting is disabled.
@@ -29,14 +36,29 @@ class AdaLanguageClient extends LanguageClient {
2936

3037
return super.handleFailedRequest(type, token, error, defaultValue, showNotification);
3138
}
39+
40+
/**
41+
* Override this function to update the client's process environment according to
42+
* VS Code settings before restarting it.
43+
*/
44+
override restart(): Promise<void> {
45+
// Update the process environment before restarting the language servers, so
46+
// that potential changes in VS Code settings related to the environment are
47+
// taken into account.
48+
setTerminalEnvironment(this.serverEnv);
49+
50+
// Restart the server
51+
logger.info('Restarting language server: ' + this.name);
52+
return super.restart();
53+
}
3254
}
3355
export function createClient(
3456
context: vscode.ExtensionContext,
3557
id: string,
3658
name: string,
3759
extra: string[],
3860
pattern: string,
39-
) {
61+
): AdaLanguageClient {
4062
let serverExecPath: string;
4163

4264
// If the ALS environment variable is specified, use it as the path of the
@@ -91,14 +113,6 @@ export function createClient(
91113
// Set custom environment
92114
setTerminalEnvironment(serverEnv);
93115

94-
logger.debug(`Environment for ${name}:`);
95-
for (const key in serverEnv) {
96-
const value = serverEnv[key];
97-
if (value) {
98-
logger.debug(`${key}=${value}`);
99-
}
100-
}
101-
102116
// Options to control the server
103117
const serverOptions: ServerOptions = {
104118
run: { command: serverExecPath, args: extra, options: { env: serverEnv } },
@@ -119,5 +133,7 @@ export function createClient(
119133
initializationOptions: () => ({ ada: vscode.workspace.getConfiguration('ada') }),
120134
};
121135
// Create the language client
122-
return new AdaLanguageClient(id, name, serverOptions, clientOptions);
136+
const client = new AdaLanguageClient(id, name, serverOptions, clientOptions);
137+
client.serverEnv = serverEnv;
138+
return client;
123139
}

integration/vscode/ada/src/commands.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ export const CMD_SPARK_LIMIT_REGION_ARG = 'ada.spark.limitRegionArg';
8787
export const CMD_SPARK_PROVE_SUBP = 'ada.spark.proveSubprogram';
8888

8989
export function registerCommands(context: vscode.ExtensionContext, clients: ExtensionState) {
90+
context.subscriptions.push(
91+
vscode.commands.registerCommand('ada.restartLanguageServers', restartLanguageServers),
92+
);
9093
context.subscriptions.push(
9194
vscode.commands.registerCommand('ada.createHelloWorldProject', createHelloWorldProject),
9295
);
@@ -467,6 +470,24 @@ async function buildAndRunMainAsk() {
467470
}
468471
}
469472

473+
/**
474+
* Handler for commands that restart language servers launched by the extension.
475+
*
476+
*/
477+
async function restartLanguageServers() {
478+
logger.info('Restarting language servers, updating the process environment first...');
479+
480+
// Restart the language server clients
481+
for (const client of [adaExtState.adaClient, adaExtState.gprClient]) {
482+
await client.restart();
483+
}
484+
485+
// Clear the extension's cache and tasks after restarting the servers
486+
adaExtState.clearCacheAndTasks(
487+
'Language servers have been restarted, clearing cache and tasks',
488+
);
489+
}
490+
470491
/**
471492
* Handler for commands that create new files.
472493
* This function creates a new editor for the given language, focus it,

integration/vscode/ada/src/helpers.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ export function setTerminalEnvironment(
203203
}
204204
}
205205
}
206+
207+
logger.debug(`New process environment after update:`);
208+
for (const key in targetEnv) {
209+
const value = targetEnv[key];
210+
if (value) {
211+
logger.debug(`${key}=${value}`);
212+
}
213+
}
206214
}
207215

208216
export function assertSupportedEnvironments(mainChannel: winston.Logger) {

integration/vscode/ada/test/general/extension.test.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as assert from 'assert';
22
import { adaExtState } from '../../src/extension';
3-
import { getObjectDir } from '../../src/helpers';
3+
import { getObjectDir, TERMINAL_ENV_SETTING_NAME } from '../../src/helpers';
44
import { activate, assertEqualToFileContent } from '../utils';
55

66
import { readFileSync, writeFileSync } from 'fs';
@@ -107,6 +107,72 @@ suite('Extensions Test Suite', function () {
107107
}
108108
});
109109

110+
test('Restart Language Server Commands', async () => {
111+
if (vscode.workspace.workspaceFolders !== undefined) {
112+
const initialEnvSettings = vscode.workspace
113+
.getConfiguration()
114+
.get(TERMINAL_ENV_SETTING_NAME);
115+
116+
try {
117+
// Update the environment ARCHIVE_SUFFIX environment variable which
118+
// controls the 'Archive_Suffix' GPR project attribute of the test
119+
// workspace's project.
120+
121+
await vscode.workspace.getConfiguration().update(
122+
TERMINAL_ENV_SETTING_NAME,
123+
{
124+
...(initialEnvSettings ?? {}),
125+
ARCHIVE_SUFFIX: '.b',
126+
},
127+
vscode.ConfigurationTarget.Workspace,
128+
);
129+
130+
// Check that the 'Archive_Suffix' project attribute to
131+
// make sure it's set to its default value ('.a') before setting
132+
// the ARCHIVE_SUFFIX environment variable to '.b'.
133+
assert.strictEqual(
134+
await adaExtState.getProjectAttributeValue('Archive_Suffix'),
135+
'.a',
136+
'Environment updates were not taken into account when restarting ALS',
137+
);
138+
139+
// Restart the server and check that we still have the same project
140+
// loaded on ALS side
141+
const oldAlsUri = await adaExtState.getProjectUri();
142+
await vscode.commands.executeCommand('ada.restartLanguageServers');
143+
const newAlsUri = await adaExtState.getProjectUri();
144+
assert.deepStrictEqual(
145+
oldAlsUri?.fsPath,
146+
newAlsUri?.fsPath,
147+
'Wrong project URI received after Ada server restarted',
148+
);
149+
150+
// Check that environment updates are taken into account after restarting:
151+
// 'Archive_Suffix' GPR project attribute should now be equal to '.b'
152+
assert.strictEqual(
153+
await adaExtState.getProjectAttributeValue('Archive_Suffix'),
154+
'.b',
155+
'Environment updates were not taken into account when restarting ALS',
156+
);
157+
158+
// Check that the GPR server runs correctly after restarting it
159+
assert.ok(
160+
adaExtState.gprClient.isRunning(),
161+
'The ALS instance for GPR files should be running after restarting it',
162+
);
163+
} finally {
164+
// Restore the terminal.integrate.env.* settings for the workspace
165+
await vscode.workspace
166+
.getConfiguration()
167+
.update(
168+
TERMINAL_ENV_SETTING_NAME,
169+
undefined,
170+
vscode.ConfigurationTarget.Workspace,
171+
);
172+
}
173+
}
174+
});
175+
110176
test('Clear Cache On Project Reload', async () => {
111177
if (vscode.workspace.workspaceFolders !== undefined) {
112178
// Get the workspace root folder

integration/vscode/ada/test/general/ws/prj.gpr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,9 @@ project Prj is
2323
for Excluded_Project_Files use ("prj.gpr");
2424
end Documentation;
2525

26+
-- This is only used to test that environment updates are taken into
27+
-- account when restarting language servers.
28+
Archive_Suffix_Var := external ("ARCHIVE_SUFFIX", ".a");
29+
for Archive_Suffix use Archive_Suffix_Var;
30+
2631
end Prj;

integration/vscode/ada/test/general/ws/prj.gpr.snap

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,42 @@
166166
# ^^^^^^^^^^^^^ source.gpr meta.project.gpr meta.package.gpr entity.name.package.gpr
167167
# ^ source.gpr meta.project.gpr meta.package.gpr punctuation.gpr
168168
>
169+
> -- This is only used to test that environment updates are taken into
170+
#^^^ source.gpr meta.project.gpr
171+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.gpr meta.project.gpr comment.line.double-dash.gpr
172+
> -- account when restarting language servers.
173+
#^^^ source.gpr meta.project.gpr
174+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ source.gpr meta.project.gpr comment.line.double-dash.gpr
175+
> Archive_Suffix_Var := external ("ARCHIVE_SUFFIX", ".a");
176+
#^^^ source.gpr meta.project.gpr
177+
# ^^^^^^^^^^^^^^^^^^ source.gpr meta.project.gpr meta.declaration.variable.gpr variable.name.gpr entity.name.gpr
178+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr
179+
# ^^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr punctuation.gpr
180+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr
181+
# ^^^^^^^^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr variable.language.gpr
182+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr
183+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr punctuation.gpr
184+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr string.quoted.double.gpr punctuation.definition.string.gpr
185+
# ^^^^^^^^^^^^^^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr string.quoted.double.gpr
186+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr string.quoted.double.gpr punctuation.definition.string.gpr
187+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr punctuation.gpr
188+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr
189+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr string.quoted.double.gpr punctuation.definition.string.gpr
190+
# ^^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr string.quoted.double.gpr
191+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr string.quoted.double.gpr punctuation.definition.string.gpr
192+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr meta.assignment.gpr meta.external.gpr meta.string_list.gpr punctuation.gpr
193+
# ^ source.gpr meta.project.gpr meta.declaration.variable.gpr punctuation.gpr
194+
> for Archive_Suffix use Archive_Suffix_Var;
195+
#^^^ source.gpr meta.project.gpr
196+
# ^^^ source.gpr meta.project.gpr meta.attribute.gpr keyword.gpr
197+
# ^ source.gpr meta.project.gpr meta.attribute.gpr
198+
# ^^^^^^^^^^^^^^ source.gpr meta.project.gpr meta.attribute.gpr entity.other.attribute-name.gpr
199+
# ^ source.gpr meta.project.gpr meta.attribute.gpr
200+
# ^^^ source.gpr meta.project.gpr meta.attribute.gpr keyword.gpr
201+
# ^ source.gpr meta.project.gpr meta.attribute.gpr
202+
# ^^^^^^^^^^^^^^^^^^ source.gpr meta.project.gpr meta.attribute.gpr variable.name.gpr
203+
# ^ source.gpr meta.project.gpr meta.attribute.gpr punctuation.gpr
204+
>
169205
>end Prj;
170206
#^^^ source.gpr meta.project.gpr keyword.gpr
171207
# ^ source.gpr meta.project.gpr

0 commit comments

Comments
 (0)