Skip to content

Commit 3fb6460

Browse files
authored
Implement isfs package/folder deletion (#922)
1 parent efee965 commit 3fb6460

File tree

3 files changed

+90
-28
lines changed

3 files changed

+90
-28
lines changed

src/api/index.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ export class AtelierAPI {
276276
return result.length ? "?" + result.join("&") : "";
277277
};
278278
method = method.toUpperCase();
279-
if (["PUT", "POST"].includes(method) && !headers["Content-Type"]) {
279+
if (body && !headers["Content-Type"]) {
280280
headers["Content-Type"] = "application/json";
281281
}
282282
headers["Cache-Control"] = "no-cache";
@@ -321,7 +321,7 @@ export class AtelierAPI {
321321
const response = await fetch(`${proto}://${host}:${port}${path}`, {
322322
method,
323323
agent,
324-
body: ["PUT", "POST"].includes(method) ? (typeof body !== "string" ? JSON.stringify(body) : body) : null,
324+
body: body ? (typeof body !== "string" ? JSON.stringify(body) : body) : null,
325325
headers: {
326326
...headers,
327327
Cookie: cookie,
@@ -498,6 +498,11 @@ export class AtelierAPI {
498498
return this.request(1, "DELETE", `${this.ns}/doc/${name}`);
499499
}
500500

501+
// v1+
502+
public deleteDocs(docs: string[]): Promise<Atelier.Response<Atelier.Document[]>> {
503+
return this.request(1, "DELETE", `${this.ns}/docs`, docs);
504+
}
505+
501506
// v1+
502507
public putDoc(
503508
name: string,

src/commands/delete.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export async function deleteExplorerItems(nodes: NodeBase[]): Promise<any> {
4545
if (nodes.length > 1) {
4646
// Ask the user to confirm if they're deleting more than one explorer node
4747
const confirm = await vscode.window.showWarningMessage(
48-
`About to delete ${items.length} documents. Are you sure you want to proceed?`,
48+
`About to delete ${items.length} document${items.length > 1 ? "s" : ""}. Are you sure you want to proceed?`,
4949
"Cancel",
5050
"Confirm"
5151
);

src/providers/FileSystemProvider/FileSystemProvider.ts

Lines changed: 82 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import { File } from "./File";
77
import { fireOtherStudioAction, OtherStudioAction } from "../../commands/studio";
88
import { StudioOpenDialog } from "../../queries";
99
import { studioOpenDialogFromURI } from "../../utils/FileProviderUtil";
10-
import { outputChannel, redirectDotvscodeRoot, workspaceFolderOfUri } from "../../utils/index";
10+
import { notNull, outputChannel, redirectDotvscodeRoot, workspaceFolderOfUri } from "../../utils/index";
1111
import { workspaceState } from "../../extension";
12+
import { DocumentContentProvider } from "../DocumentContentProvider";
13+
import { Document } from "../../api/atelier";
1214

1315
declare function setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): NodeJS.Timeout;
1416

@@ -284,43 +286,98 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
284286
);
285287
}
286288

287-
public delete(uri: vscode.Uri, options: { recursive: boolean }): void | Thenable<void> {
289+
/** Process a Document object that was successfully deleted. */
290+
private async processDeletedDoc(doc: Document, uri: vscode.Uri, csp: boolean): Promise<void> {
291+
const events: vscode.FileChangeEvent[] = [];
292+
try {
293+
if (doc.ext) {
294+
fireOtherStudioAction(OtherStudioAction.DeletedDocument, uri, doc.ext);
295+
}
296+
// Remove entry from our cache, plus any now-empty ancestor entries
297+
let thisUri = vscode.Uri.parse(uri.toString(), true);
298+
while (thisUri.path !== "/") {
299+
events.push({ type: vscode.FileChangeType.Deleted, uri: thisUri });
300+
const parentDir = await this._lookupParentDirectory(thisUri);
301+
const name = path.basename(thisUri.path);
302+
parentDir.entries.delete(name);
303+
if (!csp && parentDir.entries.size === 0) {
304+
thisUri = thisUri.with({ path: path.posix.dirname(thisUri.path) });
305+
} else {
306+
break;
307+
}
308+
}
309+
} catch {
310+
// Swallow all errors
311+
} finally {
312+
if (events.length) {
313+
this._fireSoon(...events);
314+
}
315+
}
316+
}
317+
318+
public async delete(uri: vscode.Uri, options: { recursive: boolean }): Promise<void> {
288319
const { query } = url.parse(uri.toString(true), true);
289320
const csp = query.csp === "" || query.csp === "1";
290321
const fileName = csp ? uri.path : uri.path.slice(1).replace(/\//g, ".");
322+
const api = new AtelierAPI(uri);
291323
if (fileName.startsWith(".")) {
292324
return;
293325
}
294326
if (!fileName.includes(".")) {
295-
throw new Error(`${csp ? "Folder" : "Package"} deletion is not supported on server`);
296-
}
297-
const api = new AtelierAPI(uri);
298-
return api.deleteDoc(fileName).then(
299-
async (response) => {
300-
if (response.result.ext) {
301-
fireOtherStudioAction(OtherStudioAction.DeletedDocument, uri, response.result.ext);
302-
}
303-
// Remove entry from our cache, plus any now-empty ancestor entries
304-
let thisUri = vscode.Uri.parse(uri.toString(), true);
305-
const events: vscode.FileChangeEvent[] = [];
306-
while (thisUri.path !== "/") {
307-
events.push({ type: vscode.FileChangeType.Deleted, uri: thisUri });
308-
const parentDir = await this._lookupParentDirectory(thisUri);
309-
const name = path.basename(thisUri.path);
310-
parentDir.entries.delete(name);
311-
if (!csp && parentDir.entries.size === 0) {
312-
thisUri = thisUri.with({ path: path.posix.dirname(thisUri.path) });
327+
// Get the list of documents to delete
328+
const toDelete: string[] = await studioOpenDialogFromURI(
329+
uri,
330+
options.recursive ? { flat: true } : undefined
331+
).then((data) =>
332+
data.result.content
333+
.map((entry) => {
334+
if (options.recursive) {
335+
return entry.Name;
336+
} else if (entry.Name.includes(".")) {
337+
return csp ? uri.path + entry.Name : uri.path.slice(1).replace(/\//g, ".") + entry.Name;
338+
}
339+
return null;
340+
})
341+
.filter(notNull)
342+
);
343+
if (toDelete.length == 0) {
344+
// Nothing to delete
345+
return;
346+
}
347+
// Delete the documents
348+
return api.deleteDocs(toDelete).then((data) => {
349+
let failed = 0;
350+
for (const doc of data.result) {
351+
if (doc.status == "") {
352+
this.processDeletedDoc(doc, DocumentContentProvider.getUri(doc.name, undefined, undefined, true, uri), csp);
313353
} else {
314-
break;
354+
// The document was not deleted, so log the error
355+
failed++;
356+
outputChannel.appendLine(`${failed == 1 ? "\n" : ""}${doc.status}`);
315357
}
316358
}
317-
this._fireSoon(...events);
359+
if (failed > 0) {
360+
outputChannel.show(true);
361+
throw new vscode.FileSystemError(
362+
`Failed to delete ${failed} document${
363+
failed > 1 ? "s" : ""
364+
}. Check 'ObjectScript' Output channel for details.`
365+
);
366+
}
367+
});
368+
}
369+
return api.deleteDoc(fileName).then(
370+
(response) => {
371+
this.processDeletedDoc(response.result, uri, csp);
318372
},
319373
(error) => {
320-
if (error.errorText !== "") {
321-
error.message = error.errorText;
374+
let message = `Failed to delete file '${fileName}'.`;
375+
if (error && error.errorText && error.errorText !== "") {
376+
outputChannel.appendLine("\n" + error.errorText);
377+
outputChannel.show(true);
378+
message += " Check 'ObjectScript' Output channel for details.";
322379
}
323-
throw error;
380+
throw new vscode.FileSystemError(message);
324381
}
325382
);
326383
}

0 commit comments

Comments
 (0)