diff --git a/package.json b/package.json index d342891..1dd6c48 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Supercharge Git within VS Code — Visualize code authorship at a glance via Git blame annotations and CodeLens, seamlessly navigate and explore Git repositories, gain valuable insights via rich visualizations and powerful comparison commands, and so much more", "version": "14.2.1", "engines": { - "vscode": "^1.78.0" + "vscode": "^1.80.0" }, "license": "SEE LICENSE IN LICENSE", "publisher": "eamodio", @@ -14851,7 +14851,7 @@ "@types/react": "17.0.47", "@types/react-dom": "17.0.17", "@types/sortablejs": "1.15.1", - "@types/vscode": "1.78.1", + "@types/vscode": "1.80.0", "@typescript-eslint/eslint-plugin": "6.4.1", "@typescript-eslint/parser": "6.4.1", "@vscode/test-electron": "2.3.4", diff --git a/src/views/nodes/resultsFileNode.ts b/src/views/nodes/resultsFileNode.ts index d257b17..3a0fb30 100644 --- a/src/views/nodes/resultsFileNode.ts +++ b/src/views/nodes/resultsFileNode.ts @@ -1,5 +1,5 @@ import type { Command } from 'vscode'; -import { TreeItem, TreeItemCollapsibleState } from 'vscode'; +import { TreeItem, TreeItemCheckboxState, TreeItemCollapsibleState } from 'vscode'; import type { DiffWithCommandArgs } from '../../commands'; import { Commands } from '../../constants'; import { StatusFileFormatter } from '../../git/formatters/statusFormatter'; @@ -12,9 +12,13 @@ import { joinPaths, relativeDir } from '../../system/path'; import type { View } from '../viewBase'; import type { FileNode } from './folderNode'; import type { ViewNode } from './viewNode'; -import { ContextValues, ViewRefFileNode } from './viewNode'; +import { ContextValues, getViewNodeId, ViewRefFileNode } from './viewNode'; -export class ResultsFileNode extends ViewRefFileNode implements FileNode { +type State = { + checked: TreeItemCheckboxState; +}; + +export class ResultsFileNode extends ViewRefFileNode implements FileNode { constructor( view: View, parent: ViewNode, @@ -25,6 +29,9 @@ export class ResultsFileNode extends ViewRefFileNode implements FileNode { private readonly direction: 'ahead' | 'behind' | undefined, ) { super(GitUri.fromFile(file, repoPath, ref1 || ref2), view, parent, file); + + this.updateContext({ file: file }); + this._uniqueId = getViewNodeId('results-file', this.context); } override toClipboard(): string { @@ -55,6 +62,12 @@ export class ResultsFileNode extends ViewRefFileNode implements FileNode { }; item.command = this.getCommand(); + + item.checkboxState = { + state: this.getState('checked') ?? TreeItemCheckboxState.Unchecked, + tooltip: 'Mark as Reviewed', + }; + return item; } diff --git a/src/views/nodes/resultsFilesNode.ts b/src/views/nodes/resultsFilesNode.ts index d1cca4e..abbca4b 100644 --- a/src/views/nodes/resultsFilesNode.ts +++ b/src/views/nodes/resultsFilesNode.ts @@ -16,6 +16,10 @@ import { FolderNode } from './folderNode'; import { ResultsFileNode } from './resultsFileNode'; import { ContextValues, getViewNodeId, ViewNode } from './viewNode'; +type State = { + filter: FilesQueryFilter | undefined; +}; + export enum FilesQueryFilter { Left = 0, Right = 1, @@ -29,7 +33,7 @@ export interface FilesQueryResults { filtered?: Map; } -export class ResultsFilesNode extends ViewNode { +export class ResultsFilesNode extends ViewNode { constructor( view: ViewsWithCommits, protected override parent: ViewNode, @@ -56,12 +60,12 @@ export class ResultsFilesNode extends ViewNode { } get filter(): FilesQueryFilter | undefined { - return this.view.nodeState.getState(this.id, 'filter'); + return this.getState('filter'); } set filter(value: FilesQueryFilter | undefined) { if (this.filter === value) return; - this.view.nodeState.storeState(this.id, 'filter', value); + this.storeState('filter', value, true); this._filterResults = undefined; void this.triggerChange(false); @@ -188,7 +192,7 @@ export class ResultsFilesNode extends ViewNode { override refresh(reset: boolean = false) { if (!reset) return; - this.view.nodeState.deleteState(this.id, 'filter'); + this.deleteState('filter'); this._filterResults = undefined; this._filesQueryResults = this._filesQuery(); diff --git a/src/views/nodes/viewNode.ts b/src/views/nodes/viewNode.ts index 78bf7b0..6de3cf5 100644 --- a/src/views/nodes/viewNode.ts +++ b/src/views/nodes/viewNode.ts @@ -271,12 +271,16 @@ export abstract class ViewNode = StateKey>(key: T, value: StateValue): void { + storeState = StateKey>( + key: T, + value: StateValue, + sticky?: boolean, + ): void { if (this.id == null) { debugger; throw new Error('Id is required to store state'); } - this.view.nodeState.storeState(this.id, key as string, value); + this.view.nodeState.storeState(this.id, key as string, value, sticky); } } diff --git a/src/views/viewBase.ts b/src/views/viewBase.ts index 49bc345..80261bf 100644 --- a/src/views/viewBase.ts +++ b/src/views/viewBase.ts @@ -2,6 +2,7 @@ import type { CancellationToken, ConfigurationChangeEvent, Event, + TreeCheckboxChangeEvent, TreeDataProvider, TreeItem, TreeView, @@ -312,6 +313,7 @@ export abstract class ViewBase< this.tree, this.tree.onDidChangeSelection(debounce(this.onSelectionChanged, 250), this), this.tree.onDidChangeVisibility(debounce(this.onVisibilityChanged, 250), this), + this.tree.onDidChangeCheckboxState(this.onCheckboxStateChanged, this), this.tree.onDidCollapseElement(this.onElementCollapsed, this), this.tree.onDidExpandElement(this.onElementExpanded, this), ); @@ -364,6 +366,17 @@ export abstract class ViewBase< this._onDidChangeNodeCollapsibleState.fire({ ...e, state: TreeItemCollapsibleState.Expanded }); } + protected onCheckboxStateChanged(e: TreeCheckboxChangeEvent) { + for (const [node, state] of e.items) { + if (node.id == null) { + debugger; + throw new Error('Id is required for checkboxes'); + } + + node.storeState('checked', state, true); + } + } + protected onSelectionChanged(e: TreeViewSelectionChangeEvent) { this._onDidChangeSelection.fire(e); } @@ -653,39 +666,54 @@ export abstract class ViewBase< } export class ViewNodeState implements Disposable { - private _state: Map> | undefined; + private _store: Map> | undefined; + private _stickyStore: Map> | undefined; dispose() { this.reset(); + + this._stickyStore?.clear(); + this._stickyStore = undefined; } reset() { - this._state?.clear(); - this._state = undefined; + this._store?.clear(); + this._store = undefined; } deleteState(id: string, key?: string): void { if (key == null) { - this._state?.delete(id); + this._store?.delete(id); + this._stickyStore?.delete(id); } else { - this._state?.get(id)?.delete(key); + this._store?.get(id)?.delete(key); + this._stickyStore?.get(id)?.delete(key); } } getState(id: string, key: string): T | undefined { - return this._state?.get(id)?.get(key) as T | undefined; + return (this._stickyStore?.get(id)?.get(key) ?? this._store?.get(id)?.get(key)) as T | undefined; } - storeState(id: string, key: string, value: T): void { - if (this._state == null) { - this._state = new Map(); + storeState(id: string, key: string, value: T, sticky?: boolean): void { + let store; + if (sticky) { + if (this._stickyStore == null) { + this._stickyStore = new Map(); + } + store = this._stickyStore; + } else { + if (this._store == null) { + this._store = new Map(); + } + store = this._store; } - const state = this._state.get(id); + const state = store.get(id); if (state != null) { state.set(key, value); } else { - this._state.set(id, new Map([[key, value]])); + store.set(id, new Map([[key, value]])); } } } diff --git a/yarn.lock b/yarn.lock index 314c0fd..93c8bd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -776,10 +776,10 @@ resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311" integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g== -"@types/vscode@1.78.1": - version "1.78.1" - resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.78.1.tgz#027dba038c9e4c3f8e83570e1494aab679030485" - integrity sha512-wEA+54axejHu7DhcUfnFBan1IqFD1gBDxAFz8LoX06NbNDMRJv/T6OGthOs52yZccasKfN588EyffHWABkR0fg== +"@types/vscode@1.80.0": + version "1.80.0" + resolved "https://registry.yarnpkg.com/@types/vscode/-/vscode-1.80.0.tgz#e004dd6cde74dafdb7fab64a6e1754bf8165b981" + integrity sha512-qK/CmOdS2o7ry3k6YqU4zD3R2AYlJfbwBoSbKpBoP+GpXNE+0NEgJOli4n0bm0diK5kfBnchgCEj4igQz/44Hg== "@types/yargs-parser@*": version "21.0.0"