From 0100518547990f6f8584f2d4cda8c9970e973ec9 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Wed, 18 Apr 2018 18:31:05 +0200 Subject: [PATCH] Highlight active indent guide (closes #14017) --- .../theme-abyss/themes/abyss-color-theme.json | 1 + .../theme-defaults/themes/dark_defaults.json | 1 + .../themes/hc_black_defaults.json | 1 + .../theme-defaults/themes/light_defaults.json | 1 + .../themes/dimmed-monokai-color-theme.json | 1 + .../themes/monokai-color-theme.json | 1 + .../themes/solarized-dark-color-theme.json | 3 +- .../themes/solarized-light-color-theme.json | 3 +- .../viewParts/indentGuides/indentGuides.css | 3 ++ .../viewParts/indentGuides/indentGuides.ts | 41 ++++++++++++++++--- .../editor/common/view/editorColorRegistry.ts | 1 + .../common/viewModel/splitLinesCollection.ts | 27 +++++++++++- src/vs/editor/common/viewModel/viewModel.ts | 3 +- .../editor/common/viewModel/viewModelImpl.ts | 6 ++- src/vs/editor/standalone/common/themes.ts | 5 ++- .../electron-browser/themeCompatibility.ts | 1 + 16 files changed, 87 insertions(+), 12 deletions(-) diff --git a/extensions/theme-abyss/themes/abyss-color-theme.json b/extensions/theme-abyss/themes/abyss-color-theme.json index 3c29932bdee..85f5f9a4a9e 100644 --- a/extensions/theme-abyss/themes/abyss-color-theme.json +++ b/extensions/theme-abyss/themes/abyss-color-theme.json @@ -309,6 +309,7 @@ "editor.lineHighlightBackground": "#082050", "editor.selectionBackground": "#770811", "editorIndentGuide.background": "#002952", + "editorIndentGuide.activeBackground": "#204972", "editorHoverWidget.background": "#000c38", "editorHoverWidget.border": "#004c18", "editorLineNumber.foreground": "#406385", diff --git a/extensions/theme-defaults/themes/dark_defaults.json b/extensions/theme-defaults/themes/dark_defaults.json index e15677c62c8..cb9e20066b5 100644 --- a/extensions/theme-defaults/themes/dark_defaults.json +++ b/extensions/theme-defaults/themes/dark_defaults.json @@ -6,6 +6,7 @@ "editor.foreground": "#D4D4D4", "editor.inactiveSelectionBackground": "#3A3D41", "editorIndentGuide.background": "#404040", + "editorIndentGuide.activeBackground": "#707070", "editor.selectionHighlightBackground": "#ADD6FF26", "list.dropBackground": "#383B3D", "activityBarBadge.background": "#007ACC", diff --git a/extensions/theme-defaults/themes/hc_black_defaults.json b/extensions/theme-defaults/themes/hc_black_defaults.json index 8833ff32c1f..4acf07cd7ab 100644 --- a/extensions/theme-defaults/themes/hc_black_defaults.json +++ b/extensions/theme-defaults/themes/hc_black_defaults.json @@ -5,6 +5,7 @@ "editor.background": "#000000", "editor.foreground": "#FFFFFF", "editorIndentGuide.background": "#FFFFFF", + "editorIndentGuide.activeBackground": "#FFFFFF", "sideBarTitle.foreground": "#FFFFFF" }, "settings": [ diff --git a/extensions/theme-defaults/themes/light_defaults.json b/extensions/theme-defaults/themes/light_defaults.json index 4e50029e23c..2ee46debb3a 100644 --- a/extensions/theme-defaults/themes/light_defaults.json +++ b/extensions/theme-defaults/themes/light_defaults.json @@ -6,6 +6,7 @@ "editor.foreground": "#000000", "editor.inactiveSelectionBackground": "#E5EBF1", "editorIndentGuide.background": "#D3D3D3", + "editorIndentGuide.activeBackground": "#939393", "editor.selectionHighlightBackground": "#ADD6FF4D", "editorSuggestWidget.background": "#F3F3F3", "activityBarBadge.background": "#007ACC", diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index 0a45c9ffe88..20f12faee09 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -19,6 +19,7 @@ "editorCursor.foreground": "#c07020", "editorWhitespace.foreground": "#505037", "editorIndentGuide.background": "#505037", + "editorIndentGuide.activeBackground": "#707057", "editorGroupHeader.tabsBackground": "#282828", "editorGroup.background": "#1e1e1e", "tab.inactiveBackground": "#404040", diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 610bf0dc5a2..439ed05190e 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -28,6 +28,7 @@ "editorCursor.foreground": "#f8f8f0", "editorWhitespace.foreground": "#464741", "editorIndentGuide.background": "#464741", + "editorIndentGuide.activeBackground": "#767771", "editorGroupHeader.tabsBackground": "#1e1f1c", "editorGroup.dropBackground": "#41433980", "tab.inactiveBackground": "#414339", diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index 2f5db67fbad..7d77408d2e7 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -357,7 +357,8 @@ "editor.lineHighlightBackground": "#073642", "editorLineNumber.activeForeground": "#949494", "editor.selectionBackground": "#073642", - // "editorIndentGuide.background": "", + "editorIndentGuide.background": "#93A1A180", + "editorIndentGuide.activeBackground": "#C3E1E180", "editorHoverWidget.background": "#004052", // "editorHoverWidget.border": "", // "editorLineNumber.foreground": "", diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index d752a613c38..588e21e8933 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -352,7 +352,8 @@ "editorWhitespace.foreground": "#586E7580", "editor.lineHighlightBackground": "#EEE8D5", "editor.selectionBackground": "#EEE8D5", - // "editorIndentGuide.background": "", + "editorIndentGuide.background": "#586E7580", + "editorIndentGuide.activeBackground": "#081E2580", "editorHoverWidget.background": "#CCC4B0", "editorLineNumber.activeForeground": "#567983", // "editorHoverWidget.border": "", diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css index 45992137d18..3055ad67e4e 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.css @@ -10,3 +10,6 @@ .monaco-editor .lines-content .cigr { position: absolute; } +.monaco-editor .lines-content .cigra { + position: absolute; +} diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index b8ad9dadd68..8834b5fed88 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -11,12 +11,13 @@ import { ViewContext } from 'vs/editor/common/view/viewContext'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { editorIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; +import { editorIndentGuides, editorActiveIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; import { Position } from 'vs/editor/common/core/position'; export class IndentGuidesOverlay extends DynamicViewOverlay { private _context: ViewContext; + private _primaryLineNumber: number; private _lineHeight: number; private _spaceWidth: number; private _renderResult: string[]; @@ -25,6 +26,7 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { constructor(context: ViewContext) { super(); this._context = context; + this._primaryLineNumber = 0; this._lineHeight = this._context.configuration.editor.lineHeight; this._spaceWidth = this._context.configuration.editor.fontInfo.spaceWidth; this._enabled = this._context.configuration.editor.viewInfo.renderIndentGuides; @@ -54,6 +56,17 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { } return true; } + public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + const selection = e.selections[0]; + const newPrimaryLineNumber = selection.isEmpty() ? selection.positionLineNumber : 0; + + if (this._primaryLineNumber !== newPrimaryLineNumber) { + this._primaryLineNumber = newPrimaryLineNumber; + return true; + } + + return false; + } public onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { // true for inline decorations return true; @@ -97,16 +110,28 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { const indents = this._context.model.getLinesIndentGuides(visibleStartLineNumber, visibleEndLineNumber); + let activeIndentStartLineNumber = 0; + let activeIndentEndLineNumber = 0; + let activeIndentLevel = 0; + if (this._primaryLineNumber) { + const activeIndentInfo = this._context.model.getActiveIndentGuide(this._primaryLineNumber); + activeIndentStartLineNumber = activeIndentInfo.startLineNumber; + activeIndentEndLineNumber = activeIndentInfo.endLineNumber; + activeIndentLevel = activeIndentInfo.indent; + } + let output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + const containsActiveIndentGuide = (activeIndentStartLineNumber <= lineNumber && lineNumber <= activeIndentEndLineNumber); const lineIndex = lineNumber - visibleStartLineNumber; const indent = indents[lineIndex]; let result = ''; let leftMostVisiblePosition = ctx.visibleRangeForPosition(new Position(lineNumber, 1)); let left = leftMostVisiblePosition ? leftMostVisiblePosition.left : 0; - for (let i = 0; i < indent; i++) { - result += `
`; + for (let i = 1; i <= indent; i++) { + let className = (containsActiveIndentGuide && i === activeIndentLevel ? 'cigra' : 'cigr'); + result += `
`; left += tabWidth; } @@ -128,8 +153,12 @@ export class IndentGuidesOverlay extends DynamicViewOverlay { } registerThemingParticipant((theme, collector) => { - let editorGuideColor = theme.getColor(editorIndentGuides); - if (editorGuideColor) { - collector.addRule(`.monaco-editor .lines-content .cigr { box-shadow: 1px 0 0 0 ${editorGuideColor} inset; }`); + let editorIndentGuidesColor = theme.getColor(editorIndentGuides); + if (editorIndentGuidesColor) { + collector.addRule(`.monaco-editor .lines-content .cigr { box-shadow: 1px 0 0 0 ${editorIndentGuidesColor} inset; }`); + } + let editorActiveIndentGuidesColor = theme.getColor(editorActiveIndentGuides) || editorIndentGuidesColor; + if (editorActiveIndentGuidesColor) { + collector.addRule(`.monaco-editor .lines-content .cigra { box-shadow: 1px 0 0 0 ${editorActiveIndentGuidesColor} inset; }`); } }); diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index 9bafc0f45c2..92f67358ba7 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -20,6 +20,7 @@ export const editorCursorForeground = registerColor('editorCursor.foreground', { export const editorCursorBackground = registerColor('editorCursor.background', null, nls.localize('editorCursorBackground', 'The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.')); export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hc: '#e3e4e229' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); export const editorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hc: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.')); +export const editorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', { dark: editorWhitespaces, light: editorWhitespaces, hc: editorWhitespaces }, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.')); export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#5A5A5A', light: '#2B91AF', hc: Color.white }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: null, light: null, hc: null }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.')); diff --git a/src/vs/editor/common/viewModel/splitLinesCollection.ts b/src/vs/editor/common/viewModel/splitLinesCollection.ts index b80a077c786..73f02436877 100644 --- a/src/vs/editor/common/viewModel/splitLinesCollection.ts +++ b/src/vs/editor/common/viewModel/splitLinesCollection.ts @@ -14,7 +14,7 @@ import { WrappingIndent } from 'vs/editor/common/config/editorOptions'; import { ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; import { ThemeColor, ITheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; -import { IModelDecoration, ITextModel, IModelDeltaDecoration, EndOfLinePreference } from 'vs/editor/common/model'; +import { IModelDecoration, ITextModel, IModelDeltaDecoration, EndOfLinePreference, IActiveIndentGuideInfo } from 'vs/editor/common/model'; export class OutputPosition { _outputPositionBrand: void; @@ -82,6 +82,7 @@ export interface IViewModelLinesCollection { getViewLineCount(): number; warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void; + getActiveIndentGuide(viewLineNumber: number): IActiveIndentGuideInfo; getViewLinesIndentGuides(viewStartLineNumber: number, viewEndLineNumber: number): number[]; getViewLineContent(viewLineNumber: number): string; getViewLineLength(viewLineNumber: number): number; @@ -504,6 +505,22 @@ export class SplitLinesCollection implements IViewModelLinesCollection { this.prefixSumComputer.warmUpCache(viewStartLineNumber - 1, viewEndLineNumber - 1); } + public getActiveIndentGuide(viewLineNumber: number): IActiveIndentGuideInfo { + this._ensureValidState(); + viewLineNumber = this._toValidViewLineNumber(viewLineNumber); + + const modelPosition = this.convertViewPositionToModelPosition(viewLineNumber, this.getViewLineMinColumn(viewLineNumber)); + const result = this.model.getActiveIndentGuide(modelPosition.lineNumber); + + const viewStartPosition = this.convertModelPositionToViewPosition(result.startLineNumber, 1); + const viewEndPosition = this.convertModelPositionToViewPosition(result.endLineNumber, 1); + return { + startLineNumber: viewStartPosition.lineNumber, + endLineNumber: viewEndPosition.lineNumber, + indent: result.indent + }; + } + public getViewLinesIndentGuides(viewStartLineNumber: number, viewEndLineNumber: number): number[] { this._ensureValidState(); viewStartLineNumber = this._toValidViewLineNumber(viewStartLineNumber); @@ -1241,6 +1258,14 @@ export class IdentityLinesCollection implements IViewModelLinesCollection { public warmUpLookupCache(viewStartLineNumber: number, viewEndLineNumber: number): void { } + public getActiveIndentGuide(viewLineNumber: number): IActiveIndentGuideInfo { + return { + startLineNumber: viewLineNumber, + endLineNumber: viewLineNumber, + indent: 0 + }; + } + public getViewLinesIndentGuides(viewStartLineNumber: number, viewEndLineNumber: number): number[] { const viewLineCount = viewEndLineNumber - viewStartLineNumber + 1; let result = new Array(viewLineCount); diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index b9ce5b257ce..c97d919393a 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -5,7 +5,7 @@ 'use strict'; import { INewScrollPosition } from 'vs/editor/common/editorCommon'; -import { EndOfLinePreference, IModelDecorationOptions } from 'vs/editor/common/model'; +import { EndOfLinePreference, IModelDecorationOptions, IActiveIndentGuideInfo } from 'vs/editor/common/model'; import { IViewLineTokens } from 'vs/editor/common/core/lineTokens'; import { Position, IPosition } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; @@ -132,6 +132,7 @@ export interface IViewModel { getLineCount(): number; getLineContent(lineNumber: number): string; getLineLength(lineNumber: number): number; + getActiveIndentGuide(lineNumber: number): IActiveIndentGuideInfo; getLinesIndentGuides(startLineNumber: number, endLineNumber: number): number[]; getLineMinColumn(lineNumber: number): number; getLineMaxColumn(lineNumber: number): number; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 3f9ff85de8c..8fbfa9939e7 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -23,7 +23,7 @@ import { Color } from 'vs/base/common/color'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ITheme } from 'vs/platform/theme/common/themeService'; import { ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel'; -import { ITextModel, EndOfLinePreference, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { ITextModel, EndOfLinePreference, TrackedRangeStickiness, IActiveIndentGuideInfo } from 'vs/editor/common/model'; const USE_IDENTITY_LINES_COLLECTION = true; @@ -451,6 +451,10 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel this.viewportStartLineTop = this.viewLayout.getVerticalOffsetForLineNumber(startLineNumber); } + public getActiveIndentGuide(lineNumber: number): IActiveIndentGuideInfo { + return this.lines.getActiveIndentGuide(lineNumber); + } + public getLinesIndentGuides(startLineNumber: number, endLineNumber: number): number[] { return this.lines.getViewLinesIndentGuides(startLineNumber, endLineNumber); } diff --git a/src/vs/editor/standalone/common/themes.ts b/src/vs/editor/standalone/common/themes.ts index 0a51ad13ed8..5ac8ffea7e7 100644 --- a/src/vs/editor/standalone/common/themes.ts +++ b/src/vs/editor/standalone/common/themes.ts @@ -7,7 +7,7 @@ import { IStandaloneThemeData } from 'vs/editor/standalone/common/standaloneThemeService'; import { editorBackground, editorForeground, editorSelectionHighlight, editorInactiveSelection } from 'vs/platform/theme/common/colorRegistry'; -import { editorIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; +import { editorIndentGuides, editorActiveIndentGuides } from 'vs/editor/common/view/editorColorRegistry'; /* -------------------------------- Begin vs theme -------------------------------- */ export const vs: IStandaloneThemeData = { @@ -74,6 +74,7 @@ export const vs: IStandaloneThemeData = { [editorForeground]: '#000000', [editorInactiveSelection]: '#E5EBF1', [editorIndentGuides]: '#D3D3D3', + [editorActiveIndentGuides]: '#939393', [editorSelectionHighlight]: '#ADD6FF4D' } }; @@ -144,6 +145,7 @@ export const vs_dark: IStandaloneThemeData = { [editorForeground]: '#D4D4D4', [editorInactiveSelection]: '#3A3D41', [editorIndentGuides]: '#404040', + [editorActiveIndentGuides]: '#707070', [editorSelectionHighlight]: '#ADD6FF26' } }; @@ -205,6 +207,7 @@ export const hc_black: IStandaloneThemeData = { [editorBackground]: '#000000', [editorForeground]: '#FFFFFF', [editorIndentGuides]: '#FFFFFF', + [editorActiveIndentGuides]: '#FFFFFF', } }; /* -------------------------------- End hc-black theme -------------------------------- */ diff --git a/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts b/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts index a05bfc5cf4f..7ce9567369d 100644 --- a/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts +++ b/src/vs/workbench/services/themes/electron-browser/themeCompatibility.ts @@ -66,6 +66,7 @@ addSettingMapping('rangeHighlight', editorColorRegistry.editorRangeHighlight); addSettingMapping('caret', editorColorRegistry.editorCursorForeground); addSettingMapping('invisibles', editorColorRegistry.editorWhitespaces); addSettingMapping('guide', editorColorRegistry.editorIndentGuides); +addSettingMapping('activeGuide', editorColorRegistry.editorActiveIndentGuides); const ansiColorMap = ['ansiBlack', 'ansiRed', 'ansiGreen', 'ansiYellow', 'ansiBlue', 'ansiMagenta', 'ansiCyan', 'ansiWhite', 'ansiBrightBlack', 'ansiBrightRed', 'ansiBrightGreen', 'ansiBrightYellow', 'ansiBrightBlue', 'ansiBrightMagenta', 'ansiBrightCyan', 'ansiBrightWhite'