mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-02 06:21:50 +01:00
Support dedicated lane for breakpoint decorations (#180013)
* Add fast path for margin decoration access * Support glyph margin decoration lanes * Do not center extension-contributed decorations * Show breakpoints in dedicated right lane * Only recompute lane count if glyph margin was affected * Add explicit type for className / zIndex pair and fix problems in other consumers of `DedupOverlay` * Avoid arrays of arrays * Figure out if two lanes are needed in O(N) --------- Co-authored-by: Alex Dima <alexdima@microsoft.com>
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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}\';',
|
||||
|
||||
@@ -16,5 +16,4 @@
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -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;"></div>';
|
||||
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] = (
|
||||
'<div class="cgmr codicon '
|
||||
+ renderInfo[0][0]
|
||||
+ common
|
||||
);
|
||||
let css = '';
|
||||
for (let lane = 1; lane <= this._glyphMarginDecorationLaneCount; lane += 1) {
|
||||
const decorations = renderInfo.getLaneDecorations(lane);
|
||||
if (decorations.length === 0) {
|
||||
continue;
|
||||
}
|
||||
decorations.sort((a, b) => {
|
||||
// Sort decorations to render in descending order by zIndex
|
||||
return b.zIndex - a.zIndex;
|
||||
});
|
||||
const winningDecoration: RenderedDecoration = decorations[0];
|
||||
const left = (this._glyphMarginLeft + (lane - 1) * this._lineHeight).toString();
|
||||
css += (
|
||||
'<div class="cgmr codicon '
|
||||
+ winningDecoration.className // TODO@joyceerhl Implement overflow for remaining decorations
|
||||
+ common
|
||||
+ 'left:' + left + 'px;"></div>'
|
||||
);
|
||||
}
|
||||
output[lineIndex] = css;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 += '<div class="cldr ' + classNames[i][0] + common;
|
||||
for (const decoration of decorations) {
|
||||
lineOutput += '<div class="cldr ' + decoration.className + common;
|
||||
}
|
||||
output[lineIndex] = lineOutput;
|
||||
}
|
||||
|
||||
@@ -73,15 +73,15 @@ export class MarginViewLineDecorationsOverlay 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 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 += '<div class="cmdr ' + classNames[i][0] + '" style=""></div>';
|
||||
for (const decoration of decorations) {
|
||||
lineOutput += '<div class="cmdr ' + decoration.className + '" style=""></div>';
|
||||
}
|
||||
output[lineIndex] = lineOutput;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user