2
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
- import { Disposable , DisposableStore , IDisposable } from 'vs/base/common/lifecycle' ;
6
- import { IActiveCodeEditor , ICodeEditor , IOverlayWidget , IOverlayWidgetPosition , MouseTargetType } from 'vs/editor/browser/editorBrowser' ;
5
+ import { Disposable , DisposableStore , IDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
6
+ import { IActiveCodeEditor , ICodeEditor , IOverlayWidget , IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser' ;
7
7
import * as dom from 'vs/base/browser/dom' ;
8
8
import { EditorLayoutInfo , EditorOption , RenderLineNumbersType } from 'vs/editor/common/config/editorOptions' ;
9
9
import { createStringBuilder } from 'vs/editor/common/core/stringBuilder' ;
10
10
import { RenderLineInput , renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer' ;
11
11
import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations' ;
12
12
import { Position } from 'vs/editor/common/core/position' ;
13
- import 'vs/css!./stickyScroll' ;
14
13
import { ClickLinkGesture } from 'vs/editor/contrib/gotoSymbol/browser/link/clickLinkGesture' ;
15
14
import { getDefinitionsAtPosition } from 'vs/editor/contrib/gotoSymbol/browser/goToSymbol' ;
16
15
import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures' ;
17
16
import { Location } from 'vs/editor/common/languages' ;
18
17
import { goToDefinitionWithLocation } from 'vs/editor/contrib/inlayHints/browser/inlayHintsLocations' ;
19
18
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation' ;
20
- import { CancellationTokenSource , } from 'vs/base/common/cancellation' ;
19
+ import { CancellationTokenSource } from 'vs/base/common/cancellation' ;
21
20
import { IRange } from 'vs/editor/common/core/range' ;
21
+ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent' ;
22
+ import 'vs/css!./stickyScroll' ;
23
+
24
+ interface CustomMouseEvent {
25
+ detail : string ;
26
+ element : HTMLElement ;
27
+ }
22
28
23
29
export class StickyScrollWidgetState {
24
30
constructor (
@@ -37,10 +43,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
37
43
private lineHeight : number ;
38
44
private lineNumbers : number [ ] ;
39
45
private lastLineRelativePosition : number ;
46
+
40
47
private hoverOnLine : number ;
41
- private positionOfDefinitionToJumpTo : Location ;
42
- private lastChildDecorated : HTMLElement | undefined ;
43
- private stickyPositionProjectedOnEditor : Position ;
48
+ private hoverOnColumn : number ;
49
+ private stickyRangeProjectedOnEditor : IRange ;
44
50
45
51
constructor (
46
52
private readonly _editor : ICodeEditor ,
@@ -57,9 +63,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
57
63
this . lastLineRelativePosition = 0 ;
58
64
59
65
this . hoverOnLine = - 1 ;
60
- this . positionOfDefinitionToJumpTo = { uri : { } , range : { } } as Location ;
61
- this . lastChildDecorated = undefined ;
62
- this . stickyPositionProjectedOnEditor = new Position ( - 1 , - 1 ) ;
66
+ this . hoverOnColumn = - 1 ;
67
+ this . stickyRangeProjectedOnEditor = { } as IRange ;
63
68
64
69
this . lineHeight = this . _editor . getOption ( EditorOption . lineHeight ) ;
65
70
this . _register ( this . _editor . onDidChangeConfiguration ( e => {
@@ -71,69 +76,76 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
71
76
}
72
77
73
78
private updateLinkGesture ( ) : IDisposable {
79
+
80
+
74
81
const linkGestureStore = new DisposableStore ( ) ;
75
- const gesture = linkGestureStore . add ( new ClickLinkGesture ( this . _editor , true ) ) ;
76
- const cancellationToken = new CancellationTokenSource ( ) ;
77
82
const sessionStore = new DisposableStore ( ) ;
78
- sessionStore . add ( cancellationToken ) ;
79
83
linkGestureStore . add ( sessionStore ) ;
84
+ const gesture = new ClickLinkGesture ( this . _editor , true ) ;
85
+ linkGestureStore . add ( gesture ) ;
86
+
80
87
linkGestureStore . add ( gesture . onMouseMoveOrRelevantKeyDown ( ( [ mouseEvent , _keyboardEvent ] ) => {
81
88
if ( ! this . _editor . hasModel ( ) || ! mouseEvent . hasTriggerModifier ) {
82
89
sessionStore . clear ( ) ;
83
90
return ;
84
91
}
85
- const targetMouseEvent = mouseEvent . target as any ;
86
- if ( targetMouseEvent . detail === 'editor.contrib.stickyScrollWidget' && this . hoverOnLine !== - 1 && targetMouseEvent . element . innerText === targetMouseEvent . element . innerHTML ) {
92
+ const targetMouseEvent = mouseEvent . target as unknown as CustomMouseEvent ;
93
+ if ( targetMouseEvent . detail === 'editor.contrib.stickyScrollWidget' && targetMouseEvent . element . innerText === targetMouseEvent . element . innerHTML ) {
87
94
const text = targetMouseEvent . element . innerText ;
88
- const lineNumber = this . hoverOnLine ;
89
- // TODO: workaround to find the column index, perhaps need more solid solution
90
- const column = this . _editor . getModel ( ) . getLineContent ( lineNumber ) . indexOf ( text ) ;
91
- if ( column === - 1 ) {
95
+ if ( this . hoverOnColumn === - 1 ) {
92
96
return ;
93
97
}
94
- const stickyPositionProjectedOnEditor = new Position ( lineNumber , column + 1 ) ;
95
- if ( this . stickyPositionProjectedOnEditor !== stickyPositionProjectedOnEditor ) {
96
- this . stickyPositionProjectedOnEditor = stickyPositionProjectedOnEditor ;
97
- cancellationToken . cancel ( ) ;
98
+ const lineNumber = this . hoverOnLine ;
99
+ const column = this . hoverOnColumn ;
100
+
101
+ const stickyPositionProjectedOnEditor = { startLineNumber : lineNumber , endLineNumber : lineNumber , startColumn : column , endColumn : column + text . length } as IRange ;
102
+ if ( JSON . stringify ( this . stickyRangeProjectedOnEditor ) !== JSON . stringify ( stickyPositionProjectedOnEditor ) ) {
103
+ this . stickyRangeProjectedOnEditor = stickyPositionProjectedOnEditor ;
104
+ sessionStore . clear ( ) ;
98
105
}
99
- getDefinitionsAtPosition ( this . _languageFeatureService . definitionProvider , this . _editor . getModel ( ) , stickyPositionProjectedOnEditor , cancellationToken . token ) . then ( ( candidateDefinitions => {
106
+
107
+ const cancellationToken = new CancellationTokenSource ( ) ;
108
+ sessionStore . add ( toDisposable ( ( ) => cancellationToken . dispose ( true ) ) ) ;
109
+
110
+ let currentHTMLChild : HTMLElement ;
111
+
112
+ getDefinitionsAtPosition ( this . _languageFeatureService . definitionProvider , this . _editor . getModel ( ) , new Position ( lineNumber , column + 1 ) , cancellationToken . token ) . then ( ( candidateDefinitions => {
100
113
if ( cancellationToken . token . isCancellationRequested ) {
101
114
return ;
102
115
}
103
116
if ( candidateDefinitions . length !== 0 ) {
104
- // TODO: Currently only taking the first definition but there could be other ones of interest
105
- this . positionOfDefinitionToJumpTo . uri = candidateDefinitions [ 0 ] ?. uri ;
106
- this . positionOfDefinitionToJumpTo . range = candidateDefinitions [ 0 ] ?. targetSelectionRange as IRange ;
107
-
108
117
const lineToDecorate = this . getDomNode ( ) . getElementsByClassName ( `stickyLine${ lineNumber } ` ) [ 0 ] . children [ 0 ] as HTMLElement ;
109
- let currentHTMLChild : HTMLElement | undefined = undefined ;
110
-
118
+ let childHTML : HTMLElement | undefined = undefined ;
111
119
for ( const childElement of lineToDecorate . children ) {
112
120
const childAsHTMLElement = childElement as HTMLElement ;
113
121
if ( childAsHTMLElement . innerText === text ) {
114
- currentHTMLChild = childAsHTMLElement ;
122
+ childHTML = childAsHTMLElement ;
115
123
break ;
116
124
}
117
125
}
118
- if ( ! currentHTMLChild ) {
126
+ if ( ! childHTML ) {
119
127
return ;
120
128
}
121
- if ( this . lastChildDecorated && this . lastChildDecorated !== currentHTMLChild ) {
122
- this . lastChildDecorated . style . textDecoration = 'none' ;
123
- this . lastChildDecorated = currentHTMLChild ;
124
- this . lastChildDecorated . style . textDecoration = 'underline' ;
125
- } else if ( ! this . lastChildDecorated ) {
126
- this . lastChildDecorated = currentHTMLChild ;
127
- this . lastChildDecorated . style . textDecoration = 'underline' ;
129
+ if ( currentHTMLChild !== childHTML ) {
130
+ sessionStore . clear ( ) ;
131
+ currentHTMLChild = childHTML ;
132
+ currentHTMLChild . style . textDecoration = 'underline' ;
133
+ sessionStore . add ( toDisposable ( ( ) => {
134
+ currentHTMLChild . style . textDecoration = 'none' ;
135
+ } ) ) ;
136
+ } else if ( ! currentHTMLChild ) {
137
+ currentHTMLChild = childHTML ;
138
+ currentHTMLChild . style . textDecoration = 'underline' ;
139
+ sessionStore . add ( toDisposable ( ( ) => {
140
+ currentHTMLChild . style . textDecoration = 'none' ;
141
+ } ) ) ;
128
142
}
129
- } else if ( this . lastChildDecorated ) {
130
- this . lastChildDecorated . style . textDecoration = 'none' ;
131
- this . lastChildDecorated = undefined ;
143
+ } else {
144
+ sessionStore . clear ( ) ;
132
145
}
133
146
} ) ) ;
134
- } else if ( this . lastChildDecorated ) {
135
- this . lastChildDecorated . style . textDecoration = 'none' ;
136
- this . lastChildDecorated = undefined ;
147
+ } else {
148
+ sessionStore . clear ( ) ;
137
149
}
138
150
} ) ) ;
139
151
linkGestureStore . add ( gesture . onCancel ( ( ) => {
@@ -143,7 +155,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
143
155
if ( this . hoverOnLine !== - 1 ) {
144
156
if ( e . hasTriggerModifier ) {
145
157
// Control click
146
- this . _instaService . invokeFunction ( goToDefinitionWithLocation , e , this . _editor as IActiveCodeEditor , this . positionOfDefinitionToJumpTo ) ;
158
+ this . _instaService . invokeFunction ( goToDefinitionWithLocation , e , this . _editor as IActiveCodeEditor , { uri : this . _editor . getModel ( ) ! . uri , range : this . stickyRangeProjectedOnEditor } as Location ) ;
147
159
} else {
148
160
// Normal click
149
161
this . _editor . revealPosition ( { lineNumber : this . hoverOnLine , column : 1 } ) ;
@@ -259,15 +271,20 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
259
271
child . style . top = this . lastLineRelativePosition + 'px' ;
260
272
}
261
273
262
- this . disposableStore . add ( dom . addDisposableListener ( child , 'mouseover' , ( ) => {
263
- this . hoverOnLine = line ;
274
+ this . disposableStore . add ( dom . addDisposableListener ( child , 'mouseover' , ( e ) => {
275
+ if ( this . _editor . hasModel ( ) ) {
276
+ const mouseOverEvent = new StandardMouseEvent ( e ) ;
277
+ const text = mouseOverEvent . target . innerText ;
278
+ this . hoverOnLine = line ;
279
+ // TODO: workaround to find the column index, perhaps need more solid solution
280
+ this . hoverOnColumn = this . _editor . getModel ( ) . getLineContent ( line ) . indexOf ( text ) + 1 || - 1 ;
281
+ }
264
282
} ) ) ;
265
283
266
284
return child ;
267
285
}
268
286
269
287
private renderRootNode ( ) : void {
270
-
271
288
if ( ! this . _editor . _getViewModel ( ) ) {
272
289
return ;
273
290
}
@@ -283,10 +300,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget {
283
300
} else if ( minimapSide === 'right' ) {
284
301
this . rootDomNode . style . marginLeft = '0px' ;
285
302
}
286
-
287
- this . disposableStore . add ( dom . addDisposableListener ( this . rootDomNode , 'mouseout' , ( ) => {
288
- this . hoverOnLine = - 1 ;
289
- } ) ) ;
290
303
}
291
304
292
305
public getId ( ) : string {
0 commit comments