diff --git a/CHANGELOG.md b/CHANGELOG.md index 58aa113..bcabefa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p ## [8.3.0-beta] - 2018-05-05 ### Added +- Add user-defined modes for quickly toggling between sets of settings + - Adds *Switch Mode* command (`gitlens.switchMode`) to quickly switch the active mode + - Adds a built-in *Zen* mode which for a zen-like experience, disables many visual features + - Adds *Toggle Zen Mode* command (`gitlens.toggleZenMode`) to toggle Zen mode + - Adds a built-in *Review* mode which for reviewing code, enables many visual features + - Adds *Toggle Review Mode* command (`gitlens.toggleReviewMode`) to toggle Review mode + - Adds the active mode to the status bar, optional (on by default) + - Adds `gitlens.mode.statusBar.enabled` setting to specify whether to provide the active GitLens mode on the status bar + - Adds `gitlens.mode.statusBar.alignment` setting to specify the active GitLens mode alignment in the status bar + - Adds modes settings (`gitlens.mode.*`) to the interactive settings editor + ![modes settings](https://raw.githubusercontent.com/eamodio/vscode-gitlens/develop/images/cl-modes-settings.png) + - Adds `gitlens.mode.active` settings to specify the active GitLens mode, if any + - Adds `gitlens.modes` setting to specify the user-defined GitLens modes - Adds an icon for the *Compare File with Previous Revision* command (`gitlens.diffWithPrevious`) and moves it into the editor toolbar - Adds an icon for the *Compare File with Next Revision* command (`gitlens.diffWithNext`) and moves it into the editor toolbar - Adds *Show GitLens Explorer* (`gitlens.showGitExplorer`) command — shows/expands the *GitLens* explorer diff --git a/README.md b/README.md index b508e0d..b599ffc 100644 --- a/README.md +++ b/README.md @@ -372,6 +372,16 @@ An on-demand, [customizable](#gitlens-results-view-settings "Jump to the GitLens - Click the current and previous commit ids to execute the *Show Commit Details* command (`gitlens.showQuickCommitDetails`) --- +### Modes +- GitLens supports [user-defined](#modes-settings "Jump to the Modes settings") modes for quickly toggling between sets of settings + - Adds *Switch Mode* command (`gitlens.switchMode`) to quickly switch the active mode + - Adds a built-in *Zen* mode which for a zen-like experience, disables many visual features + - Adds *Toggle Zen Mode* command (`gitlens.toggleZenMode`) to toggle Zen mode + - Adds a built-in *Review* mode which for reviewing code, enables many visual features + - Adds *Toggle Review Mode* command (`gitlens.toggleReviewMode`) to toggle Review mode + - Adds the active mode to the **status bar** ([optional](#modes-settings "Jump to the Modes settings"), on by default) + +--- ### Recent Changes

