Browse Source

Closes #836 adds checkboxes in search/compare view

main
Eric Amodio 1 year ago
parent
commit
2168bc5437
6 changed files with 75 additions and 26 deletions
  1. +2
    -2
      package.json
  2. +16
    -3
      src/views/nodes/resultsFileNode.ts
  3. +8
    -4
      src/views/nodes/resultsFilesNode.ts
  4. +6
    -2
      src/views/nodes/viewNode.ts
  5. +39
    -11
      src/views/viewBase.ts
  6. +4
    -4
      yarn.lock

+ 2
- 2
package.json View File

@ -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", "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", "version": "14.2.1",
"engines": { "engines": {
"vscode": "^1.78.0"
"vscode": "^1.80.0"
}, },
"license": "SEE LICENSE IN LICENSE", "license": "SEE LICENSE IN LICENSE",
"publisher": "eamodio", "publisher": "eamodio",
@ -14851,7 +14851,7 @@
"@types/react": "17.0.47", "@types/react": "17.0.47",
"@types/react-dom": "17.0.17", "@types/react-dom": "17.0.17",
"@types/sortablejs": "1.15.1", "@types/sortablejs": "1.15.1",
"@types/vscode": "1.78.1",
"@types/vscode": "1.80.0",
"@typescript-eslint/eslint-plugin": "6.4.1", "@typescript-eslint/eslint-plugin": "6.4.1",
"@typescript-eslint/parser": "6.4.1", "@typescript-eslint/parser": "6.4.1",
"@vscode/test-electron": "2.3.4", "@vscode/test-electron": "2.3.4",

+ 16
- 3
src/views/nodes/resultsFileNode.ts View File

