mirror of
https://github.com/silvanocerza/github-gitless-sync.git
synced 2026-07-22 12:10:28 +00:00
Avoid creating line mapping by creating ranges directly
This commit is contained in:
parent
08d5d05331
commit
dc2962cec8
1 changed files with 59 additions and 132 deletions
|
|
@ -24,159 +24,88 @@ interface UnifiedDiffViewProps {
|
|||
onConflictResolved: () => void;
|
||||
}
|
||||
|
||||
interface LineMapping {
|
||||
oldLine: number | null;
|
||||
finalLine: number;
|
||||
newLine: number | null;
|
||||
}
|
||||
|
||||
const createUnifiedDocument = (
|
||||
oldText: string,
|
||||
newText: string,
|
||||
diffChunks: DiffChunk[],
|
||||
): { doc: string; mapping: LineMapping[] } => {
|
||||
): { doc: string; lineRanges: ConflictRange[] } => {
|
||||
const sortedChunks = [...diffChunks].sort(
|
||||
(a, b) => a.startLeftLine - b.startLeftLine,
|
||||
);
|
||||
|
||||
let lineMappings = [];
|
||||
const oldLines = oldText.split(/\r?\n/);
|
||||
const newLines = newText.split(/\r?\n/);
|
||||
|
||||
let result: string[] = [];
|
||||
let lineRanges: ConflictRange[] = [];
|
||||
let linePosition = 0;
|
||||
let currentRange: ConflictRange | null = null;
|
||||
|
||||
let result = [];
|
||||
let oldTextLine = 1;
|
||||
let newTextLine = 1;
|
||||
|
||||
const oldLines = oldText.split(/\r?\n/);
|
||||
const newLines = newText.split(/\r?\n/);
|
||||
let chunkIndex = 0;
|
||||
// Used to check if a line is in a chunk
|
||||
const isInChunk = (
|
||||
line: number,
|
||||
chunk: DiffChunk | undefined,
|
||||
side: "left" | "right",
|
||||
) => {
|
||||
if (!chunk) {
|
||||
return false;
|
||||
}
|
||||
if (side === "left") {
|
||||
return line >= chunk.startLeftLine && line < chunk.endLeftLine;
|
||||
} else if (side === "right") {
|
||||
return line >= chunk.startRightLine && line < chunk.endRightLine;
|
||||
const addLine = (line: string, source: "old" | "new" | "both") => {
|
||||
result.push(line);
|
||||
|
||||
const startPos = linePosition;
|
||||
const endPos = linePosition + line.length;
|
||||
|
||||
if (currentRange && currentRange.source === source) {
|
||||
// Extend existing range
|
||||
currentRange.to = endPos;
|
||||
} else {
|
||||
// Create new range
|
||||
if (currentRange) {
|
||||
lineRanges.push(currentRange);
|
||||
}
|
||||
currentRange = { from: startPos, to: endPos, source };
|
||||
}
|
||||
|
||||
// Move position to start of next line
|
||||
linePosition = endPos + 1; // +1 for newline
|
||||
};
|
||||
|
||||
let closestChunk = sortedChunks[chunkIndex];
|
||||
while (oldTextLine <= oldLines.length || newTextLine <= newLines.length) {
|
||||
const leftLineInChunk = isInChunk(oldTextLine, closestChunk, "left");
|
||||
const rightLineInChunk = isInChunk(newTextLine, closestChunk, "right");
|
||||
|
||||
if (!leftLineInChunk && !rightLineInChunk) {
|
||||
// Neither line is part of a chunk, they must be identical
|
||||
if (oldLines[oldTextLine - 1] !== newLines[newTextLine - 1]) {
|
||||
// TODO: Remove this, just verifying stuff works
|
||||
throw Error("Lines are different");
|
||||
}
|
||||
|
||||
result.push(oldLines[oldTextLine - 1]);
|
||||
lineMappings.push({
|
||||
oldLine: oldTextLine,
|
||||
finalLine: result.length,
|
||||
newLine: newTextLine,
|
||||
});
|
||||
oldTextLine += 1;
|
||||
newTextLine += 1;
|
||||
continue;
|
||||
// Process each chunk
|
||||
for (const chunk of sortedChunks) {
|
||||
// Add common lines before the chunk
|
||||
while (
|
||||
oldTextLine < chunk.startLeftLine &&
|
||||
newTextLine < chunk.startRightLine
|
||||
) {
|
||||
addLine(oldLines[oldTextLine - 1], "both");
|
||||
oldTextLine++;
|
||||
newTextLine++;
|
||||
}
|
||||
|
||||
if (leftLineInChunk && !rightLineInChunk) {
|
||||
// Old text is in a chunk, new text is not
|
||||
// We add the old text line to the result
|
||||
result.push(oldLines[oldTextLine - 1]);
|
||||
lineMappings.push({
|
||||
oldLine: oldTextLine,
|
||||
finalLine: result.length,
|
||||
newLine: null,
|
||||
});
|
||||
oldTextLine += 1;
|
||||
continue;
|
||||
// Add removed lines (from old text)
|
||||
for (let i = oldTextLine; i < chunk.endLeftLine; i++) {
|
||||
addLine(oldLines[i - 1], "old");
|
||||
}
|
||||
|
||||
if (!leftLineInChunk && rightLineInChunk) {
|
||||
// New text is in a chunk, old text is not
|
||||
// We add the new text line to the result
|
||||
result.push(newLines[newTextLine - 1]);
|
||||
lineMappings.push({
|
||||
oldLine: null,
|
||||
finalLine: result.length,
|
||||
newLine: newTextLine,
|
||||
});
|
||||
newTextLine += 1;
|
||||
continue;
|
||||
// Add added lines (from new text)
|
||||
for (let i = newTextLine; i < chunk.endRightLine; i++) {
|
||||
addLine(newLines[i - 1], "new");
|
||||
}
|
||||
|
||||
if (leftLineInChunk && rightLineInChunk) {
|
||||
// Both lines are in a chunk.
|
||||
// First we add all the lines from the old text
|
||||
|
||||
let oldLinesToAdd = closestChunk.endLeftLine - closestChunk.startLeftLine;
|
||||
|
||||
while (oldLinesToAdd !== 0) {
|
||||
result.push(oldLines[oldTextLine - 1]);
|
||||
lineMappings.push({
|
||||
oldLine: oldTextLine,
|
||||
finalLine: result.length,
|
||||
newLine: null,
|
||||
});
|
||||
oldTextLine += 1;
|
||||
oldLinesToAdd -= 1;
|
||||
}
|
||||
|
||||
// Then we add all the lines from the new text
|
||||
let newLinesToAdd =
|
||||
closestChunk.endRightLine - closestChunk.startRightLine;
|
||||
|
||||
while (newLinesToAdd !== 0) {
|
||||
result.push(newLines[newTextLine - 1]);
|
||||
lineMappings.push({
|
||||
oldLine: null,
|
||||
finalLine: result.length,
|
||||
newLine: newTextLine,
|
||||
});
|
||||
newTextLine += 1;
|
||||
newLinesToAdd -= 1;
|
||||
}
|
||||
|
||||
// We consumed the chunk, get the next one
|
||||
chunkIndex += 1;
|
||||
closestChunk = sortedChunks[chunkIndex];
|
||||
}
|
||||
// Update line pointers
|
||||
oldTextLine = chunk.endLeftLine;
|
||||
newTextLine = chunk.endRightLine;
|
||||
}
|
||||
|
||||
return { doc: result.join("\n"), mapping: lineMappings };
|
||||
};
|
||||
// Add remaining common lines after the last chunk
|
||||
while (oldTextLine <= oldLines.length && newTextLine <= newLines.length) {
|
||||
if (oldTextLine > oldLines.length || newTextLine > newLines.length) break;
|
||||
addLine(oldLines[oldTextLine - 1], "both");
|
||||
oldTextLine++;
|
||||
newTextLine++;
|
||||
}
|
||||
|
||||
const createInitialRanges = (
|
||||
doc: string,
|
||||
mappings: LineMapping[],
|
||||
): ConflictRange[] => {
|
||||
const lines = doc.split("\n");
|
||||
return mappings.map((m) => {
|
||||
// Calculate line positions directly from text
|
||||
const lineStartPos = lines.slice(0, m.finalLine - 1).join("\n").length;
|
||||
// Add 1 for the newline except for first line
|
||||
const from = m.finalLine > 1 ? lineStartPos + 1 : 0;
|
||||
const to = from + lines[m.finalLine - 1].length;
|
||||
// Add the final range if there is one
|
||||
if (currentRange) {
|
||||
lineRanges.push(currentRange);
|
||||
}
|
||||
|
||||
return {
|
||||
from,
|
||||
to,
|
||||
source:
|
||||
m.oldLine !== null && m.newLine !== null
|
||||
? "both"
|
||||
: m.oldLine !== null
|
||||
? "old"
|
||||
: "new",
|
||||
};
|
||||
});
|
||||
return { doc: result.join("\n"), lineRanges };
|
||||
};
|
||||
|
||||
const createRangesStateField = (
|
||||
|
|
@ -251,14 +180,12 @@ const UnifiedDiffView: React.FC<UnifiedDiffViewProps> = ({
|
|||
onConflictResolved,
|
||||
}) => {
|
||||
const diffChunks = diff(initialOldText, initialNewText);
|
||||
const { doc, mapping } = createUnifiedDocument(
|
||||
const { doc, lineRanges } = createUnifiedDocument(
|
||||
initialOldText,
|
||||
initialNewText,
|
||||
diffChunks,
|
||||
);
|
||||
|
||||
const initialRanges = createInitialRanges(doc, mapping);
|
||||
const conflictRangesField = createRangesStateField(initialRanges);
|
||||
const conflictRangesField = createRangesStateField(lineRanges);
|
||||
|
||||
const extensions = [
|
||||
conflictRangesField,
|
||||
|
|
|
|||
Loading…
Reference in a new issue