@@ -2,7 +2,7 @@ import { isEqual } from 'lodash'
22import { EMPTY , NEVER , Observable , of , Subject , Subscription } from 'rxjs'
33import { distinctUntilChanged , filter , map } from 'rxjs/operators'
44import { TestScheduler } from 'rxjs/testing'
5- import { Position , Range } from 'vscode-languageserver-types'
5+ import { Range } from 'vscode-languageserver-types'
66
77import { noop } from 'lodash'
88import { propertyIsDefined } from './helpers'
@@ -12,6 +12,7 @@ import {
1212 LOADER_DELAY ,
1313 MOUSEOVER_DELAY ,
1414 PositionAdjuster ,
15+ PositionJump ,
1516 TOOLTIP_DISPLAY_DELAY ,
1617} from './hoverifier'
1718import { HoverOverlayProps } from './HoverOverlay'
@@ -50,11 +51,7 @@ describe('Hoverifier', () => {
5051 getReferencesURL : ( ) => null ,
5152 } )
5253
53- const positionJumps = new Subject < {
54- position : Position
55- codeView : HTMLElement
56- scrollElement : HTMLElement
57- } > ( )
54+ const positionJumps = new Subject < PositionJump > ( )
5855
5956 const positionEvents = of ( codeView . codeView ) . pipe ( findPositionsFromEvents ( codeView ) )
6057
@@ -119,11 +116,7 @@ describe('Hoverifier', () => {
119116 getReferencesURL : ( ) => null ,
120117 } )
121118
122- const positionJumps = new Subject < {
123- position : Position
124- codeView : HTMLElement
125- scrollElement : HTMLElement
126- } > ( )
119+ const positionJumps = new Subject < PositionJump > ( )
127120
128121 const positionEvents = of ( codeView . codeView ) . pipe ( findPositionsFromEvents ( codeView ) )
129122
@@ -195,6 +188,161 @@ describe('Hoverifier', () => {
195188 }
196189 } )
197190
191+ describe ( 'pinning' , ( ) => {
192+ it ( 'unpins upon clicking on a different position' , ( ) => {
193+ for ( const codeView of testcases ) {
194+ const scheduler = new TestScheduler ( ( a , b ) => chai . assert . deepEqual ( a , b ) )
195+
196+ const delayTime = 10
197+
198+ scheduler . run ( ( { cold, expectObservable } ) => {
199+ const hoverifier = createHoverifier ( {
200+ closeButtonClicks : NEVER ,
201+ goToDefinitionClicks : new Observable < MouseEvent > ( ) ,
202+ hoverOverlayElements : of ( null ) ,
203+ hoverOverlayRerenders : EMPTY ,
204+ // Only show on line 24, not line 25 (which is the 2nd click event below).
205+ fetchHover : position =>
206+ position . line === 24 ? createStubHoverFetcher ( { } , delayTime ) ( position ) : of ( null ) ,
207+ fetchJumpURL : position =>
208+ position . line === 24 ? createStubJumpURLFetcher ( 'def url' , delayTime ) ( position ) : of ( null ) ,
209+ pushHistory : noop ,
210+ getReferencesURL : ( ) => null ,
211+ } )
212+
213+ const positionJumps = new Subject < PositionJump > ( )
214+
215+ const positionEvents = of ( codeView . codeView ) . pipe ( findPositionsFromEvents ( codeView ) )
216+
217+ const subscriptions = new Subscription ( )
218+
219+ subscriptions . add ( hoverifier )
220+ subscriptions . add (
221+ hoverifier . hoverify ( {
222+ dom : codeView ,
223+ positionEvents,
224+ positionJumps,
225+ resolveContext : ( ) => codeView . revSpec ,
226+ } )
227+ )
228+
229+ const isPinned = hoverifier . hoverStateUpdates . pipe (
230+ map (
231+ hoverState =>
232+ ! ! hoverState . hoverOverlayProps && ! ! hoverState . hoverOverlayProps . showCloseButton
233+ ) ,
234+ distinctUntilChanged ( )
235+ )
236+
237+ const outputDiagram = `${ delayTime } ms a-c`
238+ const outputValues : {
239+ [ key : string ] : boolean
240+ } = {
241+ a : true ,
242+ c : false ,
243+ }
244+
245+ // Click (to pin) https://sourcegraph.sgdev.org/github.com/gorilla/mux@cb4698366aa625048f3b815af6a0dea8aef9280a/-/blob/mux.go#L24:6
246+ cold ( `a` ) . subscribe ( ( ) => {
247+ dispatchMouseEventAtPositionImpure ( 'click' , codeView , {
248+ line : 24 ,
249+ character : 6 ,
250+ } )
251+ } )
252+
253+ // Click to another position and ensure the hover is no longer pinned.
254+ cold ( `${ delayTime } ms --c` ) . subscribe ( ( ) =>
255+ positionJumps . next ( {
256+ codeView : codeView . codeView ,
257+ scrollElement : codeView . container ,
258+ position : { line : 1 , character : 1 } ,
259+ } )
260+ )
261+
262+ expectObservable ( isPinned ) . toBe ( outputDiagram , outputValues )
263+ } )
264+ }
265+ } )
266+
267+ it ( 'unpins upon navigation to an invalid or undefined position (such as a file with no particular position)' , ( ) => {
268+ for ( const codeView of testcases ) {
269+ const scheduler = new TestScheduler ( ( a , b ) => chai . assert . deepEqual ( a , b ) )
270+
271+ scheduler . run ( ( { cold, expectObservable } ) => {
272+ const hoverifier = createHoverifier ( {
273+ closeButtonClicks : NEVER ,
274+ goToDefinitionClicks : new Observable < MouseEvent > ( ) ,
275+ hoverOverlayElements : of ( null ) ,
276+ hoverOverlayRerenders : EMPTY ,
277+ // Only show on line 24, not line 25 (which is the 2nd click event below).
278+ fetchHover : position =>
279+ position . line === 24 ? createStubHoverFetcher ( { } ) ( position ) : of ( null ) ,
280+ fetchJumpURL : position =>
281+ position . line === 24 ? createStubJumpURLFetcher ( 'def url' ) ( position ) : of ( null ) ,
282+ pushHistory : noop ,
283+ getReferencesURL : ( ) => null ,
284+ } )
285+
286+ const positionJumps = new Subject < PositionJump > ( )
287+
288+ const positionEvents = of ( codeView . codeView ) . pipe ( findPositionsFromEvents ( codeView ) )
289+
290+ const subscriptions = new Subscription ( )
291+
292+ subscriptions . add ( hoverifier )
293+ subscriptions . add (
294+ hoverifier . hoverify ( {
295+ dom : codeView ,
296+ positionEvents,
297+ positionJumps,
298+ resolveContext : ( ) => codeView . revSpec ,
299+ } )
300+ )
301+
302+ const isPinned = hoverifier . hoverStateUpdates . pipe (
303+ map ( hoverState => {
304+ if ( ! hoverState . hoverOverlayProps ) {
305+ return 'hidden'
306+ }
307+ if ( hoverState . hoverOverlayProps . showCloseButton ) {
308+ return 'pinned'
309+ }
310+ return 'visible'
311+ } ) ,
312+ distinctUntilChanged ( )
313+ )
314+
315+ const outputDiagram = `ab`
316+ const outputValues : {
317+ [ key : string ] : 'hidden' | 'pinned' | 'visible'
318+ } = {
319+ a : 'pinned' ,
320+ b : 'hidden' ,
321+ }
322+
323+ // Click (to pin) https://sourcegraph.sgdev.org/github.com/gorilla/mux@cb4698366aa625048f3b815af6a0dea8aef9280a/-/blob/mux.go#L24:6
324+ cold ( `a` ) . subscribe ( ( ) =>
325+ dispatchMouseEventAtPositionImpure ( 'click' , codeView , {
326+ line : 24 ,
327+ character : 6 ,
328+ } )
329+ )
330+
331+ // Click to another position and ensure the hover is no longer pinned.
332+ cold ( `-b` ) . subscribe ( ( ) =>
333+ positionJumps . next ( {
334+ codeView : codeView . codeView ,
335+ scrollElement : codeView . container ,
336+ position : { line : undefined , character : undefined } ,
337+ } )
338+ )
339+
340+ expectObservable ( isPinned ) . toBe ( outputDiagram , outputValues )
341+ } )
342+ }
343+ } )
344+ } )
345+
198346 it ( 'emits loading and then state on click events' , ( ) => {
199347 for ( const codeView of testcases ) {
200348 const scheduler = new TestScheduler ( ( a , b ) => chai . assert . deepEqual ( a , b ) )
@@ -215,11 +363,7 @@ describe('Hoverifier', () => {
215363 getReferencesURL : ( ) => null ,
216364 } )
217365
218- const positionJumps = new Subject < {
219- position : Position
220- codeView : HTMLElement
221- scrollElement : HTMLElement
222- } > ( )
366+ const positionJumps = new Subject < PositionJump > ( )
223367
224368 const positionEvents = of ( codeView . codeView ) . pipe ( findPositionsFromEvents ( codeView ) )
225369
@@ -297,11 +441,7 @@ describe('Hoverifier', () => {
297441 getReferencesURL : ( ) => null ,
298442 } )
299443
300- const positionJumps = new Subject < {
301- position : Position
302- codeView : HTMLElement
303- scrollElement : HTMLElement
304- } > ( )
444+ const positionJumps = new Subject < PositionJump > ( )
305445
306446 const positionEvents = of ( codeView . codeView ) . pipe ( findPositionsFromEvents ( codeView ) )
307447
@@ -366,11 +506,7 @@ describe('Hoverifier', () => {
366506 getReferencesURL : ( ) => null ,
367507 } )
368508
369- const positionJumps = new Subject < {
370- position : Position
371- codeView : HTMLElement
372- scrollElement : HTMLElement
373- } > ( )
509+ const positionJumps = new Subject < PositionJump > ( )
374510
375511 const positionEvents = of ( codeView . codeView ) . pipe ( findPositionsFromEvents ( codeView ) )
376512
@@ -455,11 +591,7 @@ describe('Hoverifier', () => {
455591 getReferencesURL : ( ) => null ,
456592 } )
457593
458- const positionJumps = new Subject < {
459- position : Position
460- codeView : HTMLElement
461- scrollElement : HTMLElement
462- } > ( )
594+ const positionJumps = new Subject < PositionJump > ( )
463595
464596 const positionEvents = of ( codeView . codeView ) . pipe ( findPositionsFromEvents ( codeView ) )
465597
0 commit comments