Revert "Revert "Add token highlighting to gr-diff""
This reverts commit e39103dcfd5333251cf4d1e24dc38e8e47ee6afb.
Reason for revert: With fixed highlighting in unified diff.
Change-Id: I02739e9a19a1bfa984568188c7b9f7031e01ede2
diff --git a/polygerrit-ui/app/api/diff.ts b/polygerrit-ui/app/api/diff.ts
index 022492d..f7d5a59 100644
--- a/polygerrit-ui/app/api/diff.ts
+++ b/polygerrit-ui/app/api/diff.ts
@@ -298,7 +298,8 @@
annotate(
textElement: HTMLElement,
lineNumberElement: HTMLElement,
- line: GrDiffLine
+ line: GrDiffLine,
+ side: Side
): void;
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.ts
index 76ebc50..96206f2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-element.ts
@@ -46,8 +46,9 @@
import {GrDiffLine, LineNumber} from '../gr-diff/gr-diff-line';
import {GrDiffGroup} from '../gr-diff/gr-diff-group';
import {PolymerSpliceChange} from '@polymer/polymer/interfaces';
-import {getLineNumber} from '../gr-diff/gr-diff-utils';
+import {getLineNumber, getSideByLineEl} from '../gr-diff/gr-diff-utils';
import {fireAlert, fireEvent} from '../../../utils/event-util';
+import {TokenHighlightLayer} from './token-highlight-layer';
const TRAILING_WHITESPACE_PATTERN = /\s+$/;
@@ -249,6 +250,7 @@
this.$.rangeLayer,
this.$.coverageLayerLeft,
this.$.coverageLayerRight,
+ new TokenHighlightLayer(),
];
if (this.layers) {
@@ -257,26 +259,6 @@
this._layers = layers;
}
- getLineElByChild(node?: Node): HTMLElement | null {
- while (node) {
- if (node instanceof Element) {
- if (node.classList.contains('lineNum')) {
- return node as HTMLElement;
- }
- if (node.classList.contains('section')) {
- return null;
- }
- }
- node = node.previousSibling ?? node.parentElement ?? undefined;
- }
- return null;
- }
-
- getLineNumberByChild(node: Node) {
- const lineEl = this.getLineElByChild(node);
- return getLineNumber(lineEl);
- }
-
getContentTdByLine(lineNumber: LineNumber, side?: Side, root?: Element) {
if (!this._builder) return null;
return this._builder.getContentTdByLine(lineNumber, side, root);
@@ -293,7 +275,7 @@
if (!lineEl) return null;
const line = getLineNumber(lineEl);
if (!line) return null;
- const side = this.getSideByLineEl(lineEl);
+ const side = getSideByLineEl(lineEl);
// Performance optimization because we already have an element in the
// correct row
const row = this._getDiffRowByChild(lineEl);
@@ -307,10 +289,6 @@
);
}
- getSideByLineEl(lineEl: Element) {
- return lineEl.classList.contains(Side.RIGHT) ? Side.RIGHT : Side.LEFT;
- }
-
emitGroup(group: GrDiffGroup, sectionEl: HTMLElement) {
if (!this._builder) return;
this._builder.emitGroup(group, sectionEl);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.ts
index e927fdf..2067455 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder-unified.ts
@@ -122,7 +122,14 @@
Side.RIGHT
);
row.appendChild(lineNumberEl);
- row.appendChild(this._createTextEl(lineNumberEl, line));
+ let side = undefined;
+ if (line.type === GrDiffLineType.ADD || line.type === GrDiffLineType.BOTH) {
+ side = Side.RIGHT;
+ }
+ if (line.type === GrDiffLineType.REMOVE) {
+ side = Side.LEFT;
+ }
+ row.appendChild(this._createTextEl(lineNumberEl, line, side));
return row;
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
index 3c8150c..65c991c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder.ts
@@ -605,14 +605,14 @@
contentText.setAttribute('data-side', side);
}
- if (lineNumberEl) {
+ if (lineNumberEl && side) {
for (const layer of this.layers) {
if (typeof layer.annotate === 'function') {
- layer.annotate(contentText, lineNumberEl, line);
+ layer.annotate(contentText, lineNumberEl, line, side);
}
}
} else {
- console.error('The lineNumberEl is null, skipping layer annotations.');
+ console.error('lineNumberEl or side not set, skipping layer.annotate');
}
td.appendChild(contentText);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts
new file mode 100644
index 0000000..d0b3d3c
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/token-highlight-layer.ts
@@ -0,0 +1,259 @@
+/**
+ * @license
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {DiffLayer, DiffLayerListener} from '../../../types/types';
+import {GrDiffLine, Side} from '../../../api/diff';
+import {GrAnnotation} from '../gr-diff-highlight/gr-annotation';
+import {debounce, DelayedTask} from '../../../utils/async-util';
+import {
+ getLineNumberByChild,
+ lineNumberToNumber,
+} from '../gr-diff/gr-diff-utils';
+import {appContext} from '../../../services/app-context';
+import {KnownExperimentId} from '../../../services/flags/flags';
+
+const tokenMatcher = new RegExp(/[a-zA-Z0-9_-]+/g);
+
+/** CSS class for all tokens. */
+const CSS_TOKEN = 'token';
+
+/** CSS class for the currently hovered token. */
+const CSS_HIGHLIGHT = 'token-highlight';
+
+const UPDATE_TOKEN_TASK_DELAY_MS = 50;
+
+const LINE_LENGTH_LIMIT = 500;
+
+const TOKEN_LENGTH_LIMIT = 100;
+
+const TOKEN_COUNT_LIMIT = 10000;
+
+const TOKEN_OCCURRENCES_LIMIT = 1000;
+
+/**
+ * Token highlighting is only useful for code on-screen, so don't bother
+ * highlighting tokens that are further away than this threshold from where the
+ * user is hovering.
+ */
+const LINE_DISTANCE_THRESHOLD = 100;
+
+/**
+ * When a user hovers over a token in the diff, then this layer makes sure that
+ * all occurrences of this token are annotated with the 'token-highlight' css
+ * class. And removes that class when the user moves the mouse away from the
+ * token.
+ *
+ * The layer does not react to mouse events directly by adding a css class to
+ * the appropriate elements, but instead it just sets the currently highlighted
+ * token and notifies the diff renderer that certain lines must be re-rendered.
+ * And when that re-rendering happens the appropriate css class is added.
+ */
+export class TokenHighlightLayer implements DiffLayer {
+ /** The only listener is typically the renderer of gr-diff. */
+ private listeners: DiffLayerListener[] = [];
+
+ /** The currently highlighted token. */
+ private currentHighlight?: string;
+
+ /**
+ * The line of the currently highlighted token. We store this in order to
+ * re-render only relevant lines of the diff. Only lines visible on the screen
+ * need a highlight. For example in a file with 10,000 lines it is sufficient
+ * to just re-render the ~100 lines that are visible to the user.
+ *
+ * It is a known issue that we are only storing the line number on the side of
+ * where the user is hovering and we use that also to determine which line
+ * numbers to re-render on the other side, but it is non-trivial to look up or
+ * store a reliable mapping of line numbers, so we just accept this
+ * shortcoming with the reasoning that the user is mostly interested in the
+ * tokens on the side where they are hovering anyway.
+ *
+ * Another known issue is that we are not able to see past collapsed lines
+ * with the current implementation.
+ */
+ private currentHighlightLineNumber = 0;
+
+ /**
+ * Keeps track of where tokens occur in a file during rendering, so that it is
+ * easy to look up when processing mouse events.
+ */
+ private tokenToLinesLeft = new Map<string, Set<number>>();
+
+ private tokenToLinesRight = new Map<string, Set<number>>();
+
+ private updateTokenTask?: DelayedTask;
+
+ private readonly enabled = appContext.flagsService.isEnabled(
+ KnownExperimentId.TOKEN_HIGHLIGHTING
+ );
+
+ annotate(
+ el: HTMLElement,
+ _: HTMLElement,
+ line: GrDiffLine,
+ side: Side
+ ): void {
+ if (!this.enabled) return;
+ const text = el.textContent;
+ if (!text) return;
+ // Binary files encoded as text for example can have super long lines
+ // with super long tokens. Let's guard against against this scenario.
+ if (text.length > LINE_LENGTH_LIMIT) return;
+ let match;
+ let atLeastOneTokenMatched = false;
+ while ((match = tokenMatcher.exec(text))) {
+ const token = match[0];
+ const index = match.index;
+ const length = token.length;
+ // Binary files encoded as text for example can have super long lines
+ // with super long tokens. Let's guard against this scenario.
+ if (length > TOKEN_LENGTH_LIMIT) continue;
+ atLeastOneTokenMatched = true;
+ const css = token === this.currentHighlight ? CSS_HIGHLIGHT : CSS_TOKEN;
+ // We add the tk-* class so that we can look up the token later easily
+ // even if the token element was split up into multiple smaller nodes.
+ GrAnnotation.annotateElement(el, index, length, `tk-${token} ${css}`);
+ // We could try to detect whether we are re-rendering instead of initially
+ // rendering the line. Then we would not have to call storeLineForToken()
+ // again. But since the Set swallows the duplicates we don't care.
+ this.storeLineForToken(token, line, side);
+ }
+ if (atLeastOneTokenMatched) {
+ // These listeners do not have to be cleaned, because listeners are
+ // garbage collected along with the element itself once it is not attached
+ // to the DOM anymore and no references exist anymore.
+ el.addEventListener('mouseover', this.handleMouseOver);
+ el.addEventListener('mouseout', this.handleMouseOut);
+ }
+ }
+
+ private storeLineForToken(token: string, line: GrDiffLine, side: Side) {
+ const tokenToLines =
+ side === Side.LEFT ? this.tokenToLinesLeft : this.tokenToLinesRight;
+ // Just to make sure that we don't break down on large files.
+ if (tokenToLines.size > TOKEN_COUNT_LIMIT) return;
+ let numbers = tokenToLines.get(token);
+ if (!numbers) {
+ numbers = new Set<number>();
+ tokenToLines.set(token, numbers);
+ }
+ // Just to make sure that we don't break down on large files.
+ if (numbers.size > TOKEN_OCCURRENCES_LIMIT) return;
+ const lineNumber =
+ side === Side.LEFT ? line.beforeNumber : line.afterNumber;
+ numbers.add(Number(lineNumber));
+ }
+
+ private readonly handleMouseOut = (e: MouseEvent) => {
+ if (!this.currentHighlight) return;
+ if (this.interferesWithSelection(e)) return;
+ const el = this.findTokenAncestor(e?.target);
+ if (!el) return;
+ this.updateTokenHighlight(undefined, undefined);
+ };
+
+ private readonly handleMouseOver = (e: MouseEvent) => {
+ if (this.interferesWithSelection(e)) return;
+ const {line, token} = this.findTokenAncestor(e?.target);
+ if (!token) return;
+ const oldHighlight = this.currentHighlight;
+ const newHighlight = token;
+ if (!newHighlight || newHighlight === oldHighlight) return;
+ if (this.countOccurrences(newHighlight) <= 1) return;
+ this.updateTokenHighlight(line, newHighlight);
+ };
+
+ private interferesWithSelection(e: MouseEvent) {
+ if (e.buttons > 0) return true;
+ if (window.getSelection()?.type === 'Range') return true;
+ return false;
+ }
+
+ private updateTokenHighlight(
+ newLineNumber: number | undefined,
+ newHighlight: string | undefined
+ ) {
+ this.updateTokenTask = debounce(
+ this.updateTokenTask,
+ () => {
+ const oldHighlight = this.currentHighlight;
+ const oldLineNumber = this.currentHighlightLineNumber;
+ this.currentHighlight = newHighlight;
+ this.currentHighlightLineNumber = newLineNumber ?? 0;
+ this.notifyForToken(oldHighlight, oldLineNumber);
+ this.notifyForToken(newHighlight, newLineNumber ?? 0);
+ },
+ UPDATE_TOKEN_TASK_DELAY_MS
+ );
+ }
+
+ findTokenAncestor(
+ el?: EventTarget | Element | null
+ ): {
+ token?: string;
+ line: number;
+ } {
+ if (!(el instanceof Element)) return {line: 0, token: undefined};
+ if (
+ el.classList.contains(CSS_TOKEN) ||
+ el.classList.contains(CSS_HIGHLIGHT)
+ ) {
+ const tkClass = [...el.classList].find(c => c.startsWith('tk-'));
+ const line = lineNumberToNumber(getLineNumberByChild(el));
+ if (!line || !tkClass) return {line: 0, token: undefined};
+ return {line, token: tkClass.substring(3)};
+ }
+ if (el.tagName === 'TD') return {line: 0, token: undefined};
+ return this.findTokenAncestor(el.parentElement);
+ }
+
+ countOccurrences(token: string | undefined) {
+ if (!token) return 0;
+ const linesLeft = this.tokenToLinesLeft.get(token);
+ const linesRight = this.tokenToLinesRight.get(token);
+ return (linesLeft?.size ?? 0) + (linesRight?.size ?? 0);
+ }
+
+ notifyForToken(token: string | undefined, lineNumber: number) {
+ if (!token) return;
+ const linesLeft = this.tokenToLinesLeft.get(token);
+ linesLeft?.forEach(line => {
+ if (Math.abs(line - lineNumber) < LINE_DISTANCE_THRESHOLD) {
+ this.notifyListeners(line, Side.LEFT);
+ }
+ });
+ const linesRight = this.tokenToLinesRight.get(token);
+ linesRight?.forEach(line => {
+ if (Math.abs(line - lineNumber) < LINE_DISTANCE_THRESHOLD) {
+ this.notifyListeners(line, Side.RIGHT);
+ }
+ });
+ }
+
+ addListener(listener: DiffLayerListener) {
+ this.listeners.push(listener);
+ }
+
+ removeListener(listener: DiffLayerListener) {
+ this.listeners = this.listeners.filter(f => f !== listener);
+ }
+
+ notifyListeners(line: number, side: Side) {
+ for (const listener of this.listeners) {
+ listener(line, line, side);
+ }
+ }
+}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.ts b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.ts
index 76e02f0..ca003f3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight.ts
@@ -28,7 +28,13 @@
import {GrSelectionActionBox} from '../gr-selection-action-box/gr-selection-action-box';
import {GrDiffBuilderElement} from '../gr-diff-builder/gr-diff-builder-element';
import {FILE} from '../gr-diff/gr-diff-line';
-import {getRange, getSide} from '../gr-diff/gr-diff-utils';
+import {
+ getLineElByChild,
+ getLineNumberByChild,
+ getRange,
+ getSide,
+ getSideByLineEl,
+} from '../gr-diff/gr-diff-utils';
import {debounce, DelayedTask} from '../../../utils/async-util';
interface SidedRange {
@@ -356,11 +362,11 @@
): NormalizedPosition | null {
let column;
if (!node || !this.contains(node)) return null;
- const lineEl = this.diffBuilder.getLineElByChild(node);
+ const lineEl = getLineElByChild(node);
if (!lineEl) return null;
- const side = this.diffBuilder.getSideByLineEl(lineEl);
+ const side = getSideByLineEl(lineEl);
if (!side) return null;
- const line = this.diffBuilder.getLineNumberByChild(lineEl);
+ const line = getLineNumberByChild(lineEl);
if (!line || line === FILE || line === 'LOST') return null;
const contentTd = this.diffBuilder.getContentTdByLineEl(lineEl);
if (!contentTd) return null;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.js b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.js
index 07e83a8..18fbe9a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.js
@@ -438,7 +438,7 @@
const contentText = stubContent(140, 'left');
const contentTd = contentText.parentElement;
- emulateSelection(contentTd.previousElementSibling, 0,
+ emulateSelection(contentTd.parentElement, 0,
contentText.firstChild, 2);
assert.isFalse(!!element.selectedRange);
});
@@ -584,21 +584,6 @@
});
assert.equal(side, 'right');
});
-
- test('_fixTripleClickSelection empty line', () => {
- const startContent = stubContent(146, 'right');
- const endContent = stubContent(165, 'left');
- emulateSelection(startContent.firstChild, 0,
- endContent.parentElement.previousElementSibling, 0);
- const {range, side} = element.selectedRange;
- assert.deepEqual(range, {
- start_line: 146,
- start_character: 0,
- end_line: 146,
- end_character: 84,
- });
- assert.equal(side, 'right');
- });
});
});
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.ts b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.ts
index 3df2e18..b64f61d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.ts
@@ -28,7 +28,12 @@
import {DiffInfo} from '../../../types/diff';
import {Side} from '../../../constants/constants';
import {GrDiffBuilderElement} from '../gr-diff-builder/gr-diff-builder-element';
-import {getSide, isThreadEl} from '../gr-diff/gr-diff-utils';
+import {
+ getLineElByChild,
+ getSide,
+ getSideByLineEl,
+ isThreadEl,
+} from '../gr-diff/gr-diff-utils';
/**
* Possible CSS classes indicating the state of selection. Dynamically added/
@@ -110,7 +115,7 @@
// Handle the down event on comment thread in Polymer 2
const handled = this._handleDownOnRangeComment(target);
if (handled) return;
- const lineEl = this.diffBuilder.getLineElByChild(target);
+ const lineEl = getLineElByChild(target);
const blameSelected = this._elementDescendedFromClass(target, 'blame');
if (!lineEl && !blameSelected) {
return;
@@ -125,7 +130,7 @@
target,
'gr-comment'
);
- const side = this.diffBuilder.getSideByLineEl(lineEl);
+ const side = getSideByLineEl(lineEl);
targetClasses.push(
side === 'left' ? SelectionClass.LEFT : SelectionClass.RIGHT
@@ -179,9 +184,9 @@
if (this.classList.contains(SelectionClass.COMMENT)) {
commentSelected = true;
}
- const lineEl = this.diffBuilder.getLineElByChild(target);
+ const lineEl = getLineElByChild(target);
if (!lineEl) return;
- const side = this.diffBuilder.getSideByLineEl(lineEl);
+ const side = getSideByLineEl(lineEl);
const text = this._getSelectedText(side, commentSelected);
if (text && e.clipboardData) {
e.clipboardData.setData('Text', text);
@@ -224,9 +229,9 @@
return this._getCommentLines(sel, side);
}
const range = normalize(sel.getRangeAt(0));
- const startLineEl = this.diffBuilder.getLineElByChild(range.startContainer);
+ const startLineEl = getLineElByChild(range.startContainer);
if (!startLineEl) return;
- const endLineEl = this.diffBuilder.getLineElByChild(range.endContainer);
+ const endLineEl = getLineElByChild(range.endContainer);
// Happens when triple click in side-by-side mode with other side empty.
const endsAtOtherEmptySide =
!endLineEl &&
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.js
index 5c9fe3f..8d7264c 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.js
@@ -143,8 +143,8 @@
test('applies selected-left on left side click', () => {
element.classList.add('selected-right');
- element._cachedDiffBuilder.getSideByLineEl.returns('left');
- MockInteractions.down(element);
+ const lineNumberEl = element.querySelector('.lineNum.left');
+ MockInteractions.down(lineNumberEl);
assert.isTrue(
element.classList.contains('selected-left'), 'adds selected-left');
assert.isFalse(
@@ -154,8 +154,8 @@
test('applies selected-right on right side click', () => {
element.classList.add('selected-left');
- element._cachedDiffBuilder.getSideByLineEl.returns('right');
- MockInteractions.down(element);
+ const lineNumberEl = element.querySelector('.lineNum.right');
+ MockInteractions.down(lineNumberEl);
assert.isTrue(
element.classList.contains('selected-right'), 'adds selected-right');
assert.isFalse(
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
index 0ca929a..96fbd8d 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-utils.ts
@@ -43,6 +43,36 @@
return range.end_line - range.start_line > 10;
}
+export function getLineNumberByChild(node?: Node) {
+ return getLineNumber(getLineElByChild(node));
+}
+
+export function lineNumberToNumber(lineNumber?: LineNumber | null): number {
+ if (!lineNumber) return 0;
+ if (lineNumber === 'LOST') return 0;
+ if (lineNumber === 'FILE') return 0;
+ return lineNumber;
+}
+
+export function getLineElByChild(node?: Node): HTMLElement | null {
+ while (node) {
+ if (node instanceof Element) {
+ if (node.classList.contains('lineNum')) {
+ return node as HTMLElement;
+ }
+ if (node.classList.contains('section')) {
+ return null;
+ }
+ }
+ node = node.previousSibling ?? node.parentElement ?? undefined;
+ }
+ return null;
+}
+
+export function getSideByLineEl(lineEl: Element) {
+ return lineEl.classList.contains(Side.RIGHT) ? Side.RIGHT : Side.LEFT;
+}
+
export function getLineNumber(lineEl?: Element | null): LineNumber | null {
if (!lineEl) return null;
const lineNumberStr = lineEl.getAttribute('data-value');
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
index 41dd159..3b76698 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff.ts
@@ -29,6 +29,7 @@
import {LineNumber} from './gr-diff-line';
import {
getLine,
+ getLineElByChild,
getLineNumber,
getRange,
getSide,
@@ -546,7 +547,7 @@
el.classList.contains('content') ||
el.classList.contains('contentText')
) {
- const target = this.$.diffBuilder.getLineElByChild(el);
+ const target = getLineElByChild(el);
if (target) {
this._selectLine(target);
}
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
index a93d5b5..059b0ee 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_html.ts
@@ -605,6 +605,10 @@
border: 1px solid var(--diff-context-control-border-color);
text-align: center;
}
+
+ .token-highlight {
+ background-color: var(--token-highlighting-color, #fffd54);
+ }
</style>
<style include="gr-syntax-theme">
/* Workaround for empty style block - see https://github.com/Polymer/tools/issues/408 */
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
index 86946a6..53a2915 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.js
@@ -472,10 +472,12 @@
test('_handleTap content', done => {
const content = document.createElement('div');
const lineEl = document.createElement('div');
+ lineEl.className = 'lineNum';
+ const row = document.createElement('div');
+ row.appendChild(lineEl);
+ row.appendChild(content);
const selectStub = sinon.stub(element, '_selectLine');
- sinon.stub(element.$.diffBuilder, 'getLineElByChild')
- .callsFake(() => lineEl);
content.className = 'content';
content.addEventListener('click', e => {
diff --git a/polygerrit-ui/app/services/flags/flags.ts b/polygerrit-ui/app/services/flags/flags.ts
index ff950b8..6f94b8d 100644
--- a/polygerrit-ui/app/services/flags/flags.ts
+++ b/polygerrit-ui/app/services/flags/flags.ts
@@ -31,4 +31,5 @@
NEW_CHANGE_SUMMARY_UI = 'UiFeature__new_change_summary_ui',
NEW_IMAGE_DIFF_UI = 'UiFeature__new_image_diff_ui',
COMMENT_CONTEXT = 'UiFeature__comment_context',
+ TOKEN_HIGHLIGHTING = 'UiFeature__token_highlighting',
}
diff --git a/polygerrit-ui/app/styles/themes/app-theme.ts b/polygerrit-ui/app/styles/themes/app-theme.ts
index a81dcdf..d768c96 100644
--- a/polygerrit-ui/app/styles/themes/app-theme.ts
+++ b/polygerrit-ui/app/styles/themes/app-theme.ts
@@ -337,6 +337,7 @@
--coverage-covered: #e0f2f1;
--coverage-not-covered: #ffd1a4;
--ranged-comment-hint-text-color: var(--orange-900);
+ --token-highlighting-color: #fffd54;
/* syntax colors */
--syntax-attr-color: #219;
diff --git a/polygerrit-ui/app/styles/themes/dark-theme.ts b/polygerrit-ui/app/styles/themes/dark-theme.ts
index 1d20c86..dec438d 100644
--- a/polygerrit-ui/app/styles/themes/dark-theme.ts
+++ b/polygerrit-ui/app/styles/themes/dark-theme.ts
@@ -199,6 +199,7 @@
--coverage-covered: #112826;
--coverage-not-covered: #6b3600;
--ranged-comment-hint-text-color: var(--blue-50);
+ --token-highlighting-color: var(--yellow-tonal);
/* syntax colors */
--syntax-attr-color: #80cbbf;