|
'use strict';
|
|
import { Disposable, Event, EventEmitter, TextDocument, TextDocumentChangeEvent, TextEditor, window, workspace } from 'vscode';
|
|
import { CommandContext, setCommandContext } from './commands';
|
|
import { TextDocumentComparer } from './comparers';
|
|
import { GitService } from './gitService';
|
|
|
|
export interface BlameabilityChangeEvent {
|
|
blameable: boolean;
|
|
editor: TextEditor;
|
|
}
|
|
|
|
export class BlameabilityTracker extends Disposable {
|
|
|
|
private _onDidChange = new EventEmitter<BlameabilityChangeEvent>();
|
|
get onDidChange(): Event<BlameabilityChangeEvent> {
|
|
return this._onDidChange.event;
|
|
}
|
|
|
|
private _disposable: Disposable;
|
|
private _documentChangeDisposable: Disposable;
|
|
private _editor: TextEditor;
|
|
private _isBlameable: boolean;
|
|
|
|
constructor(private git: GitService) {
|
|
super(() => this.dispose());
|
|
|
|
const subscriptions: Disposable[] = [];
|
|
|
|
subscriptions.push(window.onDidChangeActiveTextEditor(this._onActiveTextEditorChanged, this));
|
|
subscriptions.push(workspace.onDidSaveTextDocument(this._onTextDocumentSaved, this));
|
|
subscriptions.push(this.git.onDidBlameFail(this._onBlameFailed, this));
|
|
|
|
this._disposable = Disposable.from(...subscriptions);
|
|
|
|
this._onActiveTextEditorChanged(window.activeTextEditor);
|
|
}
|
|
|
|
dispose() {
|
|
this._disposable && this._disposable.dispose();
|
|
this._documentChangeDisposable && this._documentChangeDisposable.dispose();
|
|
}
|
|
|
|
private _onActiveTextEditorChanged(editor: TextEditor) {
|
|
this._editor = editor;
|
|
let blameable = editor && editor.document && !editor.document.isDirty;
|
|
|
|
if (blameable) {
|
|
blameable = this.git.getBlameability(editor.document.fileName);
|
|
}
|
|
|
|
this._subscribeToDocumentChanges();
|
|
this.updateBlameability(blameable, true);
|
|
}
|
|
|
|
private _onBlameFailed(key: string) {
|
|
const fileName = this._editor && this._editor.document && this._editor.document.fileName;
|
|
if (!fileName || key !== this.git.getCacheEntryKey(fileName)) return;
|
|
|
|
this.updateBlameability(false);
|
|
}
|
|
|
|
private _onTextDocumentChanged(e: TextDocumentChangeEvent) {
|
|
if (!TextDocumentComparer.equals(this._editor && this._editor.document, e && e.document)) return;
|
|
|
|
// Can't unsubscribe here because undo doesn't trigger any other event
|
|
//this._unsubscribeToDocumentChanges();
|
|
//this.updateBlameability(false);
|
|
|
|
// We have to defer because isDirty is not reliable inside this event
|
|
setTimeout(() => this.updateBlameability(!e.document.isDirty), 1);
|
|
}
|
|
|
|
private _onTextDocumentSaved(e: TextDocument) {
|
|
if (!TextDocumentComparer.equals(this._editor && this._editor.document, e)) return;
|
|
|
|
// Don't need to resubscribe as we aren't unsubscribing on document changes anymore
|
|
//this._subscribeToDocumentChanges();
|
|
this.updateBlameability(!e.isDirty);
|
|
}
|
|
|
|
private _subscribeToDocumentChanges() {
|
|
this._unsubscribeToDocumentChanges();
|
|
this._documentChangeDisposable = workspace.onDidChangeTextDocument(this._onTextDocumentChanged, this);
|
|
}
|
|
|
|
private _unsubscribeToDocumentChanges() {
|
|
this._documentChangeDisposable && this._documentChangeDisposable.dispose();
|
|
this._documentChangeDisposable = undefined;
|
|
}
|
|
|
|
private updateBlameability(blameable: boolean, force: boolean = false) {
|
|
if (!force && this._isBlameable === blameable) return;
|
|
|
|
setCommandContext(CommandContext.IsBlameable, blameable);
|
|
this._onDidChange.fire({
|
|
blameable: blameable,
|
|
editor: this._editor
|
|
});
|
|
}
|
|
}
|