Ditch old/new nomenclature in favour of remote/local to avoid confusion

This commit is contained in:
Silvano Cerza 2025-03-09 16:47:08 +01:00
parent 41642b5174
commit 8732cdc1f0
7 changed files with 90 additions and 80 deletions

View file

@ -20,18 +20,18 @@ styles.innerHTML = `
document.head.appendChild(styles);
interface DiffViewProps {
oldText: string;
newText: string;
onOldTextChange: (content: string) => void;
onNewTextChange: (content: string) => void;
remoteText: string;
localText: string;
onRemoteTextChange: (content: string) => void;
onLocalTextChange: (content: string) => void;
onConflictResolved: () => void;
}
const DiffView: React.FC<DiffViewProps> = ({
oldText,
newText,
onOldTextChange,
onNewTextChange,
remoteText,
localText,
onRemoteTextChange,
onLocalTextChange,
onConflictResolved,
}) => {
// We need to know the line height to correctly draw the ribbon between the left
@ -46,7 +46,7 @@ const DiffView: React.FC<DiffViewProps> = ({
const [rightEditorTopOffset, setRightEditorTopOffset] =
React.useState<number>(0);
const diffs = diff(oldText, newText);
const diffs = diff(remoteText, localText);
return (
<div
@ -59,13 +59,13 @@ const DiffView: React.FC<DiffViewProps> = ({
>
<div style={{ flex: 1, overflow: "hidden" }}>
<EditorPane
content={oldText}
content={remoteText}
highlightPluginSpec={{
diff: diffs,
isOriginal: true,
}}
onEditorUpdate={handleEditorReady}
onContentChange={onOldTextChange}
onContentChange={onRemoteTextChange}
onScrollTopUpdate={setLeftEditorTopOffset}
/>
</div>
@ -94,77 +94,77 @@ const DiffView: React.FC<DiffViewProps> = ({
rightEditorTopLineOffset={rightEditorTopOffset}
onAcceptLeft={(chunk: DiffChunk) => {
if (chunk.type === "add") {
const oldLines = oldText.split("\n");
oldLines.splice(
const remoteLines = remoteText.split("\n");
remoteLines.splice(
chunk.startLeftLine - 1,
0,
...newText
...localText
.split("\n")
.slice(chunk.startRightLine - 1, chunk.endRightLine - 1),
);
onOldTextChange(oldLines.join("\n"));
onRemoteTextChange(remoteLines.join("\n"));
} else if (chunk.type === "modify") {
const oldLines = oldText.split("\n");
oldLines.splice(
const remoteLines = remoteText.split("\n");
remoteLines.splice(
chunk.startLeftLine - 1,
chunk.endLeftLine - chunk.startLeftLine,
...newText
...localText
.split("\n")
.slice(chunk.startRightLine - 1, chunk.endRightLine - 1),
);
onOldTextChange(oldLines.join("\n"));
onRemoteTextChange(remoteLines.join("\n"));
}
}}
onAcceptRight={(chunk: DiffChunk) => {
if (chunk.type === "remove") {
const newLines = newText.split("\n");
newLines.splice(
const localLines = localText.split("\n");
localLines.splice(
chunk.startRightLine - 1,
0,
...oldText
...remoteText
.split("\n")
.slice(chunk.startLeftLine - 1, chunk.endLeftLine - 1),
);
onNewTextChange(newLines.join("\n"));
onLocalTextChange(localLines.join("\n"));
} else if (chunk.type === "modify") {
const newLines = newText.split("\n");
newLines.splice(
const localLines = localText.split("\n");
localLines.splice(
chunk.startRightLine - 1,
chunk.endRightLine - chunk.startRightLine,
...oldText
...remoteText
.split("\n")
.slice(chunk.startLeftLine - 1, chunk.endLeftLine - 1),
);
onNewTextChange(newLines.join("\n"));
onLocalTextChange(localLines.join("\n"));
}
}}
onReject={(chunk: DiffChunk) => {
if (chunk.type === "add") {
const newLines = newText.split("\n");
newLines.splice(
const localLines = localText.split("\n");
localLines.splice(
chunk.startRightLine - 1,
chunk.endRightLine - chunk.startRightLine,
);
onNewTextChange(newLines.join("\n"));
onLocalTextChange(localLines.join("\n"));
} else if (chunk.type === "remove") {
const oldLines = oldText.split("\n");
oldLines.splice(
const remoteLines = remoteText.split("\n");
remoteLines.splice(
chunk.startLeftLine - 1,
chunk.endLeftLine - chunk.startLeftLine,
);
onOldTextChange(oldLines.join("\n"));
onRemoteTextChange(remoteLines.join("\n"));
}
}}
/>
</div>
<div style={{ flex: 1, overflow: "hidden" }}>
<EditorPane
content={newText}
content={localText}
highlightPluginSpec={{
diff: diffs,
isOriginal: false,
}}
onContentChange={onNewTextChange}
onContentChange={onLocalTextChange}
onScrollTopUpdate={setRightEditorTopOffset}
/>
</div>

View file

@ -89,14 +89,14 @@ const SplitView = ({
setCurrentFileIndex={setCurrentFileIndex}
/>
<DiffView
oldText={currentFile?.remoteContent || ""}
newText={currentFile?.localContent || ""}
onOldTextChange={(content: string) => {
remoteText={currentFile?.remoteContent || ""}
localText={currentFile?.localContent || ""}
onRemoteTextChange={(content: string) => {
const tempFiles = [...files];
tempFiles[currentFileIndex].remoteContent = content;
setFiles(tempFiles);
}}
onNewTextChange={(content: string) => {
onLocalTextChange={(content: string) => {
const tempFiles = [...files];
tempFiles[currentFileIndex].localContent = content;
setFiles(tempFiles);

View file

@ -5,5 +5,5 @@
interface ConflictRange {
from: number;
to: number;
source: "old" | "new" | "both";
source: "remote" | "local" | "both";
}

View file

@ -78,13 +78,13 @@ export const createLineDecorations = (
const endLine = state.doc.lineAt(range.to);
for (let i = 0; i <= endLine.number - startLine.number; i += 1) {
const line = state.doc.line(startLine.number + i);
if (range.source === "old") {
if (range.source === "remote") {
builder.add(
line.from,
line.from,
Decoration.line({ class: "cm-deletedLine" }),
);
} else if (range.source === "new") {
} else if (range.source === "local") {
builder.add(
line.from,
line.from,
@ -118,8 +118,8 @@ export const createResolutionDecorations = (
const previousRange = ranges.at(index - 1);
const nextRange = ranges.at(index + 1);
if (range.source === "old") {
const nextRangeIsNew = nextRange?.source === "new";
if (range.source === "remote") {
const nextRangeIsNew = nextRange?.source === "local";
if (nextRangeIsNew) {
const deco = Decoration.widget({
widget: new ResolutionWidget({
@ -224,7 +224,10 @@ export const createResolutionDecorations = (
});
widgets.push(deco.range(range.from));
}
} else if (range.source === "new" && previousRange?.source !== "old") {
} else if (
range.source === "local" &&
previousRange?.source !== "remote"
) {
// We draw this only in case the previous range doesn't come from the old document
// since we handle that above
const deco = Decoration.widget({

View file

@ -10,8 +10,8 @@ import {
} from "./decorations";
interface DiffViewProps {
initialOldText: string;
initialNewText: string;
initialRemoteText: string;
initialLocalText: string;
onConflictResolved: (content: string) => void;
}
@ -21,26 +21,26 @@ interface DiffViewProps {
/// ranges that specify whether a certain document range comes from
/// remote, local or both documents, so we can highlight them.
const createUnifiedDocument = (
oldText: string,
newText: string,
remoteText: string,
localText: string,
diffChunks: DiffChunk[],
): { doc: string; lineRanges: ConflictRange[] } => {
const sortedChunks = [...diffChunks].sort(
(a, b) => a.startLeftLine - b.startLeftLine,
);
const oldLines = oldText.split(/\r?\n/);
const newLines = newText.split(/\r?\n/);
const remoteLines = remoteText.split(/\r?\n/);
const localLines = localText.split(/\r?\n/);
let result: string[] = [];
let lineRanges: ConflictRange[] = [];
let linePosition = 0;
let currentRange: ConflictRange | null = null;
let oldTextLine = 1;
let newTextLine = 1;
let remoteTextLine = 1;
let localTextLine = 1;
const addLine = (line: string, source: "old" | "new" | "both") => {
const addLine = (line: string, source: "remote" | "local" | "both") => {
result.push(line);
const startPos = linePosition;
@ -65,35 +65,42 @@ const createUnifiedDocument = (
for (const chunk of sortedChunks) {
// Add common lines before the chunk
while (
oldTextLine < chunk.startLeftLine &&
newTextLine < chunk.startRightLine
remoteTextLine < chunk.startLeftLine &&
localTextLine < chunk.startRightLine
) {
addLine(oldLines[oldTextLine - 1], "both");
oldTextLine++;
newTextLine++;
addLine(remoteLines[remoteTextLine - 1], "both");
remoteTextLine++;
localTextLine++;
}
// Add removed lines (from old text)
for (let i = oldTextLine; i < chunk.endLeftLine; i++) {
addLine(oldLines[i - 1], "old");
for (let i = remoteTextLine; i < chunk.endLeftLine; i++) {
addLine(remoteLines[i - 1], "remote");
}
// Add added lines (from new text)
for (let i = newTextLine; i < chunk.endRightLine; i++) {
addLine(newLines[i - 1], "new");
for (let i = localTextLine; i < chunk.endRightLine; i++) {
addLine(localLines[i - 1], "local");
}
// Update line pointers
oldTextLine = chunk.endLeftLine;
newTextLine = chunk.endRightLine;
remoteTextLine = chunk.endLeftLine;
localTextLine = chunk.endRightLine;
}
// 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++;
while (
remoteTextLine <= remoteLines.length &&
localTextLine <= localLines.length
) {
if (
remoteTextLine > remoteLines.length ||
localTextLine > localLines.length
)
break;
addLine(remoteLines[remoteTextLine - 1], "both");
remoteTextLine++;
localTextLine++;
}
// Add the final range if there is one
@ -105,16 +112,16 @@ const createUnifiedDocument = (
};
const DiffView: React.FC<DiffViewProps> = ({
initialOldText,
initialNewText,
initialRemoteText,
initialLocalText,
onConflictResolved,
}) => {
const editorViewRef = React.useRef<EditorView | null>(null);
const diffChunks = diff(initialOldText, initialNewText);
const diffChunks = diff(initialRemoteText, initialLocalText);
const { doc, lineRanges } = createUnifiedDocument(
initialOldText,
initialNewText,
initialRemoteText,
initialLocalText,
diffChunks,
);
@ -134,7 +141,7 @@ const DiffView: React.FC<DiffViewProps> = ({
if (update.docChanged) {
const conflictRanges = update.state.field(conflictRangesField);
const allConflictsSolved = conflictRanges.some(
(range) => range.source === "old" || range.source === "new",
(range) => range.source === "remote" || range.source === "local",
);
if (!allConflictsSolved) {
@ -173,7 +180,7 @@ const DiffView: React.FC<DiffViewProps> = ({
},
}),
];
}, [initialOldText, initialNewText]);
}, [initialRemoteText, initialLocalText]);
return (
<div style={{ width: "100%", height: "100%", overflow: "hidden" }}>

View file

@ -10,7 +10,7 @@ export const UpdateRangesEffect = StateEffect.define<RangeUpdateOperation>();
export type RangeChangeSourceOperation = {
index: number;
newSource: "old" | "new" | "both";
newSource: "remote" | "local" | "both";
};
export type RangeRemoveOperation = {

View file

@ -55,8 +55,8 @@ const UnifiedView = ({
{file.filePath}
</div>
<DiffView
initialOldText={file.remoteContent || ""}
initialNewText={file.localContent || ""}
initialRemoteText={file.remoteContent || ""}
initialLocalText={file.localContent || ""}
onConflictResolved={(content: string) => {
onConflictResolved(index, content);
}}