From ac6fe72061ec2ec6b63690af4cadbf25b7fd00fc Mon Sep 17 00:00:00 2001 From: Irene Sanchez Date: Thu, 7 Aug 2025 18:38:25 -0400 Subject: [PATCH 1/2] fix: delete multiple tables from lib pane Signed-off-by: Irene Sanchez --- .../LibraryNavigator/LibraryDataProvider.ts | 9 +++++ .../LibraryNavigator/LibraryModel.ts | 22 +++++++++++ .../src/components/LibraryNavigator/const.ts | 3 ++ .../src/components/LibraryNavigator/index.ts | 39 ++++++++++++++++++- 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/client/src/components/LibraryNavigator/LibraryDataProvider.ts b/client/src/components/LibraryNavigator/LibraryDataProvider.ts index 0a2eb0c9b..dada5d219 100644 --- a/client/src/components/LibraryNavigator/LibraryDataProvider.ts +++ b/client/src/components/LibraryNavigator/LibraryDataProvider.ts @@ -50,6 +50,10 @@ class LibraryDataProvider return this._onDidChangeTreeData.event; } + get treeView(): TreeView { + return this._treeView; + } + constructor( private readonly model: LibraryModel, private readonly extensionUri: Uri, @@ -170,6 +174,11 @@ class LibraryDataProvider this._onDidChangeTreeData.fire(undefined); } + public async deleteTables(items: LibraryItem[]): Promise { + await this.model.deleteTables(items); + this._onDidChangeTreeData.fire(undefined); + } + public watch(): Disposable { // ignore, fires for all changes... return new Disposable(() => {}); diff --git a/client/src/components/LibraryNavigator/LibraryModel.ts b/client/src/components/LibraryNavigator/LibraryModel.ts index 7296db6d2..f25c73012 100644 --- a/client/src/components/LibraryNavigator/LibraryModel.ts +++ b/client/src/components/LibraryNavigator/LibraryModel.ts @@ -126,6 +126,28 @@ class LibraryModel { } } + public async deleteTables(items: LibraryItem[]) { + const failures: string[] = []; + + for (const item of items) { + try { + await this.libraryAdapter.deleteTable(item); + } catch { + failures.push(item.uid); + } + } + + if (failures.length > 0) { + throw new Error( + //TODO: look at what warning message dialog is being used throughout the codebase + l10n.t(Messages.TablesDeletionWarning, { + tableNames: failures.join(", "), + count: failures.length, + }), + ); + } + } + public async getTableInfo(item: LibraryItem) { if (this.libraryAdapter.getTableInfo) { await this.libraryAdapter.setup(); diff --git a/client/src/components/LibraryNavigator/const.ts b/client/src/components/LibraryNavigator/const.ts index 6608f42ee..6bd3f102f 100644 --- a/client/src/components/LibraryNavigator/const.ts +++ b/client/src/components/LibraryNavigator/const.ts @@ -4,6 +4,9 @@ import { l10n } from "vscode"; export const Messages = { TableDeletionError: l10n.t("Unable to delete table {tableName}."), + TablesDeletionWarning: l10n.t( + "Are you sure you want to delete {count} table(s): {tableNames}?", + ), ViewTableCommandTitle: l10n.t("View SAS Table"), }; diff --git a/client/src/components/LibraryNavigator/index.ts b/client/src/components/LibraryNavigator/index.ts index c7ad4dac4..79a4e32a2 100644 --- a/client/src/components/LibraryNavigator/index.ts +++ b/client/src/components/LibraryNavigator/index.ts @@ -7,6 +7,7 @@ import { Uri, commands, env, + l10n, window, workspace, } from "vscode"; @@ -24,6 +25,7 @@ import LibraryAdapterFactory from "./LibraryAdapterFactory"; import LibraryDataProvider from "./LibraryDataProvider"; import LibraryModel from "./LibraryModel"; import PaginatedResultSet from "./PaginatedResultSet"; +import { Messages } from "./const"; import { LibraryAdapter, LibraryItem, TableData } from "./types"; class LibraryNavigator implements SubscriptionProvider { @@ -63,8 +65,34 @@ class LibraryNavigator implements SubscriptionProvider { ), commands.registerCommand("SAS.refreshLibraries", () => this.refresh()), commands.registerCommand("SAS.deleteTable", async (item: LibraryItem) => { + const selectedItems = this.treeViewSelections(item); + + if (selectedItems.length === 0) { + return; + } + try { - await this.libraryDataProvider.deleteTable(item); + if (selectedItems.length === 1) { + await this.libraryDataProvider.deleteTable(selectedItems[0]); + } else { + const tableNames = selectedItems + .map((table) => `${table.library}.${table.name}`) + .join(", "); + + const result = await window.showWarningMessage( + l10n.t(Messages.TablesDeletionWarning, { + tableNames: tableNames, + }), + { modal: true }, + "Delete", + ); + + if (result !== "Delete") { + return; + } + + await this.libraryDataProvider.deleteTables(selectedItems); + } } catch (error) { window.showErrorMessage(error.message); } @@ -143,6 +171,15 @@ class LibraryNavigator implements SubscriptionProvider { this.libraryDataProvider.useAdapter(this.libraryAdapterForConnectionType()); } + private treeViewSelections(item: LibraryItem): LibraryItem[] { + const items = + this.libraryDataProvider.treeView.selection.length > 1 || !item + ? this.libraryDataProvider.treeView.selection + : [item]; + + return items.filter(Boolean); + } + private libraryAdapterForConnectionType(): LibraryAdapter | undefined { const activeProfile = profileConfig.getProfileByName( profileConfig.getActiveProfile(), From 5f2e18ca44bf439799f3ea02db0d0594bad1fac4 Mon Sep 17 00:00:00 2001 From: Irene Sanchez Date: Thu, 7 Aug 2025 18:38:25 -0400 Subject: [PATCH 2/2] fix: delete multiple tables from lib pane Signed-off-by: Irene Sanchez --- .../LibraryNavigator/LibraryDataProvider.ts | 9 +++++ .../LibraryNavigator/LibraryModel.ts | 18 +++++++++ .../src/components/LibraryNavigator/const.ts | 3 ++ .../src/components/LibraryNavigator/index.ts | 40 ++++++++++++++++++- 4 files changed, 69 insertions(+), 1 deletion(-) diff --git a/client/src/components/LibraryNavigator/LibraryDataProvider.ts b/client/src/components/LibraryNavigator/LibraryDataProvider.ts index 0a2eb0c9b..dada5d219 100644 --- a/client/src/components/LibraryNavigator/LibraryDataProvider.ts +++ b/client/src/components/LibraryNavigator/LibraryDataProvider.ts @@ -50,6 +50,10 @@ class LibraryDataProvider return this._onDidChangeTreeData.event; } + get treeView(): TreeView { + return this._treeView; + } + constructor( private readonly model: LibraryModel, private readonly extensionUri: Uri, @@ -170,6 +174,11 @@ class LibraryDataProvider this._onDidChangeTreeData.fire(undefined); } + public async deleteTables(items: LibraryItem[]): Promise { + await this.model.deleteTables(items); + this._onDidChangeTreeData.fire(undefined); + } + public watch(): Disposable { // ignore, fires for all changes... return new Disposable(() => {}); diff --git a/client/src/components/LibraryNavigator/LibraryModel.ts b/client/src/components/LibraryNavigator/LibraryModel.ts index adb9410d9..039dae2f7 100644 --- a/client/src/components/LibraryNavigator/LibraryModel.ts +++ b/client/src/components/LibraryNavigator/LibraryModel.ts @@ -132,6 +132,24 @@ class LibraryModel { } } + public async deleteTables(items: LibraryItem[]) { + const failures: string[] = []; + + for (const item of items) { + try { + await this.libraryAdapter.deleteTable(item); + } catch { + failures.push(item.uid); + } + } + + if (failures.length > 0) { + throw new Error( + l10n.t(Messages.TableDeletionError, { tableName: failures.join(", ") }), + ); + } + } + public async getTableInfo(item: LibraryItem) { if (this.libraryAdapter.getTableInfo) { await this.libraryAdapter.setup(); diff --git a/client/src/components/LibraryNavigator/const.ts b/client/src/components/LibraryNavigator/const.ts index 6608f42ee..6bd3f102f 100644 --- a/client/src/components/LibraryNavigator/const.ts +++ b/client/src/components/LibraryNavigator/const.ts @@ -4,6 +4,9 @@ import { l10n } from "vscode"; export const Messages = { TableDeletionError: l10n.t("Unable to delete table {tableName}."), + TablesDeletionWarning: l10n.t( + "Are you sure you want to delete {count} table(s): {tableNames}?", + ), ViewTableCommandTitle: l10n.t("View SAS Table"), }; diff --git a/client/src/components/LibraryNavigator/index.ts b/client/src/components/LibraryNavigator/index.ts index 25baf8282..0dbe3fd38 100644 --- a/client/src/components/LibraryNavigator/index.ts +++ b/client/src/components/LibraryNavigator/index.ts @@ -7,6 +7,7 @@ import { Uri, commands, env, + l10n, window, workspace, } from "vscode"; @@ -24,6 +25,7 @@ import LibraryAdapterFactory from "./LibraryAdapterFactory"; import LibraryDataProvider from "./LibraryDataProvider"; import LibraryModel from "./LibraryModel"; import PaginatedResultSet from "./PaginatedResultSet"; +import { Messages } from "./const"; import { LibraryAdapter, LibraryItem, TableData } from "./types"; class LibraryNavigator implements SubscriptionProvider { @@ -66,8 +68,35 @@ class LibraryNavigator implements SubscriptionProvider { ), commands.registerCommand("SAS.refreshLibraries", () => this.refresh()), commands.registerCommand("SAS.deleteTable", async (item: LibraryItem) => { + const selectedItems = this.treeViewSelections(item); + + if (selectedItems.length === 0) { + return; + } + try { - await this.libraryDataProvider.deleteTable(item); + if (selectedItems.length === 1) { + await this.libraryDataProvider.deleteTable(selectedItems[0]); + } else { + const tableNames = selectedItems + .map((table) => `${table.library}.${table.name}`) + .join(", "); + + const result = await window.showWarningMessage( + l10n.t(Messages.TablesDeletionWarning, { + tableNames: tableNames, + count: selectedItems.length, + }), + { modal: true }, + "Delete", + ); + + if (result !== "Delete") { + return; + } + + await this.libraryDataProvider.deleteTables(selectedItems); + } } catch (error) { window.showErrorMessage(error.message); } @@ -128,6 +157,15 @@ class LibraryNavigator implements SubscriptionProvider { this.libraryDataProvider.useAdapter(this.libraryAdapterForConnectionType()); } + private treeViewSelections(item: LibraryItem): LibraryItem[] { + const items = + this.libraryDataProvider.treeView.selection.length > 1 || !item + ? this.libraryDataProvider.treeView.selection + : [item]; + + return items.filter(Boolean); + } + private async displayTableProperties( item: LibraryItem, showPropertiesTab: boolean = false,