Recent Changes @@ -671,13 +681,22 @@ See also [Explorer Settings](#explorer-settings "Jump to the Explorer settings") |`gitlens.hovers.annotations.changes`|Specifies whether to provide a changes (diff) hover for all lines when showing blame annotations |`gitlens.hovers.annotations.details`|Specifies whether to provide a commit details hover for all lines when showing blame annotations |`gitlens.hovers.annotations.enabled`|Specifies whether to provide any hovers when showing blame annotations -|`gitlens.hovers.annotations.over`|Specifies when to trigger hovers when showing blame annotations
`annotation` - only shown when hovering over the line annotation
`line` - shown when hovering anywhere over the line +|`gitlens.hovers.annotations.over`|Specifies when to trigger hovers when showing blame annotations
`annotation` - only shown when hovering over the line annotation
`line` - shown when hovering anywhere over the line |`gitlens.hovers.currentLine.changes`|Specifies whether to provide a changes (diff) hover for the current line |`gitlens.hovers.currentLine.details`|Specifies whether to provide a commit details hover for the current line |`gitlens.hovers.currentLine.enabled`|Specifies whether to provide any hovers for the current line -|`gitlens.hovers.currentLine.over`|Specifies when to trigger hovers for the current line
`annotation` - only shown when hovering over the line annotation
`line` - shown when hovering anywhere over the line +|`gitlens.hovers.currentLine.over`|Specifies when to trigger hovers for the current line
`annotation` - only shown when hovering over the line annotation
`line` - shown when hovering anywhere over the line |`gitlens.hovers.enabled`|Specifies whether to provide any hovers +### 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 on the status bar +|`gitlens.mode.statusBar.alignment`|Specifies the active GitLens mode alignment in the status bar
`left` - align to the left
`right` - align to the right +|`gitlens.modes`|Specifies the user-defined GitLens modes + ### Recent Changes Settings |Name | Description diff --git a/images/cl-modes-settings.png b/images/cl-modes-settings.png new file mode 100644 index 0000000..4336558 Binary files /dev/null and b/images/cl-modes-settings.png differ diff --git a/package.json b/package.json index 4c826f7..344df86 100644 --- a/package.json +++ b/package.json @@ -670,6 +670,102 @@ "description": "Specifies which commands will be added to which menus", "scope": "window" }, + "gitlens.mode.active": { + "type": "string", + "description": "Specifies the active GitLens mode, if any", + "scope": "window" + }, + "gitlens.mode.statusBar.enabled": { + "type": "boolean", + "default": true, + "description": "Specifies whether to provide the active GitLens mode on the status bar", + "scope": "window" + }, + "gitlens.mode.statusBar.alignment": { + "type": "string", + "default": "right", + "enum": [ + "left", + "right" + ], + "description": "Specifies the active GitLens mode alignment in the status bar\n `left` - align to the left\n `right` - align to the right", + "scope": "window" + }, + "gitlens.modes": { + "type": "object", + "properties": { + "zen": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { "type": "string"}, + "statusBarItemName": { "type": "string"}, + "description": { "type": "string"}, + "codeLens": { "type": "boolean" }, + "currentLine": { "type": "boolean" }, + "explorers": { "type": "boolean" }, + "hovers": { "type": "boolean" }, + "statusBar": { "type": "boolean" } + } + }, + "review": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { "type": "string"}, + "statusBarItemName": { "type": "string"}, + "description": { "type": "string"}, + "codeLens": { "type": "boolean" }, + "currentLine": { "type": "boolean" }, + "explorers": { "type": "boolean" }, + "hovers": { "type": "boolean" }, + "statusBar": { "type": "boolean" } + } + } + }, + "additionalProperties": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { "type": "string"}, + "statusBarItemName": { "type": "string"}, + "description": { "type": "string"}, + "codeLens": { "type": "boolean" }, + "currentLine": { "type": "boolean" }, + "explorers": { "type": "boolean" }, + "hovers": { "type": "boolean" }, + "statusBar": { "type": "boolean" } + } + }, + "default": { + "zen": { + "name": "Zen", + "statusBarItemName": "Zen", + "description": "for a zen-like experience, disables many visual features", + "codeLens": false, + "currentLine": false, + "explorers": false, + "hovers": false, + "statusBar": false + }, + "review": { + "name": "Review", + "statusBarItemName": "Reviewing", + "description": "for reviewing code, enables many visual features", + "codeLens": true, + "currentLine": true, + "hovers": true + } + }, + "description": "Specifies the user-defined GitLens modes", + "scope": "window" + }, "gitlens.outputLevel": { "type": "string", "default": "silent", @@ -1242,6 +1338,21 @@ "category": "GitLens" }, { + "command": "gitlens.switchMode", + "title": "Switch Mode", + "category": "GitLens" + }, + { + "command": "gitlens.toggleReviewMode", + "title": "Toggle Review Mode", + "category": "GitLens" + }, + { + "command": "gitlens.toggleZenMode", + "title": "Toggle Zen Mode", + "category": "GitLens" + }, + { "command": "gitlens.showCommitSearch", "title": "Search Commits", "category": "GitLens", @@ -1848,6 +1959,18 @@ "when": "gitlens:enabled && gitlens:canToggleCodeLens" }, { + "command": "gitlens.switchMode", + "when": "gitlens:enabled" + }, + { + "command": "gitlens.toggleReviewMode", + "when": "gitlens:enabled" + }, + { + "command": "gitlens.toggleZenMode", + "when": "gitlens:enabled" + }, + { "command": "gitlens.showCommitSearch", "when": "gitlens:enabled" }, diff --git a/src/annotations/lineAnnotationController.ts b/src/annotations/lineAnnotationController.ts index 846ff44..cc398b5 100644 --- a/src/annotations/lineAnnotationController.ts +++ b/src/annotations/lineAnnotationController.ts @@ -1,7 +1,7 @@ 'use strict'; import { ConfigurationChangeEvent, debug, DecorationRangeBehavior, DecorationRenderOptions, Disposable, Range, TextEditor, TextEditorDecorationType, window } from 'vscode'; import { Annotations } from './annotations'; -import { configuration, IConfig } from './../configuration'; +import { configuration } from './../configuration'; import { isTextEditor, RangeEndOfLineIndex } from './../constants'; import { Container } from './../container'; import { LinesChangeEvent } from './../trackers/gitLineTracker'; @@ -47,8 +47,7 @@ export class LineAnnotationController extends Disposable { if (!initializing && !configuration.changed(e, configuration.name('currentLine').value)) return; if (initializing || configuration.changed(e, configuration.name('currentLine')('enabled').value)) { - const cfg = configuration.get(); - if (cfg.currentLine.enabled) { + if (Container.config.currentLine.enabled) { this._enabled = true; this.resume(); } diff --git a/src/annotations/lineHoverController.ts b/src/annotations/lineHoverController.ts index 12a0826..3dd68ea 100644 --- a/src/annotations/lineHoverController.ts +++ b/src/annotations/lineHoverController.ts @@ -1,7 +1,7 @@ 'use strict'; import { CancellationToken, ConfigurationChangeEvent, debug, Disposable, Hover, HoverProvider, languages, Position, Range, TextDocument, TextEditor, window } from 'vscode'; import { Annotations } from './annotations'; -import { configuration, IConfig } from './../configuration'; +import { configuration } from './../configuration'; import { RangeEndOfLineIndex } from './../constants'; import { Container } from './../container'; import { LinesChangeEvent } from './../trackers/gitLineTracker'; @@ -38,9 +38,7 @@ export class LineHoverController extends Disposable { !configuration.changed(e, configuration.name('hovers')('enabled').value) && !configuration.changed(e, configuration.name('hovers')('currentLine')('enabled').value)) return; - const cfg = configuration.get(); - - if (cfg.hovers.enabled && cfg.hovers.currentLine.enabled) { + if (Container.config.hovers.enabled && Container.config.hovers.currentLine.enabled) { Container.lineTracker.start( this, Disposable.from(Container.lineTracker.onDidChangeActiveLines(this.onActiveLinesChanged, this)) diff --git a/src/codeLensController.ts b/src/codeLensController.ts index 934b53a..b6e0397 100644 --- a/src/codeLensController.ts +++ b/src/codeLensController.ts @@ -1,6 +1,6 @@ 'use strict'; import { ConfigurationChangeEvent, Disposable, languages } from 'vscode'; -import { configuration, ICodeLensConfig } from './configuration'; +import { configuration } from './configuration'; import { CommandContext, setCommandContext } from './constants'; import { Container } from './container'; import { DocumentBlameStateChangeEvent, DocumentDirtyIdleTriggerEvent, GitDocumentState } from './trackers/gitDocumentTracker'; @@ -39,7 +39,7 @@ export class CodeLensController extends Disposable { Logger.log('CodeLens config changed; resetting CodeLens provider'); } - const cfg = configuration.get(section); + const cfg = Container.config.codeLens; if (cfg.enabled && (cfg.recentChange.enabled || cfg.authors.enabled)) { if (this._provider !== undefined) { this._provider.reset(); @@ -72,7 +72,7 @@ export class CodeLensController extends Disposable { private onDirtyIdleTriggered(e: DocumentDirtyIdleTriggerEvent) { if (this._provider === undefined || !e.document.isBlameable) return; - const maxLines = configuration.get(configuration.name('advanced')('blame')('sizeThresholdAfterEdit').value); + const maxLines = Container.config.advanced.blame.sizeThresholdAfterEdit; if (maxLines > 0 && e.document.lineCount > maxLines) return; Logger.log('Dirty idle triggered; resetting CodeLens provider'); diff --git a/src/commands.ts b/src/commands.ts index f3d4384..a586990 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -44,6 +44,7 @@ export * from './commands/showResultsExplorer'; export * from './commands/stashApply'; export * from './commands/stashDelete'; export * from './commands/stashSave'; +export * from './commands/switchMode'; export * from './commands/toggleCodeLens'; export * from './commands/toggleFileBlame'; export * from './commands/toggleFileHeatmap'; @@ -95,9 +96,12 @@ export function configureCommands(): void { Container.context.subscriptions.push(new Commands.StashApplyCommand()); Container.context.subscriptions.push(new Commands.StashDeleteCommand()); Container.context.subscriptions.push(new Commands.StashSaveCommand()); + Container.context.subscriptions.push(new Commands.SwitchModeCommand()); Container.context.subscriptions.push(new Commands.ToggleCodeLensCommand()); Container.context.subscriptions.push(new Commands.ToggleFileBlameCommand()); Container.context.subscriptions.push(new Commands.ToggleFileHeatmapCommand()); Container.context.subscriptions.push(new Commands.ToggleFileRecentChangesCommand()); Container.context.subscriptions.push(new Commands.ToggleLineBlameCommand()); + Container.context.subscriptions.push(new Commands.ToggleReviewModeCommand()); + Container.context.subscriptions.push(new Commands.ToggleZenModeCommand()); } \ No newline at end of file diff --git a/src/commands/common.ts b/src/commands/common.ts index d1b1282..b6be2ab 100644 --- a/src/commands/common.ts +++ b/src/commands/common.ts @@ -57,11 +57,14 @@ export enum Commands { StashApply = 'gitlens.stashApply', StashDelete = 'gitlens.stashDelete', StashSave = 'gitlens.stashSave', + SwitchMode = 'gitlens.switchMode', ToggleCodeLens = 'gitlens.toggleCodeLens', ToggleFileBlame = 'gitlens.toggleFileBlame', ToggleFileHeatmap = 'gitlens.toggleFileHeatmap', ToggleFileRecentChanges = 'gitlens.toggleFileRecentChanges', - ToggleLineBlame = 'gitlens.toggleLineBlame' + ToggleLineBlame = 'gitlens.toggleLineBlame', + ToggleReviewMode = 'gitlens.toggleReviewMode', + ToggleZenMode = 'gitlens.toggleZenMode' } export function getCommandUri(uri?: Uri, editor?: TextEditor): Uri | undefined { diff --git a/src/commands/switchMode.ts b/src/commands/switchMode.ts new file mode 100644 index 0000000..81cc953 --- /dev/null +++ b/src/commands/switchMode.ts @@ -0,0 +1,48 @@ +'use strict'; +import { ConfigurationTarget } from 'vscode'; +import { Command, Commands } from './common'; +import { configuration } from '../configuration'; +import { Container } from '../container'; +import { ModesQuickPick } from '../quickPicks/quickPicks'; + +export class SwitchModeCommand extends Command { + + constructor() { + super(Commands.SwitchMode); + } + + async execute() { + const pick = await ModesQuickPick.show(); + if (pick === undefined) return; + + await configuration.update(configuration.name('mode')('active').value, pick.key, ConfigurationTarget.Global); + } +} + +export class ToggleReviewModeCommand extends Command { + + constructor() { + super(Commands.ToggleReviewMode); + } + + async execute() { + if (!Object.keys(Container.config.modes).includes('review')) return; + + const mode = Container.config.mode.active === 'review' ? undefined : 'review'; + await configuration.update(configuration.name('mode')('active').value, mode, ConfigurationTarget.Global); + } +} + +export class ToggleZenModeCommand extends Command { + + constructor() { + super(Commands.ToggleZenMode); + } + + async execute() { + if (!Object.keys(Container.config.modes).includes('zen')) return; + + const mode = Container.config.mode.active === 'zen' ? undefined : 'zen'; + await configuration.update(configuration.name('mode')('active').value, mode, ConfigurationTarget.Global); + } +} diff --git a/src/configuration.ts b/src/configuration.ts index f0e71d5..aec833f 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -26,6 +26,21 @@ export class Configuration { return this._onDidChange.event; } + private readonly _configAffectedByMode: string[]; + + constructor() { + this._configAffectedByMode = [ + `gitlens.${this.name('mode').value}`, + `gitlens.${this.name('modes').value}`, + `gitlens.${this.name('codeLens').value}`, + `gitlens.${this.name('currentLine').value}`, + `gitlens.${this.name('gitExplorer').value}`, + `gitlens.${this.name('historyExplorer').value}`, + `gitlens.${this.name('hovers').value}`, + `gitlens.${this.name('statusBar').value}` + ]; + } + private onConfigurationChanged(e: ConfigurationChangeEvent) { if (!e.affectsConfiguration(ExtensionKey, null!)) return; @@ -40,6 +55,21 @@ export class Configuration { setCommandContext(CommandContext.KeyMap, this.get(section)); } + if (configuration.changed(e, configuration.name('mode').value) || + configuration.changed(e, configuration.name('modes').value)) { + const original = e.affectsConfiguration; + e = { + ...e, + affectsConfiguration: (section: string, resource?: Uri) => { + if (this._configAffectedByMode.some(n => section.startsWith(n))) { + return true; + } + + return original(section, resource); + } + } as ConfigurationChangeEvent; + } + this._onDidChange.fire(e); } @@ -186,4 +216,4 @@ export class Configuration { } } -export const configuration = new Configuration(); \ No newline at end of file +export const configuration = new Configuration(); diff --git a/src/container.ts b/src/container.ts index 6efb407..c700b56 100644 --- a/src/container.ts +++ b/src/container.ts @@ -23,7 +23,7 @@ export class Container { static initialize(context: ExtensionContext, config: IConfig) { this._context = context; - this._config = config; + this._config = Container.applyMode(config); context.subscriptions.push(this._lineTracker = new GitLineTracker()); context.subscriptions.push(this._tracker = new GitDocumentTracker()); @@ -79,7 +79,7 @@ export class Container { private static _config: IConfig | undefined; static get config() { if (this._config === undefined) { - this._config = configuration.get(); + this._config = Container.applyMode(configuration.get()); } return this._config; } @@ -173,4 +173,32 @@ export class Container { static resetConfig() { this._config = undefined; } + + private static applyMode(config: IConfig) { + if (!config.mode.active) return config; + + const mode = config.modes[config.mode.active]; + if (mode == null) return config; + + if (mode.codeLens != null) { + config.codeLens.enabled = mode.codeLens; + } + if (mode.currentLine != null) { + config.currentLine.enabled = mode.currentLine; + } + if (mode.explorers != null) { + config.gitExplorer.enabled = mode.explorers; + } + if (mode.explorers != null) { + config.historyExplorer.enabled = mode.explorers; + } + if (mode.hovers != null) { + config.hovers.enabled = mode.hovers; + } + if (mode.statusBar != null) { + config.statusBar.enabled = mode.statusBar; + } + + return config; + } } diff --git a/src/gitCodeLensProvider.ts b/src/gitCodeLensProvider.ts index 54f86ae..a91dd88 100644 --- a/src/gitCodeLensProvider.ts +++ b/src/gitCodeLensProvider.ts @@ -75,7 +75,7 @@ export class GitCodeLensProvider implements CodeLensProvider { if (document.isDirty) { // Only allow dirty blames if we are idle if (trackedDocument.isDirtyIdle) { - const maxLines = configuration.get(configuration.name('advanced')('blame')('sizeThresholdAfterEdit').value); + const maxLines = Container.config.advanced.blame.sizeThresholdAfterEdit; if (maxLines > 0 && document.lineCount > maxLines) { dirty = true; } @@ -433,14 +433,8 @@ export class GitCodeLensProvider implements CodeLensProvider { } private getDirtyTitle(cfg: ICodeLensConfig) { - if (cfg.recentChange.enabled && cfg.authors.enabled) { - return configuration.get(configuration.name('strings')('codeLens')('unsavedChanges')('recentChangeAndAuthors').value); - } - else if (cfg.recentChange.enabled) { - return configuration.get(configuration.name('strings')('codeLens')('unsavedChanges')('recentChangeOnly').value); - } - else { - return configuration.get(configuration.name('strings')('codeLens')('unsavedChanges')('authorsOnly').value); - } + if (cfg.recentChange.enabled && cfg.authors.enabled) return Container.config.strings.codeLens.unsavedChanges.recentChangeAndAuthors; + if (cfg.recentChange.enabled) return Container.config.strings.codeLens.unsavedChanges.recentChangeOnly; + return Container.config.strings.codeLens.unsavedChanges.authorsOnly; } } \ No newline at end of file diff --git a/src/quickPicks/modesQuickPick.ts b/src/quickPicks/modesQuickPick.ts new file mode 100644 index 0000000..8595ae8 --- /dev/null +++ b/src/quickPicks/modesQuickPick.ts @@ -0,0 +1,40 @@ +'use strict'; +import { QuickPickItem, QuickPickOptions, window } from 'vscode'; +import { Container } from '../container'; +import { GlyphChars } from '../constants'; + +export interface ModesQuickPickItem extends QuickPickItem { + key: string | undefined; +} + +export class ModesQuickPick { + + static async show(): Promise { + const modes = Object.keys(Container.config.modes); + if (modes.length === 0) return undefined; + + const mode = Container.config.mode.active; + + const items = modes.map(key => { + const modeCfg = Container.config.modes[key]; + return { + label: `${mode === key ? '$(check)\u00a0\u00a0' : '\u00a0\u00a0\u00a0\u00a0\u00a0'}${modeCfg.name} mode`, + description: modeCfg.description ? `\u00a0${GlyphChars.Dash}\u00a0 ${modeCfg.description}` : '', + key: key + } as ModesQuickPickItem; + }); + + if (mode) { + items.splice(0, 0, { + label: `Exit ${Container.config.modes[mode].name} mode`, + key: undefined + } as ModesQuickPickItem); + } + + const pick = await window.showQuickPick(items, { + placeHolder: 'select a GitLens mode to enter' + } as QuickPickOptions); + + return pick; + } +} \ No newline at end of file diff --git a/src/quickPicks/quickPicks.ts b/src/quickPicks/quickPicks.ts index db0e185..faee378 100644 --- a/src/quickPicks/quickPicks.ts +++ b/src/quickPicks/quickPicks.ts @@ -8,6 +8,7 @@ export * from './commitQuickPick'; export * from './commitsQuickPick'; export * from './commonQuickPicks'; export * from './fileHistoryQuickPick'; +export * from './modesQuickPick'; export * from './remotesQuickPick'; export * from './repositoriesQuickPick'; export * from './repoStatusQuickPick'; diff --git a/src/statusBarController.ts b/src/statusBarController.ts index 5098cd9..869afee 100644 --- a/src/statusBarController.ts +++ b/src/statusBarController.ts @@ -1,7 +1,7 @@ 'use strict'; import { ConfigurationChangeEvent, Disposable, StatusBarAlignment, StatusBarItem, TextEditor, window } from 'vscode'; import { Commands } from './commands'; -import { configuration, IConfig, StatusBarCommand } from './configuration'; +import { configuration, StatusBarCommand } from './configuration'; import { isTextEditor } from './constants'; import { Container } from './container'; import { LinesChangeEvent } from './trackers/gitLineTracker'; @@ -9,8 +9,9 @@ import { CommitFormatter, GitCommit, ICommitFormatOptions } from './gitService'; export class StatusBarController extends Disposable { + private _blameStatusBarItem: StatusBarItem | undefined; private _disposable: Disposable; - private _statusBarItem: StatusBarItem | undefined; + private _modeStatusBarItem: StatusBarItem | undefined; constructor() { super(() => this.dispose()); @@ -22,9 +23,10 @@ export class StatusBarController extends Disposable { } dispose() { - this.clear(); + this.clearBlame(); - this._statusBarItem && this._statusBarItem.dispose(); + this._blameStatusBarItem && this._blameStatusBarItem.dispose(); + this._modeStatusBarItem && this._modeStatusBarItem.dispose(); Container.lineTracker.stop(this); this._disposable && this._disposable.dispose(); @@ -33,21 +35,48 @@ export class StatusBarController extends Disposable { private onConfigurationChanged(e: ConfigurationChangeEvent) { const initializing = configuration.initializing(e); + if (initializing || configuration.changed(e, configuration.name('mode').value)) { + const mode = Container.config.mode.active && Container.config.mode.statusBar.enabled + ? Container.config.modes[Container.config.mode.active] + : undefined; + if (mode && mode.statusBarItemName) { + const alignment = Container.config.mode.statusBar.alignment !== 'left' ? StatusBarAlignment.Right : StatusBarAlignment.Left; + + if (configuration.changed(e, configuration.name('mode')('statusBar')('alignment').value)) { + if (this._modeStatusBarItem !== undefined && this._modeStatusBarItem.alignment !== alignment) { + this._modeStatusBarItem.dispose(); + this._modeStatusBarItem = undefined; + } + } + + this._modeStatusBarItem = this._modeStatusBarItem || window.createStatusBarItem(alignment, alignment === StatusBarAlignment.Right ? 999 : 1); + this._modeStatusBarItem.command = Commands.SwitchMode; + this._modeStatusBarItem.text = mode.statusBarItemName; + this._modeStatusBarItem.tooltip = `Switch GitLens Mode`; + this._modeStatusBarItem.show(); + } + else { + if (this._modeStatusBarItem !== undefined) { + this._modeStatusBarItem.dispose(); + this._modeStatusBarItem = undefined; + } + } + } + if (!initializing && !configuration.changed(e, configuration.name('statusBar').value)) return; - const cfg = configuration.get(); - if (cfg.statusBar.enabled) { - const alignment = cfg.statusBar.alignment !== 'left' ? StatusBarAlignment.Right : StatusBarAlignment.Left; + if (Container.config.statusBar.enabled) { + const alignment = Container.config.statusBar.alignment !== 'left' ? StatusBarAlignment.Right : StatusBarAlignment.Left; if (configuration.changed(e, configuration.name('statusBar')('alignment').value)) { - if (this._statusBarItem !== undefined && this._statusBarItem.alignment !== alignment) { - this._statusBarItem.dispose(); - this._statusBarItem = undefined; + if (this._blameStatusBarItem !== undefined && this._blameStatusBarItem.alignment !== alignment) { + this._blameStatusBarItem.dispose(); + this._blameStatusBarItem = undefined; } } - this._statusBarItem = this._statusBarItem || window.createStatusBarItem(alignment, alignment === StatusBarAlignment.Right ? 1000 : 0); - this._statusBarItem.command = cfg.statusBar.command; + this._blameStatusBarItem = this._blameStatusBarItem || window.createStatusBarItem(alignment, alignment === StatusBarAlignment.Right ? 1000 : 0); + this._blameStatusBarItem.command = Container.config.statusBar.command; if (initializing || configuration.changed(e, configuration.name('statusBar')('enabled').value)) { Container.lineTracker.start( @@ -60,9 +89,9 @@ export class StatusBarController extends Disposable { if (configuration.changed(e, configuration.name('statusBar')('enabled').value)) { Container.lineTracker.stop(this); - if (this._statusBarItem !== undefined) { - this._statusBarItem.dispose(); - this._statusBarItem = undefined; + if (this._blameStatusBarItem !== undefined) { + this._blameStatusBarItem.dispose(); + this._blameStatusBarItem = undefined; } } } @@ -74,7 +103,7 @@ export class StatusBarController extends Disposable { if (!e.pending && e.lines !== undefined) { const state = Container.lineTracker.getState(e.lines[0]); if (state !== undefined && state.commit !== undefined) { - this.updateStatusBar(state.commit, e.editor!); + this.updateBlame(state.commit, e.editor!); return; } @@ -83,54 +112,54 @@ export class StatusBarController extends Disposable { } if (clear) { - this.clear(); + this.clearBlame(); } } - async clear() { - if (this._statusBarItem !== undefined) { - this._statusBarItem.hide(); + async clearBlame() { + if (this._blameStatusBarItem !== undefined) { + this._blameStatusBarItem.hide(); } } - private updateStatusBar(commit: GitCommit, editor: TextEditor) { + private updateBlame(commit: GitCommit, editor: TextEditor) { const cfg = Container.config.statusBar; - if (!cfg.enabled || this._statusBarItem === undefined || !isTextEditor(editor)) return; + if (!cfg.enabled || this._blameStatusBarItem === undefined || !isTextEditor(editor)) return; - this._statusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, { + this._blameStatusBarItem.text = `$(git-commit) ${CommitFormatter.fromTemplate(cfg.format, commit, { truncateMessageAtNewLine: true, dateFormat: cfg.dateFormat === null ? Container.config.defaultDateFormat : cfg.dateFormat } as ICommitFormatOptions)}`; switch (cfg.command) { case StatusBarCommand.ToggleFileBlame: - this._statusBarItem.tooltip = 'Toggle Blame Annotations'; + this._blameStatusBarItem.tooltip = 'Toggle Blame Annotations'; break; case StatusBarCommand.DiffWithPrevious: - this._statusBarItem.command = Commands.DiffLineWithPrevious; - this._statusBarItem.tooltip = 'Compare Line Revision with Previous'; + this._blameStatusBarItem.command = Commands.DiffLineWithPrevious; + this._blameStatusBarItem.tooltip = 'Compare Line Revision with Previous'; break; case StatusBarCommand.DiffWithWorking: - this._statusBarItem.command = Commands.DiffLineWithWorking; - this._statusBarItem.tooltip = 'Compare Line Revision with Working'; + this._blameStatusBarItem.command = Commands.DiffLineWithWorking; + this._blameStatusBarItem.tooltip = 'Compare Line Revision with Working'; break; case StatusBarCommand.ToggleCodeLens: - this._statusBarItem.tooltip = 'Toggle Git CodeLens'; + this._blameStatusBarItem.tooltip = 'Toggle Git CodeLens'; break; case StatusBarCommand.ShowQuickCommitDetails: - this._statusBarItem.tooltip = 'Show Commit Details'; + this._blameStatusBarItem.tooltip = 'Show Commit Details'; break; case StatusBarCommand.ShowQuickCommitFileDetails: - this._statusBarItem.tooltip = 'Show Line Commit Details'; + this._blameStatusBarItem.tooltip = 'Show Line Commit Details'; break; case StatusBarCommand.ShowQuickFileHistory: - this._statusBarItem.tooltip = 'Show File History'; + this._blameStatusBarItem.tooltip = 'Show File History'; break; case StatusBarCommand.ShowQuickCurrentBranchHistory: - this._statusBarItem.tooltip = 'Show Branch History'; + this._blameStatusBarItem.tooltip = 'Show Branch History'; break; } - this._statusBarItem.show(); + this._blameStatusBarItem.show(); } } \ No newline at end of file diff --git a/src/ui/config.ts b/src/ui/config.ts index a7caf76..eaee284 100644 --- a/src/ui/config.ts +++ b/src/ui/config.ts @@ -223,6 +223,17 @@ export interface IMenuConfig { }; } +export interface IModeConfig { + name: string; + statusBarItemName?: string; + description?: string; + codeLens?: boolean; + currentLine?: boolean; + explorers?: boolean; + hovers?: boolean; + statusBar?: boolean; +} + export interface IResultsExplorerConfig { files: IExplorersFilesConfig; } @@ -311,12 +322,20 @@ export interface IConfig { insiders: boolean; keymap: KeyMap; menus: boolean | IMenuConfig; + mode: { + active: string; + statusBar: { + enabled: boolean; + alignment: 'left' | 'right'; + } + }; + modes: { [key: string]: IModeConfig }; outputLevel: OutputLevel; recentChanges: { highlight: { locations: HighlightLocations[]; - }; + } toggleMode: AnnotationsToggleMode; }; @@ -341,8 +360,8 @@ export interface IConfig { recentChangeAndAuthors: string; recentChangeOnly: string; authorsOnly: string; - }; - }; + } + } }; advanced: IAdvancedConfig; diff --git a/src/ui/images/settings/modes-status-bar-left.png b/src/ui/images/settings/modes-status-bar-left.png new file mode 100644 index 0000000..d766a04 Binary files /dev/null and b/src/ui/images/settings/modes-status-bar-left.png differ diff --git a/src/ui/images/settings/modes-status-bar-right.png b/src/ui/images/settings/modes-status-bar-right.png new file mode 100644 index 0000000..4e6464e Binary files /dev/null and b/src/ui/images/settings/modes-status-bar-right.png differ diff --git a/src/ui/settings/index.html b/src/ui/settings/index.html index 1366a59..f1a3b34 100644 --- a/src/ui/settings/index.html +++ b/src/ui/settings/index.html @@ -829,6 +829,57 @@ +

+
+

Modes + + + +

+

GitLens supports user-defined modes for quickly toggling between sets of settings

+
+ +
+
+
+ + +
+ +
+ + +
+
+ +
+ + + +
+ +

+ + For more advanced customizations or to add your own modes, open User Settings and search for gitlens.modes + + Use the + GitLens: Switch Mode command to quickly switch the active mode + + + Use the + GitLens: Toggle Review Mode command to toggle Review mode + + + Use the + GitLens: Toggle Zen Mode command to toggle Zen mode + +

+
+
+

Recent Changes @@ -989,6 +1040,7 @@
  • Gutter Blame
  • Gutter Heatmap
  • Hovers
  • +
  • Modes
  • Recent Changes
  • Status Bar Blame
  • diff --git a/src/views/historyExplorer.ts b/src/views/historyExplorer.ts index 859b5f2..4330c60 100644 --- a/src/views/historyExplorer.ts +++ b/src/views/historyExplorer.ts @@ -57,10 +57,10 @@ export class HistoryExplorer extends Disposable implements TreeDataProvider { getBootstrap() { return { - config: Container.config, + // Make sure to get the raw config, not from the container which has the modes mixed in + config: configuration.get(), scope: 'user', scopes: this.getAvailableScopes() } as SettingsBootstrap; diff --git a/src/webviews/webviewEditor.ts b/src/webviews/webviewEditor.ts index 2ae7546..b350cce 100644 --- a/src/webviews/webviewEditor.ts +++ b/src/webviews/webviewEditor.ts @@ -1,6 +1,6 @@ 'use strict'; import { ConfigurationChangeEvent, ConfigurationTarget, Disposable, Uri, ViewColumn, WebviewPanel, WebviewPanelOnDidChangeViewStateEvent, window, workspace } from 'vscode'; -import { configuration } from '../configuration'; +import { configuration, IConfig } from '../configuration'; import { Container } from '../container'; import { Message, SettingsChangedMessage } from '../ui/ipc'; import { Logger } from '../logger'; @@ -162,6 +162,7 @@ export abstract class WebviewEditor extends Disposable { } private postUpdatedConfiguration() { - return this.postMessage({ type: 'settingsChanged', config: Container.config } as SettingsChangedMessage, 'config'); + // Make sure to get the raw config, not from the container which has the modes mixed in + return this.postMessage({ type: 'settingsChanged', config: configuration.get() } as SettingsChangedMessage, 'config'); } }