文件
mynode/packages/editor/ProseMirror/utils/preprocessEditorInput.ts
chen 301194963b
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
feat: Initial clean release of CHM
2026-11-04 11:29:39 +08:00

72 行
2.5 KiB
TypeScript

const getRootNode = (dom: Document) => {
return dom.querySelector('body > #rendered-md') ?? dom.body ?? dom.getRootNode();
};
// The renderer attaches source map info to individual list items,
// rather than the toplevel list block.
// The markup restoration logic, however, needs this information on
// the toplevel block.
const addListSourceMapInfo = (dom: Document) => {
const root = getRootNode(dom);
const lists = [
...dom.querySelectorAll('ol, ul'),
].filter(node => node.parentElement === root);
for (const list of lists) {
const firstChild = list.children.item(0);
const lastChild = list.children.item(list.children.length - 1);
if (!firstChild || !lastChild) continue;
if (!firstChild.hasAttribute('source-line') || !lastChild.hasAttribute('source-line-end')) continue;
const firstChildLine = firstChild.getAttribute('source-line');
const lastChildLine = lastChild.getAttribute('source-line-end');
list.classList.add('maps-to-line');
list.setAttribute('source-line', firstChildLine);
list.setAttribute('source-line-end', lastChildLine);
}
};
// Uses the renderer's source-line and source-line-end attributes to
// reconstruct the original Markdown for different parts of the given
// DOM.
const addOriginalMarkdownAttributes = (dom: Document, originalMarkup: string) => {
const lines = originalMarkup.split('\n');
const root = getRootNode(dom);
const sourceMappedToplevelElements = [
...dom.querySelectorAll('.maps-to-line[source-line][source-line-end]'),
].filter(node => node.parentElement === root);
let lastEndLine = -1;
let lastElement: Element = null;
for (const elem of sourceMappedToplevelElements) {
const startLine = Number(elem.getAttribute('source-line'));
// For block elements, the end line is sometimes the start line of the next
// block (which should be excluded from the current).
const endLine = Number(elem.getAttribute('source-line-end')) - 1;
// If the source map of two adjacent nodes overlaps, we can't know which
// part of which source line corresponds to the element.
if (startLine <= lastEndLine) {
lastElement?.removeAttribute('data-original-markup');
lastElement = null;
continue;
}
const joinedLines = lines.slice(startLine, endLine + 1).join('\n');
elem.setAttribute('data-original-markup', joinedLines);
lastEndLine = endLine;
lastElement = elem;
}
};
const preprocessEditorInput = (dom: Document, originalMarkup: string) => {
addListSourceMapInfo(dom);
addOriginalMarkdownAttributes(dom, originalMarkup);
};
export default preprocessEditorInput;