Skip to content

Commit 07f5a44

Browse files
Create command to display Ada & SPARK user's guide
This command uses Microsoft's Live Preview extension, which we propose to download automatically via our extension pack. For eng/ide/ada_language_server#1705
1 parent 94bd93d commit 07f5a44

File tree

6 files changed

+172
-13
lines changed

6 files changed

+172
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
section below it for the last release. -->
55
## \<next>
66

7+
* Add `Ada: Open User's Guide` command displaying the HTML version of the Ada & SPARK extension User's Guide using Microsoft's Live Preview extension
78
* New refactoring: [Extract Variable](https://github.com/AdaCore/ada_language_server/blob/master/doc/refactoring_tools.md#extract-variable)
89
* Added a `Ada: Report Issue` command that opens the [VS Code Issue Reporter](https://code.visualstudio.com/docs/supporting/FAQ#_report-an-issue-with-a-vs-code-extension) with an extension-specific template
910
* `Go to Definition` now jumps respectively on the `begin`, `private` and `body` keywords

integration/vscode/ada/package.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
],
1515
"extensionPack": [
1616
"ms-vscode.cpptools",
17-
"ms-sarifvscode.sarif-viewer"
17+
"ms-sarifvscode.sarif-viewer",
18+
"ms-vscode.live-server"
1819
],
1920
"activationEvents": [
2021
"workspaceContains:*.gpr",
@@ -1145,6 +1146,11 @@
11451146
"title": "Add subprogram box",
11461147
"category": "Ada"
11471148
},
1149+
{
1150+
"command": "ada.openUsersGuide",
1151+
"title": "Open User's Guide",
1152+
"category": "Ada"
1153+
},
11481154
{
11491155
"command": "ada.showExtensionOutput",
11501156
"title": "Show extension output",

integration/vscode/ada/src/commands.ts

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
CMD_BUILD_AND_RUN_MAIN,
1515
CMD_GPR_PROJECT_ARGS,
1616
CMD_RESTART_LANG_SERVERS,
17+
CMD_OPEN_USERS_GUIDE,
1718
CMD_SHOW_ADA_LS_OUTPUT,
1819
CMD_SHOW_EXTENSION_LOGS,
1920
CMD_SHOW_GPR_LS_OUTPUT,
@@ -22,11 +23,18 @@ import {
2223
CMD_SPARK_LIMIT_REGION_ARG,
2324
CMD_SPARK_LIMIT_SUBP_ARG,
2425
CMD_SPARK_PROVE_SUBP,
26+
VSCODE_UG_LIVE_DOC_URL,
2527
} from './constants';
2628
import { AdaConfig, getOrAskForProgram, initializeConfig } from './debugConfigProvider';
2729
import { adaExtState, logger, mainOutputChannel } from './extension';
2830
import { loadGnatCoverageReport } from './gnattest';
29-
import { findAdaMain, getProjectFileRelPath, getSymbols } from './helpers';
31+
import {
32+
findAdaMain,
33+
getProjectFileRelPath,
34+
getSymbols,
35+
isExtensionInstalled,
36+
isRunningOnRemote,
37+
} from './helpers';
3038
import { registerSPARKTaskWrappers } from './sparkCommands';
3139
import { askSPARKOptions, getLastSPARKOptions } from './sparkOptionsPicker';
3240
import {
@@ -96,6 +104,9 @@ export function registerCommands(context: vscode.ExtensionContext, clients: Exte
96104
context.subscriptions.push(
97105
vscode.commands.registerCommand('ada.subprogramBox', addSubprogramBoxCommand),
98106
);
107+
context.subscriptions.push(
108+
vscode.commands.registerCommand(CMD_OPEN_USERS_GUIDE, openUsersGuide),
109+
);
99110
context.subscriptions.push(
100111
vscode.commands.registerCommand(CMD_SHOW_EXTENSION_LOGS, () => mainOutputChannel.show()),
101112
);
@@ -192,6 +203,117 @@ export function registerCommands(context: vscode.ExtensionContext, clients: Exte
192203
registerSPARKTaskWrappers(context);
193204
}
194205

206+
/**
207+
* Get the extension user's guide URI, if it exists on disk.
208+
*
209+
* @returns the URI of the extension user's guide, or `null` if the file
210+
* does not exist.
211+
*/
212+
export function getUsersGuideURI(): vscode.Uri | null {
213+
const ug_path = vscode.Uri.joinPath(
214+
adaExtState.context.extensionUri,
215+
'share',
216+
'doc',
217+
'als',
218+
'html',
219+
'users_guide',
220+
'index.html',
221+
);
222+
223+
if (existsSync(ug_path.fsPath)) {
224+
return ug_path;
225+
}
226+
return null;
227+
}
228+
229+
/**
230+
* Open the extension user's guide in a new editor tab using LivePreview
231+
* if doable.
232+
* This requires the "Live Preview" extension by Microsoft to be installed.
233+
* The user will be warned if this extension is not installed or if the user's
234+
* guide cannot be found on disk.
235+
*/
236+
async function openUsersGuide() {
237+
const usersGuideURI = getUsersGuideURI();
238+
239+
// The user's guide is not found on disk, warn the user and propose
240+
// to open the online documentation instead.
241+
if (!usersGuideURI) {
242+
async function showUGNotFoundError() {
243+
const action = await vscode.window.showErrorMessage(
244+
"The User's Guide was not found under the Ada & SPARK extension installation. " +
245+
'Please consult the online documentation instead.',
246+
'Open Online Docs',
247+
);
248+
if (action === 'Open Online Docs') {
249+
void vscode.env.openExternal(vscode.Uri.parse(VSCODE_UG_LIVE_DOC_URL));
250+
}
251+
}
252+
253+
void showUGNotFoundError();
254+
return;
255+
}
256+
257+
// Live Preview extension is not installed, warn the user and propose
258+
// to open the user's guide with the default browser instead.
259+
if (!isExtensionInstalled('ms-vscode.live-server')) {
260+
/** Show the LivePreview extension's page in the marketplace
261+
*/
262+
function showLivePreviewInMarketplace() {
263+
void vscode.commands.executeCommand(
264+
'workbench.extensions.search',
265+
'@id:ms-vscode.live-server',
266+
);
267+
}
268+
269+
/** Show an error message proposing to install Live Preview or open the
270+
* user's guide with the default browser instead.
271+
*/
272+
async function showNotLivePreviewInstalledError() {
273+
// If not running on a remote, propose to open the locally installed
274+
// user's guide with the default browser instead.
275+
if (!isRunningOnRemote()) {
276+
const action = await vscode.window.showErrorMessage(
277+
"Microsoft's Live Preview extension is not installed. " +
278+
"Please install it to open the User's Guide directly in VS Code, " +
279+
'otherwise open it with your system browser.',
280+
'Install Live Preview',
281+
'Open in Browser',
282+
);
283+
if (action === 'Open in Browser') {
284+
if (usersGuideURI) {
285+
void vscode.env.openExternal(usersGuideURI);
286+
}
287+
} else if (action === 'Install Live Preview') {
288+
showLivePreviewInMarketplace();
289+
}
290+
} else {
291+
// If running on a remote, propose to install Live Preview or
292+
// open the online documentation instead.
293+
const action = await vscode.window.showErrorMessage(
294+
"Microsoft's Live Preview extension is not installed. " +
295+
"Please install it to open the User's Guide directly in VS Code " +
296+
'when connected to a remote machine, or consult ' +
297+
'the online documentation instead.',
298+
'Install Live Preview',
299+
'Open Online Docs',
300+
);
301+
if (action === 'Open Online Docs') {
302+
void vscode.env.openExternal(vscode.Uri.parse(VSCODE_UG_LIVE_DOC_URL));
303+
} else if (action === 'Install Live Preview') {
304+
showLivePreviewInMarketplace();
305+
}
306+
}
307+
}
308+
309+
void showNotLivePreviewInstalledError();
310+
return;
311+
}
312+
313+
// Open the user's guide using Live Preview
314+
await vscode.commands.executeCommand('livePreview.start.internalPreview.atFile', usersGuideURI);
315+
}
316+
195317
/**
196318
* Add a subprogram box above the subprogram enclosing the cursor's position, if any.
197319
*

integration/vscode/ada/src/constants.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@
55
*
66
* @see {@link buildAndRunSpecifiedMain}
77
*/
8-
98
export const CMD_BUILD_AND_RUN_MAIN = 'ada.buildAndRunMain';
9+
1010
/**
1111
* Identifier for a hidden command used for building and debugging a project main.
1212
* The command accepts a parameter which is the URI of the main source file.
1313
* It is triggered by CodeLenses provided by the extension.
1414
*
1515
* @see {@link buildAndDebugSpecifiedMain}
1616
*/
17-
1817
export const CMD_BUILD_AND_DEBUG_MAIN = 'ada.buildAndDebugMain';
18+
1919
/**
2020
* Identifier for a hidden command used for building and running a project main,
2121
* using GNATemulator.
@@ -24,8 +24,8 @@ export const CMD_BUILD_AND_DEBUG_MAIN = 'ada.buildAndDebugMain';
2424
*
2525
* @see {@link buildAndRunMainWithGNATemulator}
2626
*/
27-
2827
export const CMD_BUILD_AND_RUN_GNATEMULATOR = 'ada.buildAndRunGNATemulator';
28+
2929
/**
3030
* Identifier for a hidden command used for building and debugging a project main,
3131
* using GNATemulator.
@@ -34,21 +34,20 @@ export const CMD_BUILD_AND_RUN_GNATEMULATOR = 'ada.buildAndRunGNATemulator';
3434
*
3535
* @see {@link buildAndDebugSpecifiedMain}
3636
*/
37-
3837
export const CMD_BUILD_AND_DEBUG_GNATEMULATOR = 'ada.buildAndDebugGNATemulator';
38+
3939
/**
4040
* Identifier for a hidden command that returns an array of strings constituting
4141
* the -P and -X project and scenario arguments.
4242
*/
43-
4443
export const CMD_GPR_PROJECT_ARGS = 'ada.gprProjectArgs';
44+
4545
/**
4646
* Identifier for a hidden command that returns a string referencing the current
4747
* project. That string is either `"$\{config:ada.projectFile\}"` if that
4848
* setting is configured, or otherwise the full path to the project file
4949
* returned from a query to the
5050
*/
51-
5251
export const CMD_GET_PROJECT_FILE = 'ada.getProjectFile';
5352

5453
export const CMD_SPARK_LIMIT_SUBP_ARG = 'ada.spark.limitSubpArg';
@@ -58,28 +57,38 @@ export const CMD_SPARK_ASK_OPTIONS = 'ada.spark.askGNATproveOptions';
5857
export const CMD_SPARK_CURRENT_GNATPROVE_OPTIONS = 'ada.spark.gnatproveOptions';
5958

6059
/**
61-
* Identifier for the command that shows the extension's output in the Output panel.
60+
* Identifier for the command that opens the extension's user's guide.
6261
*/
62+
export const CMD_OPEN_USERS_GUIDE = 'ada.openUsersGuide';
6363

64+
/**
65+
* Identifier for the command that shows the extension's output in the Output panel.
66+
*/
6467
export const CMD_SHOW_EXTENSION_LOGS = 'ada.showExtensionOutput';
68+
6569
/**
6670
* Identifier for the command that shows the output of the ALS for Ada in the Output panel.
6771
*/
68-
6972
export const CMD_SHOW_ADA_LS_OUTPUT = 'ada.showAdaLSOutput';
73+
7074
/**
7175
* Identifier for the command that shows the output of the ALS for GPR in the Output panel.
7276
*/
73-
7477
export const CMD_SHOW_GPR_LS_OUTPUT = 'ada.showGprLSOutput';
78+
7579
/**
7680
* Identifier for the command that reloads the currently loaded project on server-side.
7781
*/
78-
7982
export const CMD_RELOAD_PROJECT = 'als-reload-project';
83+
8084
/**
8185
* Identifier for the command that restarts all the language servers spawned by the extension
8286
* (Ada and GPR).
8387
*/
84-
8588
export const CMD_RESTART_LANG_SERVERS = 'ada.restartLanguageServers';
89+
90+
/**
91+
* Live doc URL of the Ada & SPARK VS Code extension User's Guide.
92+
*/
93+
export const VSCODE_UG_LIVE_DOC_URL =
94+
'https://docs.adacore.com/live/wave/als/html/als-doc/index.html';

integration/vscode/ada/src/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ import { activateE3TestsuiteIntegration } from './e3Testsuite';
3939

4040
export const EXTENSION_NAME: string = meta.displayName;
4141

42+
/** The context key that is set when the Ada extension has been
43+
* activated. This can be used in `when` clauses in `package.json` to enable or
44+
* disable commands, menu items, keybindings, etc.
45+
*/
4246
const ADA_CONTEXT = 'ADA_PROJECT_CONTEXT';
4347

4448
/**

integration/vscode/ada/src/helpers.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,23 @@ export const TERMINAL_ENV_SETTING_NAME =
120120
'terminal.integrated.env.' +
121121
(platform() == 'darwin' ? 'osx' : platform() == 'win32' ? 'windows' : 'linux');
122122

123+
/**
124+
* Check if VS Code is running in a remote environment.
125+
* @returns True if running remotely, false otherwise.
126+
*/
127+
export function isRunningOnRemote(): boolean {
128+
return vscode.env.remoteName !== undefined;
129+
}
130+
131+
/**
132+
* Check if a VS Code extension is installed.
133+
* @param extensionId - The ID of the extension to check.
134+
* @returns True if the extension is installed, false otherwise.
135+
*/
136+
export function isExtensionInstalled(extensionId: string): boolean {
137+
return vscode.extensions.getExtension(extensionId) !== undefined;
138+
}
139+
123140
/**
124141
*
125142
* @returns the value of the applicable `terminal.integrated.env.*` setting,

0 commit comments

Comments
 (0)