From 37319e5c5ddd71d69f210af967912d229bb1bb62 Mon Sep 17 00:00:00 2001 From: friebetill Date: Tue, 7 Feb 2023 16:01:58 +0800 Subject: [PATCH] Fix showing duplicate lines --- src/components/action_line.ts | 31 +++---- src/components/differences_view.ts | 127 ++++++++++++++++------------- src/data/difference.ts | 12 +-- src/data/file_differences.ts | 22 ++++- 4 files changed, 105 insertions(+), 87 deletions(-) diff --git a/src/components/action_line.ts b/src/components/action_line.ts index b45e4ed..692eda6 100644 --- a/src/components/action_line.ts +++ b/src/components/action_line.ts @@ -38,12 +38,8 @@ export class ActionLine { build(container: HTMLDivElement): void { const actionLine = container.createDiv({ cls: 'flex-row gap-2 py-2' }) - const hasPlusLines = this.difference.lines.some((l) => - l.startsWith('+') - ) - const hasMinusLines = this.difference.lines.some((l) => - l.startsWith('-') - ) + const hasMinusLines = this.difference.file1Lines.length > 0 + const hasPlusLines = this.difference.file2Lines.length > 0 if (hasPlusLines && hasMinusLines) { new ActionLineButton({ @@ -60,6 +56,7 @@ export class ActionLine { text: 'Accept All', onClick: (e) => this.acceptAllClick(e, this.difference), }).build(actionLine) + // TODO(tillf): Add button to accept none of the changes } else if (hasMinusLines) { new ActionLineButton({ text: `Accept from ${this.file1.name}`, @@ -89,10 +86,7 @@ export class ActionLine { ): Promise { event.preventDefault() - const changedLines = difference.lines - .filter((line) => line.startsWith('-')) - .map((line) => line.slice(1, line.length)) - .join('\n') + const changedLines = difference.file1Lines.join('\n') const newContent = replaceLine({ fullText: this.file2Content, newLine: changedLines, @@ -109,10 +103,7 @@ export class ActionLine { ): Promise { event.preventDefault() - const changedLines = difference.lines - .filter((line) => line.startsWith('+')) - .map((line) => line.slice(1, line.length)) - .join('\n') + const changedLines = difference.file2Lines.join('\n') const newContent = replaceLine({ fullText: this.file1Content, newLine: changedLines, @@ -129,10 +120,10 @@ export class ActionLine { ): Promise { event.preventDefault() - const changedLines = difference.lines - .filter((line) => line.startsWith('-') || line.startsWith('+')) - .map((line) => line.slice(1, line.length)) - .join('\n') + const changedLines = [ + ...difference.file1Lines, + ...difference.file2Lines, + ].join('\n') const newFile1Content = replaceLine({ fullText: this.file1Content, @@ -160,7 +151,7 @@ export class ActionLine { const newContent = deleteLines({ fullText: this.file1Content, position: difference.file1Start, - count: difference.lines.length, + count: difference.file1Lines.length, }) await app.vault.modify(this.file1, newContent) @@ -176,7 +167,7 @@ export class ActionLine { const newContent = deleteLines({ fullText: this.file2Content, position: difference.file2Start, - count: difference.lines.length, + count: difference.file2Lines.length, }) await app.vault.modify(this.file2, newContent) diff --git a/src/components/differences_view.ts b/src/components/differences_view.ts index fed4084..1e23628 100644 --- a/src/components/differences_view.ts +++ b/src/components/differences_view.ts @@ -36,6 +36,8 @@ export class DifferencesView extends ItemView { private file1Lines: string[] + private file2Lines: string[] + private lineCount: number private wasDeleteModalShown = false @@ -57,25 +59,23 @@ export class DifferencesView extends ItemView { // TODO(tillf): Find way to refresh state when one of the files changes this.file1Content = await this.app.vault.read(this.file1) - this.file1Lines = this.file1Content.split('\n') - this.file2Content = await this.app.vault.read(this.file2) - this.fileDifferences = FileDifferences.fromParsedDiff( - structuredPatch( - this.file1.path, - this.file2.path, - // Streamline empty lines at the end as this remove edge cases - this.file1Content.trimEnd().concat('\n'), - this.file2Content.trimEnd().concat('\n') - ) - ) - // Find the highest line number we need to go through. This can be the - // highest number in the differences, because the second file can have - // more lines than the first file. + const parsedDiff = structuredPatch( + this.file1.path, + this.file2.path, + // Streamline empty lines at the end as this remove edge cases + this.file1Content.trimEnd().concat('\n'), + this.file2Content.trimEnd().concat('\n') + ) + this.fileDifferences = FileDifferences.fromParsedDiff(parsedDiff) + + this.file1Lines = this.file1Content.split('\n') + this.file2Lines = this.file2Content.split('\n') + this.lineCount = Math.max( this.file1Lines.length, - ...this.fileDifferences.differences.map((d) => d.file1Start) + this.file2Lines.length ) } @@ -84,9 +84,26 @@ export class DifferencesView extends ItemView { const container = this.contentEl.createDiv({ cls: 'container' }) - for (let i = 0; i <= this.lineCount; i += 1) { - const line = i in this.file1Lines ? this.file1Lines[i] : null + this.buildLines(container) + + this.scrollToFirstDifference() + if ( + this.fileDifferences.differences.length === 0 && + this.showMergeOption && + !this.wasDeleteModalShown + ) { + this.showDeleteModal() + } + } + + private buildLines(container: HTMLDivElement): void { + let i = 0 + while (i <= this.lineCount) { + const line = + i in this.file1Lines ? this.file1Lines[i] : this.file2Lines[i] + const difference = this.fileDifferences.differences.find( + // eslint-disable-next-line no-loop-func (d) => d.file1Start === i ) @@ -95,39 +112,16 @@ export class DifferencesView extends ItemView { cls: 'difference', }) this.buildDifferenceVisualizer(differenceContainer, difference) - } - if ( - line != null && - (difference == null || !difference.hasChangesFromFile1()) - ) { + i += Math.max(difference.file1Lines.length, 1) + } else { container.createDiv({ // Necessary to give the line a height when it's empty. text: preventEmptyString(line), cls: 'line', }) + i += 1 } } - - if (this.fileDifferences.differences.length > 0) { - const containerRect = this.contentEl - .getElementsByClassName('container')[0] - .getBoundingClientRect() - const elementRect = this.contentEl - .getElementsByClassName('difference')[0] - .getBoundingClientRect() - this.contentEl.scrollTo({ - top: elementRect.top - containerRect.top - 100, - behavior: 'smooth', - }) - } - - if ( - this.fileDifferences.differences.length === 0 && - this.showMergeOption && - !this.wasDeleteModalShown - ) { - this.showDeleteModal() - } } private buildDifferenceVisualizer( @@ -150,20 +144,39 @@ export class DifferencesView extends ItemView { }).build(container) } - difference.lines.forEach((line) => { - if (line.startsWith('+')) { - container.createDiv({ - // Necessary to give the line a height when it's empty. - text: preventEmptyString(line.slice(1, line.length)), - cls: 'line bg-turquoise-light', - }) - } else if (line.startsWith('-')) { - container.createDiv({ - // Necessary to give the line a height when it's empty. - text: preventEmptyString(line.slice(1, line.length)), - cls: 'line bg-blue-light', - }) - } + for (let i = 0; i < difference.file1Lines.length; i += 1) { + const line = difference.file1Lines[i] + container.createDiv({ + // Necessary to give the line a height when it's empty. + text: preventEmptyString(line), + cls: 'line bg-turquoise-light', + }) + } + + for (let i = 0; i < difference.file2Lines.length; i += 1) { + const line = difference.file2Lines[i] + container.createDiv({ + // Necessary to give the line a height when it's empty. + text: preventEmptyString(line), + cls: 'line bg-blue-light', + }) + } + } + + private scrollToFirstDifference(): void { + if (this.fileDifferences.differences.length === 0) { + return + } + + const containerRect = this.contentEl + .getElementsByClassName('container')[0] + .getBoundingClientRect() + const elementRect = this.contentEl + .getElementsByClassName('difference')[0] + .getBoundingClientRect() + this.contentEl.scrollTo({ + top: elementRect.top - containerRect.top - 100, + behavior: 'smooth', }) } diff --git a/src/data/difference.ts b/src/data/difference.ts index 0bcff12..9b01a75 100644 --- a/src/data/difference.ts +++ b/src/data/difference.ts @@ -2,20 +2,20 @@ export class Difference { constructor(args: { file1Start: number file2Start: number - lines: string[] + file1Lines: string[] + file2Lines: string[] }) { this.file1Start = args.file1Start this.file2Start = args.file2Start - this.lines = args.lines + this.file1Lines = args.file1Lines + this.file2Lines = args.file2Lines } public readonly file1Start: number public readonly file2Start: number - public readonly lines: string[] + public readonly file1Lines: string[] - hasChangesFromFile1(): boolean { - return this.lines.some((l) => l.startsWith('-')) - } + public readonly file2Lines: string[] } diff --git a/src/data/file_differences.ts b/src/data/file_differences.ts index 43a639f..535382f 100644 --- a/src/data/file_differences.ts +++ b/src/data/file_differences.ts @@ -42,6 +42,8 @@ export class FileDifferences { const differences: Difference[] = [] parsedDiff.hunks.forEach((hunk) => { + let line1Count = 0 + let line2Count = 0 for (let i = 0; i < hunk.lines.length; i += 1) { const line = hunk.lines[i] @@ -49,7 +51,7 @@ export class FileDifferences { const start = i // Find the end of the contiguous lines - let end = i + let end = start while ( end < hunk.lines.length - 1 && (hunk.lines[end + 1].startsWith('+') || @@ -59,13 +61,25 @@ export class FileDifferences { } // Add the contiguous lines to the differences + const file1Lines = hunk.lines + .slice(start, end + 1) + .filter((l) => l.startsWith('-')) + .map((l) => l.slice(1)) + const file2Lines = hunk.lines + .slice(start, end + 1) + .filter((l) => l.startsWith('+')) + .map((l) => l.slice(1)) differences.push( new Difference({ - file1Start: hunk.oldStart + start - 1, - file2Start: hunk.newStart + start - 1, - lines: hunk.lines.slice(start, end + 1), + file1Start: hunk.oldStart + start - line2Count - 1, + file2Start: hunk.newStart + start - line1Count - 1, + file1Lines, + file2Lines, }) ) + + line1Count += file1Lines.length + line2Count += file2Lines.length i += end - start } }