From d086131bb582d2e3b720b7eeed77c30327ad7039 Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Wed, 12 Sep 2018 01:18:34 -0400 Subject: [PATCH] Fixes issue with line history refreshing on all selection changes Changes line history to use GitLineTracker for better resource usage --- src/trackers/gitLineTracker.ts | 10 ++- src/views/explorer.ts | 14 ++-- src/views/fileHistoryExplorer.ts | 6 +- src/views/lineHistoryExplorer.ts | 7 +- src/views/nodes.ts | 6 +- src/views/nodes/activeFileHistoryNode.ts | 108 --------------------------- src/views/nodes/activeLineHistoryNode.ts | 116 ----------------------------- src/views/nodes/explorerNode.ts | 2 +- src/views/nodes/fileHistoryTrackerNode.ts | 114 +++++++++++++++++++++++++++++ src/views/nodes/lineHistoryTrackerNode.ts | 117 ++++++++++++++++++++++++++++++ 10 files changed, 254 insertions(+), 246 deletions(-) delete mode 100644 src/views/nodes/activeFileHistoryNode.ts delete mode 100644 src/views/nodes/activeLineHistoryNode.ts create mode 100644 src/views/nodes/fileHistoryTrackerNode.ts create mode 100644 src/views/nodes/lineHistoryTrackerNode.ts diff --git a/src/trackers/gitLineTracker.ts b/src/trackers/gitLineTracker.ts index d7319c7..5dd6b3a 100644 --- a/src/trackers/gitLineTracker.ts +++ b/src/trackers/gitLineTracker.ts @@ -74,8 +74,12 @@ export class GitLineTracker extends LineTracker { return this._subscriptions.has(subscriber); } - start(subscriber: any, subscription: Disposable): void { - if (this.isSubscribed(subscriber)) return; + start(subscriber: any, subscription: Disposable): Disposable { + const disposable = { + dispose: () => this.stop(subscriber) + }; + + if (this.isSubscribed(subscriber)) return disposable; this._subscriptions.set(subscriber, subscription); @@ -90,6 +94,8 @@ export class GitLineTracker extends LineTracker { Container.tracker.onDidTriggerDirtyIdle(this.onDirtyIdleTriggered, this) ); } + + return disposable; } stop(subscriber: any) { diff --git a/src/views/explorer.ts b/src/views/explorer.ts index cb393db..a3f7e99 100644 --- a/src/views/explorer.ts +++ b/src/views/explorer.ts @@ -22,14 +22,9 @@ import { isPageable } from './nodes/explorerNode'; import { ResultsExplorer } from './resultsExplorer'; export enum RefreshReason { - ActiveEditorChanged = 'active-editor-changed', - AutoRefreshChanged = 'auto-refresh-changed', - Command = 'command', - ConfigurationChanged = 'configuration', - NodeCommand = 'node-command', - RepoChanged = 'repo-changed', - ViewChanged = 'view-changed', - VisibleEditorsChanged = 'visible-editors-changed' + Command = 'Command', + ConfigurationChanged = 'ConfigurationChanged', + VisibilityChanged = 'VisibilityChanged' } export type Explorer = GitExplorer | FileHistoryExplorer | LineHistoryExplorer | ResultsExplorer; @@ -139,7 +134,8 @@ export abstract class ExplorerBase implements TreeDa } } - await node.refresh(); + const cancel = await node.refresh(); + if (cancel === true) return; this.triggerNodeUpdate(node); } diff --git a/src/views/fileHistoryExplorer.ts b/src/views/fileHistoryExplorer.ts index 0ab7582..1a43273 100644 --- a/src/views/fileHistoryExplorer.ts +++ b/src/views/fileHistoryExplorer.ts @@ -5,15 +5,15 @@ import { CommandContext, setCommandContext } from '../constants'; import { Container } from '../container'; import { ExplorerBase, RefreshReason } from './explorer'; import { RefreshNodeCommandArgs } from './explorerCommands'; -import { ActiveFileHistoryNode, ExplorerNode } from './nodes'; +import { ExplorerNode, FileHistoryTrackerNode } from './nodes'; -export class FileHistoryExplorer extends ExplorerBase { +export class FileHistoryExplorer extends ExplorerBase { constructor() { super('gitlens.fileHistoryExplorer'); } getRoot() { - return new ActiveFileHistoryNode(this); + return new FileHistoryTrackerNode(this); } protected registerCommands() { diff --git a/src/views/lineHistoryExplorer.ts b/src/views/lineHistoryExplorer.ts index 7716906..7d05ab7 100644 --- a/src/views/lineHistoryExplorer.ts +++ b/src/views/lineHistoryExplorer.ts @@ -5,16 +5,15 @@ import { CommandContext, setCommandContext } from '../constants'; import { Container } from '../container'; import { ExplorerBase, RefreshReason } from './explorer'; import { RefreshNodeCommandArgs } from './explorerCommands'; -import { ExplorerNode } from './nodes'; -import { ActiveLineHistoryNode } from './nodes/activeLineHistoryNode'; +import { ExplorerNode, LineHistoryTrackerNode } from './nodes'; -export class LineHistoryExplorer extends ExplorerBase { +export class LineHistoryExplorer extends ExplorerBase { constructor() { super('gitlens.lineHistoryExplorer'); } getRoot() { - return new ActiveLineHistoryNode(this); + return new LineHistoryTrackerNode(this); } protected registerCommands() { diff --git a/src/views/nodes.ts b/src/views/nodes.ts index 963e41d..6981483 100644 --- a/src/views/nodes.ts +++ b/src/views/nodes.ts @@ -1,14 +1,14 @@ 'use strict'; export * from './nodes/explorerNode'; -export * from './nodes/activeFileHistoryNode'; -export * from './nodes/activeLineHistoryNode'; export * from './nodes/branchesNode'; export * from './nodes/branchNode'; export * from './nodes/commitFileNode'; export * from './nodes/commitNode'; export * from './nodes/fileHistoryNode'; -export * from './nodes/activeFileHistoryNode'; +export * from './nodes/fileHistoryTrackerNode'; +export * from './nodes/lineHistoryNode'; +export * from './nodes/lineHistoryTrackerNode'; export * from './nodes/remoteNode'; export * from './nodes/remotesNode'; export * from './nodes/repositoriesNode'; diff --git a/src/views/nodes/activeFileHistoryNode.ts b/src/views/nodes/activeFileHistoryNode.ts deleted file mode 100644 index 0a9b8b5..0000000 --- a/src/views/nodes/activeFileHistoryNode.ts +++ /dev/null @@ -1,108 +0,0 @@ -'use strict'; -import * as path from 'path'; -import { Disposable, TextEditor, TreeItem, TreeItemCollapsibleState, Uri, window } from 'vscode'; -import { UriComparer } from '../../comparers'; -import { Container } from '../../container'; -import { GitUri } from '../../git/gitService'; -import { Functions } from '../../system'; -import { FileHistoryExplorer } from '../fileHistoryExplorer'; -import { MessageNode } from './common'; -import { ExplorerNode, ResourceType, SubscribeableExplorerNode, unknownGitUri } from './explorerNode'; -import { FileHistoryNode } from './fileHistoryNode'; - -export class ActiveFileHistoryNode extends SubscribeableExplorerNode { - private _child: FileHistoryNode | undefined; - - constructor(explorer: FileHistoryExplorer) { - super(unknownGitUri, explorer); - } - - dispose() { - super.dispose(); - - this.resetChild(); - } - - resetChild() { - if (this._child !== undefined) { - this._child.dispose(); - this._child = undefined; - } - } - - async getChildren(): Promise { - if (this._child === undefined) { - if (this.uri === unknownGitUri) { - return [new MessageNode('There are no editors open that can provide file history')]; - } - - this._child = new FileHistoryNode(this.uri, this.explorer); - } - - return [this._child]; - } - - getTreeItem(): TreeItem { - const item = new TreeItem('File History', TreeItemCollapsibleState.Expanded); - item.contextValue = ResourceType.ActiveFileHistory; - - void this.ensureSubscription(); - - return item; - } - - async refresh() { - const editor = window.activeTextEditor; - if (editor == null || !Container.git.isTrackable(editor.document.uri)) { - if ( - this.uri === unknownGitUri || - (Container.git.isTrackable(this.uri) && - window.visibleTextEditors.some(e => e.document && UriComparer.equals(e.document.uri, this.uri))) - ) { - return; - } - - this._uri = unknownGitUri; - this.resetChild(); - - return; - } - - if (UriComparer.equals(editor!.document.uri, this.uri)) return; - - let gitUri = await GitUri.fromUri(editor!.document.uri); - - let uri; - if (gitUri.sha !== undefined) { - // If we have a sha, normalize the history to the working file (so we get a full history all the time) - const [fileName, repoPath] = await Container.git.findWorkingFileName( - gitUri.fsPath, - gitUri.repoPath, - gitUri.sha - ); - - if (fileName !== undefined) { - uri = Uri.file(repoPath !== undefined ? path.join(repoPath, fileName) : fileName); - } - } - - if (this.uri !== unknownGitUri && UriComparer.equals(uri || gitUri, this.uri)) return; - - if (uri !== undefined) { - gitUri = await GitUri.fromUri(uri); - } - - this._uri = gitUri; - this.resetChild(); - } - - protected async subscribe() { - return Disposable.from( - window.onDidChangeActiveTextEditor(Functions.debounce(this.onActiveEditorChanged, 500), this) - ); - } - - private onActiveEditorChanged(editor: TextEditor | undefined) { - void this.explorer.refreshNode(this); - } -} diff --git a/src/views/nodes/activeLineHistoryNode.ts b/src/views/nodes/activeLineHistoryNode.ts deleted file mode 100644 index 00eb8a1..0000000 --- a/src/views/nodes/activeLineHistoryNode.ts +++ /dev/null @@ -1,116 +0,0 @@ -'use strict'; -import { - Disposable, - Selection, - TextEditor, - TextEditorSelectionChangeEvent, - TreeItem, - TreeItemCollapsibleState, - window -} from 'vscode'; -import { UriComparer } from '../../comparers'; -import { Container } from '../../container'; -import { GitUri } from '../../git/gitService'; -import { Functions } from '../../system'; -import { LineHistoryExplorer } from '../lineHistoryExplorer'; -import { MessageNode } from './common'; -import { ExplorerNode, ResourceType, SubscribeableExplorerNode, unknownGitUri } from './explorerNode'; -import { LineHistoryNode } from './lineHistoryNode'; - -export class ActiveLineHistoryNode extends SubscribeableExplorerNode { - private _child: LineHistoryNode | undefined; - private _selection: Selection | undefined; - - constructor(explorer: LineHistoryExplorer) { - super(unknownGitUri, explorer); - } - - dispose() { - super.dispose(); - - this.resetChild(); - } - - resetChild() { - if (this._child !== undefined) { - this._child.dispose(); - this._child = undefined; - } - } - - async getChildren(): Promise { - if (this._child === undefined) { - if (this.uri === unknownGitUri) { - return [new MessageNode('There are no editors open that can provide line history')]; - } - - this._child = new LineHistoryNode(this.uri, this._selection!, this.explorer); - } - - return [this._child]; - } - - getTreeItem(): TreeItem { - const item = new TreeItem('Line History', TreeItemCollapsibleState.Expanded); - item.contextValue = ResourceType.ActiveLineHistory; - - void this.ensureSubscription(); - - return item; - } - - async refresh() { - const editor = window.activeTextEditor; - if (editor == null || !Container.git.isTrackable(editor.document.uri)) { - if ( - this.uri === unknownGitUri || - (Container.git.isTrackable(this.uri) && - window.visibleTextEditors.some(e => e.document && UriComparer.equals(e.document.uri, this.uri))) - ) { - return; - } - - this._uri = unknownGitUri; - this._selection = undefined; - this.resetChild(); - - return; - } - - if ( - UriComparer.equals(editor!.document.uri, this.uri) && - (this._selection !== undefined && editor.selection.isEqual(this._selection)) - ) { - return; - } - - const gitUri = await GitUri.fromUri(editor!.document.uri); - - if ( - this.uri !== unknownGitUri && - UriComparer.equals(gitUri, this.uri) && - (this._selection !== undefined && editor.selection.isEqual(this._selection)) - ) { - return; - } - - this._uri = gitUri; - this._selection = editor.selection; - this.resetChild(); - } - - protected async subscribe() { - return Disposable.from( - window.onDidChangeActiveTextEditor(Functions.debounce(this.onActiveEditorChanged, 500), this), - window.onDidChangeTextEditorSelection(Functions.debounce(this.onSelectionChanged, 500), this) - ); - } - - private onActiveEditorChanged(editor: TextEditor | undefined) { - void this.explorer.refreshNode(this); - } - - private onSelectionChanged(e: TextEditorSelectionChangeEvent) { - void this.explorer.refreshNode(this); - } -} diff --git a/src/views/nodes/explorerNode.ts b/src/views/nodes/explorerNode.ts index 47dfb55..d098c07 100644 --- a/src/views/nodes/explorerNode.ts +++ b/src/views/nodes/explorerNode.ts @@ -65,7 +65,7 @@ export abstract class ExplorerNode { return undefined; } - refresh(): void | Promise {} + refresh(): void | boolean | Promise | Promise {} } export abstract class ExplorerRefNode extends ExplorerNode { diff --git a/src/views/nodes/fileHistoryTrackerNode.ts b/src/views/nodes/fileHistoryTrackerNode.ts new file mode 100644 index 0000000..db9fda3 --- /dev/null +++ b/src/views/nodes/fileHistoryTrackerNode.ts @@ -0,0 +1,114 @@ +'use strict'; +import * as path from 'path'; +import { Disposable, TextEditor, TreeItem, TreeItemCollapsibleState, Uri, window } from 'vscode'; +import { UriComparer } from '../../comparers'; +import { Container } from '../../container'; +import { GitUri } from '../../git/gitService'; +import { Functions } from '../../system'; +import { FileHistoryExplorer } from '../fileHistoryExplorer'; +import { MessageNode } from './common'; +import { ExplorerNode, ResourceType, SubscribeableExplorerNode, unknownGitUri } from './explorerNode'; +import { FileHistoryNode } from './fileHistoryNode'; + +export class FileHistoryTrackerNode extends SubscribeableExplorerNode { + private _child: FileHistoryNode | undefined; + + constructor(explorer: FileHistoryExplorer) { + super(unknownGitUri, explorer); + } + + dispose() { + super.dispose(); + + this.resetChild(); + } + + resetChild() { + if (this._child !== undefined) { + this._child.dispose(); + this._child = undefined; + } + } + + async getChildren(): Promise { + if (this._child === undefined) { + if (this.uri === unknownGitUri) { + return [new MessageNode('There are no editors open that can provide file history')]; + } + + this._child = new FileHistoryNode(this.uri, this.explorer); + } + + return [this._child]; + } + + getTreeItem(): TreeItem { + const item = new TreeItem('File History', TreeItemCollapsibleState.Expanded); + item.contextValue = ResourceType.ActiveFileHistory; + + void this.ensureSubscription(); + + return item; + } + + async refresh() { + const editor = window.activeTextEditor; + if (editor == null || !Container.git.isTrackable(editor.document.uri)) { + if ( + this.uri === unknownGitUri || + (Container.git.isTrackable(this.uri) && + window.visibleTextEditors.some(e => e.document && UriComparer.equals(e.document.uri, this.uri))) + ) { + return true; + } + + this._uri = unknownGitUri; + this.resetChild(); + + return false; + } + + if (UriComparer.equals(editor!.document.uri, this.uri)) { + return true; + } + + let gitUri = await GitUri.fromUri(editor!.document.uri); + + let uri; + if (gitUri.sha !== undefined) { + // If we have a sha, normalize the history to the working file (so we get a full history all the time) + const [fileName, repoPath] = await Container.git.findWorkingFileName( + gitUri.fsPath, + gitUri.repoPath, + gitUri.sha + ); + + if (fileName !== undefined) { + uri = Uri.file(repoPath !== undefined ? path.join(repoPath, fileName) : fileName); + } + } + + if (this.uri !== unknownGitUri && UriComparer.equals(uri || gitUri, this.uri)) { + return true; + } + + if (uri !== undefined) { + gitUri = await GitUri.fromUri(uri); + } + + this._uri = gitUri; + this.resetChild(); + + return false; + } + + protected async subscribe() { + return Disposable.from( + window.onDidChangeActiveTextEditor(Functions.debounce(this.onActiveEditorChanged, 500), this) + ); + } + + private onActiveEditorChanged(editor: TextEditor | undefined) { + void this.explorer.refreshNode(this); + } +} diff --git a/src/views/nodes/lineHistoryTrackerNode.ts b/src/views/nodes/lineHistoryTrackerNode.ts new file mode 100644 index 0000000..79325b8 --- /dev/null +++ b/src/views/nodes/lineHistoryTrackerNode.ts @@ -0,0 +1,117 @@ +'use strict'; +import { Disposable, Selection, TreeItem, TreeItemCollapsibleState, window } from 'vscode'; +import { UriComparer } from '../../comparers'; +import { Container } from '../../container'; +import { GitUri } from '../../git/gitService'; +import { Functions } from '../../system'; +import { LinesChangeEvent } from '../../trackers/gitLineTracker'; +import { LineHistoryExplorer } from '../lineHistoryExplorer'; +import { MessageNode } from './common'; +import { ExplorerNode, ResourceType, SubscribeableExplorerNode, unknownGitUri } from './explorerNode'; +import { LineHistoryNode } from './lineHistoryNode'; + +export class LineHistoryTrackerNode extends SubscribeableExplorerNode { + private _child: LineHistoryNode | undefined; + private _selection: Selection | undefined; + + constructor(explorer: LineHistoryExplorer) { + super(unknownGitUri, explorer); + } + + dispose() { + super.dispose(); + + this.resetChild(); + } + + resetChild() { + if (this._child !== undefined) { + this._child.dispose(); + this._child = undefined; + } + } + + async getChildren(): Promise { + if (this._child === undefined) { + if (this.uri === unknownGitUri) { + return [new MessageNode('There are no editors open that can provide line history')]; + } + + this._child = new LineHistoryNode(this.uri, this._selection!, this.explorer); + } + + return [this._child]; + } + + getTreeItem(): TreeItem { + const item = new TreeItem('Line History', TreeItemCollapsibleState.Expanded); + item.contextValue = ResourceType.ActiveLineHistory; + + void this.ensureSubscription(); + + return item; + } + + async refresh() { + const editor = window.activeTextEditor; + if (editor == null || !Container.git.isTrackable(editor.document.uri)) { + if ( + this.uri === unknownGitUri || + (Container.git.isTrackable(this.uri) && + window.visibleTextEditors.some(e => e.document && UriComparer.equals(e.document.uri, this.uri))) + ) { + return true; + } + + this._uri = unknownGitUri; + this._selection = undefined; + this.resetChild(); + + return false; + } + + if ( + UriComparer.equals(editor!.document.uri, this.uri) && + (this._selection !== undefined && editor.selection.isEqual(this._selection)) + ) { + return true; + } + + const gitUri = await GitUri.fromUri(editor!.document.uri); + + if ( + this.uri !== unknownGitUri && + UriComparer.equals(gitUri, this.uri) && + (this._selection !== undefined && editor.selection.isEqual(this._selection)) + ) { + return true; + } + + this._uri = gitUri; + this._selection = editor.selection; + this.resetChild(); + + return false; + } + + protected async subscribe() { + if (Container.lineTracker.isSubscribed(this)) return undefined; + + const onActiveLinesChanged = Functions.debounce(this.onActiveLinesChanged.bind(this), 250); + + return Container.lineTracker.start( + this, + Disposable.from( + Container.lineTracker.onDidChangeActiveLines((e: LinesChangeEvent) => { + if (e.pending) return; + + onActiveLinesChanged(e); + }) + ) + ); + } + + private onActiveLinesChanged(e: LinesChangeEvent) { + void this.explorer.refreshNode(this); + } +}