diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 48707de51cb465e90c13bea3bb1b9bd25fe2d428..5f1f7ab106732fd4a2fc98f41d16aac5af2738f2 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -30,7 +30,9 @@ import { GlyphMarginOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyp import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; import { IndentGuidesOverlay } from 'vs/editor/browser/viewParts/indentGuides/indentGuides'; import { ViewLines } from 'vs/editor/browser/viewParts/lines/viewLines'; +import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; import { LinesDecorationsOverlay } from 'vs/editor/browser/viewParts/linesDecorations/linesDecorations'; +import { MarginViewLineDecorationsOverlay } from 'vs/editor/browser/viewParts/marginDecorations/marginDecorations'; import { ViewOverlayWidgets } from 'vs/editor/browser/viewParts/overlayWidgets/overlayWidgets'; import { DecorationsOverviewRuler } from 'vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler'; import { OverviewRuler } from 'vs/editor/browser/viewParts/overviewRuler/overviewRuler'; @@ -237,9 +239,14 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp let marginViewOverlays = new MarginViewOverlays(this._context, this.layoutProvider); this.viewParts.push(marginViewOverlays); marginViewOverlays.addDynamicOverlay(new GlyphMarginOverlay(this._context)); + marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new LinesDecorationsOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context)); + let margin = new Margin(this._context, this.layoutProvider); + margin.domNode.appendChild(this.viewZones.marginDomNode); + margin.domNode.appendChild(marginViewOverlays.getDomNode()); + this.viewParts.push(margin); // Content widgets this.contentWidgets = new ViewContentWidgets(this._context, this.domNode); @@ -271,8 +278,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp this.linesContent.appendChild(this.viewLines.getDomNode()); this.linesContent.appendChild(this.contentWidgets.domNode); this.linesContent.appendChild(this.viewCursors.getDomNode()); - this.overflowGuardContainer.appendChild(marginViewOverlays.getDomNode()); - this.overflowGuardContainer.appendChild(this.viewZones.marginDomNode); + this.overflowGuardContainer.appendChild(margin.domNode); this.overflowGuardContainer.appendChild(this.linesContentContainer); this.overflowGuardContainer.appendChild(scrollDecoration.getDomNode()); this.overflowGuardContainer.appendChild(this.overlayWidgets.domNode); diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index 2d7bb97988d71af1368c15feed767b23095e483c..5834fc592fd41615396d91877a7f924d4c149101 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -233,7 +233,7 @@ export class MarginViewOverlays extends ViewOverlays { this._contentLeft = context.configuration.editor.layoutInfo.contentLeft; this._canUseTranslate3d = context.configuration.editor.viewInfo.canUseTranslate3d; - this.domNode.setClassName(editorBrowser.ClassNames.MARGIN_VIEW_OVERLAYS + ' monaco-editor-background'); + this.domNode.setClassName(editorBrowser.ClassNames.MARGIN_VIEW_OVERLAYS); this.domNode.setWidth(1); Configuration.applyFontInfo(this.domNode, this._context.configuration.editor.fontInfo); @@ -283,14 +283,6 @@ export class MarginViewOverlays extends ViewOverlays { _viewOverlaysRender(ctx: IRestrictedRenderingContext): void { super._viewOverlaysRender(ctx); - if (this._canUseTranslate3d) { - let transform = 'translate3d(0px, ' + ctx.linesViewportData.visibleRangesDeltaTop + 'px, 0px)'; - this.domNode.setTransform(transform); - this.domNode.setTop(0); - } else { - this.domNode.setTransform(''); - this.domNode.setTop(ctx.linesViewportData.visibleRangesDeltaTop); - } let height = Math.min(this._layoutProvider.getTotalHeight(), 1000000); this.domNode.setHeight(height); this.domNode.setWidth(this._contentLeft); diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css index 3339c59acd3fb0592058f55cd6dd6829bf73261c..474a00605c1cfbed637efb8859f640af70ce2f3c 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.css @@ -10,6 +10,7 @@ vertical-align: middle; box-sizing: border-box; cursor: default; + height: 100%; } .monaco-editor .relative-current-line-number { diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts index d3dfaae05f972b0c2fc2334a8f1770cf757ebbd0..f86cffc5ab7c2e6bbc76cae9f27340e9c7ada2af 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts @@ -97,20 +97,15 @@ export class LineNumbersOverlay extends DynamicViewOverlay { // --- end event handlers public prepareRender(ctx: IRenderingContext): void { - if (!this.shouldRender()) { - throw new Error('I did not ask to render!'); - } - if (!this._renderLineNumbers) { this._renderResult = null; return; } let lineHeightClassName = (platform.isLinux ? (this._lineHeight % 2 === 0 ? ' lh-even' : ' lh-odd') : ''); - let lineHeight = this._lineHeight.toString(); let visibleStartLineNumber = ctx.visibleRange.startLineNumber; let visibleEndLineNumber = ctx.visibleRange.endLineNumber; - let common = '
'; + let common = '
'; let output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css index c0a5f0d205aed172cc46eb4e71bc8bc17907dc3a..452d83eeb8b7a106584573b8e352fe4305e07aac 100644 --- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.css @@ -14,4 +14,5 @@ */ .monaco-editor .margin-view-overlays .cldr { position: absolute; + height: 100%; } \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts index 1308f188f6daaa82a5fcc61f51c442fbff14c5ef..3517c9f31b42f40ce38a0447fb82de886c237b3a 100644 --- a/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts +++ b/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts @@ -14,7 +14,6 @@ import { IRenderingContext } from 'vs/editor/common/view/renderingContext'; export class LinesDecorationsOverlay extends DedupOverlay { private _context: ViewContext; - private _lineHeight: number; private _decorationsLeft: number; private _decorationsWidth: number; @@ -23,7 +22,6 @@ export class LinesDecorationsOverlay extends DedupOverlay { constructor(context: ViewContext) { super(); this._context = context; - this._lineHeight = this._context.configuration.editor.lineHeight; this._decorationsLeft = 0; this._decorationsWidth = 0; this._renderResult = null; @@ -63,9 +61,6 @@ export class LinesDecorationsOverlay extends DedupOverlay { return false; } public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean { - if (e.lineHeight) { - this._lineHeight = this._context.configuration.editor.lineHeight; - } return true; } public onLayoutChanged(layoutInfo: editorCommon.EditorLayoutInfo): boolean { @@ -95,18 +90,13 @@ export class LinesDecorationsOverlay extends DedupOverlay { } public prepareRender(ctx: IRenderingContext): void { - if (!this.shouldRender()) { - throw new Error('I did not ask to render!'); - } - let visibleStartLineNumber = ctx.visibleRange.startLineNumber; let visibleEndLineNumber = ctx.visibleRange.endLineNumber; let toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx)); - let lineHeight = this._lineHeight.toString(); let left = this._decorationsLeft.toString(); let width = this._decorationsWidth.toString(); - let common = '" style="left:' + left + 'px;width:' + width + 'px' + ';height:' + lineHeight + 'px;">
'; + let common = '" style="left:' + left + 'px;width:' + width + 'px;">
'; let output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { @@ -126,10 +116,6 @@ export class LinesDecorationsOverlay extends DedupOverlay { if (!this._renderResult) { return ''; } - let lineIndex = lineNumber - startLineNumber; - if (lineIndex < 0 || lineIndex >= this._renderResult.length) { - throw new Error('Unexpected render request'); - } - return this._renderResult[lineIndex]; + return this._renderResult[lineNumber - startLineNumber]; } } \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/margin/margin.ts b/src/vs/editor/browser/viewParts/margin/margin.ts new file mode 100644 index 0000000000000000000000000000000000000000..77a0f5c9b2c912d308aea6d6dd1aa712fc4decdc --- /dev/null +++ b/src/vs/editor/browser/viewParts/margin/margin.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { StyleMutator } from 'vs/base/browser/styleMutator'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { ViewPart } from 'vs/editor/browser/view/viewPart'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; +import { ILayoutProvider } from 'vs/editor/browser/viewLayout/layoutProvider'; + +export class Margin extends ViewPart { + public domNode: HTMLElement; + private _layoutProvider: ILayoutProvider; + private _canUseTranslate3d: boolean; + private _height: number; + private _contentLeft: number; + + constructor(context: ViewContext, layoutProvider: ILayoutProvider) { + super(context); + this._layoutProvider = layoutProvider; + this._canUseTranslate3d = this._context.configuration.editor.viewInfo.canUseTranslate3d; + this.domNode = this._createDomNode(); + this._height = this._context.configuration.editor.layoutInfo.contentHeight; + this._contentLeft = this._context.configuration.editor.layoutInfo.contentLeft; + } + + public dispose(): void { + super.dispose(); + } + + public _createDomNode(): HTMLElement { + let domNode = document.createElement('div'); + domNode.className = 'margin monaco-editor-background'; + domNode.style.position = 'absolute'; + domNode.setAttribute('role', 'presentation'); + domNode.setAttribute('aria-hidden', 'true'); + return domNode; + } + + // --- begin event handlers + + public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean { + if (e.viewInfo.canUseTranslate3d) { + this._canUseTranslate3d = this._context.configuration.editor.viewInfo.canUseTranslate3d; + } + + return super.onConfigurationChanged(e); + } + + public onScrollChanged(e: editorCommon.IScrollEvent): boolean { + return super.onScrollChanged(e) || e.scrollTopChanged; + } + + public onLayoutChanged(layoutInfo: editorCommon.EditorLayoutInfo): boolean { + this._contentLeft = layoutInfo.contentLeft; + return super.onLayoutChanged(layoutInfo) || true; + } + + // --- end event handlers + + public prepareRender(ctx: IRenderingContext): void { + // Nothing to read + } + + public render(ctx: IRestrictedRenderingContext): void { + if (this._canUseTranslate3d) { + let transform = 'translate3d(0px, ' + ctx.linesViewportData.visibleRangesDeltaTop + 'px, 0px)'; + StyleMutator.setTransform(this.domNode, transform); + StyleMutator.setTop(this.domNode, 0); + } else { + StyleMutator.setTransform(this.domNode, ''); + StyleMutator.setTop(this.domNode, ctx.linesViewportData.visibleRangesDeltaTop); + } + + let height = Math.min(this._layoutProvider.getTotalHeight(), 1000000); + StyleMutator.setHeight(this.domNode, height); + StyleMutator.setWidth(this.domNode, this._contentLeft); + } +} diff --git a/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.css b/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.css new file mode 100644 index 0000000000000000000000000000000000000000..7bd8f89ab0ea7616a1368bef8d56a274ea923ad4 --- /dev/null +++ b/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.css @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* + Keeping name short for faster parsing. + cmdr = core margin decorations rendering (div) +*/ +.monaco-editor .margin-view-overlays .cmdr { + position: absolute; + left: 0; + width: 100%; + height: 100%; +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts b/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts new file mode 100644 index 0000000000000000000000000000000000000000..6ea01669a601be048348c7d12a650687b1c3d760 --- /dev/null +++ b/src/vs/editor/browser/viewParts/marginDecorations/marginDecorations.ts @@ -0,0 +1,109 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import 'vs/css!./marginDecorations'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { DecorationToRender, DedupOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin'; +import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { IRenderingContext } from 'vs/editor/common/view/renderingContext'; + +export class MarginViewLineDecorationsOverlay extends DedupOverlay { + private _context: ViewContext; + private _renderResult: string[]; + + constructor(context: ViewContext) { + super(); + this._context = context; + this._renderResult = null; + this._context.addEventHandler(this); + } + + public dispose(): void { + this._context.removeEventHandler(this); + this._context = null; + this._renderResult = null; + } + + // --- begin event handlers + + public onModelFlushed(): boolean { + return true; + } + public onModelDecorationsChanged(e: editorCommon.IViewDecorationsChangedEvent): boolean { + return true; + } + public onModelLinesDeleted(e: editorCommon.IViewLinesDeletedEvent): boolean { + return true; + } + public onModelLineChanged(e: editorCommon.IViewLineChangedEvent): boolean { + return true; + } + public onModelLinesInserted(e: editorCommon.IViewLinesInsertedEvent): boolean { + return true; + } + public onCursorPositionChanged(e: editorCommon.IViewCursorPositionChangedEvent): boolean { + return false; + } + public onCursorSelectionChanged(e: editorCommon.IViewCursorSelectionChangedEvent): boolean { + return false; + } + public onCursorRevealRange(e: editorCommon.IViewRevealRangeEvent): boolean { + return false; + } + public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean { + return true; + } + public onLayoutChanged(layoutInfo: editorCommon.EditorLayoutInfo): boolean { + return true; + } + public onScrollChanged(e: editorCommon.IScrollEvent): boolean { + return e.scrollTopChanged; + } + public onZonesChanged(): boolean { + return true; + } + + // --- end event handlers + + protected _getDecorations(ctx: IRenderingContext): DecorationToRender[] { + let decorations = ctx.getDecorationsInViewport(); + let r: DecorationToRender[] = []; + for (let i = 0, len = decorations.length; i < len; i++) { + let d = decorations[i]; + if (d.options.marginClassName) { + r.push(new DecorationToRender(d.range.startLineNumber, d.range.endLineNumber, d.options.marginClassName)); + } + } + return r; + } + + public prepareRender(ctx: IRenderingContext): void { + let visibleStartLineNumber = ctx.visibleRange.startLineNumber; + let visibleEndLineNumber = ctx.visibleRange.endLineNumber; + let toRender = this._render(visibleStartLineNumber, visibleEndLineNumber, this._getDecorations(ctx)); + + let output: string[] = []; + for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + let lineIndex = lineNumber - visibleStartLineNumber; + let classNames = toRender[lineIndex]; + let lineOutput = ''; + for (let i = 0, len = classNames.length; i < len; i++) { + lineOutput += '
'; + } + output[lineIndex] = lineOutput; + } + + this._renderResult = output; + } + + public render(startLineNumber: number, lineNumber: number): string { + if (!this._renderResult) { + return ''; + } + return this._renderResult[lineNumber - startLineNumber]; + } +} \ No newline at end of file diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index d79a83ccc488cf51e3fcc5b71c76bb28c6e94f1a..1fb668ff8915036c02f141a13b07b0107d7dfc84 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -344,7 +344,7 @@ export class ViewZones extends ViewPart { StyleMutator.setDisplay(zone.delegate.domNode, newDisplay); if (zone.delegate.marginDomNode) { - StyleMutator.setTop(zone.delegate.marginDomNode, newTop - ctx.viewportTop); + StyleMutator.setTop(zone.delegate.marginDomNode, newTop); StyleMutator.setHeight(zone.delegate.marginDomNode, newHeight); StyleMutator.setDisplay(zone.delegate.marginDomNode, newDisplay); } diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 1b041b7332192640ed877b78f6a3bf5c3418c76d..483fe54e7e5da82ae0796db98534af22baf58c54 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -1150,6 +1150,10 @@ export interface IModelDecorationOptions { * If set, the decoration will be rendered in the lines decorations with this CSS class name. */ linesDecorationsClassName?: string; + /** + * If set, the decoration will be rendered in the margin (covering its full width) with this CSS class name. + */ + marginClassName?: string; /** * If set, the decoration will be rendered inline with the text with this CSS class name. * Please use this only for CSS rules that must impact the text. For example, use `className` diff --git a/src/vs/editor/common/model/textModelWithDecorations.ts b/src/vs/editor/common/model/textModelWithDecorations.ts index fc5515fa54d11e8e8b90d9b7fc5405bb35533ed6..f42af47cf09ada0adb4f42cede0af4f671b6458e 100644 --- a/src/vs/editor/common/model/textModelWithDecorations.ts +++ b/src/vs/editor/common/model/textModelWithDecorations.ts @@ -654,6 +654,7 @@ class ModelDecorationOptions implements editorCommon.IModelDecorationOptions { overviewRuler: editorCommon.IModelDecorationOverviewRulerOptions; glyphMarginClassName: string; linesDecorationsClassName: string; + marginClassName: string; inlineClassName: string; beforeContentClassName: string; afterContentClassName: string; @@ -667,6 +668,7 @@ class ModelDecorationOptions implements editorCommon.IModelDecorationOptions { this.overviewRuler = _normalizeOverviewRulerOptions(options.overviewRuler, options.showInOverviewRuler); this.glyphMarginClassName = cleanClassName(options.glyphMarginClassName || strings.empty); this.linesDecorationsClassName = cleanClassName(options.linesDecorationsClassName || strings.empty); + this.marginClassName = cleanClassName(options.marginClassName || strings.empty); this.inlineClassName = cleanClassName(options.inlineClassName || strings.empty); this.beforeContentClassName = cleanClassName(options.beforeContentClassName || strings.empty); this.afterContentClassName = cleanClassName(options.afterContentClassName || strings.empty); @@ -689,6 +691,7 @@ class ModelDecorationOptions implements editorCommon.IModelDecorationOptions { && this.showInOverviewRuler === other.showInOverviewRuler && this.glyphMarginClassName === other.glyphMarginClassName && this.linesDecorationsClassName === other.linesDecorationsClassName + && this.marginClassName === other.marginClassName && this.inlineClassName === other.inlineClassName && this.beforeContentClassName === other.beforeContentClassName && this.afterContentClassName === other.afterContentClassName diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index aa95e16b7d69abe2327f7b73d29d7a350aeb807e..1ed20aabbfa43fb09ac3bcdb5ff41a5955ffc0f7 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1581,6 +1581,10 @@ declare module monaco.editor { * If set, the decoration will be rendered in the lines decorations with this CSS class name. */ linesDecorationsClassName?: string; + /** + * If set, the decoration will be rendered in the margin (covering its full width) with this CSS class name. + */ + marginClassName?: string; /** * If set, the decoration will be rendered inline with the text with this CSS class name. * Please use this only for CSS rules that must impact the text. For example, use `className`