Skip to content

Commit b28eab8

Browse files
authored
Check if /_vscode web app exists before redirecting .vscode files (#1602)
1 parent b5078fc commit b28eab8

File tree

3 files changed

+135
-91
lines changed

3 files changed

+135
-91
lines changed

src/extension.ts

Lines changed: 58 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -446,21 +446,6 @@ export async function checkConnection(
446446
serverVersion: info.result.content.version,
447447
healthshare: hasHS ? "yes" : "no",
448448
});
449-
450-
// Update CSP web app cache if required
451-
const key = `${api.serverId}:${api.config.ns}`.toLowerCase();
452-
if (!cspApps.has(key)) {
453-
cspApps.set(key, await api.getCSPApps().then((data) => data.result.content || []));
454-
}
455-
if (!otherDocExts.has(key)) {
456-
otherDocExts.set(
457-
key,
458-
await api
459-
.actionQuery("SELECT Extention FROM %Library.RoutineMgr_DocumentTypes()", [])
460-
.then((data) => data.result?.content?.map((e) => e.Extention) ?? [])
461-
.catch(() => [])
462-
);
463-
}
464449
if (!api.externalServer) {
465450
await setConnectionState(configName, true);
466451
}
@@ -756,6 +741,44 @@ function setExplorerContextKeys(): void {
756741
);
757742
}
758743

744+
/** Cache the lists of web apps and abstract document types for all server-namespaces in `wsFolders` */
745+
async function updateWebAndAbstractDocsCaches(wsFolders: readonly vscode.WorkspaceFolder[]): Promise<any> {
746+
if (!wsFolders?.length) return;
747+
const keys: Set<string> = new Set();
748+
const connections: { key: string; api: AtelierAPI }[] = [];
749+
// Filter out any duplicate connections
750+
for (const wsFolder of wsFolders) {
751+
const api = new AtelierAPI(wsFolder.uri);
752+
if (!api.active) continue;
753+
const key = `${api.serverId}:${api.config.ns}`.toLowerCase();
754+
if (keys.has(key)) continue;
755+
keys.add(key);
756+
connections.push({ key, api });
757+
}
758+
return Promise.allSettled(
759+
connections.map(async (connection) => {
760+
if (!cspApps.has(connection.key)) {
761+
cspApps.set(
762+
connection.key,
763+
await connection.api
764+
.getCSPApps()
765+
.then((data) => data.result.content ?? [])
766+
.catch(() => [])
767+
);
768+
}
769+
if (!otherDocExts.has(connection.key)) {
770+
otherDocExts.set(
771+
connection.key,
772+
await connection.api
773+
.actionQuery("SELECT Extention FROM %Library.RoutineMgr_DocumentTypes()", [])
774+
.then((data) => data.result?.content?.map((e) => e.Extention) ?? [])
775+
.catch(() => [])
776+
);
777+
}
778+
})
779+
);
780+
}
781+
759782
/** The URIs of all classes that have been opened. Used when `objectscript.openClassContracted` is true */
760783
let openedClasses: string[];
761784

@@ -847,9 +870,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
847870
continue;
848871
}
849872
}
850-
await Promise.allSettled(
851-
vscode.workspace.workspaceFolders?.map((wsFolder) => addWsServerRootFolderData(wsFolder.uri)) || []
852-
);
873+
874+
await updateWebAndAbstractDocsCaches(vscode.workspace.workspaceFolders);
875+
await addWsServerRootFolderData(vscode.workspace.workspaceFolders);
853876

854877
xmlContentProvider = new XmlContentProvider();
855878

