@pierre/diffs
Version:
221 lines (220 loc) • 9.71 kB
JavaScript
import { getLineAnnotationName } from "../utils/getLineAnnotationName.js";
import { getLineAnnotationSource, recordLineAnnotationSource } from "../utils/lineAnnotationIdentity.js";
import { getLineNumberAttr, h } from "./utils.js";
//#region src/editor/lineAnnotations.ts
function getDefaultLineAnnotationName(annotation) {
return getLineAnnotationName(annotation);
}
function applyDocumentChangeToLineAnnotations(change, lineAnnotations) {
const annotationChanges = getLineAnnotationChanges(change);
if (annotationChanges.length === 0) return;
const nextLineAnnotations = [];
let changed = false;
for (const annotation of lineAnnotations) {
if ("side" in annotation && annotation.side === "deletions" || annotation.lineNumber <= 0) {
nextLineAnnotations.push(annotation);
continue;
}
let line = annotation.lineNumber - 1;
let lineCount = change.previousLineCount;
let annotationChanged = false;
for (const lineChange of annotationChanges) {
const nextLineCount = Math.max(1, lineCount + lineChange.lineDelta);
const nextLine = mapLineThroughLineChange(line, lineChange, nextLineCount);
if (nextLine === void 0) {
annotationChanged = true;
line = void 0;
break;
}
if (nextLine !== line || lineChangeTouchesAnnotationLine(line, lineChange)) annotationChanged = true;
line = nextLine;
lineCount = nextLineCount;
}
if (line === void 0) {
changed = true;
continue;
}
const lineNumber = line + 1;
if (annotationChanged) {
if (lineNumber === annotation.lineNumber) nextLineAnnotations.push(annotation);
else {
const moved = {
...annotation,
lineNumber
};
recordLineAnnotationSource(moved, getLineAnnotationSource(annotation));
nextLineAnnotations.push(moved);
}
changed = true;
continue;
}
nextLineAnnotations.push(annotation);
}
return changed ? nextLineAnnotations : void 0;
}
function getLineAnnotationChanges(change) {
if (change.changedLineChanges !== void 0) return change.changedLineChanges.flatMap(([startLine, changedEndLine, lineDelta, startCharacter = 0, _endCharacter = 0, endedAtDocumentEnd = false]) => {
if (lineDelta === 0) return [];
const insertedLineBreaks = Math.max(0, changedEndLine - startLine);
return [{
startLine,
startCharacter,
endLine: startLine + Math.max(0, insertedLineBreaks - lineDelta),
deletesEndLine: lineDelta < 0 && endedAtDocumentEnd,
insertedLineBreaks,
lineDelta
}];
});
if (change.lineDelta === 0) return change.changedLineRanges.flatMap(([startLine, endLine]) => {
const insertedLineBreaks = endLine - startLine;
if (insertedLineBreaks <= 0) return [];
return [{
startLine,
startCharacter: 0,
endLine: startLine,
deletesEndLine: false,
insertedLineBreaks,
lineDelta: insertedLineBreaks
}];
});
const removedLineCount = Math.max(0, -change.lineDelta);
const deletedToDocumentEnd = change.endedAtDocumentEnd && change.startLine + removedLineCount === change.previousLineCount - 1;
return [{
startLine: change.startLine,
startCharacter: change.startCharacter,
endLine: change.startLine + removedLineCount,
deletesEndLine: deletedToDocumentEnd,
insertedLineBreaks: Math.max(0, change.lineDelta),
lineDelta: change.lineDelta
}];
}
function mapLineThroughLineChange(line, lineChange, nextLineCount) {
if (line < lineChange.startLine) return line;
if (line > lineChange.endLine || lineChange.endLine > lineChange.startLine && line === lineChange.endLine && !lineChange.deletesEndLine) return line + lineChange.lineDelta;
if (lineChange.startLine === lineChange.endLine) {
if (lineChange.startCharacter === 0) return line + lineChange.insertedLineBreaks;
return line;
}
if (lineChangeDeletesAnnotationLine(line, lineChange)) return;
const replacementLineOffset = Math.min(Math.max(0, line - lineChange.startLine), lineChange.insertedLineBreaks);
return clampLine(lineChange.startLine + replacementLineOffset, nextLineCount);
}
function lineChangeDeletesAnnotationLine(line, lineChange) {
if (lineChange.lineDelta >= 0 || line < lineChange.startLine || line > lineChange.endLine) return false;
if (line === lineChange.startLine && lineChange.startCharacter > 0) return false;
if (line === lineChange.endLine && !lineChange.deletesEndLine) return false;
return true;
}
function lineChangeTouchesAnnotationLine(line, lineChange) {
if (lineChange.lineDelta === 0 || line < lineChange.startLine || line > lineChange.endLine) return false;
return !(lineChange.endLine > lineChange.startLine && line === lineChange.endLine && !lineChange.deletesEndLine);
}
function clampLine(line, lineCount) {
return Math.max(0, Math.min(line, Math.max(0, lineCount - 1)));
}
function renderLineAnnotations(lineAnnotations, contentEl, gutterEl, getName = getDefaultLineAnnotationName) {
const additionAnnotations = /* @__PURE__ */ new Map();
const deletionAnnotations = /* @__PURE__ */ new Map();
for (const annotation of lineAnnotations) {
const lineNumber = annotation.lineNumber;
if (!additionAnnotations.has(lineNumber)) additionAnnotations.set(lineNumber, []);
if (!deletionAnnotations.has(lineNumber)) deletionAnnotations.set(lineNumber, []);
("side" in annotation && annotation.side === "deletions" ? deletionAnnotations : additionAnnotations).get(lineNumber).push(getName(annotation));
}
const leftCodeElement = contentEl.parentElement?.previousElementSibling;
let leftGutterElement;
let leftContentElement;
if (leftCodeElement != null && leftCodeElement instanceof HTMLElement && leftCodeElement.dataset.deletions !== void 0) for (const child of leftCodeElement.children) {
const el = child;
const { gutter, content } = el.dataset;
if (gutter !== void 0) leftGutterElement = el;
else if (content !== void 0) leftContentElement = el;
}
cleanLineAnnotationElements(contentEl, gutterEl);
if (leftContentElement !== void 0) cleanLineAnnotationElements(leftContentElement, leftGutterElement);
const additionsAnnotationElements = createLineAnnotationElements(additionAnnotations, contentEl, gutterEl);
if (leftContentElement === void 0) return;
const deletionsAnnotationElements = createLineAnnotationElements(deletionAnnotations, leftContentElement, leftGutterElement);
requestAnimationFrame(() => {
syncPairedLineAnnotationHeights(additionAnnotations, deletionAnnotations, additionsAnnotationElements, deletionsAnnotationElements);
});
}
function cleanLineAnnotationElements(contentEl, gutterEl) {
const staleElements = [];
for (let i = 1; i < contentEl.childElementCount; i++) {
const el = contentEl.children[i];
if (el.dataset.lineAnnotation !== void 0) {
staleElements.push(el);
if (gutterEl !== void 0) staleElements.push(gutterEl.children[i]);
}
}
for (const el of staleElements) el.remove();
}
function createLineAnnotationElements(lineAnnotations, contentEl, gutterEl) {
const annotationElements = /* @__PURE__ */ new Map();
for (const el of contentEl.children) {
const lineNumber = getLineNumberAttr(el);
if (lineNumber !== void 0) {
const annotations = lineAnnotations.get(lineNumber);
if (annotations !== void 0) {
const annotationElement = h("div", {
dataset: { lineAnnotation: "0," + (lineNumber - 1) },
children: [h("div", {
dataset: "annotationContent",
children: annotations.map((name) => h("slot", { name }))
})]
});
el.after(annotationElement);
annotationElements.set(lineNumber, annotationElement);
}
}
}
if (gutterEl !== void 0) for (const el of gutterEl.children) {
const lineNumber = getLineNumberAttr(el, "columnNumber");
if (lineNumber !== void 0 && lineAnnotations.has(lineNumber)) {
const bufferEl = h("div", {
dataset: {
gutterBuffer: "annotation",
bufferSize: "1"
},
style: { gridRow: "span 1" }
});
el.after(bufferEl);
}
}
return annotationElements;
}
function syncPairedLineAnnotationHeights(additionAnnotations, deletionAnnotations, additionAnnotationElements, deletionAnnotationElements) {
const offsetHeights = /* @__PURE__ */ new Map();
for (const [lineNumber, annotations] of additionAnnotations.entries()) {
const annotationElement = deletionAnnotationElements.get(lineNumber);
if (annotations.length === 0 && annotationElement !== void 0) {
const height = measureAnnotationContentHeight(annotationElement);
if (height > 0) offsetHeights.set(lineNumber, height);
}
}
for (const [lineNumber, annotations] of deletionAnnotations.entries()) {
const annotationElement = additionAnnotationElements.get(lineNumber);
if (annotations.length === 0 && annotationElement !== void 0) {
const height = measureAnnotationContentHeight(annotationElement);
if (height > 0) offsetHeights.set(lineNumber, height);
}
}
applyLineAnnotationMinHeights(additionAnnotations, additionAnnotationElements, offsetHeights);
applyLineAnnotationMinHeights(deletionAnnotations, deletionAnnotationElements, offsetHeights);
}
function measureAnnotationContentHeight(lineAnnotationEl) {
const content = lineAnnotationEl.firstElementChild;
if (!(content instanceof HTMLElement)) return 0;
return content.getBoundingClientRect().height;
}
function applyLineAnnotationMinHeights(lineAnnotations, annotationElements, offsetHeights) {
for (const [lineNumber, annotationElement] of annotationElements.entries()) {
const annotations = lineAnnotations.get(lineNumber);
const offsetHeight = offsetHeights.get(lineNumber);
if (annotations?.length === 0 && offsetHeight !== void 0) annotationElement.style.setProperty("--diffs-annotation-min-height", `${offsetHeight}px`);
}
}
//#endregion
export { applyDocumentChangeToLineAnnotations, renderLineAnnotations };
//# sourceMappingURL=lineAnnotations.js.map