Skip to content

Commit 783c8bf

Browse files
Merge branch 'topic/progress-bar-graphs' into 'master'
Show a cancellable progress bar while computing graphs See merge request eng/ide/ada_language_server!2137
2 parents 19553f6 + 67c686a commit 783c8bf

File tree

5 files changed

+135
-53
lines changed

5 files changed

+135
-53
lines changed

integration/vscode/ada/package.json

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1101,26 +1101,22 @@
11011101
{
11021102
"command": "ada.visualizeFiles",
11031103
"category": "Ada",
1104-
"title": "Show File Dependencies Graph",
1105-
"enablement": "config.ada.enableExperimentalFeatures"
1104+
"title": "Show File Dependencies Graph"
11061105
},
11071106
{
11081107
"command": "ada.visualizeGPR",
11091108
"category": "Ada",
1110-
"title": "Show GPR Dependencies Graph",
1111-
"enablement": "config.ada.enableExperimentalFeatures"
1109+
"title": "Show GPR Dependencies Graph"
11121110
},
11131111
{
11141112
"command": "ada.visualizeTypes",
11151113
"category": "Types",
1116-
"title": "Show Type Hierarchy Graph",
1117-
"enablement": "config.ada.enableExperimentalFeatures"
1114+
"title": "Show Type Hierarchy Graph"
11181115
},
11191116
{
11201117
"command": "ada.visualizeCalls",
11211118
"category": "Calls",
1122-
"title": "Show Call Hierarchy Graph",
1123-
"enablement": "config.ada.enableExperimentalFeatures"
1119+
"title": "Show Call Hierarchy Graph"
11241120
},
11251121
{
11261122
"command": "ada.createNewAdaMainUnit",

integration/vscode/ada/src/alsVisualizer.ts

Lines changed: 88 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,17 @@ let canSendNextData: boolean = true;
4444
* If cancellable is set to true, it becomes a notification in the bottom right corner with a button
4545
* allowing the user to stop the task whenever he wants.
4646
*
47-
* @param task - The task to launch and wait for while displaying the progress animation.
47+
* @param task - The task to launch and wait for while displaying the progress animation. The
48+
* `token` parameter can be used to check if the user requested cancellation of the task.
49+
* @param message - The message to display alongside the progress animation.
4850
* @param cancellable - Indicates if the progress should be cancellable, which will put it in
4951
* a notification instead of the status bar.
5052
*/
51-
function withVizProgress(task: () => void | Promise<void>, message: string, cancellable = false) {
53+
function withVizProgress(
54+
task: (token?: vscode.CancellationToken) => void | Promise<void>,
55+
message: string,
56+
cancellable = false,
57+
) {
5258
vscode.window.withProgress(
5359
{
5460
location: cancellable
@@ -71,7 +77,7 @@ function withVizProgress(task: () => void | Promise<void>, message: string, canc
7177
stopProcess = true;
7278
});
7379
try {
74-
await task();
80+
await task(token);
7581
} catch (error) {
7682
logger.error(error);
7783
return;
@@ -92,48 +98,85 @@ function withVizProgress(task: () => void | Promise<void>, message: string, canc
9298
* @param hierarchy - The type of hierarchy to visualize.
9399
*/
94100
export function startVisualize(context: vscode.ExtensionContext, hierarchy: Hierarchy) {
95-
withVizProgress(async () => {
96-
if (vscode.window.activeTextEditor) {
97-
const input = new vscode.Location(
98-
vscode.window.activeTextEditor.document.uri,
99-
vscode.window.activeTextEditor.selection.active,
100-
);
101+
const direction =
102+
hierarchy === Hierarchy.CALL
103+
? RelationDirection.SUPER
104+
: hierarchy === Hierarchy.GPR
105+
? RelationDirection.SUB
106+
: RelationDirection.BOTH;
101107

102-
const languageId = vscode.window.activeTextEditor.document.languageId;
103-
const direction =
104-
hierarchy === Hierarchy.CALL
105-
? RelationDirection.SUPER
106-
: hierarchy === Hierarchy.GPR
107-
? RelationDirection.SUB
108-
: RelationDirection.BOTH;
109-
const middleNode = await createHandler(languageId).provideHierarchy(
110-
input,
111-
hierarchy,
112-
languageId,
113-
direction,
114-
);
108+
const progressLabel = getProgressLabel(hierarchy, true);
115109

116-
// Create the webView only if there is something to display.
117-
if (middleNode) {
118-
setupWebView(context, hierarchy);
119-
const panel = panels[hierarchy];
120-
// Make sure the webView was created and initialized.
121-
if (panel) {
122-
// Wait for the webView to notify the end of it's rendering
123-
const receive = panel.webview.onDidReceiveMessage((message: Message) => {
124-
if (message.command === 'rendered') {
125-
// Remove the listener as it will not be used after sending
126-
// the initial request.
127-
sendMessage(middleNode.id, hierarchy);
128-
receive.dispose();
129-
}
130-
});
131-
// Check if the client has already been rendered.
132-
panel.webview.postMessage({ command: 'isRendered' } as IsRenderedMessage);
110+
withVizProgress(
111+
async (token?: vscode.CancellationToken) => {
112+
if (vscode.window.activeTextEditor) {
113+
const input = new vscode.Location(
114+
vscode.window.activeTextEditor.document.uri,
115+
vscode.window.activeTextEditor.selection.active,
116+
);
117+
118+
const languageId = vscode.window.activeTextEditor.document.languageId;
119+
120+
const middleNode = await createHandler(languageId).provideHierarchy(
121+
input,
122+
hierarchy,
123+
languageId,
124+
direction,
125+
token,
126+
);
127+
128+
// Create the webView only if there is something to display and if the
129+
// user did not cancel the operation.
130+
if (middleNode && !token?.isCancellationRequested) {
131+
setupWebView(context, hierarchy);
132+
const panel = panels[hierarchy];
133+
// Make sure the webView was created and initialized.
134+
if (panel) {
135+
// Wait for the webView to notify the end of it's rendering
136+
const receive = panel.webview.onDidReceiveMessage((message: Message) => {
137+
if (message.command === 'rendered') {
138+
// Remove the listener as it will not be used after sending
139+
// the initial request.
140+
sendMessage(middleNode.id, hierarchy);
141+
receive.dispose();
142+
}
143+
});
144+
// Check if the client has already been rendered.
145+
panel.webview.postMessage({ command: 'isRendered' } as IsRenderedMessage);
146+
}
133147
}
134148
}
135-
}
136-
}, 'Visualizing');
149+
},
150+
progressLabel,
151+
true,
152+
);
153+
}
154+
155+
/**
156+
* Get the label to display in the progress bar depending on the hierarchy type.
157+
*
158+
* @param hierarchy - The type of hierarchy to visualize.
159+
* @param onCreate - Whether the progress is for creation or not.
160+
* @returns The updated progress label.
161+
*/
162+
function getProgressLabel(hierarchy: Hierarchy, onCreate: boolean = false): string {
163+
let hierarchyTypeLabel: string;
164+
const headerLabel: string = onCreate ? 'Creating' : 'Updating';
165+
switch (hierarchy) {
166+
case Hierarchy.TYPE:
167+
hierarchyTypeLabel = 'Type Hierarchy';
168+
break;
169+
case Hierarchy.CALL:
170+
hierarchyTypeLabel = 'Call Hierarchy';
171+
break;
172+
case Hierarchy.GPR:
173+
hierarchyTypeLabel = 'GPR Dependencies';
174+
break;
175+
default:
176+
hierarchyTypeLabel = 'File Dependencies';
177+
break;
178+
}
179+
return `${headerLabel} ${hierarchyTypeLabel} Graph`;
137180
}
138181

139182
/**
@@ -188,7 +231,7 @@ function handleMessage(message: Message) {
188231
withVizProgress(() => {
189232
const data = message;
190233
void refreshNodes(data.nodesId);
191-
}, 'Visualizing');
234+
}, 'Refreshing Graph');
192235
break;
193236
}
194237
// Stop the current loop of process (mostly for the recursive hierarchy).
@@ -218,6 +261,8 @@ function handleMessage(message: Message) {
218261
* @param data - The data necessary to expand a node.
219262
*/
220263
function requestHierarchy(data: HierarchyMessage) {
264+
const progressLabel = getProgressLabel(data.hierarchy);
265+
221266
withVizProgress(
222267
async () => {
223268
stopProcess = false;
@@ -324,7 +369,7 @@ function requestHierarchy(data: HierarchyMessage) {
324369
}, 100);
325370
});
326371
},
327-
data.recursive ? 'Gathering data...' : 'Visualizing',
372+
progressLabel,
328373
data.recursive,
329374
);
330375
}

integration/vscode/ada/src/alsVisualizerProvider.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export class VisualizerHandler {
119119
* @param hierarchy - The type of hierarchy needed.
120120
* @param languageId - The id of the language the symbol is in.
121121
* @param direction - The direction of the hierarchy
122+
* @param token - Cancellation token to cancel the operation
122123
* @returns The node linked to the location passed as an argument with
123124
* possibly children and/or parents added.
124125
*/
@@ -127,6 +128,7 @@ export class VisualizerHandler {
127128
hierarchy: Hierarchy,
128129
languageId: string,
129130
direction: RelationDirection,
131+
token?: vscode.CancellationToken,
130132
) {
131133
// -------------------------------- BEGIN NESTED FUNCTIONS ---------------------------------
132134
/**
@@ -143,6 +145,11 @@ export class VisualizerHandler {
143145
middleNode: NodeHierarchy,
144146
hierarchy: Hierarchy,
145147
) {
148+
// Check for cancellation before starting hierarchy retrieval
149+
if (token?.isCancellationRequested) {
150+
return;
151+
}
152+
146153
const commands = [
147154
['vscode.provideSupertypes', 'vscode.provideIncomingCalls'],
148155
['vscode.provideSubtypes', 'vscode.provideOutgoingCalls'],
@@ -157,6 +164,11 @@ export class VisualizerHandler {
157164
).map((item) => ('name' in item ? item : 'from' in item ? item.from : item.to));
158165

159166
for (const item of items) {
167+
// Check for cancellation in the loop
168+
if (token?.isCancellationRequested) {
169+
return;
170+
}
171+
160172
// Get only the useful information from the item.
161173
const symbol = {
162174
name: item.name,
@@ -184,6 +196,11 @@ export class VisualizerHandler {
184196

185197
// -------------------------------- END NESTED FUNCTIONS --------------------------------
186198

199+
// Check for cancellation at the start
200+
if (token?.isCancellationRequested) {
201+
return;
202+
}
203+
187204
// Other types of hierarchy will be called from subclasses of this class.
188205
if (hierarchy !== Hierarchy.CALL && hierarchy !== Hierarchy.TYPE) {
189206
logger.error(
@@ -198,6 +215,11 @@ export class VisualizerHandler {
198215
(vscode.CallHierarchyItem | vscode.TypeHierarchyItem)[]
199216
>(commands[hierarchy], location.uri, location.range.start);
200217

218+
// Check for cancellation after the command
219+
if (token?.isCancellationRequested) {
220+
return;
221+
}
222+
201223
if (items.length == 0) return;
202224

203225
// The middle node represents the current main symbol in the graph (the symbol the
@@ -206,6 +228,11 @@ export class VisualizerHandler {
206228
let middleNode;
207229

208230
for (const item of items) {
231+
// Check for cancellation in the loop
232+
if (token?.isCancellationRequested) {
233+
return;
234+
}
235+
209236
const symbol: VisualizerSymbol = {
210237
name: item.name,
211238
location: new vscode.Location(item.uri, item.selectionRange),
@@ -221,16 +248,29 @@ export class VisualizerHandler {
221248

222249
if (direction === RelationDirection.BOTH || direction === RelationDirection.SUPER) {
223250
await getHierarchy(item, RelationDirection.SUPER, middleNode, hierarchy);
251+
// Check for cancellation after hierarchy retrieval
252+
if (token?.isCancellationRequested) {
253+
return;
254+
}
224255
}
225256
if (direction === RelationDirection.BOTH || direction === RelationDirection.SUB) {
226257
await getHierarchy(item, RelationDirection.SUB, middleNode, hierarchy);
258+
// Check for cancellation after hierarchy retrieval
259+
if (token?.isCancellationRequested) {
260+
return;
261+
}
227262
}
228263

229264
// The middle node is expanded by default when we are getting its children.
230265
middleNode.expanded =
231266
direction === RelationDirection.SUPER ? middleNode.expanded : true;
232267
}
233268

269+
// Final cancellation check
270+
if (token?.isCancellationRequested) {
271+
return;
272+
}
273+
234274
// Get all the nodes that do not have parents
235275
NodesSingleton.findRoots();
236276
return middleNode;

integration/vscode/ada/src/alsVisualizerProvider/AdaVisualizerHandler.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ export class AdaVisualizerHandler extends VisualizerHandler {
5656
hierarchy: Hierarchy,
5757
languageId: string,
5858
direction: RelationDirection,
59+
token?: vscode.CancellationToken,
5960
) {
6061
if (hierarchy === Hierarchy.CALL || hierarchy === Hierarchy.TYPE)
61-
return await super.provideHierarchy(location, hierarchy, languageId, direction);
62+
return await super.provideHierarchy(location, hierarchy, languageId, direction, token);
6263
else if (hierarchy !== Hierarchy.FILE) {
6364
logger.error('Unknown type of hierarchy for Ada Language');
6465
return;

integration/vscode/ada/src/visualizing/searchBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export function SearchBar() {
146146
type="search"
147147
inputMode="search"
148148
autoComplete="off"
149-
placeholder="Search symbol name"
149+
placeholder="Search"
150150
id="visualizer__node-search-bar"
151151
onChange={onChange}
152152
onKeyDown={onKeyDown}

0 commit comments

Comments
 (0)