@pierre/diffs
Version:
185 lines (184 loc) • 7.66 kB
JavaScript
import { areOptionsEqual } from "../../utils/areOptionsEqual.js";
import { areFileTargetsEqual } from "../../utils/areFileTargetsEqual.js";
import { getLineAnnotationName } from "../../utils/getLineAnnotationName.js";
import { areDiffTargetsEqual } from "../../utils/areDiffTargetsEqual.js";
import { parseDiffFromFile } from "../../utils/parseDiffFromFile.js";
import { FileDiff } from "../../components/FileDiff.js";
import { VirtualizedFileDiff } from "../../components/VirtualizedFileDiff.js";
import { useStableCallback } from "./useStableCallback.js";
import { useCreateEditor } from "../EditContext.js";
import { noopRender } from "../constants.js";
import { WorkerPoolContext } from "../WorkerPoolContext.js";
import { useVirtualizer } from "../Virtualizer.js";
import { useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef } from "react";
//#region src/react/utils/useFileDiffInstance.ts
const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
function useFileDiffInstance({ fileDiff, oldFile, newFile, options, editorOptions, editStateKey, lineAnnotations, selectedLines, prerenderedHTML, metrics, hasGutterRenderUtility, hasCustomHeader, disableWorkerPool, edit, onEditChange: _onEditChange, onEditComplete: _onEditComplete }) {
const simpleVirtualizer = useVirtualizer();
const controlledSelection = selectedLines !== void 0;
const poolManager = useContext(WorkerPoolContext);
const createEditor = useCreateEditor();
const handleOnEditChange = useStableCallback((event) => _onEditChange?.(event));
const onEditChange = _onEditChange != null ? handleOnEditChange : void 0;
const acceptedCache = useRef(null);
const handleOnEditComplete = useStableCallback((event) => {
const decision = _onEditComplete?.(event) ?? "reject";
if (decision === "accept") acceptedCache.current = {
fileDiff: {
installed: event.fileDiff,
stale: event.originalFileDiff
},
filePair: fileDiff == null ? {
fileDiff: event.fileDiff,
installed: {
oldFile: event.oldFile,
newFile: event.newFile
},
stale: {
oldFile: oldFile ?? null,
newFile: newFile ?? null
}
} : null,
annotations: {
installed: event.lineAnnotations,
stale: event.originalLineAnnotations
}
};
return decision;
});
const onEditComplete = _onEditComplete != null ? handleOnEditComplete : void 0;
const effectiveFileDiff = useMemo(() => {
if (fileDiff != null) return fileDiff;
const { current: accepted } = acceptedCache;
const filePair = accepted?.filePair;
if (accepted != null && filePair != null) {
if (isSameFilePair(oldFile, newFile, filePair.stale) || isSameFilePair(oldFile, newFile, filePair.installed)) return filePair.fileDiff;
accepted.filePair = null;
}
return parseDiffFromFile(oldFile ?? null, newFile ?? null, options?.parseDiffOptions);
}, [
fileDiff,
oldFile,
newFile,
options?.parseDiffOptions
]);
const instanceRef = useRef(null);
const disposeEditorRef = useRef(null);
const getEditor = useStableCallback(() => {
if (createEditor == null) throw new Error("FileDiff: EditContext is not attached");
return createEditor("file-diff", editorOptions ?? {}, editStateKey);
});
const ref = useStableCallback((fileContainer) => {
if (fileContainer != null) {
if (instanceRef.current != null) throw new Error("useFileDiffInstance: An instance should not already exist when a node is created");
if (simpleVirtualizer != null) instanceRef.current = new VirtualizedFileDiff(mergeFileDiffOptions({
controlledSelection,
hasCustomHeader,
hasGutterRenderUtility,
onEditChange,
onEditComplete,
options
}), simpleVirtualizer, metrics, !disableWorkerPool ? poolManager : void 0, true);
else instanceRef.current = new FileDiff(mergeFileDiffOptions({
controlledSelection,
hasCustomHeader,
hasGutterRenderUtility,
onEditChange,
onEditComplete,
options
}), !disableWorkerPool ? poolManager : void 0, true);
if (edit && disposeEditorRef.current == null) disposeEditorRef.current = applyEdit(instanceRef.current, getEditor);
instanceRef.current.hydrate({
fileDiff: effectiveFileDiff,
fileContainer,
lineAnnotations,
prerenderedHTML
});
} else {
if (instanceRef.current == null) throw new Error("useFileDiffInstance: A FileDiff instance should exist when unmounting");
instanceRef.current.cleanUp();
instanceRef.current = null;
disposeEditorRef.current = null;
}
});
useIsomorphicLayoutEffect(() => {
const { current: instance } = instanceRef;
if (instance == null) return;
const newOptions = mergeFileDiffOptions({
controlledSelection,
hasCustomHeader,
hasGutterRenderUtility,
onEditChange,
onEditComplete,
options
});
const forceRender = newOptions !== void 0 && !areOptionsEqual(instance.options, newOptions);
instance.setOptions(newOptions);
if (!edit && disposeEditorRef.current != null) {
const { current: disposeEditor } = disposeEditorRef;
disposeEditorRef.current = null;
disposeEditor();
}
const resolved = resolveAcceptedValues(effectiveFileDiff, lineAnnotations, acceptedCache);
instance.render({
forceRender,
fileDiff: resolved.fileDiff,
lineAnnotations: resolved.lineAnnotations
});
if (selectedLines !== void 0) instance.setSelectedLines(selectedLines);
if (edit && disposeEditorRef.current == null) disposeEditorRef.current = applyEdit(instance, getEditor);
});
return {
fileDiff: effectiveFileDiff,
ref,
getHoveredLine: useCallback(() => {
return instanceRef.current?.getHoveredLine();
}, []),
getAnnotationSlotName: useCallback((annotation) => instanceRef.current?.getAnnotationSlotName(annotation) ?? getLineAnnotationName(annotation), [])
};
}
function isSameFilePair(oldFile, newFile, filePair) {
return areFileTargetsEqual(oldFile ?? void 0, filePair.oldFile ?? void 0) && areFileTargetsEqual(newFile ?? void 0, filePair.newFile ?? void 0);
}
function resolveAcceptedValues(fileDiff, lineAnnotations, acceptedCache) {
const { current: accepted } = acceptedCache;
if (accepted == null) return {
fileDiff,
lineAnnotations
};
const { fileDiff: acceptedDiff, annotations: acceptedAnnotations } = accepted;
let resolvedFileDiff = fileDiff;
if (acceptedDiff != null) if (areDiffTargetsEqual(fileDiff, acceptedDiff.stale)) resolvedFileDiff = acceptedDiff.installed;
else accepted.fileDiff = null;
let resolvedAnnotations = lineAnnotations;
if (acceptedAnnotations != null) if (lineAnnotations === acceptedAnnotations.stale) resolvedAnnotations = acceptedAnnotations.installed;
else accepted.annotations = null;
if (accepted.fileDiff == null && accepted.annotations == null && accepted.filePair == null) acceptedCache.current = null;
return {
fileDiff: resolvedFileDiff,
lineAnnotations: resolvedAnnotations
};
}
function mergeFileDiffOptions({ options, controlledSelection, hasCustomHeader, hasGutterRenderUtility, onEditChange, onEditComplete }) {
if (!(controlledSelection || hasGutterRenderUtility || hasCustomHeader || onEditChange != null || onEditComplete != null)) return options;
return {
...options,
controlledSelection,
renderCustomHeader: hasCustomHeader ? noopRender : options?.renderCustomHeader,
renderGutterUtility: hasGutterRenderUtility ? noopRender : options?.renderGutterUtility,
onEditChange,
onEditComplete
};
}
function applyEdit(instance, getEditor) {
const editor = getEditor();
try {
return editor.edit(instance);
} catch (error) {
editor.cleanUp();
throw error;
}
}
//#endregion
export { useFileDiffInstance };
//# sourceMappingURL=useFileDiffInstance.js.map