Skip to content

Commit

Permalink
fix(prosemirror-paste-rules): set open depth more precisely (#1644)
Browse files Browse the repository at this point in the history
  • Loading branch information
ocavue committed May 16, 2022
1 parent 61e1050 commit 0ac8d4a
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 12 deletions.
9 changes: 9 additions & 0 deletions .changeset/three-owls-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'prosemirror-paste-rules': patch
---

Fix open depths in node paste rules.

When excuting a node paste rule, only reset open depths ([openStart](https://prosemirror.net/docs/ref/#model.Slice.openStart) and [openEnd](https://prosemirror.net/docs/ref/#model.Slice.openEnd)) when the node paste rule is actually applied and it's for a block node.

This patch will fix the extra paragraph after pasting text.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
blockquote,
createEditor,
doc,
em,
Expand Down Expand Up @@ -365,6 +366,90 @@ describe('pasteRules', () => {
});
});

it('can handle openStart and openEnd correctly', () => {
const plugin = pasteRules([
{
regexp: /---/,
type: 'node',
nodeType: schema.nodes.hard_break,
getContent: () => {},
},
{
regexp: /===/,
type: 'node',
nodeType: schema.nodes.hard_break,
getContent: () => {},
},
{
regexp: /^# ([\s\w]+)$/,
type: 'node',
nodeType: schema.nodes.heading,
getAttributes: () => ({
level: 1,
}),
},
]);
createEditor(doc(blockquote(p('A<cursor>Z'))), { plugins: [plugin] })
.paste('B')
.callback((content) => {
expect(content.doc).toEqualProsemirrorNode(doc(blockquote(p('ABZ'))));
})
// Pasting inline node(s) should keey openStart and openEnd
.paste('---')
.callback((content) => {
expect(content.doc).toEqualProsemirrorNode(doc(blockquote(p('AB', hardBreak(), 'Z'))));
})
.paste('C')
.callback((content) => {
expect(content.doc).toEqualProsemirrorNode(doc(blockquote(p('AB', hardBreak(), 'CZ'))));
})
.paste('---===')
.callback((content) => {
expect(content.doc).toEqualProsemirrorNode(
doc(blockquote(p('AB', hardBreak(), 'C', hardBreak(), hardBreak(), 'Z'))),
);
})
.paste('D')
.callback((content) => {
expect(content.doc).toEqualProsemirrorNode(
doc(blockquote(p('AB', hardBreak(), 'C', hardBreak(), hardBreak(), 'D', 'Z'))),
);
})
// Pasting block node(s) should reset openStart and openEnd
.paste('# E\n# F\n---\n---===G\n# H')
.callback((content) => {
expect(content.doc).toEqualProsemirrorNode(
doc(
blockquote(
p('AB', hardBreak(), 'C', hardBreak(), hardBreak(), 'D'),
h1('E'),
h1('F'),
p(hardBreak()),
p(hardBreak(), hardBreak(), 'G'),
h1('H'),
p('Z'),
),
),
);
})
.paste('I')
.callback((content) => {
expect(content.doc).toEqualProsemirrorNode(
doc(
blockquote(
p('AB', hardBreak(), 'C', hardBreak(), hardBreak(), 'D'),
h1('E'),
h1('F'),
p(hardBreak()),
p(hardBreak(), hardBreak(), 'G'),
h1('H'),
p('IZ'),
),
),
);
});
});

it('can remove text with group', () => {
const plugin = pasteRules([
{
Expand Down
36 changes: 24 additions & 12 deletions packages/prosemirror-paste-rules/src/paste-rules-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,13 +96,19 @@ export function pasteRules(pasteRules: PasteRule[]): Plugin<void> {
}
}

slice = new Slice(
Fragment.fromArray(regexPasteRuleHandler(slice.content, rule, view.state.schema)),

// If we are using node rules, we don't need to keep the slice's open side for both side.
rule.type === 'node' ? 0 : slice.openStart,
rule.type === 'node' ? 0 : slice.openEnd,
const { nodes: transformedNodes, transformed } = regexPasteRuleHandler(
slice.content,
rule,
view.state.schema,
);

if (transformed) {
// If we have created a block node, we don't want to keep the slice's open depth for both side.
slice =
rule.type === 'node' && rule.nodeType.isBlock
? new Slice(Fragment.fromArray(transformedNodes), 0, 0)
: new Slice(Fragment.fromArray(transformedNodes), slice.openStart, slice.openEnd);
}
}

return slice;
Expand Down Expand Up @@ -398,9 +404,13 @@ function createPasteRuleHandler<Rule extends RegexPasteRule>(
transformer: Transformer<Rule>,
schema: EditorSchema,
) {
return function handler(props: PasteRuleHandler<Rule>): ProsemirrorNode[] {
return function handler(props: PasteRuleHandler<Rule>): {
nodes: ProsemirrorNode[];
transformed: boolean;
} {
const { fragment, rule, nodes } = props;
const { regexp, ignoreWhitespace, ignoredMarks, ignoredNodes } = rule;
let transformed = false;

fragment.forEach((child) => {
// Check if this node should be ignored.
Expand All @@ -411,13 +421,14 @@ function createPasteRuleHandler<Rule extends RegexPasteRule>(

// When the current node is not a text node, recursively dive into it's child nodes.
if (!child.isText) {
const contentNodes = handler({ fragment: child.content, rule, nodes: [] });
const content = Fragment.fromArray(contentNodes);
const childResult = handler({ fragment: child.content, rule, nodes: [] });
transformed ||= childResult.transformed;
const content = Fragment.fromArray(childResult.nodes);

if (child.type.validContent(content)) {
nodes.push(child.copy(content));
} else {
nodes.push(...contentNodes);
nodes.push(...childResult.nodes);
}

return;
Expand Down Expand Up @@ -473,6 +484,7 @@ function createPasteRuleHandler<Rule extends RegexPasteRule>(

// A transformer to push the required nodes.
transformer({ nodes, rule, textNode, match, schema });
transformed = true;
pos = end;
}

Expand All @@ -483,7 +495,7 @@ function createPasteRuleHandler<Rule extends RegexPasteRule>(
}
});

return nodes;
return { nodes, transformed };
};
}

Expand Down Expand Up @@ -550,7 +562,7 @@ function regexPasteRuleHandler(
fragment: Fragment,
rule: RegexPasteRule,
schema: EditorSchema,
): ProsemirrorNode[] {
): { nodes: ProsemirrorNode[]; transformed: boolean } {
const nodes: ProsemirrorNode[] = [];

switch (rule.type) {
Expand Down

1 comment on commit 0ac8d4a

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 Published on https://remirror.io as production
🚀 Deployed on https://6282607ffde8f32e9e75d0b8--remirror.netlify.app

Please sign in to comment.