diff --git a/package.json b/package.json index 625e5b1..859ea51 100644 --- a/package.json +++ b/package.json @@ -517,47 +517,47 @@ }, { "command": "gitlens.diffWithBranch", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" }, { "command": "gitlens.diffWithNext", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" }, { "command": "gitlens.diffWithPrevious", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" }, { "command": "gitlens.diffLineWithPrevious", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.diffWithWorking", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" }, { "command": "gitlens.diffLineWithWorking", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.showBlame", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.toggleBlame", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.toggleCodeLens", - "when": "editorTextFocus && gitlens:enabled && gitlens:canToggleCodeLens" + "when": "editorTextFocus && gitlens:isTracked && gitlens:canToggleCodeLens" }, { "command": "gitlens.showBlameHistory", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.showFileHistory", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" }, { "command": "gitlens.showLastQuickPick", @@ -565,15 +565,15 @@ }, { "command": "gitlens.showQuickCommitDetails", - "when": "gitlens:enabled && gitlens:isBlameable" + "when": "gitlens:isBlameable" }, { "command": "gitlens.showQuickCommitFileDetails", - "when": "gitlens:enabled && gitlens:isBlameable" + "when": "gitlens:isBlameable" }, { "command": "gitlens.showQuickFileHistory", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" }, { "command": "gitlens.showQuickBranchHistory", @@ -593,11 +593,11 @@ }, { "command": "gitlens.copyShaToClipboard", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.copyMessageToClipboard", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.closeUnchangedFiles", @@ -609,11 +609,11 @@ }, { "command": "gitlens.openCommitInRemote", - "when": "editorTextFocus && gitlens:enabled && gitlens:hasRemotes && gitlens:isBlameable" + "when": "editorTextFocus && gitlens:isBlameable && gitlens:hasRemotes" }, { "command": "gitlens.openFileInRemote", - "when": "editorTextFocus && gitlens:enabled && gitlens:hasRemotes" + "when": "editorTextFocus && gitlens:isTracked && gitlens:hasRemotes" }, { "command": "gitlens.stashApply", @@ -642,29 +642,29 @@ }, { "command": "gitlens.openFileInRemote", - "when": "gitlens:enabled && gitlens:hasRemotes", + "when": "gitlens:enabled", "group": "1_gitlens_1@2" } ], "editor/title": [ { "command": "gitlens.toggleBlame", - "when": "gitlens:enabled && gitlens:isBlameable", + "when": "gitlens:isBlameable", "group": "navigation@100" }, { "command": "gitlens.diffWithPrevious", - "when": "editorTextFocus && gitlens:enabled && config.gitlens.menus.diff.enabled", + "when": "editorTextFocus && gitlens:isTracked && config.gitlens.menus.diff.enabled", "group": "2_gitlens" }, { "command": "gitlens.diffWithWorking", - "when": "editorTextFocus && gitlens:enabled && config.gitlens.menus.diff.enabled", + "when": "editorTextFocus && gitlens:isTracked && config.gitlens.menus.diff.enabled", "group": "2_gitlens" }, { "command": "gitlens.showQuickFileHistory", - "when": "editorFocus && gitlens:enabled", + "when": "editorFocus && gitlens:isTracked", "group": "2_gitlens_1" }, { @@ -696,64 +696,64 @@ }, { "command": "gitlens.toggleBlame", - "when": "gitlens:enabled && gitlens:isBlameable", + "when": "gitlens:enabled", "group": "1_gitlens_1@2" }, { "command": "gitlens.openFileInRemote", - "when": "gitlens:enabled && gitlens:hasRemotes", + "when": "gitlens:enabled", "group": "1_gitlens_1@3" } ], "editor/context": [ { "command": "gitlens.diffLineWithPrevious", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable && config.gitlens.menus.diff.enabled", + "when": "editorTextFocus && gitlens:isBlameable && config.gitlens.menus.diff.enabled", "group": "1_gitlens@1" }, { "command": "gitlens.diffLineWithWorking", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable && config.gitlens.menus.diff.enabled", + "when": "editorTextFocus && gitlens:isBlameable && config.gitlens.menus.diff.enabled", "group": "1_gitlens@2" }, { "command": "gitlens.showQuickCommitFileDetails", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable", + "when": "editorTextFocus && gitlens:isBlameable", "group": "1_gitlens@3" }, { "command": "gitlens.diffWithPrevious", - "when": "editorTextFocus && gitlens:enabled && config.gitlens.menus.diff.enabled", + "when": "editorTextFocus && gitlens:isTracked && config.gitlens.menus.diff.enabled", "group": "1_gitlens_1@1" }, { "command": "gitlens.diffWithWorking", - "when": "editorTextFocus && gitlens:enabled && config.gitlens.menus.diff.enabled", + "when": "editorTextFocus && gitlens:isTracked && config.gitlens.menus.diff.enabled", "group": "1_gitlens_1@2" }, { "command": "gitlens.showQuickFileHistory", - "when": "gitlens:enabled", + "when": "gitlens:isTracked", "group": "2_gitlens@1" }, { "command": "gitlens.toggleBlame", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable", + "when": "editorTextFocus && gitlens:isBlameable", "group": "2_gitlens@2" }, { "command": "gitlens.openFileInRemote", - "when": "editorTextFocus && gitlens:enabled && gitlens:hasRemotes", + "when": "editorTextFocus && gitlens:isTracked && gitlens:hasRemotes", "group": "2_gitlens@3" }, { "command": "gitlens.copyShaToClipboard", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable", + "when": "editorTextFocus && gitlens:isBlameable", "group": "9_gitlens@1" }, { "command": "gitlens.copyMessageToClipboard", - "when": "editorTextFocus && gitlens:enabled && gitlens:isBlameable", + "when": "editorTextFocus && gitlens:isBlameable", "group": "9_gitlens@2" } ] @@ -783,13 +783,13 @@ "command": "gitlens.toggleBlame", "key": "alt+b", "mac": "alt+b", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.toggleCodeLens", "key": "alt+shift+b", "mac": "alt+shift+b", - "when": "editorTextFocus && gitlens:enabled && gitlens:canToggleCodeLens" + "when": "editorTextFocus && gitlens:isTracked && gitlens:canToggleCodeLens" }, { "command": "gitlens.showLastQuickPick", @@ -825,31 +825,31 @@ "command": "gitlens.diffWithNext", "key": "alt+.", "mac": "alt+.", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" }, { "command": "gitlens.diffLineWithPrevious", "key": "shift+alt+,", "mac": "shift+alt+,", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.diffWithPrevious", "key": "alt+,", "mac": "alt+,", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" }, { "command": "gitlens.diffLineWithWorking", "key": "alt+w", "mac": "alt+w", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isBlameable" }, { "command": "gitlens.diffWithWorking", "key": "shift+alt+w", "mac": "shift+alt+w", - "when": "editorTextFocus && gitlens:enabled" + "when": "editorTextFocus && gitlens:isTracked" } ] }, diff --git a/src/blameActiveLineController.ts b/src/blameActiveLineController.ts index dc6e9b4..56e57e0 100644 --- a/src/blameActiveLineController.ts +++ b/src/blameActiveLineController.ts @@ -1,13 +1,12 @@ 'use strict'; import { Functions, Objects } from './system'; import { DecorationOptions, DecorationInstanceRenderOptions, DecorationRenderOptions, Disposable, ExtensionContext, Range, StatusBarAlignment, StatusBarItem, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, window, workspace } from 'vscode'; -import { BlameabilityChangeEvent, BlameabilityTracker } from './blameabilityTracker'; import { BlameAnnotationController } from './blameAnnotationController'; import { BlameAnnotationFormat, BlameAnnotationFormatter } from './blameAnnotationFormatter'; import { TextEditorComparer } from './comparers'; import { IBlameConfig, IConfig, StatusBarCommand } from './configuration'; import { DocumentSchemes } from './constants'; -import { GitCommit, GitService, GitUri, IGitBlame, IGitCommitLine } from './gitService'; +import { BlameabilityChangeEvent, GitCommit, GitContextTracker, GitService, GitUri, IGitBlame, IGitCommitLine } from './gitService'; import * as moment from 'moment'; const activeLineDecoration: TextEditorDecorationType = window.createTextEditorDecorationType({ @@ -30,7 +29,7 @@ export class BlameActiveLineController extends Disposable { private _uri: GitUri; private _useCaching: boolean; - constructor(context: ExtensionContext, private git: GitService, private blameabilityTracker: BlameabilityTracker, private annotationController: BlameAnnotationController) { + constructor(context: ExtensionContext, private git: GitService, private gitContextTracker: GitContextTracker, private annotationController: BlameAnnotationController) { super(() => this.dispose()); this._updateBlameDebounced = Functions.debounce(this._updateBlame, 50); @@ -95,7 +94,7 @@ export class BlameActiveLineController extends Disposable { subscriptions.push(window.onDidChangeActiveTextEditor(this._onActiveTextEditorChanged, this)); subscriptions.push(window.onDidChangeTextEditorSelection(this._onTextEditorSelectionChanged, this)); - subscriptions.push(this.blameabilityTracker.onDidChange(this._onBlameabilityChanged, this)); + subscriptions.push(this.gitContextTracker.onDidBlameabilityChange(this._onBlameabilityChanged, this)); this._activeEditorLineDisposable = Disposable.from(...subscriptions); } diff --git a/src/blameAnnotationController.ts b/src/blameAnnotationController.ts index f779f6d..b1f747b 100644 --- a/src/blameAnnotationController.ts +++ b/src/blameAnnotationController.ts @@ -1,11 +1,10 @@ 'use strict'; import { Functions } from './system'; import { DecorationRenderOptions, Disposable, Event, EventEmitter, ExtensionContext, OverviewRulerLane, TextDocument, TextEditor, TextEditorDecorationType, TextEditorViewColumnChangeEvent, window, workspace } from 'vscode'; -import { BlameabilityChangeEvent, BlameabilityTracker } from './blameabilityTracker'; import { BlameAnnotationProvider } from './blameAnnotationProvider'; import { TextDocumentComparer, TextEditorComparer } from './comparers'; import { IBlameConfig } from './configuration'; -import { GitService, GitUri } from './gitService'; +import { BlameabilityChangeEvent, GitService, GitUri, GitContextTracker } from './gitService'; import { Logger } from './logger'; import { WhitespaceController } from './whitespaceController'; @@ -34,7 +33,7 @@ export class BlameAnnotationController extends Disposable { private _disposable: Disposable; private _whitespaceController: WhitespaceController | undefined; - constructor(private context: ExtensionContext, private git: GitService, private blameabilityTracker: BlameabilityTracker) { + constructor(private context: ExtensionContext, private git: GitService, private gitContextTracker: GitContextTracker) { super(() => this.dispose()); this._onConfigurationChanged(); @@ -177,7 +176,7 @@ export class BlameAnnotationController extends Disposable { subscriptions.push(window.onDidChangeVisibleTextEditors(Functions.debounce(this._onVisibleTextEditorsChanged, 100), this)); subscriptions.push(window.onDidChangeTextEditorViewColumn(this._onTextEditorViewColumnChanged, this)); subscriptions.push(workspace.onDidCloseTextDocument(this._onTextDocumentClosed, this)); - subscriptions.push(this.blameabilityTracker.onDidChange(this._onBlameabilityChanged, this)); + subscriptions.push(this.gitContextTracker.onDidBlameabilityChange(this._onBlameabilityChanged, this)); this._blameAnnotationsDisposable = Disposable.from(...subscriptions); } diff --git a/src/blameabilityTracker.ts b/src/blameabilityTracker.ts deleted file mode 100644 index 500daf4..0000000 --- a/src/blameabilityTracker.ts +++ /dev/null @@ -1,100 +0,0 @@ -'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(); - get onDidChange(): Event { - 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 - }); - } -} \ No newline at end of file diff --git a/src/commands/common.ts b/src/commands/common.ts index dd60f9d..52a7cee 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -46,12 +46,14 @@ export const Commands = { ToggleCodeLens: 'gitlens.toggleCodeLens' as Commands }; -export type CommandContext = 'gitlens:canToggleCodeLens' | 'gitlens:enabled' | 'gitlens:hasRemotes' | 'gitlens:isBlameable' | 'gitlens:key'; +export type CommandContext = 'gitlens:canToggleCodeLens' | 'gitlens:enabled' | 'gitlens:hasRemotes' | 'gitlens:isBlameable' | 'gitlens:isRepository' | 'gitlens:isTracked' | 'gitlens:key'; export const CommandContext = { CanToggleCodeLens: 'gitlens:canToggleCodeLens' as CommandContext, Enabled: 'gitlens:enabled' as CommandContext, HasRemotes: 'gitlens:hasRemotes' as CommandContext, IsBlameable: 'gitlens:isBlameable' as CommandContext, + IsRepository: 'gitlens:isRepository' as CommandContext, + IsTracked: 'gitlens:isTracked' as CommandContext, Key: 'gitlens:key' as CommandContext }; diff --git a/src/extension.ts b/src/extension.ts index 9c0fe32..6f749ec 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,7 +1,6 @@ 'use strict'; import { Objects } from './system'; -import { commands, Disposable, ExtensionContext, extensions, languages, TextEditor, Uri, window, workspace } from 'vscode'; -import { BlameabilityTracker } from './blameabilityTracker'; +import { commands, ExtensionContext, extensions, languages, Uri, window, workspace } from 'vscode'; import { BlameActiveLineController } from './blameActiveLineController'; import { BlameAnnotationController } from './blameAnnotationController'; import { configureCssCharacters } from './blameAnnotationFormatter'; @@ -20,7 +19,7 @@ import { Keyboard } from './commands'; import { IConfig } from './configuration'; import { ApplicationInsightsKey, BuiltInCommands, ExtensionId, WorkspaceState } from './constants'; import { GitContentProvider } from './gitContentProvider'; -import { Git, GitService } from './gitService'; +import { Git, GitContextTracker, GitService } from './gitService'; import { GitRevisionCodeLensProvider } from './gitRevisionCodeLensProvider'; import { Logger } from './logger'; import { Telemetry } from './telemetry'; @@ -69,19 +68,17 @@ export async function activate(context: ExtensionContext) { const git = new GitService(context, repoPath); context.subscriptions.push(git); - setCommandsContext(context, git); - - const blameabilityTracker = new BlameabilityTracker(git); - context.subscriptions.push(blameabilityTracker); + const gitContextTracker = new GitContextTracker(git); + context.subscriptions.push(gitContextTracker); context.subscriptions.push(workspace.registerTextDocumentContentProvider(GitContentProvider.scheme, new GitContentProvider(context, git))); context.subscriptions.push(languages.registerCodeLensProvider(GitRevisionCodeLensProvider.selector, new GitRevisionCodeLensProvider(context, git))); - const annotationController = new BlameAnnotationController(context, git, blameabilityTracker); + const annotationController = new BlameAnnotationController(context, git, gitContextTracker); context.subscriptions.push(annotationController); - const activeLineController = new BlameActiveLineController(context, git, blameabilityTracker, annotationController); + const activeLineController = new BlameActiveLineController(context, git, gitContextTracker, annotationController); context.subscriptions.push(activeLineController); context.subscriptions.push(new Keyboard()); @@ -155,54 +152,4 @@ async function notifyOnUnsupportedGitVersion(context: ExtensionContext, version: context.globalState.update(WorkspaceState.SuppressGitVersionWarning, true); } } -} - -let savedGitEnabled: boolean; -let savedInsiders: boolean; -let insidersDisposable: Disposable; - -async function setCommandsContext(context: ExtensionContext, git: GitService): Promise { - onCommandsContextConfigurationChanged(git); - context.subscriptions.push(workspace.onDidChangeConfiguration(() => onCommandsContextConfigurationChanged(git), this)); -} - -async function onCommandsContextConfigurationChanged(git: GitService) { - const gitEnabled = workspace.getConfiguration('git').get('enabled'); - if (gitEnabled !== savedGitEnabled) { - savedGitEnabled = gitEnabled; - setCommandContext(CommandContext.Enabled, gitEnabled); - } - - const insiders = workspace.getConfiguration('gitlens').get('insiders'); - if (insiders !== savedInsiders) { - savedInsiders = insiders; - - insidersDisposable && insidersDisposable.dispose(); - if (insiders) { - insidersDisposable = window.onDidChangeActiveTextEditor(e => onActiveTextEditorChanged(e, git)); - onActiveTextEditorChanged(window.activeTextEditor, git); - } - else { - insidersDisposable = undefined; - setCommandContext(CommandContext.HasRemotes, false); - } - } -} - -async function onActiveTextEditorChanged(editor: TextEditor, git: GitService) { - try { - let hasRemotes = false; - if (editor) { - const repoPath = await git.getRepoPathFromUri(editor.document.uri); - if (repoPath) { - const remotes = await git.getRemotes(repoPath); - hasRemotes = remotes.length !== 0; - } - } - - setCommandContext(CommandContext.HasRemotes, hasRemotes); - } - catch (ex) { - Logger.error(ex, 'Extension.onActiveTextEditorChanged'); - } } \ No newline at end of file diff --git a/src/git/git.ts b/src/git/git.ts index 21e6f67..90af54c 100644 --- a/src/git/git.ts +++ b/src/git/git.ts @@ -235,6 +235,15 @@ export class Git { return gitCommand(root, ...params); } + static async ls_files(repoPath: string, fileName: string): Promise { + try { + return await gitCommand(repoPath, 'ls-files', fileName); + } + catch (ex) { + return ''; + } + } + static remote(repoPath: string): Promise { return gitCommand(repoPath, 'remote', '-v'); } diff --git a/src/git/gitContextTracker.ts b/src/git/gitContextTracker.ts new file mode 100644 index 0000000..a40a61b --- /dev/null +++ b/src/git/gitContextTracker.ts @@ -0,0 +1,163 @@ +'use strict'; +import { Disposable, Event, EventEmitter, TextDocument, TextDocumentChangeEvent, TextEditor, window, workspace } from 'vscode'; +import { CommandContext, setCommandContext } from '../commands'; +import { TextDocumentComparer } from '../comparers'; +import { GitService, GitUri } from '../gitService'; +import { Logger } from '../logger'; + +export interface BlameabilityChangeEvent { + blameable: boolean; + editor: TextEditor; +} + +export class GitContextTracker extends Disposable { + + private _onDidBlameabilityChange = new EventEmitter(); + get onDidBlameabilityChange(): Event { + return this._onDidBlameabilityChange.event; + } + + private _disposable: Disposable; + private _documentChangeDisposable: Disposable; + private _editor: TextEditor; + private _gitEnabled: boolean; + private _isBlameable: boolean; + + constructor(private git: GitService) { + super(() => this.dispose()); + + const subscriptions: Disposable[] = []; + + subscriptions.push(window.onDidChangeActiveTextEditor(this._onActiveTextEditorChanged, this)); + subscriptions.push(workspace.onDidChangeConfiguration(this._onConfigurationChanged, this)); + subscriptions.push(workspace.onDidSaveTextDocument(this._onTextDocumentSaved, this)); + subscriptions.push(this.git.onDidBlameFail(this._onBlameFailed, this)); + + this._disposable = Disposable.from(...subscriptions); + + setCommandContext(CommandContext.IsRepository, !!this.git.repoPath); + + this._onConfigurationChanged(); + this._onActiveTextEditorChanged(window.activeTextEditor); + } + + dispose() { + this._disposable && this._disposable.dispose(); + this._documentChangeDisposable && this._documentChangeDisposable.dispose(); + } + + _onConfigurationChanged() { + const gitEnabled = workspace.getConfiguration('git').get('enabled'); + if (this._gitEnabled !== gitEnabled) { + this._gitEnabled = gitEnabled; + setCommandContext(CommandContext.Enabled, gitEnabled); + this._onActiveTextEditorChanged(window.activeTextEditor); + } + } + + private _onActiveTextEditorChanged(editor: TextEditor) { + this._editor = editor; + this._updateContext(this._gitEnabled && editor); + this._subscribeToDocumentChanges(); + } + + 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 async _updateContext(editor: TextEditor) { + try { + const gitUri = editor && await GitUri.fromUri(editor.document.uri, this.git); + + await Promise.all([ + this._updateEditorContext(gitUri, editor), + this._updateContextHasRemotes(gitUri) + ]); + } + catch (ex) { + Logger.error(ex, 'GitEditorTracker._updateContext'); + } + } + + private async _updateContextHasRemotes(uri: GitUri | undefined) { + try { + let hasRemotes = false; + if (uri) { + const repoPath = uri.repoPath || this.git.repoPath; + if (repoPath) { + const remotes = await this.git.getRemotes(repoPath); + hasRemotes = remotes.length !== 0; + } + } + + setCommandContext(CommandContext.HasRemotes, hasRemotes); + } + catch (ex) { + Logger.error(ex, 'GitEditorTracker._updateContextHasRemotes'); + } + } + + private async _updateEditorContext(uri: GitUri | undefined, editor: TextEditor | undefined) { + try { + const tracked = uri && await this.git.isTracked(uri); + setCommandContext(CommandContext.IsTracked, tracked); + + let blameable = tracked && editor && editor.document && !editor.document.isDirty; + if (blameable) { + blameable = await this.git.getBlameability(uri); + } + + this._updateBlameability(blameable, true); + } + catch (ex) { + Logger.error(ex, 'GitEditorTracker._updateEditorContext'); + } + } + + private _updateBlameability(blameable: boolean, force: boolean = false) { + if (!force && this._isBlameable === blameable) return; + + try { + setCommandContext(CommandContext.IsBlameable, blameable); + this._onDidBlameabilityChange.fire({ + blameable: blameable, + editor: this._editor + }); + } + catch (ex) { + Logger.error(ex, 'GitEditorTracker._updateBlameability'); + } + } +} \ No newline at end of file diff --git a/src/gitService.ts b/src/gitService.ts index dae2af0..1b81046 100644 --- a/src/gitService.ts +++ b/src/gitService.ts @@ -13,9 +13,9 @@ import * as ignore from 'ignore'; import * as moment from 'moment'; import * as path from 'path'; -export { getGitStatusIcon } from './git/git'; -export { Git, GitUri }; +export { GitUri }; export * from './git/git'; +export * from './git/gitContextTracker'; class UriCacheEntry { @@ -296,12 +296,14 @@ export class GitService extends Disposable { } } - public getBlameability(fileName: string): boolean { - if (!this.UseGitCaching) return true; + public async getBlameability(uri: GitUri): Promise { + if (!this.UseGitCaching) return await this.isTracked(uri); - const cacheKey = this.getCacheEntryKey(fileName); + const cacheKey = this.getCacheEntryKey(uri.fsPath); const entry = this._gitCache.get(cacheKey); - return !(entry && entry.hasErrors); + if (!entry) return await this.isTracked(uri); + + return !entry.hasErrors; } async getBlameForFile(uri: GitUri): Promise { @@ -752,6 +754,13 @@ export class GitService extends Disposable { return !!status; } + async isTracked(uri: GitUri): Promise { + Logger.log(`isFileUncommitted('${uri.repoPath}', '${uri.fsPath}')`); + + const result = await Git.ls_files(uri.repoPath, uri.fsPath); + return !!result; + } + openDirectoryDiff(repoPath: string, sha1: string, sha2?: string) { Logger.log(`openDirectoryDiff('${repoPath}', ${sha1}, ${sha2})`);