mindnet_obsidian/src/parser/parseEdgesFromCallouts.ts
Lars e6cd4aafec
Some checks are pending
Node.js build / build (20.x) (push) Waiting to run
Node.js build / build (22.x) (push) Waiting to run
Enhance edge parsing and settings for improved template matching
- Updated `parseEdgesFromCallouts.ts` to support edge extraction from both callout blocks and plain lines, allowing for more flexible edge definitions.
- Introduced new settings in `settings.ts` for maximum assignments collected per template, enhancing loop protection during edge processing.
- Enhanced documentation in `Entwicklerhandbuch.md` to reflect changes in edge parsing and settings.
- Improved UI components to utilize the new settings for better user experience in the Chain Workbench and related features.
2026-02-06 11:40:44 +01:00

113 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { ParsedEdge } from "./types";
// Match edge header: allow spaces between '>' characters (Obsidian uses "> >" for nesting)
const EDGE_HEADER_RE = /^\s*((?:>\s*)+)\s*\[!edge\]\s*(.+?)\s*$/i;
// Match edge header WITHOUT leading '>' (plain line) – so edges outside callout blocks are also found
const EDGE_HEADER_PLAIN_RE = /^\s*\[!edge\]\s+(.+?)\s*$/i;
const TARGET_LINK_RE = /\[\[([^\]]+?)\]\]/g;
/** Sentinel: edge block has no quote level (plain lines); only ends on next edge line or end. */
const PLAIN_EDGE_LEVEL = -1;
/**
* Extract edges from any callout nesting and from plain lines:
* - Edge with callout: > [!edge] <type> (any number of '>' allowed)
* - Edge without callout: [!edge] <type> (so edges outside abstract block are found)
* - Collect targets from subsequent lines while quoteLevel >= edgeLevel (or plain mode)
* - Stop when:
* a) next [!edge] header appears, OR
* b) quoteLevel drops below edgeLevel (block ends), ignoring blank lines, OR
* c) in plain mode: next [!edge] line
*/
export function parseEdgesFromCallouts(markdown: string): ParsedEdge[] {
const lines = markdown.split(/\r?\n/);
const edges: ParsedEdge[] = [];
let current: ParsedEdge | null = null;
let currentEdgeLevel = 0;
const getQuoteLevel = (line: string): number => {
const m = line.match(/^\s*(?:(?:>\s*)+)/);
if (!m || !m[0]) return 0;
return (m[0].match(/>/g) || []).length;
};
const flush = (endLine: number) => {
if (!current) return;
current.lineEnd = endLine;
if (current.targets.length > 0) edges.push(current);
current = null;
currentEdgeLevel = 0;
};
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line === undefined) continue;
// Start of a new edge block (with callout quote)
const edgeMatch = line.match(EDGE_HEADER_RE);
if (edgeMatch && edgeMatch[1] && edgeMatch[2]) {
flush(i - 1);
currentEdgeLevel = (edgeMatch[1].match(/>/g) || []).length;
current = {
rawType: edgeMatch[2].trim(),
targets: [],
lineStart: i,
lineEnd: i,
};
continue;
}
// Start of edge block without '>' (plain line) – e.g. edge written outside abstract block
const plainMatch = line.match(EDGE_HEADER_PLAIN_RE);
if (plainMatch && plainMatch[1]) {
flush(i - 1);
currentEdgeLevel = PLAIN_EDGE_LEVEL;
current = {
rawType: plainMatch[1].trim(),
targets: [],
lineStart: i,
lineEnd: i,
};
continue;
}
if (!current) continue;
const trimmed = line.trim();
const ql = getQuoteLevel(line);
// In plain mode, only end on next edge line (already handled above); collect [[links]] from any line
if (currentEdgeLevel === PLAIN_EDGE_LEVEL) {
TARGET_LINK_RE.lastIndex = 0;
let m: RegExpExecArray | null;
while ((m = TARGET_LINK_RE.exec(line)) !== null) {
if (m[1]) {
const t = m[1].trim();
if (t) current.targets.push(t);
}
}
continue;
}
// End of quoted edge block if quote level drops below the edge header level
if (trimmed !== "" && ql < currentEdgeLevel) {
flush(i - 1);
continue;
}
// Collect targets (multiple per line allowed)
TARGET_LINK_RE.lastIndex = 0;
let m: RegExpExecArray | null;
while ((m = TARGET_LINK_RE.exec(line)) !== null) {
if (m[1]) {
const t = m[1].trim();
if (t) current.targets.push(t);
}
}
}
flush(lines.length - 1);
return edges;
}