55
66import * as dom from 'vs/base/browser/dom' ;
77import { Selection } from 'vs/editor/common/core/selection' ;
8+ import { Range } from 'vs/editor/common/core/range' ;
89import { FastDomNode , createFastDomNode } from 'vs/base/browser/fastDomNode' ;
910import { onUnexpectedError } from 'vs/base/common/errors' ;
1011import { IDisposable } from 'vs/base/common/lifecycle' ;
1112import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler' ;
1213import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler' ;
1314import { IVisibleRangeProvider , TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler' ;
14- import { IContentWidget , IContentWidgetPosition , IOverlayWidget , IOverlayWidgetPosition , IMouseTarget , IViewZoneChangeAccessor , IEditorAriaOptions } from 'vs/editor/browser/editorBrowser' ;
15+ import { IContentWidget , IContentWidgetPosition , IOverlayWidget , IOverlayWidgetPosition , IMouseTarget , IViewZoneChangeAccessor , IEditorAriaOptions , IGlyphMarginWidget , IGlyphMarginWidgetPosition } from 'vs/editor/browser/editorBrowser' ;
1516import { ICommandDelegate , ViewController } from 'vs/editor/browser/view/viewController' ;
1617import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents' ;
1718import { ContentViewOverlays , MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays' ;
@@ -20,7 +21,6 @@ import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/c
2021import { CurrentLineHighlightOverlay , CurrentLineMarginHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight' ;
2122import { DecorationsOverlay } from 'vs/editor/browser/viewParts/decorations/decorations' ;
2223import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar' ;
23- import { GlyphMarginOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin' ;
2424import { IndentGuidesOverlay } from 'vs/editor/browser/viewParts/indentGuides/indentGuides' ;
2525import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers' ;
2626import { ViewLines } from 'vs/editor/browser/viewParts/lines/viewLines' ;
@@ -52,6 +52,8 @@ import { BlockDecorations } from 'vs/editor/browser/viewParts/blockDecorations/b
5252import { inputLatency } from 'vs/base/browser/performance' ;
5353import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent' ;
5454import { WhitespaceOverlay } from 'vs/editor/browser/viewParts/whitespace/whitespace' ;
55+ import { GlyphMarginWidgets } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin' ;
56+ import { GlyphMarginLane } from 'vs/editor/common/model' ;
5557
5658
5759export interface IContentWidgetData {
@@ -64,6 +66,11 @@ export interface IOverlayWidgetData {
6466 position : IOverlayWidgetPosition | null ;
6567}
6668
69+ export interface IGlyphMarginWidgetData {
70+ widget : IGlyphMarginWidget ;
71+ position : IGlyphMarginWidgetPosition ;
72+ }
73+
6774export class View extends ViewEventHandler {
6875
6976 private readonly _scrollbar : EditorScrollbar ;
@@ -77,6 +84,7 @@ export class View extends ViewEventHandler {
7784 private readonly _viewZones : ViewZones ;
7885 private readonly _contentWidgets : ViewContentWidgets ;
7986 private readonly _overlayWidgets : ViewOverlayWidgets ;
87+ private readonly _glyphMarginWidgets : GlyphMarginWidgets ;
8088 private readonly _viewCursors : ViewCursors ;
8189 private readonly _viewParts : ViewPart [ ] ;
8290
@@ -89,6 +97,7 @@ export class View extends ViewEventHandler {
8997 private readonly _overflowGuardContainer : FastDomNode < HTMLElement > ;
9098
9199 // Actual mutable state
100+ private _shouldRecomputeGlyphMarginLanes : boolean = false ;
92101 private _renderAnimationFrame : IDisposable | null ;
93102
94103 constructor (
@@ -160,14 +169,18 @@ export class View extends ViewEventHandler {
160169 const marginViewOverlays = new MarginViewOverlays ( this . _context ) ;
161170 this . _viewParts . push ( marginViewOverlays ) ;
162171 marginViewOverlays . addDynamicOverlay ( new CurrentLineMarginHighlightOverlay ( this . _context ) ) ;
163- marginViewOverlays . addDynamicOverlay ( new GlyphMarginOverlay ( this . _context ) ) ;
164172 marginViewOverlays . addDynamicOverlay ( new MarginViewLineDecorationsOverlay ( this . _context ) ) ;
165173 marginViewOverlays . addDynamicOverlay ( new LinesDecorationsOverlay ( this . _context ) ) ;
166174 marginViewOverlays . addDynamicOverlay ( new LineNumbersOverlay ( this . _context ) ) ;
167175
176+ // Glyph margin widgets
177+ this . _glyphMarginWidgets = new GlyphMarginWidgets ( this . _context ) ;
178+ this . _viewParts . push ( this . _glyphMarginWidgets ) ;
179+
168180 const margin = new Margin ( this . _context ) ;
169181 margin . getDomNode ( ) . appendChild ( this . _viewZones . marginDomNode ) ;
170182 margin . getDomNode ( ) . appendChild ( marginViewOverlays . getDomNode ( ) ) ;
183+ margin . getDomNode ( ) . appendChild ( this . _glyphMarginWidgets . domNode ) ;
171184 this . _viewParts . push ( margin ) ;
172185
173186 // Content widgets
@@ -226,10 +239,70 @@ export class View extends ViewEventHandler {
226239 }
227240
228241 private _flushAccumulatedAndRenderNow ( ) : void {
242+ if ( this . _shouldRecomputeGlyphMarginLanes ) {
243+ this . _shouldRecomputeGlyphMarginLanes = false ;
244+ this . _context . configuration . setGlyphMarginDecorationLaneCount ( this . _computeGlyphMarginLaneCount ( ) ) ;
245+ }
229246 inputLatency . onRenderStart ( ) ;
230247 this . _renderNow ( ) ;
231248 }
232249
250+ private _computeGlyphMarginLaneCount ( ) : number {
251+ const model = this . _context . viewModel . model ;
252+ type Glyph = { range : Range ; lane : GlyphMarginLane } ;
253+ let glyphs : Glyph [ ] = [ ] ;
254+
255+ // Add all margin decorations
256+ glyphs = glyphs . concat ( model . getAllMarginDecorations ( ) . map ( ( decoration ) => {
257+ const lane = decoration . options . glyphMargin ?. position ?? GlyphMarginLane . Left ;
258+ return { range : decoration . range , lane } ;
259+ } ) ) ;
260+
261+ // Add all glyph margin widgets
262+ glyphs = glyphs . concat ( this . _glyphMarginWidgets . getWidgets ( ) . map ( ( widget ) => {
263+ const range = model . validateRange ( widget . preference . range ) ;
264+ return { range, lane : widget . preference . lane } ;
265+ } ) ) ;
266+
267+ // Sorted by their start position
268+ glyphs . sort ( ( a , b ) => Range . compareRangesUsingStarts ( a . range , b . range ) ) ;
269+
270+ let leftDecRange : Range | null = null ;
271+ let rightDecRange : Range | null = null ;
272+ for ( const decoration of glyphs ) {
273+
274+ if ( decoration . lane === GlyphMarginLane . Left && ( ! leftDecRange || Range . compareRangesUsingEnds ( leftDecRange , decoration . range ) < 0 ) ) {
275+ // assign only if the range of `decoration` ends after, which means it has a higher chance to overlap with the other lane
276+ leftDecRange = decoration . range ;
277+ }
278+
279+ if ( decoration . lane === GlyphMarginLane . Right && ( ! rightDecRange || Range . compareRangesUsingEnds ( rightDecRange , decoration . range ) < 0 ) ) {
280+ // assign only if the range of `decoration` ends after, which means it has a higher chance to overlap with the other lane
281+ rightDecRange = decoration . range ;
282+ }
283+
284+ if ( leftDecRange && rightDecRange ) {
285+
286+ if ( leftDecRange . endLineNumber < rightDecRange . startLineNumber ) {
287+ // there's no chance for `leftDecRange` to ever intersect something going further
288+ leftDecRange = null ;
289+ continue ;
290+ }
291+
292+ if ( rightDecRange . endLineNumber < leftDecRange . startLineNumber ) {
293+ // there's no chance for `rightDecRange` to ever intersect something going further
294+ rightDecRange = null ;
295+ continue ;
296+ }
297+
298+ // leftDecRange and rightDecRange are intersecting or touching => we need two lanes
299+ return 2 ;
300+ }
301+ }
302+
303+ return 1 ;
304+ }
305+
233306 private _createPointerHandlerHelper ( ) : IPointerHandlerHelper {
234307 return {
235308 viewDomNode : this . domNode . domNode ,
@@ -317,6 +390,12 @@ export class View extends ViewEventHandler {
317390 this . _selections = e . selections ;
318391 return false ;
319392 }
393+ public override onDecorationsChanged ( e : viewEvents . ViewDecorationsChangedEvent ) : boolean {
394+ if ( e . affectsGlyphMargin ) {
395+ this . _shouldRecomputeGlyphMarginLanes = true ;
396+ }
397+ return false ;
398+ }
320399 public override onFocusChanged ( e : viewEvents . ViewFocusChangedEvent ) : boolean {
321400 this . domNode . setClassName ( this . _getEditorClassName ( ) ) ;
322401 return false ;
@@ -548,6 +627,27 @@ export class View extends ViewEventHandler {
548627 this . _scheduleRender ( ) ;
549628 }
550629
630+ public addGlyphMarginWidget ( widgetData : IGlyphMarginWidgetData ) : void {
631+ this . _glyphMarginWidgets . addWidget ( widgetData . widget ) ;
632+ this . _shouldRecomputeGlyphMarginLanes = true ;
633+ this . _scheduleRender ( ) ;
634+ }
635+
636+ public layoutGlyphMarginWidget ( widgetData : IGlyphMarginWidgetData ) : void {
637+ const newPreference = widgetData . position ;
638+ const shouldRender = this . _glyphMarginWidgets . setWidgetPosition ( widgetData . widget , newPreference ) ;
639+ if ( shouldRender ) {
640+ this . _shouldRecomputeGlyphMarginLanes = true ;
641+ this . _scheduleRender ( ) ;
642+ }
643+ }
644+
645+ public removeGlyphMarginWidget ( widgetData : IGlyphMarginWidgetData ) : void {
646+ this . _glyphMarginWidgets . removeWidget ( widgetData . widget ) ;
647+ this . _shouldRecomputeGlyphMarginLanes = true ;
648+ this . _scheduleRender ( ) ;
649+ }
650+
551651 // --- END CodeEditor helpers
552652
553653}
0 commit comments