UNPKG

@pierre/diffs

Version:

185 lines (184 loc) 7.66 kB
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