diff --git a/images/dark/git-icon-progress.svg b/images/dark/git-icon-progress.svg index c1e501d..cffc4bf 100644 --- a/images/dark/git-icon-progress.svg +++ b/images/dark/git-icon-progress.svg @@ -6,7 +6,7 @@ c0.7,0.3,1.3,0.8,1.9,1.3c2.8,2.7,2.8,7.2,0,10c-2.8,2.7-7.2,2.7-10,0c-2.8-2.8-2.8-7.2,0-10c0.7-0.7,1.5-1.2,2.3-1.5V33.8 c-0.8-0.3-1.6-0.9-2.3-1.5c-2.1-2.1-2.6-5.1-1.5-7.7L29.2,14.2L1.7,41.7c-2.3,2.3-2.3,6.1,0,8.4l40.1,40.1c2.3,2.3,6.1,2.3,8.4,0 l39.9-39.9C92.4,47.9,92.4,44.1,90,41.8z"> - + diff --git a/images/light/git-icon-progress.svg b/images/light/git-icon-progress.svg index d79bf7b..5a06d86 100644 --- a/images/light/git-icon-progress.svg +++ b/images/light/git-icon-progress.svg @@ -6,7 +6,7 @@ c0.7,0.3,1.3,0.8,1.9,1.3c2.8,2.7,2.8,7.2,0,10c-2.8,2.7-7.2,2.7-10,0c-2.8-2.8-2.8-7.2,0-10c0.7-0.7,1.5-1.2,2.3-1.5V33.8 c-0.8-0.3-1.6-0.9-2.3-1.5c-2.1-2.1-2.6-5.1-1.5-7.7L29.2,14.2L1.7,41.7c-2.3,2.3-2.3,6.1,0,8.4l40.1,40.1c2.3,2.3,6.1,2.3,8.4,0 l39.9-39.9C92.4,47.9,92.4,44.1,90,41.8z"> - + diff --git a/src/annotations/annotationController.ts b/src/annotations/annotationController.ts index 3082eb1..8ae84c6 100644 --- a/src/annotations/annotationController.ts +++ b/src/annotations/annotationController.ts @@ -1,7 +1,7 @@ 'use strict'; import { Functions, Iterables } from '../system'; import { ConfigurationChangeEvent, DecorationRangeBehavior, DecorationRenderOptions, Disposable, Event, EventEmitter, OverviewRulerLane, Progress, ProgressLocation, TextDocument, TextEditor, TextEditorDecorationType, TextEditorViewColumnChangeEvent, ThemeColor, window, workspace } from 'vscode'; -import { AnnotationProviderBase, TextEditorCorrelationKey } from './annotationProvider'; +import { AnnotationProviderBase, AnnotationStatus, TextEditorCorrelationKey } from './annotationProvider'; import { configuration, FileAnnotationType, IConfig, LineHighlightLocations } from '../configuration'; import { CommandContext, isTextEditor, setCommandContext } from '../constants'; import { Container } from '../container'; @@ -23,11 +23,6 @@ export enum AnnotationClearReason { DocumentClosed = 'DocumentClosed' } -enum AnnotationStatus { - Computing = 'computing', - Computed = 'computed' -} - export const Decorations = { blameAnnotation: window.createTextEditorDecorationType({ isWholeLine: true, @@ -49,6 +44,7 @@ export class AnnotationController extends Disposable { private _annotationsDisposable: Disposable | undefined; private _annotationProviders: Map = new Map(); private _disposable: Disposable; + private _editor: TextEditor | undefined; private _keyboardScope: KeyboardScope | undefined = undefined; constructor() { @@ -174,6 +170,7 @@ export class AnnotationController extends Disposable { private onActiveTextEditorChanged(editor: TextEditor | undefined) { if (editor !== undefined && !isTextEditor(editor)) return; + this._editor = editor; // Logger.log('AnnotationController.onActiveTextEditorChanged', editor && editor.document.uri.fsPath); const provider = this.getProvider(editor); @@ -182,7 +179,7 @@ export class AnnotationController extends Disposable { this.detachKeyboardHook(); } else { - setCommandContext(CommandContext.AnnotationStatus, AnnotationStatus.Computed); + setCommandContext(CommandContext.AnnotationStatus, provider.status); this.attachKeyboardHook(); } } @@ -262,6 +259,7 @@ export class AnnotationController extends Disposable { async showAnnotations(editor: TextEditor | undefined, type: FileAnnotationType, shaOrLine?: string | number): Promise { if (editor === undefined) return false; // || editor.viewColumn === undefined) return false; + this._editor = editor; const trackedDocument = await Container.tracker.getOrAdd(editor.document); if (!trackedDocument.isBlameable) return false; @@ -272,19 +270,20 @@ export class AnnotationController extends Disposable { return true; } - return window.withProgress({ location: ProgressLocation.Window }, async (progress: Progress<{ message: string }>) => { - const active = editor === window.activeTextEditor; - await setCommandContext(CommandContext.AnnotationStatus, active ? AnnotationStatus.Computing : undefined); + const provider = await window.withProgress({ location: ProgressLocation.Window }, async (progress: Progress<{ message: string }>) => { + await setCommandContext(CommandContext.AnnotationStatus, AnnotationStatus.Computing); const computingAnnotations = this.showAnnotationsCore(currentProvider, editor, type, shaOrLine, progress); - const result = await computingAnnotations; + const provider = await computingAnnotations; - if (active) { - await setCommandContext(CommandContext.AnnotationStatus, result ? AnnotationStatus.Computed : undefined); + if (editor === this._editor) { + await setCommandContext(CommandContext.AnnotationStatus, provider && provider.status); } return computingAnnotations; }); + + return provider !== undefined; } async toggleAnnotations(editor: TextEditor | undefined, type: FileAnnotationType, shaOrLine?: string | number): Promise { @@ -310,7 +309,7 @@ export class AnnotationController extends Disposable { this._keyboardScope = await Container.keyboard.beginScope({ escape: { onDidPressKey: async (key: Keys) => { - const e = window.activeTextEditor; + const e = this._editor; if (e === undefined) return undefined; await this.clear(e, AnnotationClearReason.User); @@ -330,7 +329,7 @@ export class AnnotationController extends Disposable { this._annotationProviders.delete(key); await provider.dispose(); - if (key === AnnotationProviderBase.getCorrelationKey(window.activeTextEditor)) { + if (this._annotationProviders.size === 0 || key === AnnotationProviderBase.getCorrelationKey(this._editor)) { await setCommandContext(CommandContext.AnnotationStatus, undefined); await this.detachKeyboardHook(); } @@ -352,7 +351,7 @@ export class AnnotationController extends Disposable { this._keyboardScope = undefined; } - private async showAnnotationsCore(currentProvider: AnnotationProviderBase | undefined, editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number, progress?: Progress<{ message: string}>): Promise { + private async showAnnotationsCore(currentProvider: AnnotationProviderBase | undefined, editor: TextEditor, type: FileAnnotationType, shaOrLine?: string | number, progress?: Progress<{ message: string}>): Promise { if (progress !== undefined) { let annotationsLabel = 'annotations'; switch (type) { @@ -396,7 +395,7 @@ export class AnnotationController extends Disposable { provider = new RecentChangesAnnotationProvider(editor, trackedDocument, undefined, Decorations.recentChangesHighlight!); break; } - if (provider === undefined || !(await provider.validate())) return false; + if (provider === undefined || !(await provider.validate())) return undefined; if (currentProvider !== undefined) { await this.clearCore(currentProvider.correlationKey, AnnotationClearReason.User); @@ -408,7 +407,7 @@ export class AnnotationController extends Disposable { this._annotationsDisposable = Disposable.from( window.onDidChangeActiveTextEditor(Functions.debounce(this.onActiveTextEditorChanged, 50), this), window.onDidChangeTextEditorViewColumn(this.onTextEditorViewColumnChanged, this), - window.onDidChangeVisibleTextEditors(this.onVisibleTextEditorsChanged, this), + window.onDidChangeVisibleTextEditors(Functions.debounce(this.onVisibleTextEditorsChanged, 50), this), workspace.onDidCloseTextDocument(this.onTextDocumentClosed, this), Container.tracker.onDidChangeBlameState(this.onBlameStateChanged, this), Container.tracker.onDidChangeDirtyState(this.onDirtyStateChanged, this) @@ -418,9 +417,9 @@ export class AnnotationController extends Disposable { this._annotationProviders.set(provider.correlationKey, provider); if (await provider.provideAnnotation(shaOrLine)) { this._onDidToggleAnnotations.fire(); - return true; + return provider; } - return false; + return undefined; } } \ No newline at end of file diff --git a/src/annotations/annotationProvider.ts b/src/annotations/annotationProvider.ts index d55a8dd..3eb4bcd 100644 --- a/src/annotations/annotationProvider.ts +++ b/src/annotations/annotationProvider.ts @@ -3,8 +3,14 @@ import { Functions } from '../system'; import { DecorationOptions, Disposable, Range, TextDocument, TextEditor, TextEditorDecorationType, TextEditorSelectionChangeEvent, Uri, window } from 'vscode'; import { FileAnnotationType } from '../configuration'; import { TextDocumentComparer } from '../comparers'; +import { CommandContext, setCommandContext } from '../constants'; import { GitDocumentState, TrackedDocument } from '../trackers/documentTracker'; +export enum AnnotationStatus { + Computing = 'computing', + Computed = 'computed' +} + export type TextEditorCorrelationKey = string; export abstract class AnnotationProviderBase extends Disposable { @@ -13,9 +19,10 @@ export abstract class AnnotationProviderBase extends Disposable { return editor !== undefined ? (editor as any).id : ''; } - public annotationType: FileAnnotationType; - public correlationKey: TextEditorCorrelationKey; - public document: TextDocument; + annotationType: FileAnnotationType; + correlationKey: TextEditorCorrelationKey; + document: TextDocument; + status: AnnotationStatus | undefined; protected decorations: DecorationOptions[] | undefined; protected disposable: Disposable; @@ -61,6 +68,7 @@ export abstract class AnnotationProviderBase extends Disposable { protected additionalDecorations: { decoration: TextEditorDecorationType, ranges: Range[] }[] | undefined; async clear() { + this.status = undefined; if (this.editor === undefined) return; if (this.decoration !== undefined) { @@ -110,10 +118,15 @@ export abstract class AnnotationProviderBase extends Disposable { await this.provideAnnotation(this.editor === undefined ? undefined : this.editor.selection.active.line); } - restore(editor: TextEditor, force: boolean = false) { + async restore(editor: TextEditor) { // If the editor isn't disposed then we don't need to do anything // Explicitly check for `false` - if (!force && (this.editor as any)._disposed === false) return; + if ((this.editor as any)._disposed === false) return; + + this.status = AnnotationStatus.Computing; + if (editor === window.activeTextEditor) { + await setCommandContext(CommandContext.AnnotationStatus, this.status); + } this.editor = editor; this.correlationKey = AnnotationProviderBase.getCorrelationKey(editor); @@ -128,10 +141,23 @@ export abstract class AnnotationProviderBase extends Disposable { } } } + + this.status = AnnotationStatus.Computed; + if (editor === window.activeTextEditor) { + await setCommandContext(CommandContext.AnnotationStatus, this.status); + await this.selection(editor.selection.active.line); + } } - provideAnnotation(shaOrLine?: string | number): Promise { - return this.onProvideAnnotation(shaOrLine); + async provideAnnotation(shaOrLine?: string | number): Promise { + this.status = AnnotationStatus.Computing; + if (await this.onProvideAnnotation(shaOrLine)) { + this.status = AnnotationStatus.Computed; + return true; + } + + this.status = undefined; + return false; } abstract async onProvideAnnotation(shaOrLine?: string | number): Promise; diff --git a/src/commands/clearFileAnnotations.ts b/src/commands/clearFileAnnotations.ts index 91f952e..3dadd31 100644 --- a/src/commands/clearFileAnnotations.ts +++ b/src/commands/clearFileAnnotations.ts @@ -1,6 +1,7 @@ 'use strict'; import { TextEditor, TextEditorEdit, Uri, window } from 'vscode'; import { Commands, EditorCommand } from './common'; +import { UriComparer } from '../comparers'; import { Container } from '../container'; import { Logger } from '../logger'; @@ -13,6 +14,14 @@ export class ClearFileAnnotationsCommand extends EditorCommand { async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri): Promise { if (editor === undefined) return undefined; + // Handle the case where we are focused on a non-editor editor (output, debug console) + if (uri !== undefined && !UriComparer.equals(uri, editor.document.uri)) { + const e = window.visibleTextEditors.find(e => UriComparer.equals(uri, e.document.uri)); + if (e !== undefined) { + editor = e; + } + } + try { return Container.annotations.clear(editor); } diff --git a/src/commands/toggleFileBlame.ts b/src/commands/toggleFileBlame.ts index 14d1769..1b99f61 100644 --- a/src/commands/toggleFileBlame.ts +++ b/src/commands/toggleFileBlame.ts @@ -37,7 +37,7 @@ export class ToggleFileBlameCommand extends EditorCommand { } catch (ex) { Logger.error(ex, 'ToggleFileBlameCommand'); - return window.showErrorMessage(`Unable to toggle file blame annotations. See output channel for more details`); + return window.showErrorMessage(`Unable to toggle file ${args.type} annotations. See output channel for more details`); } } } \ No newline at end of file diff --git a/src/commands/toggleFileHeatmap.ts b/src/commands/toggleFileHeatmap.ts index ae301ac..8082db5 100644 --- a/src/commands/toggleFileHeatmap.ts +++ b/src/commands/toggleFileHeatmap.ts @@ -1,10 +1,8 @@ 'use strict'; -import { TextEditor, TextEditorEdit, Uri, window } from 'vscode'; +import { commands, TextEditor, TextEditorEdit, Uri } from 'vscode'; +import { ToggleFileBlameCommandArgs } from '../commands'; import { Commands, EditorCommand } from './common'; -import { UriComparer } from '../comparers'; import { FileAnnotationType } from '../configuration'; -import { Container } from '../container'; -import { Logger } from '../logger'; export class ToggleFileHeatmapCommand extends EditorCommand { @@ -13,22 +11,6 @@ export class ToggleFileHeatmapCommand extends EditorCommand { } async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri): Promise { - if (editor === undefined) return undefined; - - // Handle the case where we are focused on a non-editor editor (output, debug console) - if (uri !== undefined && !UriComparer.equals(uri, editor.document.uri)) { - const e = window.visibleTextEditors.find(e => UriComparer.equals(uri, e.document.uri)); - if (e !== undefined) { - editor = e; - } - } - - try { - return Container.annotations.toggleAnnotations(editor, FileAnnotationType.Heatmap); - } - catch (ex) { - Logger.error(ex, 'ToggleFileHeatmapCommand'); - return window.showErrorMessage(`Unable to toggle heatmap annotations. See output channel for more details`); - } + commands.executeCommand(Commands.ToggleFileBlame, uri, { type: FileAnnotationType.Heatmap } as ToggleFileBlameCommandArgs); } } \ No newline at end of file diff --git a/src/commands/toggleFileRecentChanges.ts b/src/commands/toggleFileRecentChanges.ts index c7e5512..415d00a 100644 --- a/src/commands/toggleFileRecentChanges.ts +++ b/src/commands/toggleFileRecentChanges.ts @@ -1,10 +1,8 @@ 'use strict'; -import { TextEditor, TextEditorEdit, Uri, window } from 'vscode'; +import { commands, TextEditor, TextEditorEdit, Uri } from 'vscode'; +import { ToggleFileBlameCommandArgs } from '../commands'; import { Commands, EditorCommand } from './common'; -import { UriComparer } from '../comparers'; import { FileAnnotationType } from '../configuration'; -import { Container } from '../container'; -import { Logger } from '../logger'; export class ToggleFileRecentChangesCommand extends EditorCommand { @@ -13,22 +11,6 @@ export class ToggleFileRecentChangesCommand extends EditorCommand { } async execute(editor: TextEditor, edit: TextEditorEdit, uri?: Uri): Promise { - if (editor === undefined) return undefined; - - // Handle the case where we are focused on a non-editor editor (output, debug console) - if (uri !== undefined && !UriComparer.equals(uri, editor.document.uri)) { - const e = window.visibleTextEditors.find(e => UriComparer.equals(uri, e.document.uri)); - if (e !== undefined) { - editor = e; - } - } - - try { - return Container.annotations.toggleAnnotations(editor, FileAnnotationType.RecentChanges); - } - catch (ex) { - Logger.error(ex, 'ToggleFileRecentChangesCommand'); - return window.showErrorMessage(`Unable to toggle recent file changes annotations. See output channel for more details`); - } + commands.executeCommand(Commands.ToggleFileBlame, uri, { type: FileAnnotationType.RecentChanges } as ToggleFileBlameCommandArgs); } } \ No newline at end of file