From 2d81082d0c77c79e727b6749229b25a6f9391b9a Mon Sep 17 00:00:00 2001 From: Eric Amodio Date: Mon, 11 Nov 2019 23:35:22 -0500 Subject: [PATCH] Fixes #667 - Updates tracked lines on content changes --- src/trackers/documentTracker.ts | 21 ++++++++++++++++- src/trackers/gitLineTracker.ts | 51 ++++++++++++++++++++++++++--------------- src/trackers/lineTracker.ts | 37 ++++++++++++++++++++++++++---- 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/trackers/documentTracker.ts b/src/trackers/documentTracker.ts index d49c437..eb9a3ff 100644 --- a/src/trackers/documentTracker.ts +++ b/src/trackers/documentTracker.ts @@ -9,6 +9,7 @@ import { Range, TextDocument, TextDocumentChangeEvent, + TextDocumentContentChangeEvent, TextEditor, TextLine, Uri, @@ -23,6 +24,12 @@ import { DocumentBlameStateChangeEvent, TrackedDocument } from './trackedDocumen export * from './trackedDocument'; +export interface DocumentContentChangeEvent { + readonly editor: TextEditor; + readonly document: TrackedDocument; + readonly contentChanges: ReadonlyArray; +} + export interface DocumentDirtyStateChangeEvent { readonly editor: TextEditor; readonly document: TrackedDocument; @@ -40,6 +47,11 @@ export class DocumentTracker implements Disposable { return this._onDidChangeBlameState.event; } + private _onDidChangeContent = new EventEmitter>(); + get onDidChangeContent(): Event> { + return this._onDidChangeContent.event; + } + private _onDidChangeDirtyState = new EventEmitter>(); get onDidChangeDirtyState(): Event> { return this._onDidChangeDirtyState.event; @@ -127,7 +139,9 @@ export class DocumentTracker implements Disposable { private onTextDocumentChanged(e: TextDocumentChangeEvent) { const { scheme } = e.document.uri; - if (scheme !== DocumentSchemes.File && scheme !== DocumentSchemes.Vsls) return; + if (scheme !== DocumentSchemes.File && scheme !== DocumentSchemes.Git && scheme !== DocumentSchemes.Vsls) { + return; + } let doc = this._documentMap.get(e.document); if (doc === undefined) { @@ -148,6 +162,11 @@ export class DocumentTracker implements Disposable { } } + // Only fire change events for the active document + if (editor !== undefined && editor.document === e.document) { + this._onDidChangeContent.fire({ editor: editor, document: doc, contentChanges: e.contentChanges }); + } + if (!doc.forceDirtyStateChangeOnNextDocumentChange && doc.dirty === dirty) return; doc.resetForceDirtyStateChangeOnNextDocumentChange(); diff --git a/src/trackers/gitLineTracker.ts b/src/trackers/gitLineTracker.ts index 6037d2d..0f1f197 100644 --- a/src/trackers/gitLineTracker.ts +++ b/src/trackers/gitLineTracker.ts @@ -4,6 +4,7 @@ import { Container } from '../container'; import { GitBlameCommit, GitLogCommit } from '../git/gitService'; import { DocumentBlameStateChangeEvent, + DocumentContentChangeEvent, DocumentDirtyIdleTriggerEvent, DocumentDirtyStateChangeEvent, GitDocumentState @@ -22,21 +23,39 @@ export class GitLineTracker extends LineTracker { this.reset(); let updated = false; - if (!this._suspended && !e.pending && e.lines !== undefined && e.editor !== undefined) { + if (!this.suspended && !e.pending && e.lines !== undefined && e.editor !== undefined) { updated = await this.updateState(e.lines, e.editor); } return super.fireLinesChanged(updated ? e : { ...e, lines: undefined }); } + private _subscriptionOnlyWhenActive: Disposable | undefined; + protected onStart(): Disposable | undefined { + this.onResume(); + return Disposable.from( + { dispose: () => this.onSuspend() }, Container.tracker.onDidChangeBlameState(this.onBlameStateChanged, this), Container.tracker.onDidChangeDirtyState(this.onDirtyStateChanged, this), Container.tracker.onDidTriggerDirtyIdle(this.onDirtyIdleTriggered, this) ); } + protected onResume(): void { + if (this._subscriptionOnlyWhenActive === undefined) { + this._subscriptionOnlyWhenActive = Container.tracker.onDidChangeContent(this.onContentChanged, this); + } + } + + protected onSuspend(): void { + if (this._subscriptionOnlyWhenActive === undefined) return; + + this._subscriptionOnlyWhenActive.dispose(); + this._subscriptionOnlyWhenActive = undefined; + } + @debug({ args: { 0: (e: DocumentBlameStateChangeEvent) => @@ -51,6 +70,18 @@ export class GitLineTracker extends LineTracker { @debug({ args: { + 0: (e: DocumentContentChangeEvent) => + `editor=${e.editor.document.uri.toString(true)}, doc=${e.document.uri.toString(true)}` + } + }) + private onContentChanged(e: DocumentContentChangeEvent) { + if (e.contentChanges.some(cc => this.lines?.some(l => cc.range.start.line <= l && cc.range.end.line >= l))) { + this.trigger('editor'); + } + } + + @debug({ + args: { 0: (e: DocumentDirtyIdleTriggerEvent) => `editor=${e.editor.document.uri.toString(true)}, doc=${e.document.uri.toString(true)}` } @@ -76,24 +107,6 @@ export class GitLineTracker extends LineTracker { } } - private _suspended = false; - - @debug() - private resume(options: { force?: boolean } = {}) { - if (!options.force && !this._suspended) return; - - this._suspended = false; - this.trigger('editor'); - } - - @debug() - private suspend(options: { force?: boolean } = {}) { - if (!options.force && this._suspended) return; - - this._suspended = true; - this.trigger('editor'); - } - private async updateState(lines: number[], editor: TextEditor): Promise { const trackedDocument = await Container.tracker.getOrAdd(editor.document); if (!trackedDocument.isBlameable || !this.includesAll(lines)) return false; diff --git a/src/trackers/lineTracker.ts b/src/trackers/lineTracker.ts index b0236f0..fa02ac2 100644 --- a/src/trackers/lineTracker.ts +++ b/src/trackers/lineTracker.ts @@ -1,7 +1,7 @@ 'use strict'; import { Disposable, Event, EventEmitter, TextEditor, TextEditorSelectionChangeEvent, window } from 'vscode'; import { isTextEditor } from '../constants'; -import { Deferrable, Functions } from '../system'; +import { debug, Deferrable, Functions } from '../system'; export interface LinesChangeEvent { readonly editor: TextEditor | undefined; @@ -88,10 +88,9 @@ export class LineTracker implements Disposable { return this._subscriptions.has(subscriber); } - protected onStart(): Disposable | undefined { - return undefined; - } + protected onStart?(): Disposable | undefined; + @debug({ args: false }) start(subscriber: any, subscription: Disposable): Disposable { const disposable = { dispose: () => this.stop(subscriber) @@ -111,7 +110,7 @@ export class LineTracker implements Disposable { this._disposable = Disposable.from( window.onDidChangeActiveTextEditor(Functions.debounce(this.onActiveTextEditorChanged, 0), this), window.onDidChangeTextEditorSelection(this.onTextEditorSelectionChanged, this), - this.onStart() ?? { dispose: () => {} } + this.onStart?.() ?? { dispose: () => {} } ); setImmediate(() => this.onActiveTextEditorChanged(window.activeTextEditor)); @@ -120,6 +119,7 @@ export class LineTracker implements Disposable { return disposable; } + @debug({ args: false }) stop(subscriber: any) { const subs = this._subscriptions.get(subscriber); if (subs === undefined) return; @@ -141,6 +141,33 @@ export class LineTracker implements Disposable { } } + private _suspended = false; + get suspended() { + return this._suspended; + } + + protected onResume?(): void; + + @debug() + resume(options: { force?: boolean } = {}) { + if (!options.force && !this._suspended) return; + + this._suspended = false; + void this.onResume?.(); + this.trigger('editor'); + } + + protected onSuspend?(): void; + + @debug() + suspend(options: { force?: boolean } = {}) { + if (!options.force && this._suspended) return; + + this._suspended = true; + void this.onSuspend?.(); + this.trigger('editor'); + } + protected fireLinesChanged(e: LinesChangeEvent) { this._onDidChangeActiveLines.fire(e); }