Skip to content

Commit de9eaea

Browse files
committed
implement async project loading
1 parent 732a34c commit de9eaea

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,6 +992,24 @@
992992
"minimum": 0,
993993
"markdownDescription": "After how many projects to start checking the `#d.manyProjectsAction#` to load or not load further projects."
994994
},
995+
"d.manyProjectsAllowList": {
996+
"type": "array",
997+
"scope": "machine-overridable",
998+
"items": {
999+
"type": "string"
1000+
},
1001+
"default": [],
1002+
"markdownDescription": "List of absolute project paths to **always** load if more than `#d.manyProjectsThreshold#` projects are attempted to be loaded. You would usually set this in the workspace settings."
1003+
},
1004+
"d.manyProjectsDenyList": {
1005+
"type": "array",
1006+
"scope": "machine-overridable",
1007+
"items": {
1008+
"type": "string"
1009+
},
1010+
"default": [],
1011+
"markdownDescription": "List of absolute project paths to **never** load if more than `#d.manyProjectsThreshold#` projects are attempted to be loaded. You would usually set this in the workspace settings."
1012+
},
9951013
"d.ignoreDebugHints": {
9961014
"type": "boolean",
9971015
"scope": "window",

src/extension.ts

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ export class ServeD extends EventEmitter implements vscode.TreeDataProvider<DubD
175175
return this.client.sendRequest("served/getActiveDubConfig");
176176
}
177177

