Skip to content

Commit 67c686a

Browse files
Show a cancellable progress bar while computing graphs
This helps the user understanding that the command is actually being processed, and allows to cancel it easily if needed. Also make the search bar placeholder smaller to make it always visible no matter the size of the WebView. For eng/ide/ada_language_server#1702
1 parent e10e21c commit 67c686a

File tree

4 files changed

+131
-45
lines changed

4 files changed

+131
-45
lines changed

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)