diff --git a/src/vs/editor/browser/config/editorConfiguration.ts b/src/vs/editor/browser/config/editorConfiguration.ts
index 7e99e40ce4799..fc197da1d5726 100644
--- a/src/vs/editor/browser/config/editorConfiguration.ts
+++ b/src/vs/editor/browser/config/editorConfiguration.ts
@@ -47,6 +47,7 @@ export class EditorConfiguration extends Disposable implements IEditorConfigurat
private _viewLineCount: number = 1;
private _lineNumbersDigitCount: number = 1;
private _reservedHeight: number = 0;
+ private _glyphMarginDecorationLaneCount: number = 1;
private readonly _computeOptionsMemory: ComputeOptionsMemory = new ComputeOptionsMemory();
/**
@@ -117,7 +118,8 @@ export class EditorConfiguration extends Disposable implements IEditorConfigurat
emptySelectionClipboard: partialEnv.emptySelectionClipboard,
pixelRatio: partialEnv.pixelRatio,
tabFocusMode: TabFocus.getTabFocusMode(TabFocusContext.Editor),
- accessibilitySupport: partialEnv.accessibilitySupport
+ accessibilitySupport: partialEnv.accessibilitySupport,
+ glyphMarginDecorationLaneCount: this._glyphMarginDecorationLaneCount
};
return EditorOptionsUtil.computeOptions(this._validatedOptions, env);
}
@@ -193,6 +195,14 @@ export class EditorConfiguration extends Disposable implements IEditorConfigurat
this._reservedHeight = reservedHeight;
this._recomputeOptions();
}
+
+ public setGlyphMarginDecorationLaneCount(decorationLaneCount: number): void {
+ if (this._glyphMarginDecorationLaneCount === decorationLaneCount) {
+ return;
+ }
+ this._glyphMarginDecorationLaneCount = decorationLaneCount;
+ this._recomputeOptions();
+ }
}
function digitCount(n: number): number {
diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts
index 62db35d5a0ccf..0225250768c40 100644
--- a/src/vs/editor/browser/services/abstractCodeEditorService.ts
+++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts
@@ -585,7 +585,7 @@ export const _CSS_MAP: { [prop: string]: string } = {
cursor: 'cursor:{0};',
letterSpacing: 'letter-spacing:{0};',
- gutterIconPath: 'background:{0} center center no-repeat;',
+ gutterIconPath: 'background:{0} no-repeat;',
gutterIconSize: 'background-size:{0};',
contentText: 'content:\'{0}\';',
diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css
index 17f7d5defd5b5..ee86a567698a1 100644
--- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css
+++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.css
@@ -16,5 +16,4 @@
position: absolute;
display: flex;
align-items: center;
- justify-content: center;
}
diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts
index f7ef3e47d64cc..0af60f7571f6b 100644
--- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts
+++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts
@@ -18,23 +18,60 @@ export class DecorationToRender {
public endLineNumber: number;
public className: string;
public readonly zIndex: number;
+ public readonly decorationLane: number;
- constructor(startLineNumber: number, endLineNumber: number, className: string, zIndex?: number) {
+ constructor(startLineNumber: number, endLineNumber: number, className: string, zIndex?: number, decorationLane?: number) {
this.startLineNumber = +startLineNumber;
this.endLineNumber = +endLineNumber;
this.className = String(className);
this.zIndex = zIndex ?? 0;
+ this.decorationLane = decorationLane ?? 1;
+ }
+}
+
+export class RenderedDecoration {
+ constructor(
+ public readonly className: string,
+ public readonly zIndex: number,
+ ) { }
+}
+
+export class LineRenderedDecorations {
+
+ private readonly lanes: RenderedDecoration[][] = [];
+
+ public add(lane: number, decoration: RenderedDecoration) {
+ while (lane >= this.lanes.length) {
+ this.lanes.push([]);
+ }
+ this.lanes[lane].push(decoration);
+ }
+
+ public getLaneDecorations(laneIndex: number): RenderedDecoration[] {
+ if (laneIndex < this.lanes.length) {
+ return this.lanes[laneIndex];
+ }
+ return [];
+ }
+
+ public isEmpty(): boolean {
+ for (const lane of this.lanes) {
+ if (lane.length > 0) {
+ return false;
+ }
+ }
+ return true;
}
}
export abstract class DedupOverlay extends DynamicViewOverlay {
- protected _render(visibleStartLineNumber: number, visibleEndLineNumber: number, decorations: DecorationToRender[]): [string, number][][] {
+ protected _render(visibleStartLineNumber: number, visibleEndLineNumber: number, decorations: DecorationToRender[], decorationLaneCount: number): LineRenderedDecorations[] {
- const output: [string, number][][] = [];
+ const output: LineRenderedDecorations[] = [];
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
const lineIndex = lineNumber - visibleStartLineNumber;
- output[lineIndex] = [];
+ output[lineIndex] = new LineRenderedDecorations();
}
if (decorations.length === 0) {
@@ -59,6 +96,7 @@ export abstract class DedupOverlay extends DynamicViewOverlay {
const zIndex = d.zIndex;
let startLineIndex = Math.max(d.startLineNumber, visibleStartLineNumber) - visibleStartLineNumber;
const endLineIndex = Math.min(d.endLineNumber, visibleEndLineNumber) - visibleStartLineNumber;
+ const lane = Math.min(d.decorationLane, decorationLaneCount);
if (prevClassName === className) {
startLineIndex = Math.max(prevEndLineIndex + 1, startLineIndex);
@@ -69,7 +107,7 @@ export abstract class DedupOverlay extends DynamicViewOverlay {
}
for (let i = startLineIndex; i <= prevEndLineIndex; i++) {
- output[i].push([className, zIndex]);
+ output[i].add(lane, new RenderedDecoration(className, zIndex));
}
}
@@ -84,6 +122,7 @@ export class GlyphMarginOverlay extends DedupOverlay {
private _glyphMargin: boolean;
private _glyphMarginLeft: number;
private _glyphMarginWidth: number;
+ private _glyphMarginDecorationLaneCount: number;
private _renderResult: string[] | null;
constructor(context: ViewContext) {
@@ -97,6 +136,7 @@ export class GlyphMarginOverlay extends DedupOverlay {
this._glyphMargin = options.get(EditorOption.glyphMargin);
this._glyphMarginLeft = layoutInfo.glyphMarginLeft;
this._glyphMarginWidth = layoutInfo.glyphMarginWidth;
+ this._glyphMarginDecorationLaneCount = layoutInfo.glyphMarginDecorationLaneCount;
this._renderResult = null;
this._context.addEventHandler(this);
}
@@ -117,6 +157,7 @@ export class GlyphMarginOverlay extends DedupOverlay {
this._glyphMargin = options.get(EditorOption.glyphMargin);
this._glyphMarginLeft = layoutInfo.glyphMarginLeft;
this._glyphMarginWidth = layoutInfo.glyphMarginWidth;
+ this._glyphMarginDecorationLaneCount = layoutInfo.glyphMarginDecorationLaneCount;
return true;
}
public override onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean {
@@ -151,8 +192,9 @@ export class GlyphMarginOverlay extends DedupOverlay {
const d = decorations[i];
const glyphMarginClassName = d.options.glyphMarginClassName;
const zIndex = d.options.zIndex;
+ const lane = d.options.glyphMargin?.position;
if (glyphMarginClassName) {
- r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName, zIndex);
+ r[rLen++] = new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, glyphMarginClassName, zIndex, lane);
}
}
return r;
@@ -167,31 +209,40 @@ export class GlyphMarginOverlay extends DedupOverlay {
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
const decorationsToRender = this._getDecorations(ctx);
- const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, decorationsToRender);
+ const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, decorationsToRender, this._glyphMarginDecorationLaneCount);
const lineHeight = this._lineHeight.toString();
- const left = this._glyphMarginLeft.toString();
const width = this._glyphMarginWidth.toString();
- const common = '" style="left:' + left + 'px;width:' + width + 'px' + ';height:' + lineHeight + 'px;">';
+ const common = '" style="width:' + width + 'px' + ';height:' + lineHeight + 'px;';
const output: string[] = [];
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
const lineIndex = lineNumber - visibleStartLineNumber;
const renderInfo = toRender[lineIndex];
- if (renderInfo.length === 0) {
+ if (renderInfo.isEmpty()) {
output[lineIndex] = '';
} else {
- // Sort decorations to render in descending order by zIndex
- renderInfo.sort(([_, aIndex], [__, bIndex]) => {
- return bIndex - aIndex;
- });
-
- output[lineIndex] = (
- '
'
+ );
+ }
+ output[lineIndex] = css;
}
}
diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts
index b0e9e91912833..9ed72e6acf0bf 100644
--- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts
+++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts
@@ -91,7 +91,7 @@ export class LinesDecorationsOverlay extends DedupOverlay {
public prepareRender(ctx: RenderingContext): void {
const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
- const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx));
+ const toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx), 1);
const left = this._decorationsLeft.toString();
const width = this._decorationsWidth.toString();
@@ -100,10 +100,10 @@ export class LinesDecorationsOverlay extends DedupOverlay {
const output: string[] = [];
for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
const lineIndex = lineNumber - visibleStartLineNumber;
- const classNames = toRender[lineIndex];
+ const decorations = toRender[lineIndex].getLaneDecorations(1); // there is only one lane, see _render call above
let lineOutput = '';
- for (let i = 0, len = classNames.length; i < len; i++) {
- lineOutput += '';
+ for (const decoration of decorations) {
+ lineOutput += '';
}
output[lineIndex] = lineOutput;
}
diff --git a/src/vs/editor/common/config/editorConfiguration.ts b/src/vs/editor/common/config/editorConfiguration.ts
index 454d81cb089ec..415f8532d8526 100644
--- a/src/vs/editor/common/config/editorConfiguration.ts
+++ b/src/vs/editor/common/config/editorConfiguration.ts
@@ -55,4 +55,8 @@ export interface IEditorConfiguration extends IDisposable {
* Set reserved height above.
*/
setReservedHeight(reservedHeight: number): void;
+ /**
+ * Set the number of decoration lanes to be rendered in the glyph margin.
+ */
+ setGlyphMarginDecorationLaneCount(decorationLaneCount: number): void;
}
diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts
index 12efdc4b3b4b6..52f6dcd515324 100644
--- a/src/vs/editor/common/config/editorOptions.ts
+++ b/src/vs/editor/common/config/editorOptions.ts
@@ -834,6 +834,7 @@ export interface IEnvironmentalOptions {
readonly pixelRatio: number;
readonly tabFocusMode: boolean;
readonly accessibilitySupport: AccessibilitySupport;
+ readonly glyphMarginDecorationLaneCount: number;
}
/**
@@ -2070,6 +2071,11 @@ export interface EditorLayoutInfo {
*/
readonly glyphMarginWidth: number;
+ /**
+ * The number of decoration lanes to render in the glyph margin.
+ */
+ readonly glyphMarginDecorationLaneCount: number;
+
/**
* Left position for the line numbers.
*/
@@ -2157,6 +2163,7 @@ export interface EditorLayoutInfoComputerEnv {
readonly typicalHalfwidthCharacterWidth: number;
readonly maxDigitWidth: number;
readonly pixelRatio: number;
+ readonly glyphMarginDecorationLaneCount: number;
}
/**
@@ -2224,7 +2231,8 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption | null = null;
+ private _affectsGlyphMargin: boolean;
constructor(private readonly handleBeforeFire: (affectedInjectedTextLines: Set | null) => void) {
super();
@@ -2351,6 +2362,7 @@ class DidChangeDecorationsEmitter extends Disposable {
this._shouldFireDeferred = false;
this._affectsMinimap = false;
this._affectsOverviewRuler = false;
+ this._affectsGlyphMargin = false;
}
hasListeners(): boolean {
@@ -2387,12 +2399,16 @@ class DidChangeDecorationsEmitter extends Disposable {
if (!this._affectsOverviewRuler) {
this._affectsOverviewRuler = options.overviewRuler && options.overviewRuler.color ? true : false;
}
+ if (!this._affectsGlyphMargin) {
+ this._affectsGlyphMargin = options.glyphMarginClassName ? true : false;
+ }
this.tryFire();
}
public fire(): void {
this._affectsMinimap = true;
this._affectsOverviewRuler = true;
+ this._affectsGlyphMargin = true;
this.tryFire();
}
@@ -2409,11 +2425,13 @@ class DidChangeDecorationsEmitter extends Disposable {
const event: IModelDecorationsChangedEvent = {
affectsMinimap: this._affectsMinimap,
- affectsOverviewRuler: this._affectsOverviewRuler
+ affectsOverviewRuler: this._affectsOverviewRuler,
+ affectsGlyphMargin: this._affectsGlyphMargin
};
this._shouldFireDeferred = false;
this._affectsMinimap = false;
this._affectsOverviewRuler = false;
+ this._affectsGlyphMargin = false;
this._actual.fire(event);
}
}
diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts
index c4c38394c3242..4552f43e07224 100644
--- a/src/vs/editor/common/standalone/standaloneEnums.ts
+++ b/src/vs/editor/common/standalone/standaloneEnums.ts
@@ -349,6 +349,14 @@ export enum EndOfLineSequence {
CRLF = 1
}
+/**
+ * Vertical Lane in the glyph margin of the editor.
+ */
+export enum GlyphMarginLane {
+ Left = 1,
+ Right = 2
+}
+
/**
* Describes what to do with the indentation when pressing Enter.
*/
diff --git a/src/vs/editor/common/textModelEvents.ts b/src/vs/editor/common/textModelEvents.ts
index c078ae0ec4d90..8c25e06776d81 100644
--- a/src/vs/editor/common/textModelEvents.ts
+++ b/src/vs/editor/common/textModelEvents.ts
@@ -90,6 +90,7 @@ export interface IModelContentChangedEvent {
export interface IModelDecorationsChangedEvent {
readonly affectsMinimap: boolean;
readonly affectsOverviewRuler: boolean;
+ readonly affectsGlyphMargin: boolean;
}
/**
diff --git a/src/vs/editor/common/viewEvents.ts b/src/vs/editor/common/viewEvents.ts
index 2182d17b7f8cd..0e6cb0fa16670 100644
--- a/src/vs/editor/common/viewEvents.ts
+++ b/src/vs/editor/common/viewEvents.ts
@@ -75,14 +75,17 @@ export class ViewDecorationsChangedEvent {
readonly affectsMinimap: boolean;
readonly affectsOverviewRuler: boolean;
+ readonly affectsGlyphMargin: boolean;
constructor(source: IModelDecorationsChangedEvent | null) {
if (source) {
this.affectsMinimap = source.affectsMinimap;
this.affectsOverviewRuler = source.affectsOverviewRuler;
+ this.affectsGlyphMargin = source.affectsGlyphMargin;
} else {
this.affectsMinimap = true;
this.affectsOverviewRuler = true;
+ this.affectsGlyphMargin = true;
}
}
}
diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts
index eb18dced3c1f6..cf61c0bac20b5 100644
--- a/src/vs/editor/common/viewModel/viewModelImpl.ts
+++ b/src/vs/editor/common/viewModel/viewModelImpl.ts
@@ -19,7 +19,7 @@ import { Range } from 'vs/editor/common/core/range';
import { ISelection, Selection } from 'vs/editor/common/core/selection';
import { ICommand, ICursorState, IViewState, ScrollType } from 'vs/editor/common/editorCommon';
import { IEditorConfiguration } from 'vs/editor/common/config/editorConfiguration';
-import { EndOfLinePreference, IAttachedView, ICursorStateComputer, IIdentifiedSingleEditOperation, ITextModel, PositionAffinity, TrackedRangeStickiness } from 'vs/editor/common/model';
+import { EndOfLinePreference, GlyphMarginLane, IAttachedView, ICursorStateComputer, IIdentifiedSingleEditOperation, ITextModel, PositionAffinity, TrackedRangeStickiness } from 'vs/editor/common/model';
import { IActiveIndentGuideInfo, BracketGuideOptions, IndentGuide } from 'vs/editor/common/textModelGuides';
import { ModelDecorationMinimapOptions, ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from 'vs/editor/common/model/textModel';
import * as textModelEvents from 'vs/editor/common/textModelEvents';
@@ -459,6 +459,54 @@ export class ViewModel extends Disposable implements IViewModel {
this._register(this.model.onDidChangeDecorations((e) => {
this._decorations.onModelDecorationsChanged();
+
+ // Determine whether we need to resize the glyph margin
+ if (e.affectsGlyphMargin) {
+ const decorations = this.model.getAllMarginDecorations();
+
+ let hasTwoLanes = false;
+
+ // Decorations are already sorted by their start position, but protect against future changes
+ decorations.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range));
+
+ let leftDecRange: Range | null = null;
+ let rightDecRange: Range | null = null;
+ for (const decoration of decorations) {
+ const position = decoration.options.glyphMargin?.position ?? GlyphMarginLane.Left;
+
+ if (position === GlyphMarginLane.Left && (!leftDecRange || Range.compareRangesUsingEnds(leftDecRange, decoration.range) < 0)) {
+ // assign only if the range of `decoration` ends after, which means it has a higher chance to overlap with the other lane
+ leftDecRange = decoration.range;
+ }
+
+ if (position === GlyphMarginLane.Right && (!rightDecRange || Range.compareRangesUsingEnds(rightDecRange, decoration.range) < 0)) {
+ // assign only if the range of `decoration` ends after, which means it has a higher chance to overlap with the other lane
+ rightDecRange = decoration.range;
+ }
+
+ if (leftDecRange && rightDecRange) {
+
+ if (leftDecRange.endLineNumber < rightDecRange.startLineNumber) {
+ // there's no chance for `leftDecRange` to ever intersect something going further
+ leftDecRange = null;
+ continue;
+ }
+
+ if (rightDecRange.endLineNumber < leftDecRange.startLineNumber) {
+ // there's no chance for `rightDecRange` to ever intersect something going further
+ rightDecRange = null;
+ continue;
+ }
+
+ // leftDecRange and rightDecRange are intersecting or touching => we need two lanes
+ hasTwoLanes = true;
+ break;
+ }
+ }
+
+ this._configuration.setGlyphMarginDecorationLaneCount(hasTwoLanes ? 2 : 1);
+ }
+
this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e));
this._eventDispatcher.emitOutgoingEvent(new ModelDecorationsChangedEvent(e));
}));
diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts
index 56bda1f9ac9f8..09a1bb51898b2 100644
--- a/src/vs/editor/standalone/browser/standaloneEditor.ts
+++ b/src/vs/editor/standalone/browser/standaloneEditor.ts
@@ -560,6 +560,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
MouseTargetType: standaloneEnums.MouseTargetType,
OverlayWidgetPositionPreference: standaloneEnums.OverlayWidgetPositionPreference,
OverviewRulerLane: standaloneEnums.OverviewRulerLane,
+ GlyphMarginLane: standaloneEnums.GlyphMarginLane,
RenderLineNumbersType: standaloneEnums.RenderLineNumbersType,
RenderMinimap: standaloneEnums.RenderMinimap,
ScrollbarVisibility: standaloneEnums.ScrollbarVisibility,
diff --git a/src/vs/editor/test/browser/config/editorLayoutProvider.test.ts b/src/vs/editor/test/browser/config/editorLayoutProvider.test.ts
index b8c1d0d459276..87882eeb24b41 100644
--- a/src/vs/editor/test/browser/config/editorLayoutProvider.test.ts
+++ b/src/vs/editor/test/browser/config/editorLayoutProvider.test.ts
@@ -96,6 +96,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
typicalHalfwidthCharacterWidth: input.typicalHalfwidthCharacterWidth,
maxDigitWidth: input.maxDigitWidth,
pixelRatio: input.pixelRatio,
+ glyphMarginDecorationLaneCount: 1,
});
assert.deepStrictEqual(actual, expected);
}
@@ -127,6 +128,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -195,6 +197,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -263,6 +266,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -331,6 +335,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -399,6 +404,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -467,6 +473,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 50,
@@ -535,6 +542,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 50,
@@ -603,6 +611,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 60,
@@ -671,6 +680,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 30,
@@ -739,6 +749,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 30,
@@ -807,6 +818,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -875,6 +887,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -943,6 +956,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -1011,6 +1025,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 55,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 55,
lineNumbersWidth: 0,
@@ -1081,6 +1096,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -1151,6 +1167,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -1221,6 +1238,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -1291,6 +1309,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 0,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 0,
lineNumbersWidth: 0,
@@ -1359,6 +1378,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => {
glyphMarginLeft: 0,
glyphMarginWidth: 30,
+ glyphMarginDecorationLaneCount: 1,
lineNumbersLeft: 30,
lineNumbersWidth: 36,
diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts
index 68fa314066d3c..2ca90ed4e329c 100644
--- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts
+++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts
@@ -41,7 +41,7 @@ suite('Decoration Render Options', () => {
const styleSheet = s.globalStyleSheet;
s.registerDecorationType('test', 'example', options);
const sheet = readStyleSheet(styleSheet);
- assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0);
+ assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') no-repeat;background-size:contain;}`) >= 0);
assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0);
});
@@ -108,14 +108,14 @@ suite('Decoration Render Options', () => {
// URI, only minimal encoding
s.registerDecorationType('test', 'example', { gutterIconPath: URI.parse('data:image/svg+xml;base64,PHN2ZyB4b+') });
- assert(readStyleSheet(styleSheet).indexOf(`{background:url('data:image/svg+xml;base64,PHN2ZyB4b+') center center no-repeat;}`) > 0);
+ assert(readStyleSheet(styleSheet).indexOf(`{background:url('data:image/svg+xml;base64,PHN2ZyB4b+') no-repeat;}`) > 0);
s.removeDecorationType('example');
function assertBackground(url1: string, url2: string) {
const actual = readStyleSheet(styleSheet);
assert(
- actual.indexOf(`{background:url('${url1}') center center no-repeat;}`) > 0
- || actual.indexOf(`{background:url('${url2}') center center no-repeat;}`) > 0
+ actual.indexOf(`{background:url('${url1}') no-repeat;}`) > 0
+ || actual.indexOf(`{background:url('${url2}') no-repeat;}`) > 0
);
}
@@ -142,7 +142,7 @@ suite('Decoration Render Options', () => {
}
s.registerDecorationType('test', 'example', { gutterIconPath: URI.parse('http://test/pa\'th') });
- assert(readStyleSheet(styleSheet).indexOf(`{background:url('http://test/pa%27th') center center no-repeat;}`) > 0);
+ assert(readStyleSheet(styleSheet).indexOf(`{background:url('http://test/pa%27th') no-repeat;}`) > 0);
s.removeDecorationType('example');
});
});
diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts
index 6e8779a8bb536..24a7930ef5385 100644
--- a/src/vs/monaco.d.ts
+++ b/src/vs/monaco.d.ts
@@ -1529,6 +1529,14 @@ declare namespace monaco.editor {
Full = 7
}
+ /**
+ * Vertical Lane in the glyph margin of the editor.
+ */
+ export enum GlyphMarginLane {
+ Left = 1,
+ Right = 2
+ }
+
/**
* Position in the minimap to render the decoration.
*/
@@ -1550,6 +1558,13 @@ declare namespace monaco.editor {
darkColor?: string | ThemeColor;
}
+ export interface IModelDecorationGlyphMarginOptions {
+ /**
+ * The position in the glyph margin.
+ */
+ position: GlyphMarginLane;
+ }
+
/**
* Options for rendering a model decoration in the overview ruler.
*/
@@ -1561,11 +1576,11 @@ declare namespace monaco.editor {
}
/**
- * Options for rendering a model decoration in the overview ruler.
+ * Options for rendering a model decoration in the minimap.
*/
export interface IModelDecorationMinimapOptions extends IDecorationOptions {
/**
- * The position in the overview ruler.
+ * The position in the minimap.
*/
position: MinimapPosition;
}
@@ -1625,6 +1640,11 @@ declare namespace monaco.editor {
* If set, the decoration will be rendered in the glyph margin with this CSS class name.
*/
glyphMarginClassName?: string | null;
+ /**
+ * If set and the decoration has {@link glyphMarginClassName} set, render this decoration
+ * with the specified {@link IModelDecorationGlyphMarginOptions} in the glyph margin.
+ */
+ glyphMargin?: IModelDecorationGlyphMarginOptions | null;
/**
* If set, the decoration will be rendered in the lines decorations with this CSS class name.
*/
@@ -2951,6 +2971,7 @@ declare namespace monaco.editor {
export interface IModelDecorationsChangedEvent {
readonly affectsMinimap: boolean;
readonly affectsOverviewRuler: boolean;
+ readonly affectsGlyphMargin: boolean;
}
export interface IModelOptionsChangedEvent {
@@ -4038,6 +4059,10 @@ declare namespace monaco.editor {
* The width of the glyph margin.
*/
readonly glyphMarginWidth: number;
+ /**
+ * The number of decoration lanes to render in the glyph margin.
+ */
+ readonly glyphMarginDecorationLaneCount: number;
/**
* Left position for the line numbers.
*/
diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts
index 53e9e2fff5939..33dbb1b2c4dad 100644
--- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts
+++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts
@@ -26,7 +26,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IPosition } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { ILanguageService } from 'vs/editor/common/languages/language';
-import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
+import { GlyphMarginLane, IModelDecorationOptions, IModelDecorationOverviewRulerOptions, IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
import * as nls from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -54,6 +54,7 @@ interface IBreakpointDecoration {
const breakpointHelperDecoration: IModelDecorationOptions = {
description: 'breakpoint-helper-decoration',
glyphMarginClassName: ThemeIcon.asClassName(icons.debugBreakpointHint),
+ glyphMargin: { position: GlyphMarginLane.Right },
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
};
@@ -133,6 +134,7 @@ function getBreakpointDecorationOptions(accessor: ServicesAccessor, model: IText
const renderInline = breakpoint.column && (hasOtherBreakpointsOnLine || breakpoint.column > model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber));
return {
description: 'breakpoint-decoration',
+ glyphMargin: { position: GlyphMarginLane.Right },
glyphMarginClassName: ThemeIcon.asClassName(icon),
glyphMarginHoverMessage,
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,