Merge pull request #42 from gitbutlerapp/sc-render-removals

Better rendering of removed files
This commit is contained in:
Scott Chacon 2023-03-23 15:07:17 +01:00 committed by GitHub
commit d2fdecc752
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 152 additions and 145 deletions

View File

@ -1,165 +1,165 @@
import { Operation, type DiffArray, charDiff } from './diff';
export interface Token {
text: string;
className: string;
text: string;
className: string;
}
export interface Row {
originalLineNumber: number;
currentLineNumber: number;
tokens: Token[];
type: RowType;
size: number;
originalLineNumber: number;
currentLineNumber: number;
tokens: Token[];
type: RowType;
size: number;
}
export const enum RowType {
Deletion = 'deletion',
Addition = 'addition',
Equal = 'equal',
Spacer = 'spacer'
Deletion = 'deletion',
Addition = 'addition',
Equal = 'equal',
Spacer = 'spacer'
}
export function buildDiffRows(
diff: DiffArray,
opts = { paddingLines: 10000 }
diff: DiffArray,
opts = { paddingLines: 10000 }
): {
originalLines: readonly string[];
currentLines: readonly string[];
rows: readonly Row[];
originalLines: readonly string[];
currentLines: readonly string[];
rows: readonly Row[];
} {
const { paddingLines } = opts;
const { paddingLines } = opts;
let currentLineNumber = 0;
let originalLineNumber = 0;
let currentLineNumber = 0;
let originalLineNumber = 0;
const originalLines: string[] = [];
const currentLines: string[] = [];
const rows: Row[] = [];
const originalLines: string[] = [];
const currentLines: string[] = [];
const rows: Row[] = [];
for (let i = 0; i < diff.length; ++i) {
const token = diff[i];
switch (token[0]) {
case Operation.Equal:
rows.push(...createEqualRows(token[1], i === 0, i === diff.length - 1));
originalLines.push(...token[1]);
currentLines.push(...token[1]);
break;
case Operation.Insert:
for (const line of token[1]) {
rows.push(createRow(line, RowType.Addition));
}
currentLines.push(...token[1]);
break;
case Operation.Delete:
originalLines.push(...token[1]);
if (diff[i + 1] && diff[i + 1][0] === Operation.Insert) {
i++;
rows.push(...createModifyRows(token[1].join('\n'), diff[i][1].join('\n')));
currentLines.push(...diff[i][1]);
} else {
for (const line of token[1]) {
rows.push(createRow(line, RowType.Deletion));
}
}
break;
}
}
for (let i = 0; i < diff.length; ++i) {
const token = diff[i];
switch (token[0]) {
case Operation.Equal:
rows.push(...createEqualRows(token[1], i === 0, i === diff.length - 1));
originalLines.push(...token[1]);
currentLines.push(...token[1]);
break;
case Operation.Insert:
for (const line of token[1]) {
rows.push(createRow(line, RowType.Addition));
}
currentLines.push(...token[1]);
break;
case Operation.Delete:
originalLines.push(...token[1]);
if (diff[i + 1] && diff[i + 1][0] === Operation.Insert) {
i++;
rows.push(...createModifyRows(token[1].join('\n'), diff[i][1].join('\n')));
currentLines.push(...diff[i][1]);
} else {
for (const line of token[1]) {
rows.push(createRow(line, RowType.Deletion));
}
}
break;
}
}
return { originalLines, currentLines, rows };
return { originalLines, currentLines, rows };
function createEqualRows(lines: string[], atStart: boolean, atEnd: boolean): Row[] {
const equalRows = [];
if (!atStart) {
for (let i = 0; i < paddingLines && i < lines.length; i++) {
equalRows.push(createRow(lines[i], RowType.Equal));
}
if (lines.length > paddingLines * 2 + 1 && !atEnd) {
equalRows.push(
createRow(
`skipping ${lines.length - paddingLines * 2} matching lines`,
RowType.Spacer,
lines.length - paddingLines * 2
)
);
}
}
if (!atEnd) {
const start = Math.max(lines.length - paddingLines - 1, atStart ? 0 : paddingLines);
let skip = lines.length - paddingLines - 1;
if (!atStart) {
skip -= paddingLines;
}
if (skip > 0) {
originalLineNumber += skip;
currentLineNumber += skip;
}
function createEqualRows(lines: string[], atStart: boolean, atEnd: boolean): Row[] {
const equalRows = [];
if (!atStart) {
for (let i = 0; i < paddingLines && i < lines.length; i++) {
equalRows.push(createRow(lines[i], RowType.Equal));
}
if (lines.length > paddingLines * 2 + 1 && !atEnd) {
equalRows.push(
createRow(
`skipping ${lines.length - paddingLines * 2} matching lines`,
RowType.Spacer,
lines.length - paddingLines * 2
)
);
}
}
if (!atEnd) {
const start = Math.max(lines.length - paddingLines - 1, atStart ? 0 : paddingLines);
let skip = lines.length - paddingLines - 1;
if (!atStart) {
skip -= paddingLines;
}
if (skip > 0) {
originalLineNumber += skip;
currentLineNumber += skip;
}
for (let i = start; i < lines.length; i++) {
equalRows.push(createRow(lines[i], RowType.Equal));
}
}
return equalRows;
}
for (let i = start; i < lines.length; i++) {
equalRows.push(createRow(lines[i], RowType.Equal));
}
}
return equalRows;
}
function createModifyRows(before: string, after: string): Row[] {
const internalDiff = charDiff(before, after, true /* cleanup diff */);
const deletionRows = [createRow('', RowType.Deletion)];
const insertionRows = [createRow('', RowType.Addition)];
function createModifyRows(before: string, after: string): Row[] {
const internalDiff = charDiff(before, after, true /* cleanup diff */);
const deletionRows = [createRow('', RowType.Deletion)];
const insertionRows = [createRow('', RowType.Addition)];
for (const token of internalDiff) {
const text = token[1];
const type = token[0];
const className = type === Operation.Equal ? '' : 'inner-diff';
const lines = text.split('\n');
for (let i = 0; i < lines.length; i++) {
if (i > 0 && type !== Operation.Insert) {
deletionRows.push(createRow('', RowType.Deletion));
}
if (i > 0 && type !== Operation.Delete) {
insertionRows.push(createRow('', RowType.Addition));
}
if (!lines[i]) {
continue;
}
if (type !== Operation.Insert) {
deletionRows[deletionRows.length - 1].tokens.push({ text: lines[i], className });
}
if (type !== Operation.Delete) {
insertionRows[insertionRows.length - 1].tokens.push({ text: lines[i], className });
}
}
}
return deletionRows.concat(insertionRows);
}
for (const token of internalDiff) {
const text = token[1];
const type = token[0];
const className = type === Operation.Equal ? '' : 'inner-diff';
const lines = text.split('\n');
for (let i = 0; i < lines.length; i++) {
if (i > 0 && type !== Operation.Insert) {
deletionRows.push(createRow('', RowType.Deletion));
}
if (i > 0 && type !== Operation.Delete) {
insertionRows.push(createRow('', RowType.Addition));
}
if (!lines[i]) {
continue;
}
if (type !== Operation.Insert) {
deletionRows[deletionRows.length - 1].tokens.push({ text: lines[i], className });
}
if (type !== Operation.Delete) {
insertionRows[insertionRows.length - 1].tokens.push({ text: lines[i], className });
}
}
}
return deletionRows.concat(insertionRows);
}
function createRow(text: string, type: RowType, size = 1): Row {
if (type === RowType.Addition) {
currentLineNumber++;
}
if (type === RowType.Deletion) {
originalLineNumber++;
}
if (type === RowType.Equal) {
originalLineNumber++;
currentLineNumber++;
}
function createRow(text: string, type: RowType, size = 1): Row {
if (type === RowType.Addition) {
currentLineNumber++;
}
if (type === RowType.Deletion) {
originalLineNumber++;
}
if (type === RowType.Equal) {
originalLineNumber++;
currentLineNumber++;
}
return {
originalLineNumber,
currentLineNumber,
tokens: text ? [{ text, className: 'inner-diff' }] : [],
type,
size
};
}
return {
originalLineNumber,
currentLineNumber,
tokens: text ? [{ text, className: 'inner-diff' }] : [],
type,
size
};
}
}
export function documentMap(lines: readonly string[]): Map<number, number> {
const map = new Map<number, number>();
for (let pos = 0, lineNo = 0; lineNo < lines.length; lineNo++) {
map.set(lineNo + 1, pos);
pos += lines[lineNo].length + 1;
}
return map;
const map = new Map<number, number>();
for (let pos = 0, lineNo = 0; lineNo < lines.length; lineNo++) {
map.set(lineNo + 1, pos);
pos += lines[lineNo].length + 1;
}
return map;
}