178+
forceLoadProjects(roots: string[]): Thenable<boolean[]> {
179+
return this.client.sendRequest("served/forceLoadProjects", roots);
180+
}
181+
178182
private static taskGroups: vscode.TaskGroup[] = [
179183
vscode.TaskGroup.Build,
180184
vscode.TaskGroup.Clean,
@@ -193,6 +197,7 @@ async function startClient(context: vscode.ExtensionContext) {
193197
"--provide", "context-snippets",
194198
"--provide", "default-snippets",
195199
"--provide", "tasks-current",
200+
"--provide", "async-ask-load",
196201
];
197202

198203
let executable: ServerOptions = {
@@ -273,6 +278,128 @@ async function startClient(context: vscode.ExtensionContext) {
273278
served.refreshDependencies();
274279
});
275280

281+
client.onNotification("coded/skippedLoads", async function (roots: string[]) {
282+
if (typeof(roots) === "object" && !Array.isArray(roots))
283+
roots = (<any>roots).roots;
284+
285+
if (typeof(roots) === "string")
286+
roots = <string[]>[roots];
287+
else if (!Array.isArray(roots))
288+
throw new Error("Unexpected roots with coded/skippedLoads: " + JSON.stringify(roots));
289+
290+
var allowList = config(null).get<string[]>("manyProjectsAllowList") || [];
291+
var denyList = config(null).get<string[]>("manyProjectsDenyList") || [];
292+
var decisions = new Array(roots.length);
293+
let decidedNum = 0;
294+
for (let i = 0; i < roots.length; i++) {
295+
const root = roots[i];
296+
if (allowList.includes(root))
297+
decisions[i] = true;
298+
else if (denyList.includes(root))
299+
decisions[i] = false;
300+
else
301+
continue;
302+
decidedNum++;
303+
}
304+
305+
console.log("Asking for late init for projects ", roots, " (allowlist: ", allowList, ", denylist: ", denyList, ")");
306+
307+
let btnLoadAll = decidedNum > 0
308+
? "Load Remaining (" + (roots.length - decidedNum) + ")"
309+
: roots.length == 1
310+
? "Load"
311+
: "Load All (" + roots.length + ")";
312+
let btnSkipAll = decidedNum > 0
313+
? "Skip Remaining"
314+
: roots.length == 1
315+
? "Skip"
316+
: "Skip All";
317+
let btnInteractive = "More Options...";
318+
let msg = "There are too many subprojects in this project according to d.manyProjectsThreshold. Load "
319+
+ (roots.length == 1 ? "1 extra project?" : roots.length + " extra projects?")
320+
+ (decidedNum > 0 ? ("\n" + (decidedNum == 1 ? "1 project has" : decidedNum + " projects have")
321+
+ " been decided on based on d.manyProjects{Allow/Deny}List already.") : "");
322+
let result = await vscode.window.showInformationMessage(msg,
323+
btnLoadAll, btnSkipAll, btnInteractive);
324+
325+
function setRemaining(b: boolean) {
326+
for (let i = 0; i < decisions.length; i++)
327+
if (decisions[i] === undefined)
328+
decisions[i] = b;
329+
}
330+
331+
switch (result)
332+
{
333+
case btnLoadAll:
334+
setRemaining(true);
335+
break;
336+
case btnInteractive:
337+
let result = await vscode.window.showQuickPick<vscode.QuickPickItem>(roots.map((r, i) => <vscode.QuickPickItem>{
338+
_root: r,
339+
_id: i,
340+
label: r,
341+
picked: decisions[i],
342+
}).concat([
343+
{
344+
kind: vscode.QuickPickItemKind.Separator,
345+
label: "Options",
346+
alwaysShow: true,
347+
},
348+
<any>{
349+
_id: "remember",
350+
label: "Remember Selection (workspace settings)",
351+
alwaysShow: true
352+
}
353+
]), {
354+
canPickMany: true,
355+
ignoreFocusOut: true,
356+
title: "Select projects to load"
357+
});
358+
359+
result?.forEach(r => {
360+
let root = <string>(<any>r)._root;
361+
let id = <number>(<any>r)._id;
362+
if (!root)
363+
return;
364+
365+
if (!allowList.includes(root))
366+
allowList.push(root);
367+
let denyIndex = denyList.indexOf(root);
368+
if (denyIndex != -1)
369+
denyList.splice(denyIndex, 1);
370+
371+
decisions[id] = true;
372+
});
373+
374+
for (let i = 0; i < decisions.length; i++) {
375+
if (decisions[i] === undefined) {
376+
let root = roots[i];
377+
if (!denyList.includes(root))
378+
denyList.push(root);
379+
let allowIndex = allowList.indexOf(root);
380+
if (allowIndex != -1)
381+
allowList.splice(allowIndex, 1);
382+
decisions[i] = false;
383+
}
384+
}
385+
386+
let save = (result?.findIndex(r => (<any>r)._id == "remember") ?? -1) >= 0;
387+
if (save)
388+
{
389+
config(null).update("manyProjectsAllowList", allowList, vscode.ConfigurationTarget.Workspace);
390+
config(null).update("manyProjectsDenyList", denyList, vscode.ConfigurationTarget.Workspace);
391+
}
392+
return;
393+
case btnSkipAll:
394+
default:
395+
setRemaining(false);
396+
break;
397+
}
398+
399+
let toLoad = roots.filter((_, i) => decisions[i] === true);
400+
served.forceLoadProjects(toLoad);
401+
});
402+
276403
const startupProgress = new statusbar.StartupProgress();
277404
client.onNotification("window/logMessage", function (info: { type: MessageType, message: string }) {
278405
if (info.type == MessageType.Log && info.message.startsWith("[progress]")) {
@@ -913,6 +1040,20 @@ function shortenPath(p: string) {
9131040
return short;
9141041
}
9151042

1043+
function getOwnerWorkspace(p: string): vscode.WorkspaceFolder | null {
1044+
if (p.endsWith("serve-d-dummy-workspace"))
1045+
return null;
1046+
let best: vscode.WorkspaceFolder | null = null;
1047+
if (vscode.workspace.workspaceFolders)
1048+
vscode.workspace.workspaceFolders.forEach(element => {
1049+
const dir = element.uri.fsPath;
1050+
if (dir.length >= (best?.uri?.fsPath?.length ?? 0) && dir.startsWith(p)) {
1051+
best = element;
1052+
}
1053+
});
1054+
return best;
1055+
}
1056+
9161057
/**
9171058
* Watches for config updates that need a vscode window reload to be effective
9181059
* and shows a hint to the user in these cases.

0 commit comments

Comments
 (0)