5
5
6
6
import * as dom from 'vs/base/browser/dom' ;
7
7
import { Selection } from 'vs/editor/common/core/selection' ;
8
+ import { Range } from 'vs/editor/common/core/range' ;
8
9
import { FastDomNode , createFastDomNode } from 'vs/base/browser/fastDomNode' ;
9
10
import { onUnexpectedError } from 'vs/base/common/errors' ;
10
11
import { IDisposable } from 'vs/base/common/lifecycle' ;
11
12
import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler' ;
12
13
import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler' ;
13
14
import { 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' ;
15
16
import { ICommandDelegate , ViewController } from 'vs/editor/browser/view/viewController' ;
16
17
import { ViewUserInputEvents } from 'vs/editor/browser/view/viewUserInputEvents' ;
17
18
import { ContentViewOverlays , MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays' ;
@@ -20,7 +21,6 @@ import { ViewContentWidgets } from 'vs/editor/browser/viewParts/contentWidgets/c
20
21
import { CurrentLineHighlightOverlay , CurrentLineMarginHighlightOverlay } from 'vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight' ;
21
22
import { DecorationsOverlay } from 'vs/editor/browser/viewParts/decorations/decorations' ;
22
23
import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar' ;
23
- import { GlyphMarginOverlay } from 'vs/editor/browser/viewParts/glyphMargin/glyphMargin' ;
24
24
import { IndentGuidesOverlay } from 'vs/editor/browser/viewParts/indentGuides/indentGuides' ;
25
25
import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers' ;
26
26
import { ViewLines } from 'vs/editor/browser/viewParts/lines/viewLines' ;
@@ -52,6 +52,8 @@ import { BlockDecorations } from 'vs/editor/browser/viewParts/blockDecorations/b
52
52
import { inputLatency } from 'vs/base/browser/performance' ;
53
53
import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent' ;
54
54
import { 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' ;
55
57
56
58
57
59
export interface IContentWidgetData {
@@ -64,6 +66,11 @@ export interface IOverlayWidgetData {
64
66
position : IOverlayWidgetPosition | null ;
65
67
}
66
68
69
+ export interface IGlyphMarginWidgetData {
70
+ widget : IGlyphMarginWidget ;
71
+ position : IGlyphMarginWidgetPosition ;
72
+ }
73
+
67
74
export class View extends ViewEventHandler {
68
75
69
76
private readonly _scrollbar : EditorScrollbar ;
@@ -77,6 +84,7 @@ export class View extends ViewEventHandler {
77
84
private readonly _viewZones : ViewZones ;
78
85
private readonly _contentWidgets : ViewContentWidgets ;
79
86
private readonly _overlayWidgets : ViewOverlayWidgets ;
87
+ private readonly _glyphMarginWidgets : GlyphMarginWidgets ;
80
88
private readonly _viewCursors : ViewCursors ;
81
89
private readonly _viewParts : ViewPart [ ] ;
82
90
@@ -89,6 +97,7 @@ export class View extends ViewEventHandler {
89
97
private readonly _overflowGuardContainer : FastDomNode < HTMLElement > ;
90
98
91
99
// Actual mutable state
100
+ private _shouldRecomputeGlyphMarginLanes : boolean = false ;
92
101
private _renderAnimationFrame : IDisposable | null ;
93
102
94
103
constructor (
@@ -160,14 +169,18 @@ export class View extends ViewEventHandler {
160
169
const marginViewOverlays = new MarginViewOverlays ( this . _context ) ;
161
170
this . _viewParts . push ( marginViewOverlays ) ;
162
171
marginViewOverlays . addDynamicOverlay ( new CurrentLineMarginHighlightOverlay ( this . _context ) ) ;
163
- marginViewOverlays . addDynamicOverlay ( new GlyphMarginOverlay ( this . _context ) ) ;
164
172
marginViewOverlays . addDynamicOverlay ( new MarginViewLineDecorationsOverlay ( this . _context ) ) ;
165
173
marginViewOverlays . addDynamicOverlay ( new LinesDecorationsOverlay ( this . _context ) ) ;
166
174
marginViewOverlays . addDynamicOverlay ( new LineNumbersOverlay ( this . _context ) ) ;
167
175
176
+ // Glyph margin widgets
177
+ this . _glyphMarginWidgets = new GlyphMarginWidgets ( this . _context ) ;
178
+ this . _viewParts . push ( this . _glyphMarginWidgets ) ;
179
+
168
180
const margin = new Margin ( this . _context ) ;
169
181
margin . getDomNode ( ) . appendChild ( this . _viewZones . marginDomNode ) ;
170
182
margin . getDomNode ( ) . appendChild ( marginViewOverlays . getDomNode ( ) ) ;
183
+ margin . getDomNode ( ) . appendChild ( this . _glyphMarginWidgets . domNode ) ;
171
184
this . _viewParts . push ( margin ) ;
172
185
173
186
// Content widgets
@@ -226,10 +239,70 @@ export class View extends ViewEventHandler {
226
239
}
227
240
228
241
private _flushAccumulatedAndRenderNow ( ) : void {
242
+ if ( this . _shouldRecomputeGlyphMarginLanes ) {
243
+ this . _shouldRecomputeGlyphMarginLanes = false ;
244
+ this . _context . configuration . setGlyphMarginDecorationLaneCount ( this . _computeGlyphMarginLaneCount ( ) ) ;
245
+ }
229
246
inputLatency . onRenderStart ( ) ;
230
247
this . _renderNow ( ) ;
231
248
}
232
249
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
+
233
306
private _createPointerHandlerHelper ( ) : IPointerHandlerHelper {
234
307
return {
235
308
viewDomNode : this . domNode . domNode ,
@@ -317,6 +390,12 @@ export class View extends ViewEventHandler {
317
390
this . _selections = e . selections ;
318
391
return false ;
319
392
}
393
+ public override onDecorationsChanged ( e : viewEvents . ViewDecorationsChangedEvent ) : boolean {
394
+ if ( e . affectsGlyphMargin ) {
395
+ this . _shouldRecomputeGlyphMarginLanes = true ;
396
+ }
397
+ return false ;
398
+ }
320
399
public override onFocusChanged ( e : viewEvents . ViewFocusChangedEvent ) : boolean {
321
400
this . domNode . setClassName ( this . _getEditorClassName ( ) ) ;
322
401
return false ;
@@ -548,6 +627,27 @@ export class View extends ViewEventHandler {
548
627
this . _scheduleRender ( ) ;
549
628
}
550
629
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
+
551
651
// --- END CodeEditor helpers
552
652
553
653
}
0 commit comments