Skip to content

Commit 2b0b383

Browse files
committed
Implement custom workspace folder picker
1 parent 3134001 commit 2b0b383

File tree

6 files changed

+256
-255
lines changed

6 files changed

+256
-255
lines changed

src/commands/addServerNamespaceToWorkspace.ts

+13-19
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
filesystemSchemas,
1010
smExtensionId,
1111
} from "../extension";
12-
import { cspAppsForUri, handleError, notIsfs } from "../utils";
12+
import { cspAppsForUri, getWsFolder, handleError, notIsfs } from "../utils";
1313
import { pickProject } from "./project";
1414
import { isfsConfig, IsfsUriParam } from "../utils/FileProviderUtil";
1515

@@ -372,18 +372,12 @@ export async function modifyWsFolder(wsFolderUri?: vscode.Uri): Promise<void> {
372372
let wsFolder: vscode.WorkspaceFolder;
373373
if (!wsFolderUri) {
374374
// Select a workspace folder to modify
375-
if (vscode.workspace.workspaceFolders == undefined || vscode.workspace.workspaceFolders.length == 0) {
376-
vscode.window.showErrorMessage("No workspace folders are open.", "Dismiss");
377-
return;
378-
} else if (vscode.workspace.workspaceFolders.length == 1) {
379-
wsFolder = vscode.workspace.workspaceFolders[0];
380-
} else {
381-
wsFolder = await vscode.window.showWorkspaceFolderPick({
382-
placeHolder: "Pick the workspace folder to modify",
383-
ignoreFocusOut: true,
384-
});
385-
}
375+
wsFolder = await getWsFolder("Pick the workspace folder to modify", false, true);
386376
if (!wsFolder) {
377+
if (wsFolder === undefined) {
378+
// Strict equality needed because undefined == null
379+
vscode.window.showErrorMessage("No server-side workspace folders are open.", "Dismiss");
380+
}
387381
return;
388382
}
389383
} else {
@@ -392,13 +386,13 @@ export async function modifyWsFolder(wsFolderUri?: vscode.Uri): Promise<void> {
392386
if (!wsFolder) {
393387
return;
394388
}
395-
}
396-
if (notIsfs(wsFolder.uri)) {
397-
vscode.window.showErrorMessage(
398-
`Workspace folder '${wsFolder.name}' does not have scheme 'isfs' or 'isfs-readonly'.`,
399-
"Dismiss"
400-
);
401-
return;
389+
if (notIsfs(wsFolder.uri)) {
390+
vscode.window.showErrorMessage(
391+
`Workspace folder '${wsFolder.name}' does not have scheme 'isfs' or 'isfs-readonly'.`,
392+
"Dismiss"
393+
);
394+
return;
395+
}
402396
}
403397

404398
// Prompt the user to modify the uri

src/commands/compile.ts

+132-143
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
currentFileFromContent,
2222
CurrentTextFile,
2323
exportedUris,
24+
getWsFolder,
2425
handleError,
2526
isClassDeployed,
2627
isClassOrRtn,
@@ -746,162 +747,150 @@ interface XMLQuickPickItem extends vscode.QuickPickItem {
746747
export async function importXMLFiles(): Promise<any> {
747748
try {
748749
// Use the server connection from a workspace folder
749-
let connectionUri: vscode.Uri;
750-
const workspaceFolders = vscode.workspace.workspaceFolders || [];
751-
if (workspaceFolders.length == 0) {
752-
vscode.window.showErrorMessage("'Import XML Files...' command requires an open workspace.", "Dismiss");
753-
} else if (workspaceFolders.length == 1) {
754-
// Use the current connection
755-
connectionUri = workspaceFolders[0].uri;
756-
} else {
757-
// Pick from the workspace folders
758-
connectionUri = (
759-
await vscode.window.showWorkspaceFolderPick({
760-
ignoreFocusOut: true,
761-
placeHolder: "Pick a workspace folder. Server-side folders import from the local file system.",
762-
})
763-
)?.uri;
750+
const wsFolder = await getWsFolder(
751+
"Pick a workspace folder. Server-side folders import from the local file system."
752+
);
753+
if (!wsFolder) {
754+
if (wsFolder === undefined) {
755+
// Strict equality needed because undefined == null
756+
vscode.window.showErrorMessage("'Import XML Files...' command requires an open workspace.", "Dismiss");
757+
}
758+
return;
759+
}
760+
const api = new AtelierAPI(wsFolder.uri);
761+
// Make sure the server connection is active
762+
if (!api.active || api.ns == "") {
763+
vscode.window.showErrorMessage("'Import XML Files...' command requires an active server connection.", "Dismiss");
764+
return;
765+
}
766+
// Make sure the server has the xml endpoints
767+
if (api.config.apiVersion < 7) {
768+
vscode.window.showErrorMessage(
769+
"'Import XML Files...' command requires InterSystems IRIS version 2023.2 or above.",
770+
"Dismiss"
771+
);
772+
return;
764773
}
765-
if (connectionUri) {
766-
const api = new AtelierAPI(connectionUri);
767-
// Make sure the server connection is active
768-
if (!api.active || api.ns == "") {
774+
let defaultUri = wsFolder.uri;
775+
if (defaultUri.scheme == FILESYSTEM_SCHEMA) {
776+
// Need a default URI without the isfs scheme or the open dialog
777+
// will show the server-side files instead of local ones
778+
defaultUri = vscode.workspace.workspaceFile;
779+
if (defaultUri.scheme != "file") {
769780
vscode.window.showErrorMessage(
770-
"'Import XML Files...' command requires an active server connection.",
781+
"'Import XML Files...' command is not supported for unsaved workspaces.",
771782
"Dismiss"
772783
);
773784
return;
774785
}
775-
// Make sure the server has the xml endpoints
776-
if (api.config.apiVersion < 7) {
777-
vscode.window.showErrorMessage(
778-
"'Import XML Files...' command requires InterSystems IRIS version 2023.2 or above.",
779-
"Dismiss"
786+
// Remove the file name from the URI
787+
defaultUri = defaultUri.with({ path: defaultUri.path.split("/").slice(0, -1).join("/") });
788+
}
789+
// Prompt the user the file to import
790+
let uris = await vscode.window.showOpenDialog({
791+
canSelectFiles: true,
792+
canSelectFolders: false,
793+
canSelectMany: true,
794+
openLabel: "Import",
795+
filters: {
796+
"XML Files": ["xml"],
797+
},
798+
defaultUri,
799+
});
800+
if (!Array.isArray(uris) || uris.length == 0) {
801+
// No file to import
802+
return;
803+
}
804+
// Filter out non-XML files
805+
uris = uris.filter((uri) => uri.path.split(".").pop().toLowerCase() == "xml");
806+
if (uris.length == 0) {
807+
vscode.window.showErrorMessage("No XML files were selected.", "Dismiss");
808+
return;
809+
}
810+
// Read the XML files
811+
const fileTimestamps: Map<string, string> = new Map();
812+
const filesToList = await Promise.allSettled(
813+
uris.map(async (uri) => {
814+
fileTimestamps.set(
815+
uri.fsPath,
816+
new Date((await vscode.workspace.fs.stat(uri)).mtime).toISOString().replace("T", " ").split(".")[0]
780817
);
781-
return;
782-
}
783-
let defaultUri = vscode.workspace.getWorkspaceFolder(connectionUri)?.uri ?? connectionUri;
784-
if (defaultUri.scheme == FILESYSTEM_SCHEMA) {
785-
// Need a default URI without the isfs scheme or the open dialog
786-
// will show the server-side files instead of local ones
787-
defaultUri = vscode.workspace.workspaceFile;
788-
if (defaultUri.scheme != "file") {
789-
vscode.window.showErrorMessage(
790-
"'Import XML Files...' command is not supported for unsaved workspaces.",
791-
"Dismiss"
792-
);
793-
return;
818+
return {
819+
file: uri.fsPath,
820+
content: new TextDecoder().decode(await vscode.workspace.fs.readFile(uri)).split(/\r?\n/),
821+
};
822+
})
823+
).then((results) => results.map((result) => (result.status == "fulfilled" ? result.value : null)).filter(notNull));
824+
if (filesToList.length == 0) {
825+
return;
826+
}
827+
// List the documents in the XML files
828+
const documentsPerFile = await api.actionXMLList(filesToList).then((data) => data.result.content);
829+
// Prompt the user to select documents to import
830+
const quickPickItems = documentsPerFile
831+
.filter((file) => {
832+
if (file.status != "") {
833+
outputChannel.appendLine(`Failed to list documents in file '${file.file}': ${file.status}`);
834+
return false;
835+
} else {
836+
return true;
794837
}
795-
// Remove the file name from the URI
796-
defaultUri = defaultUri.with({ path: defaultUri.path.split("/").slice(0, -1).join("/") });
797-
}
798-
// Prompt the user the file to import
799-
let uris = await vscode.window.showOpenDialog({
800-
canSelectFiles: true,
801-
canSelectFolders: false,
802-
canSelectMany: true,
803-
openLabel: "Import",
804-
filters: {
805-
"XML Files": ["xml"],
806-
},
807-
defaultUri,
808-
});
809-
if (!Array.isArray(uris) || uris.length == 0) {
810-
// No file to import
811-
return;
812-
}
813-
// Filter out non-XML files
814-
uris = uris.filter((uri) => uri.path.split(".").pop().toLowerCase() == "xml");
815-
if (uris.length == 0) {
816-
vscode.window.showErrorMessage("No XML files were selected.", "Dismiss");
817-
return;
818-
}
819-
// Read the XML files
820-
const fileTimestamps: Map<string, string> = new Map();
821-
const filesToList = await Promise.allSettled(
822-
uris.map(async (uri) => {
823-
fileTimestamps.set(
824-
uri.fsPath,
825-
new Date((await vscode.workspace.fs.stat(uri)).mtime).toISOString().replace("T", " ").split(".")[0]
826-
);
827-
return {
828-
file: uri.fsPath,
829-
content: new TextDecoder().decode(await vscode.workspace.fs.readFile(uri)).split(/\r?\n/),
830-
};
831-
})
832-
).then((results) =>
833-
results.map((result) => (result.status == "fulfilled" ? result.value : null)).filter(notNull)
834-
);
835-
if (filesToList.length == 0) {
836-
return;
837-
}
838-
// List the documents in the XML files
839-
const documentsPerFile = await api.actionXMLList(filesToList).then((data) => data.result.content);
840-
// Prompt the user to select documents to import
841-
const quickPickItems = documentsPerFile
842-
.filter((file) => {
843-
if (file.status != "") {
844-
outputChannel.appendLine(`Failed to list documents in file '${file.file}': ${file.status}`);
845-
return false;
846-
} else {
847-
return true;
848-
}
849-
})
850-
.flatMap((file) => {
851-
const items: XMLQuickPickItem[] = [];
852-
if (file.documents.length > 0) {
853-
// Add a separator for this file
838+
})
839+
.flatMap((file) => {
840+
const items: XMLQuickPickItem[] = [];
841+
if (file.documents.length > 0) {
842+
// Add a separator for this file
843+
items.push({
844+
label: file.file,
845+
kind: vscode.QuickPickItemKind.Separator,
846+
file: file.file,
847+
});
848+
file.documents.forEach((doc) =>
854849
items.push({
855-
label: file.file,
856-
kind: vscode.QuickPickItemKind.Separator,
850+
label: doc.name,
851+
picked: true,
852+
detail: `${
853+
doc.ts.toString() != "-1" ? `Server timestamp: ${doc.ts.split(".")[0]}` : "Does not exist on server"
854+
}, ${fileTimestamps.has(file.file) ? `File timestamp: ${fileTimestamps.get(file.file)}` : ""}`,
857855
file: file.file,
858-
});
859-
file.documents.forEach((doc) =>
860-
items.push({
861-
label: doc.name,
862-
picked: true,
863-
detail: `${
864-
doc.ts.toString() != "-1" ? `Server timestamp: ${doc.ts.split(".")[0]}` : "Does not exist on server"
865-
}, ${fileTimestamps.has(file.file) ? `File timestamp: ${fileTimestamps.get(file.file)}` : ""}`,
866-
file: file.file,
867-
})
868-
);
869-
}
870-
return items;
871-
});
872-
// Prompt the user for documents to import
873-
const docsToImport = await vscode.window.showQuickPick(quickPickItems, {
874-
canPickMany: true,
875-
ignoreFocusOut: true,
876-
title: `Select the documents to import into namespace '${api.ns.toUpperCase()}' on server '${api.serverId}'`,
877-
});
878-
if (docsToImport == undefined || docsToImport.length == 0) {
879-
return;
880-
}
881-
if (filesystemSchemas.includes(connectionUri.scheme)) {
882-
// The user is importing into a server-side folder, so fire source control hook
883-
await new StudioActions().fireImportUserAction(api, [...new Set(docsToImport.map((qpi) => qpi.label))]);
884-
}
885-
// Import the selected documents
886-
const filesToLoad: { file: string; content: string[]; selected: string[] }[] = filesToList.map((f) => {
887-
return { selected: [], ...f };
888-
});
889-
docsToImport.forEach((qpi) =>
890-
// This is safe because every document came from a file
891-
filesToLoad[filesToLoad.findIndex((f) => f.file == qpi.file)].selected.push(qpi.label)
892-
);
893-
const importedPerFile = await api
894-
.actionXMLLoad(filesToLoad.filter((f) => f.selected.length > 0))
895-
.then((data) => data.result.content);
896-
const imported = importedPerFile.flatMap((file) => {
897-
if (file.status != "") {
898-
outputChannel.appendLine(`Importing documents from file '${file.file}' produced error: ${file.status}`);
856+
})
857+
);
899858
}
900-
return file.imported;
859+
return items;
901860
});
902-
// Prompt the user for compilation
903-
promptForCompile([...new Set(imported)], api, filesystemSchemas.includes(connectionUri.scheme));
861+
// Prompt the user for documents to import
862+
const docsToImport = await vscode.window.showQuickPick(quickPickItems, {
863+
canPickMany: true,
864+
ignoreFocusOut: true,
865+
title: `Select the documents to import into namespace '${api.ns.toUpperCase()}' on server '${api.serverId}'`,
866+
});
867+
if (docsToImport == undefined || docsToImport.length == 0) {
868+
return;
904869
}
870+
const isIsfs = filesystemSchemas.includes(wsFolder.uri.scheme);
871+
if (isIsfs) {
872+
// The user is importing into a server-side folder, so fire source control hook
873+
await new StudioActions().fireImportUserAction(api, [...new Set(docsToImport.map((qpi) => qpi.label))]);
874+
}
875+
// Import the selected documents
876+
const filesToLoad: { file: string; content: string[]; selected: string[] }[] = filesToList.map((f) => {
877+
return { selected: [], ...f };
878+
});
879+
docsToImport.forEach((qpi) =>
880+
// This is safe because every document came from a file
881+
filesToLoad[filesToLoad.findIndex((f) => f.file == qpi.file)].selected.push(qpi.label)
882+
);
883+
const importedPerFile = await api
884+
.actionXMLLoad(filesToLoad.filter((f) => f.selected.length > 0))
885+
.then((data) => data.result.content);
886+
const imported = importedPerFile.flatMap((file) => {
887+
if (file.status != "") {
888+
outputChannel.appendLine(`Importing documents from file '${file.file}' produced error: ${file.status}`);
889+
}
890+
return file.imported;
891+
});
892+
// Prompt the user for compilation
893+
promptForCompile([...new Set(imported)], api, isIsfs);
905894
} catch (error) {
906895
handleError(error, "Error executing 'Import XML Files...' command.");
907896
}

0 commit comments

Comments
 (0)