View File

@ -14,6 +14,8 @@
let middleDiff = '';
let currentOffset = 0;
let htmlTagRegex = /(<([^>]+)>)/gi;
$: if (diff) {
middleDiff = '';
currentDiff = '';
@ -28,15 +30,16 @@
let diffLines = middleDiff.split('<br>');
diffLines.forEach((line, index) => {
lineClass = 'lineContext bg-zinc-800';
let firstChar = line.replace(htmlTagRegex, '').slice(0, 1);
if (index < 4) {
lineClass = 'lineDiff bg-zinc-900 text-zinc-500';
} else if (line.slice(0, 2) == '@@') {
lineClass = 'lineSplit bg-blue-900';
} else if (line.slice(0, 1) == '+') {
} else if (firstChar == '+') {
if (!line.includes('+++')) {
lineClass = 'lineSplit bg-green-900';
}
} else if (line.slice(0, 1) == '-') {
} else if (firstChar == '-') {
if (!line.includes('---')) {
lineClass = 'lineSplit bg-red-900';
}

View File

@ -76,21 +76,24 @@
let currentPath = '';
let currentDiff = '';
let addedContents = '';
let fileContents = '';
let fileContentsStatus = '';
// Replace HTML tags with an empty string
function selectPath(path) {
currentDiff = '';
addedContents = '';
fileContents = '';
if (gitDiff[path]) {
currentPath = path;
currentDiff = gitDiff[path];
} else {
let file = $filesStatus.filter((file) => file.path === path)[0];
if (file && file.status === 'added') {
if (file) {
fileContentsStatus = file.status;
getFile({ projectId: $project?.id, path: path }).then((contents) => {
currentPath = path;
addedContents = contents;
fileContents = contents;
});
}
}
@ -278,8 +281,9 @@
<div class="h-100 h-full max-h-screen flex-grow overflow-auto p-2">
{#if currentDiff}
<DiffViewer diff={currentDiff} path={currentPath} />
{:else if addedContents}
<pre class="bg-green-900">{addedContents}</pre>
{:else if fileContents}
<pre
class={fileContentsStatus == 'added' ? 'bg-green-900' : 'bg-red-900'}>{fileContents}</pre>
{:else}
<div class="p-20 text-center text-lg text-zinc-400">Select a file to view changes.</div>
{/if}