@magic-marker/prosemirror-suggest-changes 0.3.3-wrap-unwrap.8 → 0.3.3-wrap-unwrap.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/features/wrapUnwrap/revert/revertMoveOp.d.ts +5 -3
- package/dist/features/wrapUnwrap/revert/revertMoveOp.js +53 -73
- package/dist/features/wrapUnwrap/revert/revertStructureSuggestions.js +5 -13
- package/dist/features/wrapUnwrap/types.d.ts +1 -11
- package/dist/features/wrapUnwrap/types.js +0 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/listInputRules.d.ts +2 -0
- package/dist/listInputRules.js +14 -0
- package/dist/wrappingInputRule.d.ts +4 -0
- package/dist/wrappingInputRule.js +28 -0
- package/package.json +1 -1
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { type Transform } from "prosemirror-transform";
|
|
2
2
|
import { type MoveOp } from "../types.js";
|
|
3
3
|
import { type Node } from "prosemirror-model";
|
|
4
|
-
import { type
|
|
4
|
+
import { type Parent } from "../types.js";
|
|
5
5
|
export declare function revertMoveOp(op: MoveOp, tr: Transform, node: Node, pos: number): void;
|
|
6
|
-
export declare function getNodesWithChildren(doc: Node): Map<string, NodeWithChildren>;
|
|
7
6
|
export declare function getDeepestSurvivingParent(parentChain: Parent[], doc: Node): {
|
|
8
7
|
parent: Parent;
|
|
9
8
|
node: Node;
|
|
@@ -11,4 +10,7 @@ export declare function getDeepestSurvivingParent(parentChain: Parent[], doc: No
|
|
|
11
10
|
remainingChain: Parent[];
|
|
12
11
|
};
|
|
13
12
|
export declare function wrapNodeInParentChain(parentChain: Parent[], node: Node): Node;
|
|
14
|
-
export declare function findInsertionPos(node: Node, pos: number | null, parent: Parent): number
|
|
13
|
+
export declare function findInsertionPos(node: Node, pos: number | null, parent: Parent, child: Node): number | {
|
|
14
|
+
from: number;
|
|
15
|
+
to: number;
|
|
16
|
+
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { deleteNodeUpwards } from "./deleteNodeUpwards.js";
|
|
2
2
|
import { getNodeId } from "../getNodeId.js";
|
|
3
|
-
import { guardDocParent, guardDocWithChildren } from "../types.js";
|
|
4
3
|
export function revertMoveOp(op, tr, node, pos) {
|
|
5
4
|
console.log("revertMoveOp", "node", node.toString(), "was moved from", op.from, {
|
|
6
5
|
op,
|
|
@@ -11,50 +10,15 @@ export function revertMoveOp(op, tr, node, pos) {
|
|
|
11
10
|
parent
|
|
12
11
|
});
|
|
13
12
|
const child = wrapNodeInParentChain(parent.remainingChain, node);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
});
|
|
21
|
-
tr.insert(insertionPos, child);
|
|
13
|
+
const insertTo = findInsertionPos(parent.node, parent.pos, parent.parent, child);
|
|
14
|
+
if (typeof insertTo === "number") {
|
|
15
|
+
tr.insert(insertTo, child);
|
|
16
|
+
} else {
|
|
17
|
+
tr.replaceWith(insertTo.from, insertTo.to, child);
|
|
18
|
+
}
|
|
22
19
|
const mappedPos = tr.mapping.map(pos);
|
|
23
20
|
deleteNodeUpwards(tr, node, mappedPos);
|
|
24
21
|
}
|
|
25
|
-
export function getNodesWithChildren(doc) {
|
|
26
|
-
const nodesWithChildren = new Map();
|
|
27
|
-
const docWithChildren = {
|
|
28
|
-
node: doc,
|
|
29
|
-
pos: null,
|
|
30
|
-
children: new Set()
|
|
31
|
-
};
|
|
32
|
-
doc.children.forEach((child)=>{
|
|
33
|
-
const nodeId = getNodeId(child);
|
|
34
|
-
if (nodeId == null) return;
|
|
35
|
-
docWithChildren.children.add(nodeId);
|
|
36
|
-
});
|
|
37
|
-
nodesWithChildren.set("__doc__", docWithChildren);
|
|
38
|
-
doc.descendants((node, pos)=>{
|
|
39
|
-
if (node.isText) return true;
|
|
40
|
-
const nodeId = getNodeId(node);
|
|
41
|
-
if (nodeId == null) return true;
|
|
42
|
-
if (nodesWithChildren.has(nodeId)) return true;
|
|
43
|
-
const children = node.children.reduce((acc, child)=>{
|
|
44
|
-
const childId = getNodeId(child);
|
|
45
|
-
if (childId == null) return acc;
|
|
46
|
-
acc.add(childId);
|
|
47
|
-
return acc;
|
|
48
|
-
}, new Set());
|
|
49
|
-
nodesWithChildren.set(nodeId, {
|
|
50
|
-
node,
|
|
51
|
-
pos,
|
|
52
|
-
children
|
|
53
|
-
});
|
|
54
|
-
return true;
|
|
55
|
-
});
|
|
56
|
-
return nodesWithChildren;
|
|
57
|
-
}
|
|
58
22
|
// given a chain of parent node descriptors, follow the chain from top to bottom as long as nodes exist
|
|
59
23
|
// return the deepest existing parent node descriptor, along with the actual node and the pos in the current document
|
|
60
24
|
// also return the remaining part of the chain
|
|
@@ -62,38 +26,41 @@ export function getDeepestSurvivingParent(parentChain, doc) {
|
|
|
62
26
|
const chain = [
|
|
63
27
|
...parentChain
|
|
64
28
|
].reverse();
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
throw new Error("doc not found in nodesWithChildren");
|
|
69
|
-
}
|
|
70
|
-
let parent = chain.shift(); // get doc node descriptor from parent chain
|
|
71
|
-
if (!guardDocParent(parent)) {
|
|
72
|
-
throw new Error("doc parent not found in op chain");
|
|
29
|
+
const root = chain.shift();
|
|
30
|
+
if (root == null) {
|
|
31
|
+
throw new Error("Parent chain is empty");
|
|
73
32
|
}
|
|
74
|
-
let
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
33
|
+
let result = {
|
|
34
|
+
parent: root,
|
|
35
|
+
node: doc,
|
|
36
|
+
pos: null
|
|
37
|
+
};
|
|
38
|
+
let remainingChain = [
|
|
39
|
+
...chain
|
|
40
|
+
];
|
|
41
|
+
// follow the chain up-down
|
|
42
|
+
// look for the node with the matching id in the children of the previously found node
|
|
43
|
+
for (const [index, item] of chain.entries()){
|
|
44
|
+
let found = false;
|
|
45
|
+
result.node.forEach((child, offset)=>{
|
|
46
|
+
if (found) return;
|
|
47
|
+
if (child.attrs["id"] !== item.nodeId) return;
|
|
48
|
+
found = true;
|
|
49
|
+
const pos = result.pos == null ? offset : result.pos + 1 + offset;
|
|
50
|
+
result = {
|
|
51
|
+
parent: item,
|
|
52
|
+
node: child,
|
|
53
|
+
pos
|
|
54
|
+
};
|
|
55
|
+
remainingChain = chain.slice(index + 1);
|
|
56
|
+
});
|
|
57
|
+
if (!found) break;
|
|
89
58
|
}
|
|
90
59
|
return {
|
|
91
|
-
parent,
|
|
92
|
-
node:
|
|
93
|
-
pos:
|
|
94
|
-
remainingChain:
|
|
95
|
-
...remainingChain
|
|
96
|
-
].reverse()
|
|
60
|
+
parent: result.parent,
|
|
61
|
+
node: result.node,
|
|
62
|
+
pos: result.pos,
|
|
63
|
+
remainingChain: remainingChain.reverse()
|
|
97
64
|
};
|
|
98
65
|
}
|
|
99
66
|
// given a chain of parent node descriptors and a node
|
|
@@ -107,14 +74,17 @@ export function wrapNodeInParentChain(parentChain, node) {
|
|
|
107
74
|
throw new Error(`node type ${parent.nodeType} not found in schema`);
|
|
108
75
|
}
|
|
109
76
|
const marks = parent.nodeMarks.map((mark)=>schema.markFromJSON(mark));
|
|
110
|
-
|
|
77
|
+
const parentNode = nodeType.createAndFill(parent.nodeAttrs, child, marks);
|
|
78
|
+
if (parentNode == null) throw new Error(`Unable to create node ${nodeType.name} with child ${child.toString()}`);
|
|
79
|
+
child = parentNode;
|
|
80
|
+
child.check();
|
|
111
81
|
}
|
|
112
82
|
return child;
|
|
113
83
|
}
|
|
114
84
|
// given a node, its position, and a parent descriptor of this node in some parent chain,
|
|
115
85
|
// use the info from the descriptor to find the insertion position in the node
|
|
116
86
|
// first try to find siblings, fallback to end of node
|
|
117
|
-
export function findInsertionPos(node, pos, parent) {
|
|
87
|
+
export function findInsertionPos(node, pos, parent, child) {
|
|
118
88
|
let leftSibling = null;
|
|
119
89
|
let rightSibling = null;
|
|
120
90
|
node.descendants((child, localChildPos)=>{
|
|
@@ -137,6 +107,16 @@ export function findInsertionPos(node, pos, parent) {
|
|
|
137
107
|
return false;
|
|
138
108
|
});
|
|
139
109
|
if (rightSibling != null) {
|
|
110
|
+
// special case: we need to insert as the first child, but the existing first child is an empty node of the same type
|
|
111
|
+
// in this case, we need to replace the existing first child with the new node
|
|
112
|
+
const firstChild = node.children[0];
|
|
113
|
+
if (parent.childSiblingIds[0] == null && firstChild?.type === child.type && firstChild.textContent === "") {
|
|
114
|
+
const from = pos != null ? pos + 1 : 0;
|
|
115
|
+
return {
|
|
116
|
+
from,
|
|
117
|
+
to: from + firstChild.nodeSize
|
|
118
|
+
};
|
|
119
|
+
}
|
|
140
120
|
// insert before right sibling
|
|
141
121
|
return rightSibling.pos;
|
|
142
122
|
}
|
|
@@ -43,11 +43,11 @@ function revertStructureSuggestionWithPrerequisites(tr, suggestionId) {
|
|
|
43
43
|
function revertOneStructureSuggestion(tr, suggestionId) {
|
|
44
44
|
console.group("revertStructureSuggestion", "reverting structure suggestion", suggestionId);
|
|
45
45
|
let structureMark = findNextStructureMark(tr.doc, suggestionId);
|
|
46
|
-
while(structureMark
|
|
47
|
-
console.groupCollapsed("revertStructureSuggestion", "reverting structure mark", structureMark.mark.attrs["id"], "at pos", structureMark
|
|
46
|
+
while(structureMark !== null){
|
|
47
|
+
console.groupCollapsed("revertStructureSuggestion", "reverting structure mark", structureMark.mark.attrs["id"], "at pos", structureMark.pos, "at node", structureMark.node.toString(), {
|
|
48
48
|
structureMark
|
|
49
49
|
});
|
|
50
|
-
revertStructureMark(tr, structureMark.mark, structureMark
|
|
50
|
+
revertStructureMark(tr, structureMark.mark, structureMark.pos);
|
|
51
51
|
console.groupEnd();
|
|
52
52
|
structureMark = findNextStructureMark(tr.doc, suggestionId);
|
|
53
53
|
}
|
|
@@ -154,7 +154,6 @@ function buildOrderedSuggestionIds(node, suggestionId, materializedPaths) {
|
|
|
154
154
|
return Array.from(suggestionIds).reverse();
|
|
155
155
|
}
|
|
156
156
|
function findNextStructureMark(node, suggestionId) {
|
|
157
|
-
console.log("findNextStructureMark", suggestionId);
|
|
158
157
|
const { structure } = getSuggestionMarks(node.type.schema);
|
|
159
158
|
const structureMarks = [];
|
|
160
159
|
node.descendants((descendant, pos)=>{
|
|
@@ -163,18 +162,11 @@ function findNextStructureMark(node, suggestionId) {
|
|
|
163
162
|
structureMarks.push({
|
|
164
163
|
mark,
|
|
165
164
|
node: descendant,
|
|
165
|
+
pos,
|
|
166
166
|
$pos: node.resolve(pos)
|
|
167
167
|
});
|
|
168
168
|
return true;
|
|
169
169
|
});
|
|
170
170
|
structureMarks.sort((a, b)=>b.$pos.depth - a.$pos.depth);
|
|
171
|
-
|
|
172
|
-
console.log("findStructureMark", "found structure mark with id", suggestionId, {
|
|
173
|
-
structureMark: structureMarks[0],
|
|
174
|
-
suggestionId
|
|
175
|
-
});
|
|
176
|
-
} else {
|
|
177
|
-
console.log("findStructureMark", "no structure mark found with id", suggestionId);
|
|
178
|
-
}
|
|
179
|
-
return structureMarks[0];
|
|
171
|
+
return structureMarks[0] ?? null;
|
|
180
172
|
}
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import { type Attrs
|
|
2
|
-
interface NonDocNodeWithChildren {
|
|
3
|
-
node: Node;
|
|
4
|
-
pos: number;
|
|
5
|
-
children: Set<string>;
|
|
6
|
-
}
|
|
7
|
-
export interface DocWithChildren extends Omit<NonDocNodeWithChildren, "pos"> {
|
|
8
|
-
pos: null;
|
|
9
|
-
}
|
|
10
|
-
export type NodeWithChildren = NonDocNodeWithChildren | DocWithChildren;
|
|
1
|
+
import { type Attrs } from "prosemirror-model";
|
|
11
2
|
interface NodeParent {
|
|
12
3
|
nodeId: string;
|
|
13
4
|
nodeType: string;
|
|
@@ -41,5 +32,4 @@ export type MaterializedPaths = Map<string, {
|
|
|
41
32
|
}>;
|
|
42
33
|
export declare function guardDocParent(parent: Parent | undefined): parent is DocParent;
|
|
43
34
|
export declare function guardStructureMarkAttrs(attrs: Attrs): attrs is StructureMarkAttrs;
|
|
44
|
-
export declare function guardDocWithChildren(nodeWithChildren: NodeWithChildren | undefined): nodeWithChildren is DocWithChildren;
|
|
45
35
|
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,3 +5,4 @@ export { withSuggestChanges, transformToSuggestionTransaction, } from "./withSug
|
|
|
5
5
|
export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled, } from "./ensureSelectionPlugin.js";
|
|
6
6
|
export { guardStructureMarkAttrs } from "./features/wrapUnwrap/types.js";
|
|
7
7
|
export type { Op as StructureOp, StructureMarkAttrs, } from "./features/wrapUnwrap/types.js";
|
|
8
|
+
export { wrappingInputRule as experimental_wrappingInputRule } from "./wrappingInputRule.js";
|
package/dist/index.js
CHANGED
|
@@ -4,3 +4,4 @@ export { suggestChanges, suggestChangesKey, isSuggestChangesEnabled } from "./pl
|
|
|
4
4
|
export { withSuggestChanges, transformToSuggestionTransaction } from "./withSuggestChanges.js";
|
|
5
5
|
export { ensureSelection as experimental_ensureSelection, ensureSelectionKey as experimental_ensureSelectionKey, isEnsureSelectionEnabled as experimental_isEnsureSelectionEnabled } from "./ensureSelectionPlugin.js";
|
|
6
6
|
export { guardStructureMarkAttrs } from "./features/wrapUnwrap/types.js";
|
|
7
|
+
export { wrappingInputRule as experimental_wrappingInputRule } from "./wrappingInputRule.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { wrappingInputRule } from "./wrappingInputRule.js";
|
|
2
|
+
import { ZWSP } from "./constants.js";
|
|
3
|
+
export function listInputRules(bulletListNodeType, orderedListNodeType) {
|
|
4
|
+
const bulletListInputRule = wrappingInputRule(// ^ string start, [${ZWSP}\\s]* zero or more ZWSP or whitespace, ([-+*]) one of -+* , \\s one whitespace, $ end of string
|
|
5
|
+
// "u" flag treats \u as unicode code points instead of literal "u"
|
|
6
|
+
new RegExp(`^[${ZWSP}\\s]*([-+*])\\s$`, "u"), bulletListNodeType);
|
|
7
|
+
// ^ string start, [${ZWSP}\\s]* zero or more ZWSP or whitespace, ([0-9]+\\.) digit followed by dot, \\s one whitespace, $ end of string
|
|
8
|
+
// "u" flag treats \u as unicode code points instead of literal "u"
|
|
9
|
+
const orderedListInputRule = wrappingInputRule(new RegExp(`^[${ZWSP}\\s]*([0-9]+\\.)\\s$`, "u"), orderedListNodeType);
|
|
10
|
+
return [
|
|
11
|
+
bulletListInputRule,
|
|
12
|
+
orderedListInputRule
|
|
13
|
+
];
|
|
14
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { InputRule } from "prosemirror-inputrules";
|
|
2
|
+
import { type Node } from "prosemirror-model";
|
|
3
|
+
import { type Attrs, type NodeType } from "prosemirror-model";
|
|
4
|
+
export declare function wrappingInputRule(regexp: RegExp, nodeType: NodeType, getAttrs?: Attrs | null | ((matches: RegExpMatchArray) => Attrs | null), joinPredicate?: (match: RegExpMatchArray, node: Node) => boolean): InputRule;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { InputRule } from "prosemirror-inputrules";
|
|
2
|
+
import { canJoin, findWrapping } from "prosemirror-transform";
|
|
3
|
+
import { getSuggestionMarks } from "./utils.js";
|
|
4
|
+
import { ZWSP } from "./constants.js";
|
|
5
|
+
/// return a boolean to indicate whether a join should happen.
|
|
6
|
+
export function wrappingInputRule(regexp, nodeType, getAttrs = null, joinPredicate) {
|
|
7
|
+
return new InputRule(regexp, (state, match, start, end)=>{
|
|
8
|
+
const attrs = getAttrs instanceof Function ? getAttrs(match) : getAttrs;
|
|
9
|
+
const tr = state.tr;
|
|
10
|
+
// check and try to preserve zwsp
|
|
11
|
+
const { insertion } = getSuggestionMarks(state.doc.type.schema);
|
|
12
|
+
let $start = tr.doc.resolve(start);
|
|
13
|
+
if (insertion.isInSet($start.nodeAfter?.marks ?? []) && match[0].startsWith(ZWSP)) {
|
|
14
|
+
// preserve a single ZWSP at the start
|
|
15
|
+
tr.delete(start + 1, end);
|
|
16
|
+
} else {
|
|
17
|
+
tr.delete(start, end);
|
|
18
|
+
}
|
|
19
|
+
// the rest of the rule unchanged
|
|
20
|
+
$start = tr.doc.resolve(start);
|
|
21
|
+
const range = $start.blockRange(), wrapping = range && findWrapping(range, nodeType, attrs);
|
|
22
|
+
if (!wrapping) return null;
|
|
23
|
+
tr.wrap(range, wrapping);
|
|
24
|
+
const before = tr.doc.resolve(start - 1).nodeBefore;
|
|
25
|
+
if (before && before.type == nodeType && canJoin(tr.doc, start - 1) && (!joinPredicate || joinPredicate(match, before))) tr.join(start - 1);
|
|
26
|
+
return tr;
|
|
27
|
+
});
|
|
28
|
+
}
|