diff options
author | Ben Rohlfs <brohlfs@google.com> | 2023-05-02 10:32:17 +0200 |
---|---|---|
committer | Paladox none <thomasmulhall410@yahoo.com> | 2023-05-03 13:57:32 +0000 |
commit | ad1a780468e2c770ff4dc9df1cd75aac1ded553d (patch) | |
tree | 84a96eff121a5b0eb8c386037461bfc7d47b3384 | |
parent | abefbeab92ac25c34da7d1ffb4e75eae017d926f (diff) |
Consolidate gr-diff-builder and gr-diff-builder-lit into one class
The abstract base class `GrDiffBuilder` was only introduced for finding
common ground between legacy and lit diff builders. Legacy diff is gone,
so let's remove this extra hierarchy level. It allows quite some
simplifications.
Release-Notes: skip
Change-Id: Id92e18526731768860579dbb658d5d20fab50b73
(cherry picked from commit ce3b5a6c341e4ddd4b49bdf1edec87bd46e0520d)
9 files changed, 194 insertions, 487 deletions
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts index e527633e12..737e964100 100644 --- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts +++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts @@ -1168,7 +1168,7 @@ suite('gr-diff-view tests', () => { }); test( - '_prefs.manual_review true means set reviewed is not ' + + 'prefs.manual_review true means set reviewed is not ' + 'automatically called', async () => { const setReviewedFileStatusStub = sinon @@ -1204,7 +1204,7 @@ suite('gr-diff-view tests', () => { } ); - test('_prefs.manual_review false means set reviewed is called', async () => { + test('prefs.manual_review false means set reviewed is called', async () => { const setReviewedFileStatusStub = sinon .stub(changeModel, 'setReviewedFilesStatus') .callsFake(() => Promise.resolve()); diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-binary.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-binary.ts index 502c6cff66..cc45e1ea7e 100644 --- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-binary.ts +++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-binary.ts @@ -3,17 +3,13 @@ * Copyright 2017 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -import {GrDiffBuilderLit} from './gr-diff-builder-lit'; +import {GrDiffBuilder} from './gr-diff-builder'; import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff'; import {createElementDiff} from '../gr-diff/gr-diff-utils'; import {GrDiffGroup} from '../gr-diff/gr-diff-group'; import {html, render} from 'lit'; -import {BinaryDiffBuilder} from './gr-diff-builder'; -export class GrDiffBuilderBinary - extends GrDiffBuilderLit - implements BinaryDiffBuilder -{ +export class GrDiffBuilderBinary extends GrDiffBuilder { constructor( diff: DiffInfo, prefs: DiffPreferencesInfo, diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element.ts index 682e747481..396e9e2549 100644 --- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element.ts +++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element.ts @@ -7,15 +7,13 @@ import '../gr-diff-processor/gr-diff-processor'; import '../../../elements/shared/gr-hovercard/gr-hovercard'; import {GrAnnotation} from '../gr-diff-highlight/gr-annotation'; import { - DiffBuilder, - ImageDiffBuilder, + GrDiffBuilder, DiffContextExpandedEventDetail, isImageDiffBuilder, isBinaryDiffBuilder, } from './gr-diff-builder'; import {GrDiffBuilderImage} from './gr-diff-builder-image'; import {GrDiffBuilderBinary} from './gr-diff-builder-binary'; -import {GrDiffBuilderLit} from './gr-diff-builder-lit'; import {CancelablePromise, makeCancelable} from '../../../utils/async-util'; import {BlameInfo, ImageInfo} from '../../../types/common'; import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff'; @@ -117,7 +115,7 @@ export class GrDiffBuilderElement implements GroupConsumer { layers: DiffLayer[] = []; // visible for testing - builder?: DiffBuilder | ImageDiffBuilder; + builder?: GrDiffBuilder; /** * All layers, both from the outside and the default ones. See `layers` for @@ -263,31 +261,21 @@ export class GrDiffBuilderElement implements GroupConsumer { this.layersInternal = layers; } - getContentTdByLine(lineNumber: LineNumber, side?: Side, root?: Element) { - if (!this.builder) return null; - return this.builder.getContentTdByLine(lineNumber, side, root); + getContentTdByLine(lineNumber: LineNumber, side?: Side) { + if (!this.builder) return undefined; + return this.builder.getContentTdByLine(lineNumber, side); } - private getDiffRowByChild(child: Element) { - while (!child.classList.contains('diff-row') && child.parentElement) { - child = child.parentElement; - } - return child; - } - - getContentTdByLineEl(lineEl?: Element): Element | null { - if (!lineEl) return null; + getContentTdByLineEl(lineEl?: Element): Element | undefined { + if (!lineEl) return undefined; const line = getLineNumber(lineEl); - if (!line) return null; + if (!line) return undefined; const side = getSideByLineEl(lineEl); - // Performance optimization because we already have an element in the - // correct row - const row = this.getDiffRowByChild(lineEl); - return this.getContentTdByLine(line, side, row); + return this.getContentTdByLine(line, side); } getLineElByNumber(lineNumber: LineNumber, side?: Side) { - if (!this.builder) return null; + if (!this.builder) return undefined; return this.builder.getLineElByNumber(lineNumber, side); } @@ -409,7 +397,7 @@ export class GrDiffBuilderElement implements GroupConsumer { } // visible for testing - getDiffBuilder(): DiffBuilder { + getDiffBuilder(): GrDiffBuilder { assertIsDefined(this.diff, 'diff'); assertIsDefined(this.diffElement, 'diff table'); if (isNaN(this.prefs.tab_size) || this.prefs.tab_size <= 0) { @@ -445,7 +433,7 @@ export class GrDiffBuilderElement implements GroupConsumer { ...this.renderPrefs, view_mode: DiffViewMode.SIDE_BY_SIDE, }; - builder = new GrDiffBuilderLit( + builder = new GrDiffBuilder( this.diff, localPrefs, this.diffElement, @@ -457,7 +445,7 @@ export class GrDiffBuilderElement implements GroupConsumer { ...this.renderPrefs, view_mode: DiffViewMode.UNIFIED, }; - builder = new GrDiffBuilderLit( + builder = new GrDiffBuilder( this.diff, localPrefs, this.diffElement, diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element_test.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element_test.ts index d2147d1590..d776164716 100644 --- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element_test.ts +++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element_test.ts @@ -26,17 +26,17 @@ import {createDefaultDiffPrefs} from '../../../constants/constants'; import {KeyLocations} from '../gr-diff-processor/gr-diff-processor'; import {fixture, html, assert} from '@open-wc/testing'; import {GrDiffRow} from './gr-diff-row'; -import {GrDiffBuilderLit} from './gr-diff-builder-lit'; +import {GrDiffBuilder} from './gr-diff-builder'; const DEFAULT_PREFS = createDefaultDiffPrefs(); suite('gr-diff-builder tests', () => { let element: GrDiffBuilderElement; - let builder: GrDiffBuilderLit; + let builder: GrDiffBuilder; let diffTable: HTMLTableElement; const setBuilderPrefs = (prefs: Partial<DiffPreferencesInfo>) => { - builder = new GrDiffBuilderLit( + builder = new GrDiffBuilder( createEmptyDiff(), {...createDefaultDiffPrefs(), ...prefs}, diffTable @@ -69,8 +69,8 @@ suite('gr-diff-builder tests', () => { tab_size: 4, line_length: 50, }; - builder = element.getDiffBuilder() as GrDiffBuilderLit; - assert.equal(builder._prefs.line_length, 50); + builder = element.getDiffBuilder(); + assert.equal(builder.prefs.line_length, 50); }); test(`line_length ignored for commit msg under ${mode}`, () => { @@ -82,8 +82,8 @@ suite('gr-diff-builder tests', () => { tab_size: 4, line_length: 50, }; - builder = element.getDiffBuilder() as GrDiffBuilderLit; - assert.equal(builder._prefs.line_length, 72); + builder = element.getDiffBuilder(); + assert.equal(builder.prefs.line_length, 72); }); }); diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-image.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-image.ts index f0cc69f8fa..3bffe08bc0 100644 --- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-image.ts +++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-image.ts @@ -7,19 +7,15 @@ import {ImageInfo} from '../../../types/common'; import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff'; import {RenderPreferences, Side} from '../../../api/diff'; import '../gr-diff-image-viewer/gr-image-viewer'; -import {ImageDiffBuilder} from './gr-diff-builder'; import {html, LitElement, nothing} from 'lit'; import {customElement, property, query, state} from 'lit/decorators.js'; -import {GrDiffBuilderLit} from './gr-diff-builder-lit'; +import {GrDiffBuilder} from './gr-diff-builder'; // MIME types for images we allow showing. Do not include SVG, it can contain // arbitrary JavaScript. const IMAGE_MIME_PATTERN = /^image\/(bmp|gif|x-icon|jpeg|jpg|png|tiff|webp)$/; -export class GrDiffBuilderImage - extends GrDiffBuilderLit - implements ImageDiffBuilder -{ +export class GrDiffBuilderImage extends GrDiffBuilder { constructor( diff: DiffInfo, prefs: DiffPreferencesInfo, diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-lit.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-lit.ts deleted file mode 100644 index c834afc9fb..0000000000 --- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-lit.ts +++ /dev/null @@ -1,214 +0,0 @@ -/** - * @license - * Copyright 2022 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -import {DiffViewMode, RenderPreferences} from '../../../api/diff'; -import {LineNumber} from '../gr-diff/gr-diff-line'; -import {GrDiffGroup} from '../gr-diff/gr-diff-group'; -import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff'; -import {Side} from '../../../constants/constants'; -import {DiffLayer, isDefined} from '../../../types/types'; -import {diffClasses} from '../gr-diff/gr-diff-utils'; -import {GrDiffBuilder} from './gr-diff-builder'; -import {BlameInfo} from '../../../types/common'; -import {html, nothing, render} from 'lit'; -import {GrDiffSection} from './gr-diff-section'; -import '../gr-context-controls/gr-context-controls'; -import './gr-diff-section'; -import {GrDiffRow} from './gr-diff-row'; - -/** - * Base class for builders that are creating the diff using Lit elements. - */ -export class GrDiffBuilderLit extends GrDiffBuilder { - constructor( - diff: DiffInfo, - prefs: DiffPreferencesInfo, - outputEl: HTMLElement, - layers: DiffLayer[] = [], - renderPrefs?: RenderPreferences - ) { - super(diff, prefs, outputEl, layers, renderPrefs); - } - - override getContentTdByLine( - lineNumber: LineNumber, - side?: Side, - _root: Element = this.outputEl - ): HTMLTableCellElement | null { - if (!side) return null; - const row = this.findRow(lineNumber, side); - return row?.getContentCell(side) ?? null; - } - - override getLineElByNumber(lineNumber: LineNumber, side: Side) { - const row = this.findRow(lineNumber, side); - return row?.getLineNumberCell(side) ?? null; - } - - private findRow(lineNumber?: LineNumber, side?: Side): GrDiffRow | undefined { - if (!side || !lineNumber) return undefined; - const group = this.findGroup(side, lineNumber); - if (!group) return undefined; - const section = this.findSection(group); - if (!section) return undefined; - return section.findRow(side, lineNumber); - } - - private getDiffRows() { - const sections = [ - ...this.outputEl.querySelectorAll<GrDiffSection>('gr-diff-section'), - ]; - return sections.map(s => s.getDiffRows()).flat(); - } - - override getLineNumberRows(): HTMLTableRowElement[] { - const rows = this.getDiffRows(); - return rows.map(r => r.getTableRow()).filter(isDefined); - } - - override getLineNumEls(side: Side): HTMLTableCellElement[] { - const rows = this.getDiffRows(); - return rows.map(r => r.getLineNumberCell(side)).filter(isDefined); - } - - override getBlameTdByLine(lineNumber: number): Element | undefined { - return this.findRow(lineNumber, Side.LEFT)?.getBlameCell(); - } - - override getContentByLine( - lineNumber: LineNumber, - side?: Side, - _root?: HTMLElement - ): HTMLElement | null { - const cell = this.getContentTdByLine(lineNumber, side); - return (cell?.firstChild ?? null) as HTMLElement | null; - } - - /** This is used when layers initiate an update. */ - override renderContentByRange( - start: LineNumber, - end: LineNumber, - side: Side - ) { - const groups = this.getGroupsByLineRange(start, end, side); - for (const group of groups) { - const section = this.findSection(group); - for (const row of section?.getDiffRows() ?? []) { - row.requestUpdate(); - } - } - } - - private findSection(group?: GrDiffGroup): GrDiffSection | undefined { - if (!group) return undefined; - const leftClass = `left-${group.startLine(Side.LEFT)}`; - const rightClass = `right-${group.startLine(Side.RIGHT)}`; - return ( - this.outputEl.querySelector<GrDiffSection>( - `gr-diff-section.${leftClass}.${rightClass}` - ) ?? undefined - ); - } - - override renderBlameByRange( - blameInfo: BlameInfo, - start: number, - end: number - ) { - for (let lineNumber = start; lineNumber <= end; lineNumber++) { - const row = this.findRow(lineNumber, Side.LEFT); - if (!row) continue; - row.blameInfo = blameInfo; - } - } - - // TODO: Refactor this such that adding the move controls becomes part of the - // lit element. - protected override getMoveControlsConfig() { - return { - numberOfCells: 6, // How many cells does the diff table have? - movedOutIndex: 2, // Index of left content column in diff table. - movedInIndex: 5, // Index of right content column in diff table. - lineNumberCols: [0, 3], // Indices of line number columns in diff table. - signCols: {left: 1, right: 4}, - }; - } - - protected override buildSectionElement(group: GrDiffGroup): HTMLElement { - const leftCl = `left-${group.startLine(Side.LEFT)}`; - const rightCl = `right-${group.startLine(Side.RIGHT)}`; - const section = html` - <gr-diff-section - class="${leftCl} ${rightCl}" - .group=${group} - .diff=${this._diff} - .layers=${this.layers} - .diffPrefs=${this._prefs} - .renderPrefs=${this.renderPrefs} - ></gr-diff-section> - `; - // When using Lit's `render()` method it wants to be in full control of the - // element that it renders into, so we let it render into a temp element. - // Rendering into the diff table directly would interfere with - // `clearDiffContent()`for example. - // TODO: Remove legacy diff builder, then convert <gr-diff> to be fully lit - // controlled, then this code will become part of the standard `render()` of - // <gr-diff> as a LitElement. - const tempEl = document.createElement('div'); - render(section, tempEl); - const sectionEl = tempEl.firstElementChild as GrDiffSection; - return sectionEl; - } - - override addColumns(outputEl: HTMLElement, lineNumberWidth: number): void { - const colgroup = html` - <colgroup> - <col class=${diffClasses('blame')}></col> - ${this.renderUnifiedColumns(lineNumberWidth)} - ${this.renderSideBySideColumns(Side.LEFT, lineNumberWidth)} - ${this.renderSideBySideColumns(Side.RIGHT, lineNumberWidth)} - </colgroup> - `; - // When using Lit's `render()` method it wants to be in full control of the - // element that it renders into, so we let it render into a temp element. - // Rendering into the diff table directly would interfere with - // `clearDiffContent()`for example. - // TODO: Remove legacy diff builder, then convert <gr-diff> to be fully lit - // controlled, then this code will become part of the standard `render()` of - // <gr-diff> as a LitElement. - const tempEl = document.createElement('div'); - render(colgroup, tempEl); - const colgroupEl = tempEl.firstElementChild as HTMLElement; - outputEl.appendChild(colgroupEl); - } - - private renderUnifiedColumns(lineNumberWidth: number) { - if (this.renderPrefs?.view_mode !== DiffViewMode.UNIFIED) return nothing; - return html` - <col class=${diffClasses()} width=${lineNumberWidth}></col> - <col class=${diffClasses()} width=${lineNumberWidth}></col> - <col class=${diffClasses()}></col> - `; - } - - private renderSideBySideColumns(side: Side, lineNumberWidth: number) { - if (this.renderPrefs?.view_mode === DiffViewMode.UNIFIED) return nothing; - return html` - <col class=${diffClasses(side)} width=${lineNumberWidth}></col> - <col class=${diffClasses(side, 'sign')}></col> - <col class=${diffClasses(side)}></col> - `; - } - - protected override getNextContentOnSide( - _content: HTMLElement, - _side: Side - ): HTMLElement | null { - // TODO: getNextContentOnSide() is not required by lit based rendering. - // So let's refactor it to be moved into gr-diff-builder-legacy. - console.warn('unimplemented method getNextContentOnSide() called'); - return null; - } -} diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts index 2581c03a0c..f38ba5c3b1 100644 --- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts +++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder.ts @@ -3,19 +3,27 @@ * Copyright 2016 Google LLC * SPDX-License-Identifier: Apache-2.0 */ +import './gr-diff-section'; +import '../gr-context-controls/gr-context-controls'; import { ContentLoadNeededEventDetail, DiffContextExpandedExternalDetail, + DiffViewMode, RenderPreferences, } from '../../../api/diff'; -import {GrDiffLine, GrDiffLineType, LineNumber} from '../gr-diff/gr-diff-line'; +import {LineNumber} from '../gr-diff/gr-diff-line'; import {GrDiffGroup} from '../gr-diff/gr-diff-group'; -import {assert} from '../../../utils/common-util'; -import '../gr-context-controls/gr-context-controls'; import {BlameInfo} from '../../../types/common'; import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff'; import {Side} from '../../../constants/constants'; -import {DiffLayer} from '../../../types/types'; +import {DiffLayer, isDefined} from '../../../types/types'; +import {GrDiffRow} from './gr-diff-row'; +import {GrDiffSection} from './gr-diff-section'; +import {html, render} from 'lit'; +import {diffClasses} from '../gr-diff/gr-diff-utils'; +import {when} from 'lit/directives/when.js'; +import {GrDiffBuilderImage} from './gr-diff-builder-image'; +import {GrDiffBuilderBinary} from './gr-diff-builder-binary'; export interface DiffContextExpandedEventDetail extends DiffContextExpandedExternalDetail { @@ -32,83 +40,34 @@ declare global { } } -/** - * Given that GrDiffBuilder has ~1,000 lines of code, this interface is just - * making refactorings easier by emphasizing what the public facing "contract" - * of this class is. There are no plans for adding separate implementations. - */ -export interface DiffBuilder { - init(): void; - cleanup(): void; - addGroups(groups: readonly GrDiffGroup[]): void; - clearGroups(): void; - replaceGroup( - contextControl: GrDiffGroup, - groups: readonly GrDiffGroup[] - ): void; - findGroup(side: Side, line: LineNumber): GrDiffGroup | undefined; - addColumns(outputEl: HTMLElement, fontSize: number): void; - // TODO: Change `null` to `undefined`. - getContentTdByLine( - lineNumber: LineNumber, - side?: Side, - root?: Element - ): HTMLTableCellElement | null; - getLineElByNumber( - lineNumber: LineNumber, - side?: Side - ): HTMLTableCellElement | null; - getLineNumberRows(): HTMLTableRowElement[]; - getLineNumEls(side: Side): HTMLTableCellElement[]; - setBlame(blame: BlameInfo[]): void; - updateRenderPrefs(renderPrefs: RenderPreferences): void; -} - -export interface ImageDiffBuilder extends DiffBuilder { - renderImageDiff(): void; +export function isImageDiffBuilder<T extends GrDiffBuilder>( + x: T | GrDiffBuilderImage | undefined +): x is GrDiffBuilderImage { + return !!x && !!(x as GrDiffBuilderImage).renderImageDiff; } -export function isImageDiffBuilder( - x: DiffBuilder | ImageDiffBuilder | undefined -): x is ImageDiffBuilder { - return !!x && !!(x as ImageDiffBuilder).renderImageDiff; -} - -export interface BinaryDiffBuilder extends DiffBuilder { - renderBinaryDiff(): void; -} - -export function isBinaryDiffBuilder( - x: DiffBuilder | BinaryDiffBuilder | undefined -): x is BinaryDiffBuilder { - return !!x && !!(x as BinaryDiffBuilder).renderBinaryDiff; +export function isBinaryDiffBuilder<T extends GrDiffBuilder>( + x: T | GrDiffBuilderBinary | undefined +): x is GrDiffBuilderBinary { + return !!x && !!(x as GrDiffBuilderBinary).renderBinaryDiff; } /** - * Base class for different diff builders, like side-by-side, unified etc. - * * The builder takes GrDiffGroups, and builds the corresponding DOM elements, * called sections. Only the builder should add or remove sections from the * DOM. Callers can use the ...group() methods to modify groups and thus cause * rendering changes. - * - * TODO: Do not subclass `GrDiffBuilder`. Use composition and interfaces. */ -export abstract class GrDiffBuilder implements DiffBuilder { - protected readonly _diff: DiffInfo; - - protected readonly numLinesLeft: number; - - // visible for testing - readonly _prefs: DiffPreferencesInfo; +export class GrDiffBuilder { + private readonly diff: DiffInfo; - protected renderPrefs?: RenderPreferences; + readonly prefs: DiffPreferencesInfo; - protected readonly outputEl: HTMLElement; + renderPrefs?: RenderPreferences; - protected groups: GrDiffGroup[]; + readonly outputEl: HTMLElement; - private blameInfo: BlameInfo[] = []; + private groups: GrDiffGroup[]; private readonly layerUpdateListener: ( start: LineNumber, @@ -123,14 +82,8 @@ export abstract class GrDiffBuilder implements DiffBuilder { readonly layers: DiffLayer[] = [], renderPrefs?: RenderPreferences ) { - this._diff = diff; - this.numLinesLeft = this._diff.content - ? this._diff.content.reduce((sum, chunk) => { - const left = chunk.a || chunk.ab; - return sum + (left?.length || chunk.skip || 0); - }, 0) - : 0; - this._prefs = prefs; + this.diff = diff; + this.prefs = prefs; this.renderPrefs = renderPrefs; this.outputEl = outputEl; this.groups = []; @@ -151,6 +104,138 @@ export abstract class GrDiffBuilder implements DiffBuilder { this.init(); } + getContentTdByLine( + lineNumber: LineNumber, + side?: Side + ): HTMLTableCellElement | undefined { + if (!side) return undefined; + const row = this.findRow(lineNumber, side); + return row?.getContentCell(side); + } + + getLineElByNumber( + lineNumber: LineNumber, + side?: Side + ): HTMLTableCellElement | undefined { + if (!side) return undefined; + const row = this.findRow(lineNumber, side); + return row?.getLineNumberCell(side); + } + + private findRow(lineNumber?: LineNumber, side?: Side): GrDiffRow | undefined { + if (!side || !lineNumber) return undefined; + const group = this.findGroup(side, lineNumber); + if (!group) return undefined; + const section = this.findSection(group); + if (!section) return undefined; + return section.findRow(side, lineNumber); + } + + private getDiffRows() { + const sections = [ + ...this.outputEl.querySelectorAll<GrDiffSection>('gr-diff-section'), + ]; + return sections.map(s => s.getDiffRows()).flat(); + } + + getLineNumberRows(): HTMLTableRowElement[] { + const rows = this.getDiffRows(); + return rows.map(r => r.getTableRow()).filter(isDefined); + } + + getLineNumEls(side: Side): HTMLTableCellElement[] { + const rows = this.getDiffRows(); + return rows.map(r => r.getLineNumberCell(side)).filter(isDefined); + } + + /** This is used when layers initiate an update. */ + renderContentByRange(start: LineNumber, end: LineNumber, side: Side) { + const groups = this.getGroupsByLineRange(start, end, side); + for (const group of groups) { + const section = this.findSection(group); + for (const row of section?.getDiffRows() ?? []) { + row.requestUpdate(); + } + } + } + + private findSection(group: GrDiffGroup): GrDiffSection | undefined { + const leftClass = `left-${group.startLine(Side.LEFT)}`; + const rightClass = `right-${group.startLine(Side.RIGHT)}`; + return ( + this.outputEl.querySelector<GrDiffSection>( + `gr-diff-section.${leftClass}.${rightClass}` + ) ?? undefined + ); + } + + buildSectionElement(group: GrDiffGroup): HTMLElement { + const leftCl = `left-${group.startLine(Side.LEFT)}`; + const rightCl = `right-${group.startLine(Side.RIGHT)}`; + const section = html` + <gr-diff-section + class="${leftCl} ${rightCl}" + .group=${group} + .diff=${this.diff} + .layers=${this.layers} + .diffPrefs=${this.prefs} + .renderPrefs=${this.renderPrefs} + ></gr-diff-section> + `; + // When using Lit's `render()` method it wants to be in full control of the + // element that it renders into, so we let it render into a temp element. + // Rendering into the diff table directly would interfere with + // `clearDiffContent()`for example. + // TODO: Convert <gr-diff> to be fully lit controlled and incorporate this + // method into Lit's `render()` cycle. + const tempEl = document.createElement('div'); + render(section, tempEl); + const sectionEl = tempEl.firstElementChild as GrDiffSection; + return sectionEl; + } + + addColumns(outputEl: HTMLElement, lineNumberWidth: number): void { + const colgroup = html` + <colgroup> + <col class=${diffClasses('blame')}></col> + ${when( + this.renderPrefs?.view_mode === DiffViewMode.UNIFIED, + () => html` ${this.renderUnifiedColumns(lineNumberWidth)} `, + () => html` + ${this.renderSideBySideColumns(Side.LEFT, lineNumberWidth)} + ${this.renderSideBySideColumns(Side.RIGHT, lineNumberWidth)} + ` + )} + </colgroup> + `; + // When using Lit's `render()` method it wants to be in full control of the + // element that it renders into, so we let it render into a temp element. + // Rendering into the diff table directly would interfere with + // `clearDiffContent()`for example. + // TODO: Convert <gr-diff> to be fully lit controlled and incorporate this + // method into Lit's `render()` cycle. + const tempEl = document.createElement('div'); + render(colgroup, tempEl); + const colgroupEl = tempEl.firstElementChild as HTMLElement; + outputEl.appendChild(colgroupEl); + } + + private renderUnifiedColumns(lineNumberWidth: number) { + return html` + <col class=${diffClasses()} width=${lineNumberWidth}></col> + <col class=${diffClasses()} width=${lineNumberWidth}></col> + <col class=${diffClasses()}></col> + `; + } + + private renderSideBySideColumns(side: Side, lineNumberWidth: number) { + return html` + <col class=${diffClasses(side)} width=${lineNumberWidth}></col> + <col class=${diffClasses(side, 'sign')}></col> + <col class=${diffClasses(side)}></col> + `; + } + /** * This is meant to be called when the gr-diff component re-connects, or when * the diff is (re-)rendered. @@ -182,10 +267,6 @@ export abstract class GrDiffBuilder implements DiffBuilder { } } - abstract addColumns(outputEl: HTMLElement, fontSize: number): void; - - protected abstract buildSectionElement(group: GrDiffGroup): HTMLElement; - addGroups(groups: readonly GrDiffGroup[]) { for (const group of groups) { this.groups.push(group); @@ -248,151 +329,19 @@ export abstract class GrDiffBuilder implements DiffBuilder { .filter(group => group.lines.length > 0); } - // TODO: Change `null` to `undefined`. - abstract getContentTdByLine( - lineNumber: LineNumber, - side?: Side, - root?: Element - ): HTMLTableCellElement | null; - - // TODO: Change `null` to `undefined`. - abstract getLineElByNumber( - lineNumber: LineNumber, - side?: Side - ): HTMLTableCellElement | null; - - abstract getLineNumberRows(): HTMLTableRowElement[]; - - abstract getLineNumEls(side: Side): HTMLTableCellElement[]; - - protected abstract getBlameTdByLine(lineNum: number): Element | undefined; - - // TODO: Change `null` to `undefined`. - protected abstract getContentByLine( - lineNumber: LineNumber, - side?: Side, - root?: HTMLElement - ): HTMLElement | null; - - /** - * Find line elements or line objects by a range of line numbers and a side. - * - * @param start The first line number - * @param end The last line number - * @param side The side of the range. Either 'left' or 'right'. - * @param out_lines The output list of line objects. - * TODO: Change to camelCase. - * @param out_elements The output list of line elements. - * TODO: Change to camelCase. - */ - // visible for testing - findLinesByRange( - start: LineNumber, - end: LineNumber, - side: Side, - out_lines: GrDiffLine[], - out_elements: HTMLElement[] - ) { - const groups = this.getGroupsByLineRange(start, end, side); - for (const group of groups) { - let content: HTMLElement | null = null; - for (const line of group.lines) { - if ( - (side === 'left' && line.type === GrDiffLineType.ADD) || - (side === 'right' && line.type === GrDiffLineType.REMOVE) - ) { - continue; - } - const lineNumber = - side === 'left' ? line.beforeNumber : line.afterNumber; - if (lineNumber < start || lineNumber > end) { - continue; - } - - if (content) { - content = this.getNextContentOnSide(content, side); - } else { - content = this.getContentByLine(lineNumber, side, group.element); - } - if (content) { - // out_lines and out_elements must match. So if we don't have an - // element to push, then also don't push a line. - out_lines.push(line); - out_elements.push(content); - } - } - } - assert( - out_lines.length === out_elements.length, - 'findLinesByRange: lines and elements arrays must have same length' - ); - } - - protected abstract renderContentByRange( - start: LineNumber, - end: LineNumber, - side: Side - ): void; - - protected abstract renderBlameByRange( - blame: BlameInfo, - start: number, - end: number - ): void; - - /** - * Finds the next DIV.contentText element following the given element, and on - * the same side. Will only search within a group. - * - * TODO: Change `null` to `undefined`. - */ - protected abstract getNextContentOnSide( - content: HTMLElement, - side: Side - ): HTMLElement | null; - - /** - * Gets configuration for creating move controls for chunks marked with - * dueToMove - */ - protected abstract getMoveControlsConfig(): { - numberOfCells: number; - movedOutIndex: number; - movedInIndex: number; - lineNumberCols: number[]; - signCols?: {left: number; right: number}; - }; - /** * Set the blame information for the diff. For any already-rendered line, * re-render its blame cell content. */ setBlame(blame: BlameInfo[]) { - this.blameInfo = blame; - for (const commit of blame) { - for (const range of commit.ranges) { - this.renderBlameByRange(commit, range.start, range.end); - } - } - } - - /** - * Given a base line number, return the commit containing that line in the - * current set of blame information. If no blame information has been - * provided, null is returned. - * - * @return The commit information. - */ - // visible for testing - getBlameCommitForBaseLine(lineNum: LineNumber): BlameInfo | undefined { - for (const blameCommit of this.blameInfo) { - for (const range of blameCommit.ranges) { - if (range.start <= lineNum && range.end >= lineNum) { - return blameCommit; + for (const blameInfo of blame) { + for (const range of blameInfo.ranges) { + for (let line = range.start; line <= range.end; line++) { + const row = this.findRow(line, Side.LEFT); + if (row) row.blameInfo = blameInfo; } } } - return undefined; } /** diff --git a/polygerrit-ui/app/embed/diff/gr-diff-highlight/gr-diff-highlight.ts b/polygerrit-ui/app/embed/diff/gr-diff-highlight/gr-diff-highlight.ts index 02f2233d33..69c0f5c43c 100644 --- a/polygerrit-ui/app/embed/diff/gr-diff-highlight/gr-diff-highlight.ts +++ b/polygerrit-ui/app/embed/diff/gr-diff-highlight/gr-diff-highlight.ts @@ -44,7 +44,7 @@ interface NormalizedRange { * fully blown dependency on GrDiffBuilderElement. */ export interface DiffBuilderInterface { - getContentTdByLineEl(lineEl?: Element): Element | null; + getContentTdByLineEl(lineEl?: Element): Element | undefined; } /** diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-group.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-group.ts index 3b2fa81cc4..2ec0a2e4ec 100644 --- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-group.ts +++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-group.ts @@ -506,16 +506,8 @@ export class GrDiffGroup { return Promise.resolve(); } assertIsDefined(this.element); - // This is a temporary hack while migration to lit based diff rendering: - // Elements with 'display: contents;' do not have a height, so they - // won't work as intended with `untilRendered()`. - const isLitDiff = this.element.tagName === 'GR-DIFF-SECTION'; - if (isLitDiff) { - await (this.element as LitElement).updateComplete; - await untilRendered(this.element.firstElementChild as HTMLElement); - } else { - await untilRendered(this.element); - } + await (this.element as LitElement).updateComplete; + await untilRendered(this.element.firstElementChild as HTMLElement); } /** |