Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 71 additions & 28 deletions src/commands/filterTree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface TreeFilterState {
}

const treeFilters = new Map<TreePrefix, TreeFilterState>();
const filterMementoKeyPrefix = "vscode-containers.filters";
Comment thread
bwateratmsft marked this conversation as resolved.

// Only support filtering for containers and images
const contextKeys: Partial<Record<TreePrefix, string>> = {
Expand All @@ -25,16 +26,49 @@ export function getTreeFilter(treePrefix: TreePrefix): TreeFilterState {
return treeFilters.get(treePrefix) || { filterText: "", isActive: false };
}

function setTreeFilter(treePrefix: TreePrefix, filterText: string): void {
function getFilterMementoKey(treePrefix: TreePrefix): string {
return `${filterMementoKeyPrefix}.${treePrefix}`;
}

function loadPersistedTreeFilter(treePrefix: TreePrefix): void {
const persistedFilter = ext.context.workspaceState.get<string>(
getFilterMementoKey(treePrefix)
);

if (persistedFilter !== undefined) {
const normalizedFilter = persistedFilter.toLowerCase();
treeFilters.set(treePrefix, {
filterText: normalizedFilter,
isActive: normalizedFilter.length > 0,
});
} else {
treeFilters.delete(treePrefix);
}
}
Comment thread
pdjohntony marked this conversation as resolved.

async function setTreeFilter(
treePrefix: TreePrefix,
filterText: string
): Promise<void> {
const normalizedFilterText = filterText.toLowerCase();

treeFilters.set(treePrefix, {
filterText: filterText.toLowerCase(),
isActive: filterText.length > 0,
filterText: normalizedFilterText,
isActive: normalizedFilterText.length > 0,
});
setFilterContextValue(treePrefix, filterText.length > 0);
await ext.context.workspaceState.update(
getFilterMementoKey(treePrefix),
normalizedFilterText || undefined
);
setFilterContextValue(treePrefix, normalizedFilterText.length > 0);
}

function clearTreeFilter(treePrefix: TreePrefix): void {
treeFilters.set(treePrefix, { filterText: "", isActive: false });
async function clearTreeFilter(treePrefix: TreePrefix): Promise<void> {
treeFilters.delete(treePrefix);
await ext.context.workspaceState.update(
getFilterMementoKey(treePrefix),
undefined
);
setFilterContextValue(treePrefix, false);
}

Expand All @@ -47,8 +81,10 @@ function setFilterContextValue(treePrefix: TreePrefix, value: boolean): void {

export function setInitialFilterContextValues(): void {
for (const treePrefix of Object.keys(contextKeys) as TreePrefix[]) {
loadPersistedTreeFilter(treePrefix);
const filter = getTreeFilter(treePrefix);
setFilterContextValue(treePrefix, filter.isActive);
updateTreeViewTitle(treePrefix);
}
}

Expand Down Expand Up @@ -122,24 +158,31 @@ async function filterTreeView(
}

quickPick.onDidAccept(() => {
const value = quickPick.value.trim();
const selectedItem = quickPick.selectedItems[0];

// Check if "Clear Filter" was selected
if (selectedItem?.label === clearFilterLabel) {
clearTreeFilter(treePrefix);
context.telemetry.properties.action = "clearFilter";
} else if (value) {
setTreeFilter(treePrefix, value);
context.telemetry.properties.action = "applyFilter";
context.telemetry.properties.filterLength = value.length.toString();
} else {
clearTreeFilter(treePrefix);
context.telemetry.properties.action = "clearFilter";
}

quickPick.hide();
void refreshTreeView(treePrefix);
void (async () => {
const value = quickPick.value.trim();
const selectedItem = quickPick.selectedItems[0];

// Check if "Clear Filter" was selected
if (selectedItem?.label === clearFilterLabel) {
await clearTreeFilter(treePrefix);
context.telemetry.properties.action = "clearFilter";
} else if (value) {
await setTreeFilter(treePrefix, value);
context.telemetry.properties.action = "applyFilter";
context.telemetry.properties.filterLength =
value.length.toString();
} else {
await clearTreeFilter(treePrefix);
context.telemetry.properties.action = "clearFilter";
}

quickPick.hide();
await refreshTreeView(treePrefix);
})().catch((error) => {
void vscode.window.showErrorMessage(
vscode.l10n.t("Failed to apply filter: {0}", String(error))
);
});
});

quickPick.onDidHide(() => {
Expand Down Expand Up @@ -223,15 +266,15 @@ export async function filterImagesTree(context: IActionContext): Promise<void> {
export async function clearContainersFilter(
context: IActionContext
): Promise<void> {
clearTreeFilter("containers");
await clearTreeFilter("containers");
context.telemetry.properties.action = "clearFilter";
void refreshTreeView("containers");
await refreshTreeView("containers");
}

export async function clearImagesFilter(
context: IActionContext
): Promise<void> {
clearTreeFilter("images");
await clearTreeFilter("images");
context.telemetry.properties.action = "clearFilter";
void refreshTreeView("images");
await refreshTreeView("images");
}
13 changes: 9 additions & 4 deletions src/test/TestMemento.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,23 @@
import * as vscode from 'vscode';

export class TestMemento implements vscode.Memento {
private readonly values: { [key: string]: never } = {};
private readonly values: Record<string, unknown> = {};

keys(): readonly string[] {
return Object.keys(this.values);
}

get<T>(key: string, defaultValue?: T): T | undefined {
return this.values[key] ?? defaultValue;
return (this.values[key] as T | undefined) ?? defaultValue;
}

update(key: string, value: never): Thenable<void> {
this.values[key] = value;
update(key: string, value: unknown): Thenable<void> {
if (value === undefined) {
delete this.values[key];
} else {
this.values[key] = value;
}

return Promise.resolve();
}
}
17 changes: 8 additions & 9 deletions src/tree/LocalRootTreeItemBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ export abstract class LocalRootTreeItemBase<TItem extends AnyContainerObject, TP
ext.activityMeasurementService.recordActivity('overallnoedit');

this._currentItems = await this.getCachedItems(context, clearCache);
this.groupBySetting = this.getTreeSetting(groupByKey, this.groupBySettingInfo);
context.telemetry.properties.groupBySetting = this.groupBySetting;
this.sortBySetting = this.getTreeSetting(sortByKey, this.sortBySettingInfo);
context.telemetry.properties.sortBySetting = this.sortBySetting;
this.labelSetting = this.getTreeSetting(labelKey, this.labelSettingInfo);
context.telemetry.properties.labelSetting = this.labelSetting;
this.descriptionSetting = this.getTreeArraySetting(descriptionKey, this.descriptionSettingInfo);
context.telemetry.properties.descriptionSetting = this.descriptionSetting.toString();
Comment thread
bwateratmsft marked this conversation as resolved.

const filter = getTreeFilter(this.treePrefix);
if (filter.isActive && this._currentItems) {
Expand All @@ -123,15 +131,6 @@ export abstract class LocalRootTreeItemBase<TItem extends AnyContainerObject, TP
context.telemetry.properties.noItems = 'true';
return this.getTreeItemForEmptyList();
} else {
this.groupBySetting = this.getTreeSetting(groupByKey, this.groupBySettingInfo);
context.telemetry.properties.groupBySetting = this.groupBySetting;
this.sortBySetting = this.getTreeSetting(sortByKey, this.sortBySettingInfo);
context.telemetry.properties.sortBySetting = this.sortBySetting;
this.labelSetting = this.getTreeSetting(labelKey, this.labelSettingInfo);
context.telemetry.properties.labelSetting = this.labelSetting;
this.descriptionSetting = this.getTreeArraySetting(descriptionKey, this.descriptionSettingInfo);
context.telemetry.properties.descriptionSetting = this.descriptionSetting.toString();

return this.groupItems(this._currentItems);
}
}
Expand Down