@prosekit/extensions 0.0.0-next-20240421132240 → 0.0.0-next-20240427200701
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.
@@ -16,6 +16,7 @@ import { IndentListOptions } from 'prosemirror-flat-list';
|
|
16
16
|
import { InputRule } from '@prosekit/pm/inputrules';
|
17
17
|
import { ListAttributes } from 'prosemirror-flat-list';
|
18
18
|
import { ListDOMSerializer } from 'prosemirror-flat-list';
|
19
|
+
import { MarkType } from '@prosekit/pm/model';
|
19
20
|
import { NodeRange } from 'prosemirror-model';
|
20
21
|
import { NodeType } from '@prosekit/pm/model';
|
21
22
|
import { Options } from 'tsup';
|
@@ -28,6 +29,8 @@ import { ToggleCollapsedOptions } from 'prosemirror-flat-list';
|
|
28
29
|
import { Transaction } from '@prosekit/pm/state';
|
29
30
|
import { UnwrapListOptions } from 'prosemirror-flat-list';
|
30
31
|
|
32
|
+
export declare function applyMarkRules(rules: MarkRule[], transactions: readonly Transaction[], oldState: EditorState, newState: EditorState): Transaction | null;
|
33
|
+
|
31
34
|
declare class AutocompleteRule {
|
32
35
|
readonly regex: RegExp;
|
33
36
|
readonly onMatch: MatchHandler;
|
@@ -467,6 +470,12 @@ export declare function defineListSpec(): Extension<{
|
|
467
470
|
NODES: "list";
|
468
471
|
}>;
|
469
472
|
|
473
|
+
/**
|
474
|
+
* A mark rule is something that can automatically apply marks to text if it
|
475
|
+
* matches a certain pattern, and remove them if it doesn't match anymore.
|
476
|
+
*/
|
477
|
+
export declare function defineMarkRule(options: MarkRuleOptions): Extension<ExtensionTyping<string, string, CommandArgs>>;
|
478
|
+
|
470
479
|
/**
|
471
480
|
* @public
|
472
481
|
*/
|
@@ -758,6 +767,8 @@ export declare type EnterRuleOptions = {
|
|
758
767
|
*/
|
759
768
|
export declare const exitTable: Command;
|
760
769
|
|
770
|
+
export declare function getCheckRanges(transactions: readonly Transaction[], oldState: EditorState, newState: EditorState): Array<[number, number]>;
|
771
|
+
|
761
772
|
export { getHighlighter }
|
762
773
|
|
763
774
|
export declare function getPluginState(state: EditorState): PredictionPluginState | undefined;
|
@@ -812,6 +823,42 @@ export declare interface LinkAttrs {
|
|
812
823
|
|
813
824
|
export { ListDOMSerializer }
|
814
825
|
|
826
|
+
/**
|
827
|
+
* @internal
|
828
|
+
*/
|
829
|
+
export declare class MarkRule {
|
830
|
+
readonly regex: RegExp;
|
831
|
+
readonly type: string | MarkType;
|
832
|
+
readonly getAttrs: (match: RegExpMatchArray) => Attrs | null;
|
833
|
+
constructor({ regex, type, attrs }: MarkRuleOptions);
|
834
|
+
}
|
835
|
+
|
836
|
+
/**
|
837
|
+
* The options for {@link defineMarkRule}.
|
838
|
+
*
|
839
|
+
* @public
|
840
|
+
*/
|
841
|
+
declare interface MarkRuleOptions {
|
842
|
+
/**
|
843
|
+
* The regular expression to match against. It must has a `g` flag to match
|
844
|
+
* all instances of the mark.
|
845
|
+
*/
|
846
|
+
regex: RegExp;
|
847
|
+
/**
|
848
|
+
* The mark type to apply to the matched text.
|
849
|
+
*/
|
850
|
+
type: string | MarkType;
|
851
|
+
/**
|
852
|
+
* Attributes to set on the mark. If a function is provided, it will be called
|
853
|
+
* with the matched result from the regular expression.
|
854
|
+
*
|
855
|
+
* @default null
|
856
|
+
*/
|
857
|
+
attrs?: Attrs | null | ((match: RegExpMatchArray) => Attrs | null);
|
858
|
+
}
|
859
|
+
export { MarkRuleOptions }
|
860
|
+
export { MarkRuleOptions as MarkRuleOptions_alias_1 }
|
861
|
+
|
815
862
|
declare type MatchHandler = (options: {
|
816
863
|
state: EditorState;
|
817
864
|
match: RegExpExecArray;
|
@@ -1,9 +1,9 @@
|
|
1
|
-
import {
|
2
|
-
defineTextBlockInputRule
|
3
|
-
} from "./chunk-DYFRBXUX.js";
|
4
1
|
import {
|
5
2
|
defineTextBlockEnterRule
|
6
3
|
} from "./chunk-ASTUC4KT.js";
|
4
|
+
import {
|
5
|
+
defineTextBlockInputRule
|
6
|
+
} from "./chunk-DYFRBXUX.js";
|
7
7
|
|
8
8
|
// src/code-block/code-block.ts
|
9
9
|
import { union } from "@prosekit/core";
|
@@ -0,0 +1,184 @@
|
|
1
|
+
// src/mark-rule/index.ts
|
2
|
+
import { Facet, pluginFacet } from "@prosekit/core";
|
3
|
+
import { ProseMirrorPlugin } from "@prosekit/pm/state";
|
4
|
+
|
5
|
+
// src/mark-rule/apply.ts
|
6
|
+
import { OBJECT_REPLACEMENT_CHARACTER, getMarkType } from "@prosekit/core";
|
7
|
+
import "@prosekit/pm/model";
|
8
|
+
import "@prosekit/pm/state";
|
9
|
+
|
10
|
+
// src/mark-rule/range.ts
|
11
|
+
import "@prosekit/pm/state";
|
12
|
+
function getSpanTextRanges($from, $to) {
|
13
|
+
const nodeRange = $from.blockRange($to);
|
14
|
+
if (!nodeRange) {
|
15
|
+
return [];
|
16
|
+
}
|
17
|
+
const stack = [];
|
18
|
+
let start = nodeRange.start;
|
19
|
+
for (let i = nodeRange.startIndex; i < nodeRange.endIndex; i++) {
|
20
|
+
const child = nodeRange.parent.child(i);
|
21
|
+
stack.push([start, child]);
|
22
|
+
start += child.nodeSize;
|
23
|
+
}
|
24
|
+
const ranges = [];
|
25
|
+
while (stack.length > 0) {
|
26
|
+
const [start2, node] = stack.pop();
|
27
|
+
if (node.type.spec.code) {
|
28
|
+
continue;
|
29
|
+
}
|
30
|
+
if (node.type.isTextblock) {
|
31
|
+
ranges.push([start2 + 1, start2 + 1 + node.content.size]);
|
32
|
+
continue;
|
33
|
+
}
|
34
|
+
node.forEach((child, offset) => {
|
35
|
+
stack.push([start2 + offset + 1, child]);
|
36
|
+
});
|
37
|
+
}
|
38
|
+
return ranges;
|
39
|
+
}
|
40
|
+
function getInlineTextRange($from, $to) {
|
41
|
+
return [$from.start(), $to.end()];
|
42
|
+
}
|
43
|
+
function getTextRanges(doc, from, to) {
|
44
|
+
const $from = doc.resolve(from);
|
45
|
+
const $to = doc.resolve(to);
|
46
|
+
if ($from.sameParent($to) && $from.parent.isTextblock) {
|
47
|
+
return [getInlineTextRange($from, $to)];
|
48
|
+
} else {
|
49
|
+
const nodeRange = $from.blockRange($to);
|
50
|
+
if (!nodeRange) {
|
51
|
+
return [];
|
52
|
+
}
|
53
|
+
return getSpanTextRanges($from, $to);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
function getMapRange(transactions, oldState, newState) {
|
57
|
+
let lo = oldState.selection.from;
|
58
|
+
let hi = oldState.selection.to;
|
59
|
+
for (const tr of transactions) {
|
60
|
+
for (const map of tr.mapping.maps) {
|
61
|
+
lo = map.map(lo);
|
62
|
+
hi = map.map(hi);
|
63
|
+
map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {
|
64
|
+
lo = Math.min(lo, hi, newStart);
|
65
|
+
hi = Math.max(hi, hi, newEnd);
|
66
|
+
});
|
67
|
+
}
|
68
|
+
}
|
69
|
+
lo = Math.min(lo, hi, newState.selection.from);
|
70
|
+
hi = Math.min(lo, hi, newState.selection.to);
|
71
|
+
return [lo, hi];
|
72
|
+
}
|
73
|
+
function getCheckRanges(transactions, oldState, newState) {
|
74
|
+
const [from, to] = getMapRange(transactions, oldState, newState);
|
75
|
+
return getTextRanges(newState.doc, from, to);
|
76
|
+
}
|
77
|
+
|
78
|
+
// src/mark-rule/apply.ts
|
79
|
+
function getExpectedMarkings(rules, doc, from, to) {
|
80
|
+
const text = doc.textBetween(from, to, OBJECT_REPLACEMENT_CHARACTER);
|
81
|
+
const result = [];
|
82
|
+
for (const rule of rules) {
|
83
|
+
rule.regex.lastIndex = 0;
|
84
|
+
const matches = text.matchAll(rule.regex);
|
85
|
+
const markType = getMarkType(doc.type.schema, rule.type);
|
86
|
+
for (const match of matches) {
|
87
|
+
const index = match.index;
|
88
|
+
if (index == null)
|
89
|
+
continue;
|
90
|
+
const attrs = rule.getAttrs(match);
|
91
|
+
const mark = markType.create(attrs);
|
92
|
+
result.push([mark, from + index, from + index + match[0].length]);
|
93
|
+
}
|
94
|
+
}
|
95
|
+
return result;
|
96
|
+
}
|
97
|
+
function getReceivedMarkings(rules, doc, from, to) {
|
98
|
+
const result = [];
|
99
|
+
const schema = doc.type.schema;
|
100
|
+
const markTypes = rules.map((rule) => getMarkType(schema, rule.type));
|
101
|
+
doc.nodesBetween(from, to, (node, pos) => {
|
102
|
+
if (!node.isInline) {
|
103
|
+
return;
|
104
|
+
}
|
105
|
+
for (const markType of markTypes) {
|
106
|
+
const mark = node.marks.find((mark2) => mark2.type === markType);
|
107
|
+
if (mark) {
|
108
|
+
result.push([mark, pos, pos + node.nodeSize]);
|
109
|
+
}
|
110
|
+
}
|
111
|
+
});
|
112
|
+
return result;
|
113
|
+
}
|
114
|
+
function markingEquals(a, b) {
|
115
|
+
return a[1] === b[1] && a[2] === b[2] && a[0].eq(b[0]);
|
116
|
+
}
|
117
|
+
function markingDiffs(a, b) {
|
118
|
+
return a.filter((x) => !b.some((y) => markingEquals(x, y)));
|
119
|
+
}
|
120
|
+
function applyMarkRules(rules, transactions, oldState, newState) {
|
121
|
+
if (transactions.length === 0 || transactions.every((tr2) => !tr2.docChanged)) {
|
122
|
+
return null;
|
123
|
+
}
|
124
|
+
const ranges = getCheckRanges(transactions, oldState, newState);
|
125
|
+
const toRemove = [];
|
126
|
+
const toCreate = [];
|
127
|
+
for (const [from, to] of ranges) {
|
128
|
+
const expected = getExpectedMarkings(rules, newState.doc, from, to);
|
129
|
+
const received = getReceivedMarkings(rules, newState.doc, from, to);
|
130
|
+
toRemove.push(...markingDiffs(received, expected));
|
131
|
+
toCreate.push(...markingDiffs(expected, received));
|
132
|
+
}
|
133
|
+
if (toCreate.length === 0 && toRemove.length === 0) {
|
134
|
+
return null;
|
135
|
+
}
|
136
|
+
const tr = newState.tr;
|
137
|
+
for (const [mark, from, to] of toRemove) {
|
138
|
+
tr.removeMark(from, to, mark);
|
139
|
+
}
|
140
|
+
for (const [mark, from, to] of toCreate) {
|
141
|
+
tr.addMark(from, to, mark);
|
142
|
+
}
|
143
|
+
return tr;
|
144
|
+
}
|
145
|
+
|
146
|
+
// src/mark-rule/rule.ts
|
147
|
+
import "@prosekit/pm/model";
|
148
|
+
var MarkRule = class {
|
149
|
+
constructor({ regex, type, attrs = null }) {
|
150
|
+
this.regex = regex;
|
151
|
+
this.type = type;
|
152
|
+
this.getAttrs = typeof attrs === "function" ? attrs : () => attrs;
|
153
|
+
}
|
154
|
+
};
|
155
|
+
|
156
|
+
// src/mark-rule/index.ts
|
157
|
+
function defineMarkRule(options) {
|
158
|
+
return markRuleFacet.extension([new MarkRule(options)]);
|
159
|
+
}
|
160
|
+
var markRuleFacet = Facet.define({
|
161
|
+
converter: () => {
|
162
|
+
let rules = [];
|
163
|
+
const plugin = new ProseMirrorPlugin({
|
164
|
+
appendTransaction: (transactions, oldState, newState) => {
|
165
|
+
return applyMarkRules(rules, transactions, oldState, newState);
|
166
|
+
}
|
167
|
+
});
|
168
|
+
const pluginFunc = () => [plugin];
|
169
|
+
return {
|
170
|
+
create: (inputs) => {
|
171
|
+
rules = inputs;
|
172
|
+
return pluginFunc;
|
173
|
+
},
|
174
|
+
update: (inputs) => {
|
175
|
+
rules = inputs;
|
176
|
+
return null;
|
177
|
+
}
|
178
|
+
};
|
179
|
+
},
|
180
|
+
next: pluginFacet
|
181
|
+
});
|
182
|
+
export {
|
183
|
+
defineMarkRule
|
184
|
+
};
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@prosekit/extensions",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.0.0-next-
|
4
|
+
"version": "0.0.0-next-20240427200701",
|
5
5
|
"private": false,
|
6
6
|
"author": {
|
7
7
|
"name": "ocavue",
|
@@ -30,6 +30,11 @@
|
|
30
30
|
"import": "./dist/prosekit-extensions.js",
|
31
31
|
"default": "./dist/prosekit-extensions.js"
|
32
32
|
},
|
33
|
+
"./mark-rule": {
|
34
|
+
"types": "./dist/prosekit-extensions-mark-rule.d.ts",
|
35
|
+
"import": "./dist/prosekit-extensions-mark-rule.js",
|
36
|
+
"default": "./dist/prosekit-extensions-mark-rule.js"
|
37
|
+
},
|
33
38
|
"./autocomplete": {
|
34
39
|
"types": "./dist/prosekit-extensions-autocomplete.d.ts",
|
35
40
|
"import": "./dist/prosekit-extensions-autocomplete.js",
|
@@ -147,8 +152,8 @@
|
|
147
152
|
"dist"
|
148
153
|
],
|
149
154
|
"dependencies": {
|
150
|
-
"@prosekit/core": "0.0.0-next-
|
151
|
-
"@prosekit/pm": "0.0.0-next-
|
155
|
+
"@prosekit/core": "0.0.0-next-20240427200701",
|
156
|
+
"@prosekit/pm": "0.0.0-next-20240427200701",
|
152
157
|
"prosemirror-dropcursor": "^1.8.1",
|
153
158
|
"prosemirror-flat-list": "^0.5.0",
|
154
159
|
"prosemirror-highlight": "^0.5.0",
|
@@ -159,7 +164,7 @@
|
|
159
164
|
"@prosekit/dev": "*",
|
160
165
|
"tsup": "^8.0.2",
|
161
166
|
"typescript": "^5.4.5",
|
162
|
-
"vitest": "^1.5.
|
167
|
+
"vitest": "^1.5.2"
|
163
168
|
},
|
164
169
|
"scripts": {
|
165
170
|
"build:tsup": "tsup",
|
@@ -171,6 +176,9 @@
|
|
171
176
|
".": [
|
172
177
|
"./dist/prosekit-extensions.d.ts"
|
173
178
|
],
|
179
|
+
"mark-rule": [
|
180
|
+
"./dist/prosekit-extensions-mark-rule.d.ts"
|
181
|
+
],
|
174
182
|
"autocomplete": [
|
175
183
|
"./dist/prosekit-extensions-autocomplete.d.ts"
|
176
184
|
],
|