Extra line of copied type added on paste

If I go to the ProseMirror website and in the basic editor type a couple of headings, say

Heading 1

Heading 2

Heading 3


and then press enter when my cursor is at the end of Heading 3, a new paragraph line is generated, just as expected.

However, if I copy the whole thing, and paste it when my cursor is at the end of Heading 3 (without first pressing enter), an extra h3 line appears after paste, while the expected behaviour was for the cursor to either be at the end of Heading 3 without an extra line after that, or for the extra line to be of paragraph type.

This is shown in this screenshot:

I’ve tested it in both Chrome and Firefox, and the behaviour is the same.

So is this a bug in ProseMirror, expected behaviour, or something else @marjin?

This only happens when you do ctrl/cmd-a in the editor before copying, right? When I copy from this forum message, or drag across the entire document with the pointer and then copy, this doesn’t occur (there’s no blank line at the end).

When copying after select-all, you get a slice that isn’t ‘open’ at its end—i.e. the nodes inside it are considered entire, closed nodes, not pieces that have to be joined to other nodes. That is causing the replace algorithm to cut the h3 node that holds the selection in two, leaving the empty selection node at the end.

This is somewhat arbitrary, but doesn’t seem like a big enough problem to warrant special handling.

Yes, it does indeed only happen when ctrl+a is used to select the whole document.

I agree with you in the fact that the problem isn’t big enough.

Thank you for clarifying in detail why it is behaving the way it is!

I resolved this by adding a br tag rule to paragraph and in getAttrs on both paragraph and hard break opposite checks on the parent node name. This limits breaks to being inside PARAGRAPH and SPAN, otherwise a paragraph gets created.

I’d actually like to fix this (Ctrl+A, Ctrl+C, Ctrl+V sequence adds an unexpected newline). It’s unusual behavior compared to how most inputs work.

What would be the solution here? Should this be handled on copy or on paste?

This feels very hacky and I’m not sure what side-effects it could have but this did work to fix the issue:

// Plugin to transform pasted content
const newlineFixPlugin = new Plugin({
	props: {
		transformPasted(slice) {

			return new Slice(
				slice.content,
				// Math.max(n, 1) here to fix Select-All + Copy + Paste adding newlines
				Math.max(slice.openStart, 1),
				Math.max(slice.openEnd, 1)
			);

		}
	}
});