You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

99 lines
3.6 KiB

  1. 'use strict';
  2. import { Disposable, Event, EventEmitter, TextDocument, TextDocumentChangeEvent, TextEditor, window, workspace } from 'vscode';
  3. import { CommandContext, setCommandContext } from './commands';
  4. import { TextDocumentComparer } from './comparers';
  5. import { GitService } from './gitService';
  6. export interface BlameabilityChangeEvent {
  7. blameable: boolean;
  8. editor: TextEditor;
  9. }
  10. export class BlameabilityTracker extends Disposable {
  11. private _onDidChange = new EventEmitter<BlameabilityChangeEvent>();
  12. get onDidChange(): Event<BlameabilityChangeEvent> {
  13. return this._onDidChange.event;
  14. }
  15. private _disposable: Disposable;
  16. private _documentChangeDisposable: Disposable;
  17. private _editor: TextEditor;
  18. private _isBlameable: boolean;
  19. constructor(private git: GitService) {
  20. super(() => this.dispose());
  21. const subscriptions: Disposable[] = [];
  22. subscriptions.push(window.onDidChangeActiveTextEditor(this._onActiveTextEditorChanged, this));
  23. subscriptions.push(workspace.onDidSaveTextDocument(this._onTextDocumentSaved, this));
  24. subscriptions.push(this.git.onDidBlameFail(this._onBlameFailed, this));
  25. this._disposable = Disposable.from(...subscriptions);
  26. this._onActiveTextEditorChanged(window.activeTextEditor);
  27. }
  28. dispose() {
  29. this._disposable && this._disposable.dispose();
  30. this._documentChangeDisposable && this._documentChangeDisposable.dispose();
  31. }
  32. private _onActiveTextEditorChanged(editor: TextEditor) {
  33. this._editor = editor;
  34. let blameable = editor && editor.document && !editor.document.isDirty;
  35. if (blameable) {
  36. blameable = this.git.getBlameability(editor.document.fileName);
  37. }
  38. this._subscribeToDocumentChanges();
  39. this.updateBlameability(blameable, true);
  40. }
  41. private _onBlameFailed(key: string) {
  42. const fileName = this._editor && this._editor.document && this._editor.document.fileName;
  43. if (!fileName || key !== this.git.getCacheEntryKey(fileName)) return;
  44. this.updateBlameability(false);
  45. }
  46. private _onTextDocumentChanged(e: TextDocumentChangeEvent) {
  47. if (!TextDocumentComparer.equals(this._editor && this._editor.document, e && e.document)) return;
  48. // Can't unsubscribe here because undo doesn't trigger any other event
  49. //this._unsubscribeToDocumentChanges();
  50. //this.updateBlameability(false);
  51. // We have to defer because isDirty is not reliable inside this event
  52. setTimeout(() => this.updateBlameability(!e.document.isDirty), 1);
  53. }
  54. private _onTextDocumentSaved(e: TextDocument) {
  55. if (!TextDocumentComparer.equals(this._editor && this._editor.document, e)) return;
  56. // Don't need to resubscribe as we aren't unsubscribing on document changes anymore
  57. //this._subscribeToDocumentChanges();
  58. this.updateBlameability(!e.isDirty);
  59. }
  60. private _subscribeToDocumentChanges() {
  61. this._unsubscribeToDocumentChanges();
  62. this._documentChangeDisposable = workspace.onDidChangeTextDocument(this._onTextDocumentChanged, this);
  63. }
  64. private _unsubscribeToDocumentChanges() {
  65. this._documentChangeDisposable && this._documentChangeDisposable.dispose();
  66. this._documentChangeDisposable = undefined;
  67. }
  68. private updateBlameability(blameable: boolean, force: boolean = false) {
  69. if (!force && this._isBlameable === blameable) return;
  70. setCommandContext(CommandContext.IsBlameable, blameable);
  71. this._onDidChange.fire({
  72. blameable: blameable,
  73. editor: this._editor
  74. });
  75. }
  76. }