From f41af294ba348c634801f99a6e0ac78a3b765acb Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 12 Jun 2023 00:58:38 -0400 Subject: [PATCH] Adds better typing for view commands --- src/constants.ts | 25 ++++++++++++++++++++----- src/views/branchesView.ts | 2 +- src/views/commitsView.ts | 2 +- src/views/contributorsView.ts | 2 +- src/views/fileHistoryView.ts | 6 +++++- src/views/lineHistoryView.ts | 2 +- src/views/remotesView.ts | 2 +- src/views/repositoriesView.ts | 2 +- src/views/searchAndCompareView.ts | 6 +++++- src/views/stashesView.ts | 2 +- src/views/tagsView.ts | 2 +- src/views/viewBase.ts | 34 ++++++++++++++++++---------------- src/views/workspacesView.ts | 2 +- src/views/worktreesView.ts | 2 +- 14 files changed, 58 insertions(+), 33 deletions(-) diff --git a/src/constants.ts b/src/constants.ts index 373a289..3af5360 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -392,8 +392,13 @@ export type TreeViewCommands = `gitlens.views.${ | `setShowAvatars${'On' | 'Off'}` | `setFilesFilterOn${'Left' | 'Right'}` | 'setFilesFilterOff'}` - | `stashes.${'copy' | 'refresh' | `setLayoutTo${'List' | 'Tree'}` | `setFilesLayoutTo${'Auto' | 'List' | 'Tree'}`}` - | `tags.${'copy' | 'refresh' | `setFilesLayoutTo${'Auto' | 'List' | 'Tree'}`}` + | `stashes.${'copy' | 'refresh' | `setFilesLayoutTo${'Auto' | 'List' | 'Tree'}`}` + | `tags.${ + | 'copy' + | 'refresh' + | `setLayoutTo${'List' | 'Tree'}` + | `setFilesLayoutTo${'Auto' | 'List' | 'Tree'}` + | `setShowAvatars${'On' | 'Off'}`}` | `workspaces.${ | 'copy' | 'refresh' @@ -411,9 +416,19 @@ export type TreeViewCommands = `gitlens.views.${ | `setShowAvatars${'On' | 'Off'}` | `setShowBranchComparison${'On' | 'Off'}` | `setShowBranchPullRequest${'On' | 'Off'}`}`}`; -export type TreeViewCommandsByViewId = TreeViewCommands extends `${T}.${infer U}` - ? `${U}` - : never; + +type ExtractSuffix = U extends `${Prefix}${infer V}` ? V : never; +type FilterCommands = U extends `${Prefix}${infer V}` ? `${Prefix}${V}` : never; + +export type TreeViewCommandsByViewId = FilterCommands; +export type TreeViewCommandsByViewType = FilterCommands< + `gitlens.views.${T}.`, + TreeViewCommands +>; +export type TreeViewCommandSuffixesByViewType = ExtractSuffix< + `gitlens.views.${T}.`, + FilterCommands<`gitlens.views.${T}.`, TreeViewCommands> +>; export type CustomEditorTypes = 'rebase'; export type CustomEditorIds = `gitlens.${CustomEditorTypes}`; diff --git a/src/views/branchesView.ts b/src/views/branchesView.ts index 6f878f9..04f7358 100644 --- a/src/views/branchesView.ts +++ b/src/views/branchesView.ts @@ -93,7 +93,7 @@ export class BranchesViewNode extends RepositoriesSubscribeableNode { +export class BranchesView extends ViewBase<'branches', BranchesViewNode, BranchesViewConfig> { protected readonly configKey = 'branches'; constructor(container: Container) { diff --git a/src/views/commitsView.ts b/src/views/commitsView.ts index 0defe2e..febab45 100644 --- a/src/views/commitsView.ts +++ b/src/views/commitsView.ts @@ -190,7 +190,7 @@ interface CommitsViewState { myCommitsOnly?: boolean; } -export class CommitsView extends ViewBase { +export class CommitsView extends ViewBase<'commits', CommitsViewNode, CommitsViewConfig> { protected readonly configKey = 'commits'; constructor(container: Container) { diff --git a/src/views/contributorsView.ts b/src/views/contributorsView.ts index d020acf..167c6f5 100644 --- a/src/views/contributorsView.ts +++ b/src/views/contributorsView.ts @@ -111,7 +111,7 @@ export class ContributorsViewNode extends RepositoriesSubscribeableNode { +export class ContributorsView extends ViewBase<'contributors', ContributorsViewNode, ContributorsViewConfig> { protected readonly configKey = 'contributors'; constructor(container: Container) { diff --git a/src/views/fileHistoryView.ts b/src/views/fileHistoryView.ts index 576ed6a..a6b97a0 100644 --- a/src/views/fileHistoryView.ts +++ b/src/views/fileHistoryView.ts @@ -13,7 +13,11 @@ import { registerViewCommand } from './viewCommands'; const pinnedSuffix = ' (pinned)'; -export class FileHistoryView extends ViewBase { +export class FileHistoryView extends ViewBase< + 'fileHistory', + FileHistoryTrackerNode | LineHistoryTrackerNode, + FileHistoryViewConfig +> { protected readonly configKey = 'fileHistory'; private _followCursor: boolean = false; diff --git a/src/views/lineHistoryView.ts b/src/views/lineHistoryView.ts index aa9bd12..206daec 100644 --- a/src/views/lineHistoryView.ts +++ b/src/views/lineHistoryView.ts @@ -11,7 +11,7 @@ import { registerViewCommand } from './viewCommands'; const pinnedSuffix = ' (pinned)'; -export class LineHistoryView extends ViewBase { +export class LineHistoryView extends ViewBase<'lineHistory', LineHistoryTrackerNode, LineHistoryViewConfig> { protected readonly configKey = 'lineHistory'; constructor(container: Container) { diff --git a/src/views/remotesView.ts b/src/views/remotesView.ts index ff539fa..3412411 100644 --- a/src/views/remotesView.ts +++ b/src/views/remotesView.ts @@ -94,7 +94,7 @@ export class RemotesViewNode extends RepositoriesSubscribeableNode { +export class RemotesView extends ViewBase<'remotes', RemotesViewNode, RemotesViewConfig> { protected readonly configKey = 'remotes'; constructor(container: Container) { diff --git a/src/views/repositoriesView.ts b/src/views/repositoriesView.ts index 3f45c39..317ee61 100644 --- a/src/views/repositoriesView.ts +++ b/src/views/repositoriesView.ts @@ -40,7 +40,7 @@ import { WorktreesNode } from './nodes/worktreesNode'; import { ViewBase } from './viewBase'; import { registerViewCommand } from './viewCommands'; -export class RepositoriesView extends ViewBase { +export class RepositoriesView extends ViewBase<'repositories', RepositoriesNode, RepositoriesViewConfig> { protected readonly configKey = 'repositories'; constructor(container: Container) { diff --git a/src/views/searchAndCompareView.ts b/src/views/searchAndCompareView.ts index 5cae6ec..d5c16e9 100644 --- a/src/views/searchAndCompareView.ts +++ b/src/views/searchAndCompareView.ts @@ -245,7 +245,11 @@ export class SearchAndCompareViewNode extends ViewNode { } } -export class SearchAndCompareView extends ViewBase { +export class SearchAndCompareView extends ViewBase< + 'searchAndCompare', + SearchAndCompareViewNode, + SearchAndCompareViewConfig +> { protected readonly configKey = 'searchAndCompare'; constructor(container: Container) { diff --git a/src/views/stashesView.ts b/src/views/stashesView.ts index 8360e65..9bdce9a 100644 --- a/src/views/stashesView.ts +++ b/src/views/stashesView.ts @@ -88,7 +88,7 @@ export class StashesViewNode extends RepositoriesSubscribeableNode { +export class StashesView extends ViewBase<'stashes', StashesViewNode, StashesViewConfig> { protected readonly configKey = 'stashes'; constructor(container: Container) { diff --git a/src/views/tagsView.ts b/src/views/tagsView.ts index 33f85dd..540da93 100644 --- a/src/views/tagsView.ts +++ b/src/views/tagsView.ts @@ -81,7 +81,7 @@ export class TagsViewNode extends RepositoriesSubscribeableNode { +export class TagsView extends ViewBase<'tags', TagsViewNode, TagsViewConfig> { protected readonly configKey = 'tags'; constructor(container: Container) { diff --git a/src/views/viewBase.ts b/src/views/viewBase.ts index 27372ac..d0fadd3 100644 --- a/src/views/viewBase.ts +++ b/src/views/viewBase.ts @@ -28,7 +28,7 @@ import type { WorktreesViewConfig, } from '../config'; import { viewsCommonConfigKeys, viewsConfigKeys } from '../config'; -import type { TreeViewCommandsByViewId, TreeViewTypes } from '../constants'; +import type { TreeViewCommandSuffixesByViewType, TreeViewTypes } from '../constants'; import type { Container } from '../container'; import { executeCoreCommand } from '../system/command'; import { configuration } from '../system/configuration'; @@ -94,6 +94,7 @@ export interface TreeViewNodeCollapsibleStateChangeEvent extends TreeViewExpa } export abstract class ViewBase< + Type extends TreeViewTypes, RootNode extends ViewNode, ViewConfig extends | BranchesViewConfig @@ -110,6 +111,10 @@ export abstract class ViewBase< | WorktreesViewConfig, > implements TreeDataProvider, Disposable { + get id(): `gitlens.views.${Type}` { + return `gitlens.views.${this.type}`; + } + protected _onDidChangeTreeData = new EventEmitter(); get onDidChangeTreeData(): Event { return this._onDidChangeTreeData.event; @@ -136,15 +141,12 @@ export abstract class ViewBase< private readonly _lastKnownLimits = new Map(); - readonly id: `gitlens.views.${TreeViewTypes}`; - constructor( public readonly container: Container, - public readonly type: TreeViewTypes, + public readonly type: Type, public readonly name: string, private readonly trackingFeature: TrackedUsageFeatures, ) { - this.id = `gitlens.views.${type}`; this.disposables.push(once(container.onReady)(this.onReady, this)); if (this.container.debugging || configuration.get('debug')) { @@ -169,7 +171,7 @@ export abstract class ViewBase< } const getTreeItemFn = this.getTreeItem; - this.getTreeItem = async function (this: ViewBase, node: ViewNode) { + this.getTreeItem = async function (this: ViewBase, node: ViewNode) { const item = await getTreeItemFn.apply(this, [node]); if (node.resolveTreeItem == null) { @@ -181,7 +183,7 @@ export abstract class ViewBase< const resolveTreeItemFn = this.resolveTreeItem; this.resolveTreeItem = async function ( - this: ViewBase, + this: ViewBase, item: TreeItem, node: ViewNode, ) { @@ -284,8 +286,8 @@ export abstract class ViewBase< } } - getQualifiedCommand(command: TreeViewCommandsByViewId) { - return `${this.id}.${command}`; // satisfies ViewsCommandsById; + getQualifiedCommand(command: TreeViewCommandSuffixesByViewType) { + return `gitlens.views.${this.type}.${command}` as const; } protected abstract getRoot(): RootNode; @@ -381,7 +383,7 @@ export abstract class ViewBase< return this.tree?.visible ?? false; } - @log['findNode']>({ + @log['findNode']>({ args: { 0: '', 1: opts => `options=${JSON.stringify({ ...opts, canTraverse: undefined, token: undefined })}`, @@ -403,7 +405,7 @@ export abstract class ViewBase< ): Promise { const scope = getLogScope(); - async function find(this: ViewBase) { + async function find(this: ViewBase) { try { const node = await this.findNodeCoreBFS( predicate, @@ -546,7 +548,7 @@ export abstract class ViewBase< this.triggerNodeChange(); } - @debug['refreshNode']>({ args: { 0: n => n.toString() } }) + @debug['refreshNode']>({ args: { 0: n => n.toString() } }) async refreshNode(node: ViewNode, reset: boolean = false, force: boolean = false) { const cancel = await node.refresh?.(reset); if (!force && cancel === true) return; @@ -554,7 +556,7 @@ export abstract class ViewBase< this.triggerNodeChange(node); } - @log['reveal']>({ args: { 0: n => n.toString() } }) + @log['reveal']>({ args: { 0: n => n.toString() } }) async reveal( node: ViewNode, options?: { @@ -588,7 +590,7 @@ export abstract class ViewBase< return this._lastKnownLimits.get(node.id); } - @debug['loadMoreNodeChildren']>({ + @debug['loadMoreNodeChildren']>({ args: { 0: n => n.toString(), 2: n => n?.toString() }, }) async loadMoreNodeChildren( @@ -605,7 +607,7 @@ export abstract class ViewBase< this._lastKnownLimits.set(node.id, node.limit); } - @debug['resetNodeLastKnownLimit']>({ + @debug['resetNodeLastKnownLimit']>({ args: { 0: n => n.toString() }, singleLine: true, }) @@ -613,7 +615,7 @@ export abstract class ViewBase< this._lastKnownLimits.delete(node.id); } - @debug['triggerNodeChange']>({ args: { 0: n => n?.toString() } }) + @debug['triggerNodeChange']>({ args: { 0: n => n?.toString() } }) triggerNodeChange(node?: ViewNode) { // Since the root node won't actually refresh, force everything this._onDidChangeTreeData.fire(node != null && node !== this.root ? node : undefined); diff --git a/src/views/workspacesView.ts b/src/views/workspacesView.ts index 85bbd3f..eb8e0ad 100644 --- a/src/views/workspacesView.ts +++ b/src/views/workspacesView.ts @@ -18,7 +18,7 @@ import { WorkspacesViewNode } from './nodes/workspacesViewNode'; import { ViewBase } from './viewBase'; import { registerViewCommand } from './viewCommands'; -export class WorkspacesView extends ViewBase { +export class WorkspacesView extends ViewBase<'workspaces', WorkspacesViewNode, WorkspacesViewConfig> { protected readonly configKey = 'repositories'; private _workspacesChangedDisposable: Disposable; private _visibleDisposable: Disposable | undefined; diff --git a/src/views/worktreesView.ts b/src/views/worktreesView.ts index cf5e51b..3a01baa 100644 --- a/src/views/worktreesView.ts +++ b/src/views/worktreesView.ts @@ -92,7 +92,7 @@ export class WorktreesViewNode extends RepositoriesSubscribeableNode { +export class WorktreesView extends ViewBase<'worktrees', WorktreesViewNode, WorktreesViewConfig> { protected readonly configKey = 'worktrees'; constructor(container: Container) {