You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-04-24 19:55:13 +02:00
89 lines
2.6 KiB
TypeScript
89 lines
2.6 KiB
TypeScript
import { EditorState } from '@codemirror/state';
|
|
import { SyntaxNodeRef } from '@lezer/common';
|
|
|
|
export interface HtmlNodeInfo {
|
|
node: SyntaxNodeRef;
|
|
opening: boolean;
|
|
closing: boolean;
|
|
from: number;
|
|
to: number;
|
|
tagName: ()=> string;
|
|
getAttr: (attributeName: string)=> string;
|
|
}
|
|
|
|
type OnGetNodeContent = (node: SyntaxNodeRef)=> string;
|
|
|
|
const removeQuotes = (quoted: string) => quoted.replace(/^["'](.*)["']$/, '$1');
|
|
|
|
const getHtmlNodeAttr = (node: SyntaxNodeRef, attrName: string, getText: OnGetNodeContent) => {
|
|
if (node.from === node.to) return null; // Empty
|
|
const content = node.node.resolveInner(node.from + 1);
|
|
|
|
// Search for the "id" attribute
|
|
const attributes = content.getChildren('Attribute');
|
|
for (const attribute of attributes) {
|
|
const nameNode = attribute.getChild('AttributeName');
|
|
const valueNode = attribute.getChild('AttributeValue');
|
|
|
|
if (nameNode && valueNode) {
|
|
const name = getText(nameNode).toLowerCase().replace(/^"(.*)"$/, '$1');
|
|
if (name === attrName) {
|
|
return removeQuotes(getText(valueNode));
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
// Utility function to access CodeMirror HTML node information, based on
|
|
// the corresponding Markdown node.
|
|
const htmlNodeInfo = (node: SyntaxNodeRef, state: EditorState, offset = 0): HtmlNodeInfo|null => {
|
|
// Already an HTML node?
|
|
if (node.name === 'OpenTag' || node.name === 'CloseTag' || node.name === 'SelfClosingTag') {
|
|
const getNodeText = (childNode: SyntaxNodeRef) => state.sliceDoc(childNode.from + offset, childNode.to + offset);
|
|
const selfClosing = node.name === 'SelfClosingTag';
|
|
|
|
return {
|
|
node,
|
|
opening: node.name === 'OpenTag' || selfClosing,
|
|
closing: node.name === 'CloseTag' || selfClosing,
|
|
from: node.from + offset,
|
|
to: node.to + offset,
|
|
tagName: () => {
|
|
const nodeText = getNodeText(node).trim();
|
|
const tagNameMatch = nodeText.match(/^<\/?([^>\s]+)/);
|
|
if (tagNameMatch) {
|
|
return tagNameMatch[1];
|
|
}
|
|
return null;
|
|
},
|
|
getAttr: (name: string) => {
|
|
return getHtmlNodeAttr(node, name, getNodeText);
|
|
},
|
|
};
|
|
}
|
|
|
|
// Convert Markdown HTML nodes to HTML nodes
|
|
if (node.name === 'HTMLTag' || node.name === 'HTMLBlock') {
|
|
const globalOffset = node.from + offset;
|
|
let resolved: HtmlNodeInfo|null = null;
|
|
|
|
// CodeMirror adds HTML information to Markdown documents using overlays attached
|
|
// to HTMLTag and HTMLBlock nodes.
|
|
// Use .enter to enter the overlay and visit the HTML nodes:
|
|
node.node.enter(node.from, 1).toTree().iterate({
|
|
enter: (subNode) => {
|
|
resolved ??= htmlNodeInfo(subNode, state, globalOffset);
|
|
return !resolved;
|
|
},
|
|
});
|
|
|
|
return resolved;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
export default htmlNodeInfo;
|