Files
outline/shared/editor/lib/markInputRule.ts
2022-01-19 18:43:15 -08:00

66 lines
1.9 KiB
TypeScript

import { InputRule } from "prosemirror-inputrules";
import { MarkType, Mark } from "prosemirror-model";
import { EditorState } from "prosemirror-state";
function getMarksBetween(start: number, end: number, state: EditorState) {
let marks: { start: number; end: number; mark: Mark }[] = [];
state.doc.nodesBetween(start, end, (node, pos) => {
marks = [
...marks,
...node.marks.map((mark) => ({
start: pos,
end: pos + node.nodeSize,
mark,
})),
];
});
return marks;
}
export default function (
regexp: RegExp,
markType: MarkType,
getAttrs?: (match: string[]) => Record<string, unknown>
): InputRule {
return new InputRule(
regexp,
(state: EditorState, match: string[], start: number, end: number) => {
const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs;
const { tr } = state;
const m = match.length - 1;
let markEnd = end;
let markStart = start;
if (match[m]) {
const matchStart = start + match[0].indexOf(match[m - 1]);
const matchEnd = matchStart + match[m - 1].length - 1;
const textStart = matchStart + match[m - 1].lastIndexOf(match[m]);
const textEnd = textStart + match[m].length;
const excludedMarks = getMarksBetween(start, end, state)
.filter((item) => item.mark.type.excludes(markType))
.filter((item) => item.end > matchStart);
if (excludedMarks.length) {
return null;
}
if (textEnd < matchEnd) {
tr.delete(textEnd, matchEnd);
}
if (textStart > matchStart) {
tr.delete(matchStart, textStart);
}
markStart = matchStart;
markEnd = markStart + match[m].length;
}
tr.addMark(markStart, markEnd, markType.create(attrs));
tr.removeStoredMark(markType);
return tr;
}
);
}