@ -1,5 +1,5 @@
import type { Command } from 'vscode'; import type { Command } from 'vscode';
import { TreeItem, TreeItemCollapsibleState } from 'vscode';
import { TreeItem, TreeItemCheckboxState, TreeItemCollapsibleState } from 'vscode';
import type { DiffWithCommandArgs } from '../../commands'; import type { DiffWithCommandArgs } from '../../commands';
import { Commands } from '../../constants'; import { Commands } from '../../constants';
import { StatusFileFormatter } from '../../git/formatters/statusFormatter'; import { StatusFileFormatter } from '../../git/formatters/statusFormatter';
@ -12,9 +12,13 @@ import { joinPaths, relativeDir } from '../../system/path';
import type { View } from '../viewBase'; import type { View } from '../viewBase';
import type { FileNode } from './folderNode'; import type { FileNode } from './folderNode';
import type { ViewNode } from './viewNode'; 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<View, State> implements FileNode {
constructor( constructor(
view: View, view: View,
parent: ViewNode, parent: ViewNode,
@ -25,6 +29,9 @@ export class ResultsFileNode extends ViewRefFileNode implements FileNode {
private readonly direction: 'ahead' | 'behind' | undefined, private readonly direction: 'ahead' | 'behind' | undefined,
) { ) {
super(GitUri.fromFile(file, repoPath, ref1 || ref2), view, parent, file); super(GitUri.fromFile(file, repoPath, ref1 || ref2), view, parent, file);
this.updateContext({ file: file });
this._uniqueId = getViewNodeId('results-file', this.context);
} }
override toClipboard(): string { override toClipboard(): string {
@ -55,6 +62,12 @@ export class ResultsFileNode extends ViewRefFileNode implements FileNode {
}; };
item.command = this.getCommand(); item.command = this.getCommand();
item.checkboxState = {
state: this.getState('checked') ?? TreeItemCheckboxState.Unchecked,
tooltip: 'Mark as Reviewed',
};
return item; return item;
} }

+ 8
- 4
src/views/nodes/resultsFilesNode.ts View File

@ -16,6 +16,10 @@ import { FolderNode } from './folderNode';
import { ResultsFileNode } from './resultsFileNode'; import { ResultsFileNode } from './resultsFileNode';
import { ContextValues, getViewNodeId, ViewNode } from './viewNode'; import { ContextValues, getViewNodeId, ViewNode } from './viewNode';
type State = {
filter: FilesQueryFilter | undefined;
};
export enum FilesQueryFilter { export enum FilesQueryFilter {
Left = 0, Left = 0,
Right = 1, Right = 1,
@ -29,7 +33,7 @@ export interface FilesQueryResults {
filtered?: Map<FilesQueryFilter, GitFile[]>; filtered?: Map<FilesQueryFilter, GitFile[]>;
} }
export class ResultsFilesNode extends ViewNode<ViewsWithCommits> {
export class ResultsFilesNode extends ViewNode<ViewsWithCommits, State> {
constructor( constructor(
view: ViewsWithCommits, view: ViewsWithCommits,
protected override parent: ViewNode, protected override parent: ViewNode,
@ -56,12 +60,12 @@ export class ResultsFilesNode extends ViewNode {
} }
get filter(): FilesQueryFilter | undefined { get filter(): FilesQueryFilter | undefined {
return this.view.nodeState.getState<FilesQueryFilter>(this.id, 'filter');
return this.getState('filter');
} }
set filter(value: FilesQueryFilter | undefined) { set filter(value: FilesQueryFilter | undefined) {
if (this.filter === value) return; if (this.filter === value) return;
this.view.nodeState.storeState(this.id, 'filter', value);
this.storeState('filter', value, true);
this._filterResults = undefined; this._filterResults = undefined;
void this.triggerChange(false); void this.triggerChange(false);
@ -188,7 +192,7 @@ export class ResultsFilesNode extends ViewNode {
override refresh(reset: boolean = false) { override refresh(reset: boolean = false) {
if (!reset) return; if (!reset) return;
this.view.nodeState.deleteState(this.id, 'filter');
this.deleteState('filter');
this._filterResults = undefined; this._filterResults = undefined;
this._filesQueryResults = this._filesQuery(); this._filesQueryResults = this._filesQuery();

+ 6
- 2
src/views/nodes/viewNode.ts View File

@ -271,12 +271,16 @@ export abstract class ViewNode
return this.view.nodeState.getState(this.id, key as string); return this.view.nodeState.getState(this.id, key as string);
} }
storeState<T extends StateKey<State> = StateKey<State>>(key: T, value: StateValue<State, T>): void {
storeState<T extends StateKey<State> = StateKey<State>>(
key: T,
value: StateValue<State, T>,
sticky?: boolean,
): void {
if (this.id == null) { if (this.id == null) {
debugger; debugger;
throw new Error('Id is required to store state'); 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);
} }
} }

+ 39
- 11
src/views/viewBase.ts View File

@ -2,6 +2,7 @@ import type {
CancellationToken, CancellationToken,
ConfigurationChangeEvent, ConfigurationChangeEvent,
Event, Event,
TreeCheckboxChangeEvent,
TreeDataProvider, TreeDataProvider,
TreeItem, TreeItem,
TreeView, TreeView,
@ -312,6 +313,7 @@ export abstract class ViewBase<
this.tree, this.tree,
this.tree.onDidChangeSelection(debounce(this.onSelectionChanged, 250), this), this.tree.onDidChangeSelection(debounce(this.onSelectionChanged, 250), this),
this.tree.onDidChangeVisibility(debounce(this.onVisibilityChanged, 250), this), this.tree.onDidChangeVisibility(debounce(this.onVisibilityChanged, 250), this),
this.tree.onDidChangeCheckboxState(this.onCheckboxStateChanged, this),
this.tree.onDidCollapseElement(this.onElementCollapsed, this), this.tree.onDidCollapseElement(this.onElementCollapsed, this),
this.tree.onDidExpandElement(this.onElementExpanded, this), this.tree.onDidExpandElement(this.onElementExpanded, this),
); );
@ -364,6 +366,17 @@ export abstract class ViewBase<
this._onDidChangeNodeCollapsibleState.fire({ ...e, state: TreeItemCollapsibleState.Expanded }); this._onDidChangeNodeCollapsibleState.fire({ ...e, state: TreeItemCollapsibleState.Expanded });
} }
protected onCheckboxStateChanged(e: TreeCheckboxChangeEvent<ViewNode>) {
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<ViewNode>) { protected onSelectionChanged(e: TreeViewSelectionChangeEvent<ViewNode>) {
this._onDidChangeSelection.fire(e); this._onDidChangeSelection.fire(e);
} }
@ -653,39 +666,54 @@ export abstract class ViewBase<
} }
export class ViewNodeState implements Disposable { export class ViewNodeState implements Disposable {
private _state: Map<string, Map<string, unknown>> | undefined;
private _store: Map<string, Map<string, unknown>> | undefined;
private _stickyStore: Map<string, Map<string, unknown>> | undefined;
dispose() { dispose() {
this.reset(); this.reset();
this._stickyStore?.clear();
this._stickyStore = undefined;
} }
reset() { reset() {
this._state?.clear();
this._state = undefined;
this._store?.clear();
this._store = undefined;
} }
deleteState(id: string, key?: string): void { deleteState(id: string, key?: string): void {
if (key == null) { if (key == null) {
this._state?.delete(id);
this._store?.delete(id);
this._stickyStore?.delete(id);
} else { } else {
this._state?.get(id)?.delete(key);
this._store?.get(id)?.delete(key);
this._stickyStore?.get(id)?.delete(key);
} }
} }
getState<T>(id: string, key: string): T | undefined { getState<T>(id: string, key: string): T | undefined {
return this._state?.get(id)?.get(key) as T | undefined;
return (this._stickyStore?an>.get(id)?.get(key) ?? this._store?.get(id)?.get(key)) as T | undefined;
} }
storeState<T>(id: string, key: string, value: T): void {
if (this._state == null) {
this._state = new Map();
storeState<T>(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) { if (state != null) {
state.set(key, value); state.set(key, value);
} else { } else {
this._state.set(id, new Map([[key, value]]));
store.set(id, new Map([[key, value]]));
} }
} }
} }

+ 4
- 4
yarn.lock View File

@ -776,10 +776,10 @@
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311"
integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g== 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@*": "@types/yargs-parser@*":
version "21.0.0" version "21.0.0"

Loading…
Cancel
Save