Skip to content
This repository was archived by the owner on Nov 25, 2021. It is now read-only.

Commit d6117cf

Browse files
ijsnowfelixfbecker
authored andcommitted
feat: support diff code views
Passes the diff part to DOM functions
1 parent 85c6713 commit d6117cf

File tree

5 files changed

+56
-36
lines changed

5 files changed

+56
-36
lines changed

src/hoverifier.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { isDefined } from './helpers'
1919
import { overlayUIHasContent, scrollIntoCenterIfNeeded } from './helpers'
2020
import { HoverOverlayProps, isJumpURL } from './HoverOverlay'
2121
import { calculateOverlayPosition } from './overlay_position'
22-
import { PositionEvent, SupportedMouseEvent } from './positions'
22+
import { DiffPart, PositionEvent, SupportedMouseEvent } from './positions'
2323
import { createObservableStateContainer } from './state'
2424
import {
2525
convertNode,
@@ -104,7 +104,7 @@ export interface PositionJump {
104104
/**
105105
* The position within the code view to jump to
106106
*/
107-
position: LineOrPositionOrRange
107+
position: LineOrPositionOrRange & { part?: DiffPart }
108108
/**
109109
* The code view
110110
*/
@@ -306,15 +306,15 @@ export const createHoverifier = ({
306306
/** Emits DOM elements at new positions found in the URL */
307307
const jumpTargets = allPositionJumps.pipe(
308308
// Only use line and character for comparison
309-
map(({ position: { line, character }, ...rest }) => ({ position: { line, character }, ...rest })),
309+
map(({ position: { line, character, part }, ...rest }) => ({ position: { line, character, part }, ...rest })),
310310
// Ignore same values
311311
// It's important to do this before filtering otherwise navigating from
312312
// a position, to a line-only position, back to the first position would get ignored
313313
distinctUntilChanged((a, b) => isEqual(a, b)),
314314
// Ignore undefined or partial positions (e.g. line only)
315315
filter((jump): jump is typeof jump & { position: Position } => Position.is(jump.position)),
316316
map(({ position, codeView, dom, ...rest }) => {
317-
const cell = dom.getCodeElementFromLineNumber(codeView, position.line)
317+
const cell = dom.getCodeElementFromLineNumber(codeView, position.line, position.part)
318318
if (!cell) {
319319
return undefined
320320
}
@@ -365,7 +365,7 @@ export const createHoverifier = ({
365365
const hoverObservables = resolvedPositions.pipe(
366366
map(({ position, ...rest }) => {
367367
if (!position) {
368-
return of({ ...rest, hoverOrError: undefined })
368+
return of({ ...rest, hoverOrError: undefined, part: undefined })
369369
}
370370
// Fetch the hover for that position
371371
const hoverFetch = fetchHover(position).pipe(
@@ -387,15 +387,15 @@ export const createHoverifier = ({
387387
takeUntil(hoverFetch)
388388
),
389389
hoverFetch
390-
).pipe(map(hoverOrError => ({ ...rest, hoverOrError })))
390+
).pipe(map(hoverOrError => ({ ...rest, hoverOrError, part: position.part })))
391391
}),
392392
share()
393393
)
394394
// Highlight the hover range returned by the language server
395395
subscription.add(
396396
hoverObservables
397397
.pipe(switchMap(hoverObservable => hoverObservable))
398-
.subscribe(({ hoverOrError, codeView, dom }) => {
398+
.subscribe(({ hoverOrError, codeView, dom, part }) => {
399399
container.update({
400400
hoverOrError,
401401
// Reset the hover position, it's gonna be repositioned after the hover was rendered
@@ -410,7 +410,7 @@ export const createHoverifier = ({
410410
}
411411
// LSP is 0-indexed, the code in the webapp currently is 1-indexed
412412
const { line, character } = hoverOrError.range.start
413-
const token = getTokenAtPosition(codeView, { line: line + 1, character: character + 1 }, dom)
413+
const token = getTokenAtPosition(codeView, { line: line + 1, character: character + 1 }, dom, part)
414414
if (!token) {
415415
return
416416
}

src/positions.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { propertyIsDefined } from './helpers'
1010
import { findPositionsFromEvents } from './positions'
1111
import { HoveredToken } from './token_position'
1212

13-
describe('position_listener', () => {
13+
describe('positions', () => {
1414
const dom = new DOM()
1515
after(dom.cleanup)
1616

src/positions.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { fromEvent, merge, Observable } from 'rxjs'
22
import { filter, map, switchMap, tap } from 'rxjs/operators'
33
import { Position } from 'vscode-languageserver-types'
4-
import { convertCodeElementIdempotent, DOMFunctions, HoveredToken, locateTarget } from './token_position'
4+
import { convertCodeElementIdempotent, DiffPart, DOMFunctions, HoveredToken, locateTarget } from './token_position'
55

66
export type SupportedMouseEvent = 'click' | 'mousemove' | 'mouseover'
77

@@ -25,7 +25,7 @@ export interface PositionEvent {
2525
codeView: HTMLElement
2626
}
2727

28-
export { DOMFunctions }
28+
export { DOMFunctions, DiffPart }
2929
export const findPositionsFromEvents = (options: DOMFunctions) => (
3030
elements: Observable<HTMLElement>
3131
): Observable<PositionEvent> =>
@@ -67,7 +67,10 @@ export const findPositionsFromEvents = (options: DOMFunctions) => (
6767
).pipe(
6868
// Find out the position that was hovered over
6969
map(({ target, codeView, ...rest }) => {
70-
const hoveredToken = locateTarget(target, options)
70+
const hoveredToken = locateTarget(target, {
71+
ignoreFirstChar: !!options.getDiffCodePart,
72+
...options,
73+
})
7174
const position = Position.is(hoveredToken) ? hoveredToken : undefined
7275
return { position, codeView, ...rest }
7376
})

src/testutils/dom.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,18 +44,19 @@ export interface CodeViewProps extends DOMFunctions {
4444

4545
// BEGIN setup test cases
4646

47-
// Abstract implemetation for GitHub and Sourcegraph. Could potentially be sufficient for any code host
47+
// Abstract implementation for GitHub and Sourcegraph. Could potentially be sufficient for any code host
4848
// but we may want to keep this as a configuration point.
49-
const getDiffCodePart = (codeElement: HTMLElement): 'head' | 'base' | undefined => {
50-
switch (codeElement.textContent!.charAt(0)) {
51-
case '+':
52-
return 'head'
53-
case '-':
54-
return 'base'
55-
default:
56-
return undefined
57-
}
58-
}
49+
// Commented out cause we only have tests for non-diff code views so far
50+
// const getDiffCodePart = (codeElement: HTMLElement): DiffPart => {
51+
// switch (codeElement.textContent!.charAt(0)) {
52+
// case '+':
53+
// return 'head'
54+
// case '-':
55+
// return 'base'
56+
// default:
57+
// return null
58+
// }
59+
// }
5960

6061
const createGitHubCodeView = (): CodeViewProps => {
6162
const codeView = document.createElement('div')
@@ -114,7 +115,6 @@ const createGitHubCodeView = (): CodeViewProps => {
114115
getCodeElementFromTarget,
115116
getCodeElementFromLineNumber,
116117
getLineNumberFromCodeElement,
117-
getDiffCodePart,
118118
}
119119
}
120120

@@ -176,7 +176,6 @@ const createSourcegraphCodeView = (): CodeViewProps => {
176176
getCodeElementFromTarget,
177177
getCodeElementFromLineNumber,
178178
getLineNumberFromCodeElement,
179-
getDiffCodePart,
180179
}
181180
}
182181

src/token_position.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,33 @@ export interface DOMFunctions {
1414
* @returns the element containing the code for a line or null if it can't be found. For example, the second <td> inside a <tr> on Sourcegraph and Github.
1515
*/
1616
getCodeElementFromTarget: (target: HTMLElement) => HTMLElement | null
17+
1718
/**
1819
* Get the element containing the code for a line from a code view given a line number.
20+
*
1921
* @param codeView is the code view itself. For example, the <code> element on Sourcegraph or a <table> on GitHub.
22+
* @param part If the code view is a diff view, the part of the diff that the line number refers to.
2023
* @returns the element containing the code for the given line number or null if it can't be found.
2124
*/
22-
getCodeElementFromLineNumber: (codeView: HTMLElement, line: number) => HTMLElement | null
25+
getCodeElementFromLineNumber: (codeView: HTMLElement, line: number, part?: DiffPart) => HTMLElement | null
26+
2327
/**
2428
* Gets the line number for a given element containing code for a line.
25-
* @param codeElement is the element containing code for a line. When this function is called,
26-
* it will be passed the result of either `getCodeElementFromTarget` or `getCodeElementFromLineNumber`.
27-
* @returns the line number.
29+
* When this function is called, it will be passed the result of either `getCodeElementFromTarget` or `getCodeElementFromLineNumber`.
30+
* @param codeElement The element containing code for a line.
31+
* @returns The line number.
2832
*/
2933
getLineNumberFromCodeElement: (codeElement: HTMLElement) => number
34+
3035
/**
31-
* Determine whether a code element is from the base or head part of a diff or not part of a diff.
36+
* If the code view is a diff view, must be provided to determine whether
37+
* a code element is from the base, head or unchanged part of the diff.
38+
* Must be `undefined` if the code view is not a diff view.
39+
*
3240
* @param codeElement is the element containing a line of code.
33-
* @returns whether the line is `'base'`, `'head'` or `undefined` if not part of a diff.
41+
* @returns The part of the diff `codeElement` belongs to
3442
*/
35-
getDiffCodePart?: (codeElement: HTMLElement) => 'base' | 'head' | undefined
43+
getDiffCodePart?: (codeElement: HTMLElement) => DiffPart
3644
}
3745

3846
/**
@@ -199,6 +207,11 @@ export function findElementWithOffset(codeElement: HTMLElement, offset: number):
199207
return walkNode(codeElement)
200208
}
201209

210+
/**
211+
* Whether a line belongs to the base rev of the diff (removed), the head (added) or `null` if either (not changed).
212+
*/
213+
export type DiffPart = 'base' | 'head' | null
214+
202215
/**
203216
* Returned when only the line is known.
204217
*
@@ -213,7 +226,7 @@ export interface HoveredToken {
213226
line: number
214227
/** 1-indexed */
215228
character: number
216-
part?: 'base' | 'head'
229+
part?: DiffPart
217230
}
218231

219232
interface LocateTargetOptions extends DOMFunctions {
@@ -284,11 +297,13 @@ export function locateTarget(
284297
interface GetCodeElementsInRangeOptions extends Pick<DOMFunctions, 'getCodeElementFromLineNumber'> {
285298
codeView: HTMLElement
286299
position?: LineOrPositionOrRange
300+
part?: DiffPart
287301
}
288302

289303
export const getCodeElementsInRange = ({
290304
codeView,
291305
position,
306+
part,
292307
getCodeElementFromLineNumber,
293308
}: GetCodeElementsInRangeOptions): {
294309
/** 1-indexed line number */
@@ -302,7 +317,7 @@ export const getCodeElementsInRange = ({
302317

303318
const elements: { line: number; element: HTMLElement }[] = []
304319
for (let line = position.line; line <= (position.endLine || position.line); line++) {
305-
const element = getCodeElementFromLineNumber(codeView, position.line)
320+
const element = getCodeElementFromLineNumber(codeView, position.line, part)
306321
if (!element) {
307322
break
308323
}
@@ -316,13 +331,16 @@ export const getCodeElementsInRange = ({
316331
*
317332
* @param codeView The code view
318333
* @param position 1-indexed position
334+
* @param options Code-host specific implementations of DOM retrieval functions
335+
* @param part If the code view is a diff view, the part of the diff that the position refers to
319336
*/
320337
export const getTokenAtPosition = (
321338
codeView: HTMLElement,
322339
position: Position,
323-
options: DOMFunctions
340+
options: DOMFunctions,
341+
part?: DiffPart
324342
): HTMLElement | undefined => {
325-
const codeCell = options.getCodeElementFromLineNumber(codeView, position.line)
343+
const codeCell = options.getCodeElementFromLineNumber(codeView, position.line, part)
326344
if (!codeCell) {
327345
return undefined
328346
}

0 commit comments

Comments
 (0)