diff --git a/CHANGELOG.md b/CHANGELOG.md index c78bd4f..d683329 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Added + +- Adds the ability to turn on file annotations (blame, heatmap, and recent changes) via user-defined modes — closes [#542](https://github.com/eamodio/vscode-gitlens/issues/542) + ## [9.2.4] - 2018-12-26 ### Added diff --git a/README.md b/README.md index 20661d1..6b82722 100644 --- a/README.md +++ b/README.md @@ -831,12 +831,12 @@ See also [View Settings](#view-settings- 'Jump to the View settings') ### Modes Settings [#](#modes-settings- 'Modes Settings') -| Name | Description | -| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | -| `gitlens.mode.active` | Specifies the active GitLens mode, if any | -| `gitlens.mode.statusBar.enabled` | Specifies whether to provide the active GitLens mode in the status bar | -| `gitlens.mode.statusBar.alignment` | Specifies the active GitLens mode alignment in the status bar

`left` - aligns to the left
`right` - aligns to the right | -| `gitlens.modes` | Specifies the user-defined GitLens modes | +| Name | Description | +| ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `gitlens.mode.active` | Specifies the active GitLens mode, if any | +| `gitlens.mode.statusBar.enabled` | Specifies whether to provide the active GitLens mode in the status bar | +| `gitlens.mode.statusBar.alignment` | Specifies the active GitLens mode alignment in the status bar

`left` - aligns to the left
`right` - aligns to the right | +| `gitlens.modes` | Specifies the user-defined GitLens modes

Example — adds heatmap annotations to the built-in _Reviewing_ mode
`"gitlens.modes": { "review": { "annotations": "heatmap" } }`

Example — adds a new _Annotating_ mode with blame annotations
`"gitlens.modes": {`
    `"annotate": {`
        `"name": "Annotating",`
        `"statusBarItemName": "Annotating",`
        `"description": "for root cause analysis",`
        `"annotations": "blame",`
        `"codeLens": false,`
        `"currentLine": false,`
        `"hovers": true`
    `}`
`}` | ### Advanced Settings [#](#advanced-settings- 'Advanced Settings') diff --git a/package.json b/package.json index 9e5ba6c..f9b665a 100644 --- a/package.json +++ b/package.json @@ -841,65 +841,75 @@ "properties": { "zen": { "type": "object", - "required": [ - "name" - ], "properties": { "name": { - "type": "string" + "type": "string", + "description": "Specifies the friendly name of this user-defined mode" }, "statusBarItemName": { - "type": "string" + "type": "string", + "description": "Specifies the name shown in the status bar when this user-defined mode is active" }, "description": { - "type": "string" + "type": "string", + "description": "Specifies the description of this user-defined mode" }, "codeLens": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any Git code lens when this user-defined mode is active" }, "currentLine": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show a blame annotation for the current line when this user-defined mode is active" }, "hovers": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any hovers when this user-defined mode is active" }, "statusBar": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show blame information in the status bar when this user-defined mode is active" }, "views": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any GitLens views when this user-defined mode is active" } } }, "review": { "type": "object", - "required": [ - "name" - ], "properties": { "name": { - "type": "string" + "type": "string", + "description": "Specifies the friendly name of this user-defined mode" }, "statusBarItemName": { - "type": "string" + "type": "string", + "description": "Specifies the name shown in the status bar when this user-defined mode is active" }, "description": { - "type": "string" + "type": "string", + "description": "Specifies the description of this user-defined mode" }, "codeLens": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any Git code lens when this user-defined mode is active" }, "currentLine": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show a blame annotation for the current line when this user-defined mode is active" }, "hovers": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any hovers when this user-defined mode is active" }, "statusBar": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show blame information in the status bar when this user-defined mode is active" }, "views": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any GitLens views when this user-defined mode is active" } } } @@ -911,28 +921,50 @@ ], "properties": { "name": { - "type": "string" + "type": "string", + "description": "Specifies the friendly name of this user-defined mode" }, "statusBarItemName": { - "type": "string" + "type": "string", + "description": "Specifies the name shown in the status bar when this user-defined mode is active" }, "description": { - "type": "string" + "type": "string", + "description": "Specifies the description of this user-defined mode" + }, + "annotations": { + "type": "string", + "enum": [ + "blame", + "heatmap", + "recentChanges" + ], + "enumDescriptions": [ + "Shows the gutter blame annotations", + "Shows the gutter heatmap annotations", + "Shows the recently changed lines annotations" + ], + "description": "Specifies which (if any) file annotations will be shown when this user-defined mode is active" }, "codeLens": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any Git code lens when this user-defined mode is active" }, "currentLine": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show a blame annotation for the current line when this user-defined mode is active" }, "hovers": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any hovers when this user-defined mode is active" }, "statusBar": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show blame information in the status bar when this user-defined mode is active" }, "views": { - "type": "boolean" + "type": "boolean", + "description": "Specifies whether to show any GitLens views when this user-defined mode is active" } } }, @@ -967,6 +999,12 @@ "verbose", "debug" ], + "enumDescriptions": [ + "Logs nothing", + "Logs only errors", + "Logs all errors, warnings, and messages", + "Logs all errors, warnings, and messages with extra context useful for debugging" + ], "markdownDescription": "Specifies how much (if any) output will be sent to the GitLens output channel", "scope": "window" }, diff --git a/src/annotations/annotations.ts b/src/annotations/annotations.ts index 12a8e89..8223605 100644 --- a/src/annotations/annotations.ts +++ b/src/annotations/annotations.ts @@ -344,10 +344,7 @@ export class Annotations { static heatmapRenderOptions(): IRenderOptions { return { borderStyle: 'solid', - borderWidth: '0 0 0 2px', - contentText: GlyphChars.ZeroWidthSpace, - height: '100%', - margin: '0 26px -1px 0' + borderWidth: '0 0 0 2px' } as IRenderOptions; } diff --git a/src/annotations/fileAnnotationController.ts b/src/annotations/fileAnnotationController.ts index 1f0d906..01d92e3 100644 --- a/src/annotations/fileAnnotationController.ts +++ b/src/annotations/fileAnnotationController.ts @@ -19,7 +19,7 @@ import { workspace } from 'vscode'; import { AnnotationsToggleMode, configuration, FileAnnotationType, HighlightLocations } from '../configuration'; -import { CommandContext, isTextEditor, setCommandContext } from '../constants'; +import { CommandContext, GlyphChars, isTextEditor, setCommandContext } from '../constants'; import { Container } from '../container'; import { KeyboardScope, KeyCommand, Keys } from '../keyboard'; import { Logger } from '../logger'; @@ -49,7 +49,13 @@ export const Decorations = { textDecoration: 'none' } as DecorationRenderOptions), blameHighlight: undefined as TextEditorDecorationType | undefined, - heatmapAnnotation: window.createTextEditorDecorationType({} as DecorationRenderOptions), + heatmapAnnotation: window.createTextEditorDecorationType({ + before: { + contentText: GlyphChars.ZeroWidthSpace, + height: '100%', + margin: '0 26px -1px 0' + } + } as DecorationRenderOptions), heatmapHighlight: undefined as TextEditorDecorationType | undefined, recentChangesAnnotation: undefined as TextEditorDecorationType | undefined, recentChangesHighlight: undefined as TextEditorDecorationType | undefined @@ -389,7 +395,8 @@ export class FileAnnotationController implements Disposable { async toggle( editor: TextEditor | undefined, type: FileAnnotationType, - shaOrLine?: string | number + shaOrLine?: string | number, + on?: boolean ): Promise { if (editor !== undefined) { const trackedDocument = await Container.tracker.getOrAdd(editor.document); @@ -405,6 +412,7 @@ export class FileAnnotationController implements Disposable { if (provider === undefined) return this.show(editor!, type, shaOrLine); const reopen = provider.annotationType !== type; + if (on === true && !reopen) return true; if (this.isInWindowToggle()) { await this.clearAll(); diff --git a/src/commands/annotations.ts b/src/commands/annotations.ts index d653d19..70dd15b 100644 --- a/src/commands/annotations.ts +++ b/src/commands/annotations.ts @@ -36,6 +36,7 @@ export class ClearFileAnnotationsCommand extends EditorCommand { } export interface ToggleFileBlameCommandArgs { + on?: boolean; sha?: string; type?: FileAnnotationType; } @@ -65,7 +66,8 @@ export class ToggleFileBlameCommand extends ActiveEditorCommand { return Container.fileAnnotations.toggle( editor, args.type!, - args.sha !== undefined ? args.sha : editor && editor.selection.active.line + args.sha !== undefined ? args.sha : editor && editor.selection.active.line, + args.on ); } catch (ex) { @@ -83,8 +85,9 @@ export class ToggleFileHeatmapCommand extends ActiveEditorCommand { super(Commands.ToggleFileHeatmap); } - async execute(editor: TextEditor, uri?: Uri): Promise { + async execute(editor: TextEditor, uri?: Uri, args: ToggleFileBlameCommandArgs = {}): Promise { commands.executeCommand(Commands.ToggleFileBlame, uri, { + ...args, type: FileAnnotationType.Heatmap } as ToggleFileBlameCommandArgs); } @@ -96,8 +99,9 @@ export class ToggleFileRecentChangesCommand extends ActiveEditorCommand { super(Commands.ToggleFileRecentChanges); } - async execute(editor: TextEditor, uri?: Uri): Promise { + async execute(editor: TextEditor, uri?: Uri, args: ToggleFileBlameCommandArgs = {}): Promise { commands.executeCommand(Commands.ToggleFileBlame, uri, { + ...args, type: FileAnnotationType.RecentChanges } as ToggleFileBlameCommandArgs); } diff --git a/src/commands/switchMode.ts b/src/commands/switchMode.ts index e8f346f..b6e03be 100644 --- a/src/commands/switchMode.ts +++ b/src/commands/switchMode.ts @@ -15,6 +15,20 @@ export class SwitchModeCommand extends Command { const pick = await ModesQuickPick.show(); if (pick === undefined) return; + const active = Container.config.mode.active; + if (active === pick.key) return; + + // Check if we have applied any annotations and clear them if we won't be applying them again + if (active != null && active.length !== 0) { + const activeAnnotations = Container.config.modes[active].annotations; + if (activeAnnotations != null) { + const newAnnotations = pick.key != null ? Container.config.modes[pick.key].annotations : undefined; + if (activeAnnotations !== newAnnotations) { + await Container.fileAnnotations.clearAll(); + } + } + } + await configuration.update(configuration.name('mode')('active').value, pick.key, ConfigurationTarget.Global); } } diff --git a/src/configuration.ts b/src/configuration.ts index 2ac3223..edc1344 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -40,9 +40,12 @@ export class Configuration { this._configAffectedByMode = [ `gitlens.${this.name('mode').value}`, `gitlens.${this.name('modes').value}`, + `gitlens.${this.name('blame')('toggleMode').value}`, `gitlens.${this.name('codeLens').value}`, `gitlens.${this.name('currentLine').value}`, + `gitlens.${this.name('heatmap')('toggleMode').value}`, `gitlens.${this.name('hovers').value}`, + `gitlens.${this.name('recentChanges')('toggleMode').value}`, `gitlens.${this.name('statusBar').value}`, `gitlens.${this.name('views')('compare').value}`, `gitlens.${this.name('views')('fileHistory').value}`, diff --git a/src/container.ts b/src/container.ts index 9c3017a..c2d561b 100644 --- a/src/container.ts +++ b/src/container.ts @@ -1,9 +1,10 @@ 'use strict'; -import { Disposable, ExtensionContext } from 'vscode'; +import { commands, Disposable, ExtensionContext } from 'vscode'; import { FileAnnotationController } from './annotations/fileAnnotationController'; import { LineAnnotationController } from './annotations/lineAnnotationController'; import { GitCodeLensController } from './codelens/codeLensController'; -import { Config, configuration } from './configuration'; +import { Commands, ToggleFileBlameCommandArgs } from './commands'; +import { AnnotationsToggleMode, Config, configuration } from './configuration'; import { GitFileSystemProvider } from './git/fsProvider'; import { GitService } from './git/gitService'; import { LineHoverController } from './hovers/lineHoverController'; @@ -244,6 +245,35 @@ export class Container { const mode = config.modes[config.mode.active]; if (mode == null) return config; + if (mode.annotations != null) { + let command: string | undefined; + switch (mode.annotations) { + case 'blame': + config.blame.toggleMode = AnnotationsToggleMode.Window; + command = Commands.ToggleFileBlame; + break; + case 'heatmap': + config.heatmap.toggleMode = AnnotationsToggleMode.Window; + command = Commands.ToggleFileHeatmap; + break; + case 'recentChanges': + config.recentChanges.toggleMode = AnnotationsToggleMode.Window; + command = Commands.ToggleFileRecentChanges; + break; + } + + if (command !== undefined) { + // Make sure to delay the execution by a bit so that the configuration changes get propegated first + setTimeout( + () => + commands.executeCommand(command!, { + on: true + } as ToggleFileBlameCommandArgs), + 50 + ); + } + } + if (mode.codeLens != null) { config.codeLens.enabled = mode.codeLens; } diff --git a/src/ui/config.ts b/src/ui/config.ts index d35dd1f..af521f8 100644 --- a/src/ui/config.ts +++ b/src/ui/config.ts @@ -312,6 +312,7 @@ export interface ModeConfig { name: string; statusBarItemName?: string; description?: string; + annotations?: 'blame' | 'heatmap' | 'recentChanges'; codeLens?: boolean; currentLine?: boolean; hovers?: boolean;