@@ -1373,22 +1396,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
13731396
},
13741397
supportsMultipleEditorsPerDocument: false,
13751398
}),
1376-
vscode.workspace.onDidChangeWorkspaceFolders(async ({ added }) => {
1377-
// Make sure we have a resolved connection spec for the targets of all added folders
1378-
const toCheck = new Map<string, vscode.Uri>();
1379-
added.map((workspaceFolder) => {
1380-
const uri = workspaceFolder.uri;
1381-
const { configName } = connectionTarget(uri);
1382-
toCheck.set(configName, uri);
1383-
});
1384-
for await (const oneToCheck of toCheck) {
1385-
const configName = oneToCheck[0];
1386-
const uri = oneToCheck[1];
1387-
const serverName = notIsfs(uri) ? config("conn", configName).server : configName;
1388-
await resolveConnectionSpec(serverName);
1389-
}
1390-
await Promise.allSettled(added.map((wsFolder) => addWsServerRootFolderData(wsFolder.uri)));
1391-
}),
13921399
vscode.workspace.onDidChangeConfiguration(async ({ affectsConfiguration }) => {
13931400
if (affectsConfiguration("objectscript.conn") || affectsConfiguration("intersystems.servers")) {
13941401
if (affectsConfiguration("intersystems.servers")) {
@@ -1545,7 +1552,23 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
15451552
"vscode-objectscript.webSocketTerminal",
15461553
new WebSocketTerminalProfileProvider()
15471554
),
1548-
vscode.workspace.onDidChangeWorkspaceFolders((e) => {
1555+
vscode.workspace.onDidChangeWorkspaceFolders(async (e) => {
1556+
// Make sure we have a resolved connection spec for the targets of all added folders
1557+
const toCheck = new Map<string, vscode.Uri>();
1558+
e.added.map((workspaceFolder) => {
1559+
const uri = workspaceFolder.uri;
1560+
const { configName } = connectionTarget(uri);
1561+
toCheck.set(configName, uri);
1562+
});
1563+
for await (const oneToCheck of toCheck) {
1564+
const configName = oneToCheck[0];
1565+
const uri = oneToCheck[1];
1566+
const serverName = notIsfs(uri) ? config("conn", configName).server : configName;
1567+
await resolveConnectionSpec(serverName);
1568+
}
1569+
// await this so the next step can take advantage of the caching
1570+
await updateWebAndAbstractDocsCaches(e.added);
1571+
addWsServerRootFolderData(e.added);
15491572
// Show the proposed API prompt if required
15501573
proposedApiPrompt(proposed.length > 0, e.added);
15511574
// Warn about SystemMode

src/providers/FileSystemProvider/FileSystemProvider.ts

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
243243
if (!api.active) throw vscode.FileSystemError.Unavailable("Server connection is inactive");
244244
let entryPromise: Promise<Entry>;
245245
let result: Entry;
246-
const redirectedUri = redirectDotvscodeRoot(uri);
246+
const redirectedUri = redirectDotvscodeRoot(uri, vscode.FileSystemError.FileNotFound(uri));
247247
if (redirectedUri.path !== uri.path) {
248248
// When redirecting the /.vscode subtree we must fill in as-yet-unvisited folders to fix https://github.com/intersystems-community/vscode-objectscript/issues/1143
249249
entryPromise = this._lookup(redirectedUri, true);
@@ -290,8 +290,8 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
290290
}
291291

292292
public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> {
293-
if (uri.path.includes(".vscode/")) {
294-
throw vscode.FileSystemError.NoPermissions("Cannot read the /.vscode directory");
293+
if (uri.path.includes(".vscode/") || uri.path.endsWith(".vscode")) {
294+
throw new vscode.FileSystemError("Cannot read the /.vscode directory");
295295
}
296296
const parent = await this._lookupAsDirectory(uri);
297297
const api = new AtelierAPI(uri);
@@ -406,7 +406,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
406406
}
407407

408408
public createDirectory(uri: vscode.Uri): void | Thenable<void> {
409-
uri = redirectDotvscodeRoot(uri);
409+
uri = redirectDotvscodeRoot(uri, new vscode.FileSystemError("Server does not have a /_vscode web application"));
410410
const basename = path.posix.basename(uri.path);
411411
const dirname = uri.with({ path: path.posix.dirname(uri.path) });
412412
return this._lookupAsDirectory(dirname).then((parent) => {
@@ -435,9 +435,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
435435
overwrite: boolean;
436436
}
437437
): void | Thenable<void> {
438-
uri = redirectDotvscodeRoot(uri);
438+
uri = redirectDotvscodeRoot(uri, new vscode.FileSystemError("Server does not have a /_vscode web application"));
439439
if (uri.path.startsWith("/.")) {
440-
throw vscode.FileSystemError.NoPermissions("dot-folders not supported by server");
440+
throw new vscode.FileSystemError("dot-folders are not supported by server");
441441
}
442442
const csp = isCSP(uri);
443443
const fileName = isfsDocumentName(uri, csp);
@@ -462,10 +462,12 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
462462
[, clsname] = match;
463463
}
464464
if (clsname == "") {
465-
throw new Error("Cannot save a malformed class");
465+
throw new vscode.FileSystemError("Cannot save a malformed class");
466466
}
467467
if (fileName.slice(0, -4) != clsname) {
468-
throw new Error("Cannot save an isfs class where the class name and file name do not match");
468+
throw new vscode.FileSystemError(
469+
"Cannot save an isfs class where the class name and file name do not match"
470+
);
469471
}
470472
if (openLowCodeEditors.has(uri.toString())) {
471473
// This class is open in a low-code editor, so any
@@ -474,7 +476,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
474476
}
475477
// Check if the class is deployed
476478
if (await isClassDeployed(fileName, api)) {
477-
throw new Error("Cannot overwrite a deployed class");
479+
throw new vscode.FileSystemError("Cannot overwrite a deployed class");
478480
}
479481
}
480482
const contentBuffer = Buffer.from(content);
@@ -505,7 +507,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
505507
.catch((error) => {
506508
// Throw all failures
507509
const errorStr = stringifyError(error);
508-
throw errorStr ? errorStr : vscode.FileSystemError.Unavailable(uri);
510+
throw errorStr ? new vscode.FileSystemError(errorStr) : vscode.FileSystemError.Unavailable(uri);
509511
});
510512
},
511513
(error) => {
@@ -529,7 +531,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
529531
.catch((error) => {
530532
// Throw all failures
531533
const errorStr = stringifyError(error);
532-
throw errorStr ? errorStr : vscode.FileSystemError.Unavailable(uri);
534+
throw errorStr ? new vscode.FileSystemError(errorStr) : vscode.FileSystemError.Unavailable(uri);
533535
})
534536
.then((data) => {
535537
// New file has been written
@@ -639,7 +641,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
639641
}
640642

641643
public async delete(uri: vscode.Uri, options: { recursive: boolean }): Promise<void> {
642-
uri = redirectDotvscodeRoot(uri);
644+
uri = redirectDotvscodeRoot(uri, vscode.FileSystemError.FileNotFound(uri));
643645
const { project } = isfsConfig(uri);
644646
const csp = isCSP(uri);
645647
const api = new AtelierAPI(uri);
@@ -724,13 +726,13 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
724726

725727
public async rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { overwrite: boolean }): Promise<void> {
726728
if (!oldUri.path.split("/").pop().includes(".")) {
727-
throw vscode.FileSystemError.NoPermissions("Cannot rename a package/folder");
729+
throw new vscode.FileSystemError("Cannot rename a package/folder");
728730
}
729731
if (oldUri.path.split(".").pop().toLowerCase() != newUri.path.split(".").pop().toLowerCase()) {
730-
throw vscode.FileSystemError.NoPermissions("Cannot change a file's extension during rename");
732+
throw new vscode.FileSystemError("Cannot change a file's extension during rename");
731733
}
732734
if (vscode.workspace.getWorkspaceFolder(oldUri) != vscode.workspace.getWorkspaceFolder(newUri)) {
733-
throw vscode.FileSystemError.NoPermissions("Cannot rename a file across workspace folders");
735+
throw new vscode.FileSystemError("Cannot rename a file across workspace folders");
734736
}
735737
// Check if the destination exists
736738
let newFileStat: vscode.FileStat;
@@ -774,7 +776,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
774776
.catch((error) => {
775777
// Throw all failures
776778
const errorStr = stringifyError(error);
777-
throw errorStr ? errorStr : vscode.FileSystemError.Unavailable(newUri);
779+
throw errorStr ? new vscode.FileSystemError(errorStr) : vscode.FileSystemError.Unavailable(newUri);
778780
})
779781
.then(async (response) => {
780782
// New file has been written
@@ -807,7 +809,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
807809
*/
808810
public async compile(uri: vscode.Uri, file?: File, update?: boolean): Promise<void> {
809811
if (!uri || uri.scheme != FILESYSTEM_SCHEMA) return;
810-
uri = redirectDotvscodeRoot(uri);
812+
uri = redirectDotvscodeRoot(uri, new vscode.FileSystemError("Server does not have a /_vscode web application"));
811813
const compileList: string[] = [];
812814
try {
813815
const entry = file || (await this._lookup(uri, true));
@@ -958,9 +960,9 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
958960

959961
// Fetch from server and cache it, optionally the passed cached copy if unchanged on server
960962
private async _lookupAsFile(uri: vscode.Uri, cachedFile?: File): Promise<File> {
961-
uri = redirectDotvscodeRoot(uri);
963+
uri = redirectDotvscodeRoot(uri, vscode.FileSystemError.FileNotFound(uri));
962964
if (uri.path.startsWith("/.")) {
963-
throw vscode.FileSystemError.NoPermissions("dot-folders not supported by server");
965+
throw new vscode.FileSystemError("dot-folders are not supported by server");
964966
}
965967
const csp = isCSP(uri);
966968
const name = path.basename(uri.path);
@@ -992,13 +994,13 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
992994
throw error?.statusCode == 404
993995
? vscode.FileSystemError.FileNotFound(uri)
994996
: errorStr
995-
? errorStr
997+
? new vscode.FileSystemError(errorStr)
996998
: vscode.FileSystemError.Unavailable(uri);
997999
});
9981000
}
9991001

10001002
private async _lookupParentDirectory(uri: vscode.Uri): Promise<Directory> {
1001-
uri = redirectDotvscodeRoot(uri);
1003+
uri = redirectDotvscodeRoot(uri, new vscode.FileSystemError("Server does not have a /_vscode web application"));
10021004
return this._lookupAsDirectory(uri.with({ path: path.posix.dirname(uri.path) }));
10031005
}
10041006

0 commit comments

Comments
 (0)