|
| 1 | +/// |
| 2 | +/// Copy of https://github.com/ProseMirror/prosemirror-inputrules/blob/1.4.0/src/rulebuilders.ts |
| 3 | +/// Added a check for the presence of a code mark |
| 4 | +/// |
| 5 | + |
| 6 | +import {InputRule} from 'prosemirror-inputrules'; |
| 7 | +import {Attrs, Node, NodeType} from 'prosemirror-model'; |
| 8 | +import {canJoin, findWrapping} from 'prosemirror-transform'; |
| 9 | + |
| 10 | +import {hasCodeMark} from './inputrules'; |
| 11 | + |
| 12 | +/// Build an input rule for automatically wrapping a textblock when a |
| 13 | +/// given string is typed. The `regexp` argument is |
| 14 | +/// directly passed through to the `InputRule` constructor. You'll |
| 15 | +/// probably want the regexp to start with `^`, so that the pattern can |
| 16 | +/// only occur at the start of a textblock. |
| 17 | +/// |
| 18 | +/// `nodeType` is the type of node to wrap in. If it needs attributes, |
| 19 | +/// you can either pass them directly, or pass a function that will |
| 20 | +/// compute them from the regular expression match. |
| 21 | +/// |
| 22 | +/// By default, if there's a node with the same type above the newly |
| 23 | +/// wrapped node, the rule will try to [join](#transform.Transform.join) those |
| 24 | +/// two nodes. You can pass a join predicate, which takes a regular |
| 25 | +/// expression match and the node before the wrapped node, and can |
| 26 | +/// return a boolean to indicate whether a join should happen. |
| 27 | +export function wrappingInputRule( |
| 28 | + regexp: RegExp, |
| 29 | + nodeType: NodeType, |
| 30 | + getAttrs: Attrs | null | ((matches: RegExpMatchArray) => Attrs | null) = null, |
| 31 | + joinPredicate?: (match: RegExpMatchArray, node: Node) => boolean, |
| 32 | +) { |
| 33 | + return new InputRule(regexp, (state, match, start, end) => { |
| 34 | + if (hasCodeMark(state, match, start, end)) return null; |
| 35 | + |
| 36 | + const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs; |
| 37 | + const tr = state.tr.delete(start, end); |
| 38 | + const $start = tr.doc.resolve(start), |
| 39 | + range = $start.blockRange(), |
| 40 | + wrapping = range && findWrapping(range, nodeType, attrs); |
| 41 | + if (!wrapping) return null; |
| 42 | + tr.wrap(range!, wrapping); |
| 43 | + const before = tr.doc.resolve(start - 1).nodeBefore; |
| 44 | + if ( |
| 45 | + before && |
| 46 | + before.type == nodeType && |
| 47 | + canJoin(tr.doc, start - 1) && |
| 48 | + (!joinPredicate || joinPredicate(match, before)) |
| 49 | + ) |
| 50 | + tr.join(start - 1); |
| 51 | + return tr; |
| 52 | + }); |
| 53 | +} |
| 54 | + |
| 55 | +/// Build an input rule that changes the type of a textblock when the |
| 56 | +/// matched text is typed into it. You'll usually want to start your |
| 57 | +/// regexp with `^` to that it is only matched at the start of a |
| 58 | +/// textblock. The optional `getAttrs` parameter can be used to compute |
| 59 | +/// the new node's attributes, and works the same as in the |
| 60 | +/// `wrappingInputRule` function. |
| 61 | +export function textblockTypeInputRule( |
| 62 | + regexp: RegExp, |
| 63 | + nodeType: NodeType, |
| 64 | + getAttrs: Attrs | null | ((match: RegExpMatchArray) => Attrs | null) = null, |
| 65 | +) { |
| 66 | + return new InputRule(regexp, (state, match, start, end) => { |
| 67 | + if (hasCodeMark(state, match, start, end)) return null; |
| 68 | + |
| 69 | + const $start = state.doc.resolve(start); |
| 70 | + const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs; |
| 71 | + if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), nodeType)) |
| 72 | + return null; |
| 73 | + return state.tr.delete(start, end).setBlockType(start, start, nodeType, attrs); |
| 74 | + }); |
| 75 | +} |
0 commit comments