Merge pasted list items into the list item under the cursor in the rich text editor
What does this MR do and why?
The rich text editor pastes markdown by rendering it through the API and inserting the result as closed block nodes. Only a lone paragraph was special-cased to inline content. Copying text out of a list item puts the whole list on the clipboard (* text), so pasting it into another list item nested a new list inside that item instead of inserting the text at the cursor. Blockquotes and headings pasted into the same kind of block recreated the block the same way.
When the pasted content's outer blocks match the blocks around the cursor (list flavours count as one kind, and so do their items), open the pasted slice through them and insert it with ProseMirror's replaceRange, the way a native paste does, so the content merges into the current block. The end of the slice stays open as deep as it goes so the text after the cursor continues in the last pasted block. Everything else keeps the existing insertion: a list pasted into a paragraph still becomes a list block, a heading pasted into a paragraph still becomes a heading, and table cells and code blocks are untouched.
Resolves #512151 (closed)
References
- Reporter's GIF and the agreed expectation: content copied from a block-level element and pasted into a similar block-level element should reuse the existing block (note by pedroms, note by himkp).
- A new end-to-end example in the rich text editor copy/paste shared examples drives the real Chrome clipboard: copying the word
oneout ofitem oneand pasting it at the end ofitem twoyields* item twofollowed by a nested* oneat the merge base and* item twoonewith this change. A second example pastes the same word copied from a plain paragraph and passes on both, isolating the list-context payload. All 10 examples of the shared group pass locally. Pre-approval pipelines select RSpec predictively and do not map this shared example to the spec that includes it, so it first runs in CI at tier-2 or tier-3, or withpipeline:run-all-rspec. - New jest examples in the extension spec: 17 fail at the merge base and pass with this change (list item content pasted at the end, start and middle of an item and into an empty item; ordered and task list items both ways; several items becoming siblings; a nested target; sub-items kept; marks kept; text after the cursor continuing in the last pasted block; a selection replaced; typing while the render is pending; blockquote into blockquote; heading into heading). Four more lock the unchanged behaviour: a list into an empty paragraph, a list block inside a paragraph, a list block inside a blockquote paragraph, a heading block inside a paragraph. The extension spec passes 74/74 and the content editor and markdown trees 1574/1574 under Vue 2 and Vue 3.
- The single-paragraph special case comes from Improve paste markdown behavior in Content Editor (!115574); this change generalizes it to lists, blockquotes and headings and keeps its behaviour for paragraphs. The same closed insertion produced URL pasted from clipboard are inserted as new lines (#378079) for paragraphs.
- Stock ProseMirror pastes non-editor HTML as a maximally open slice (
Slice.maxOpenin prosemirror-view'sparseFromClipboard) and fits it withreplaceRange. This change uses the same primitives but opens only through matching blocks: opening everything would merge a pasted table's cells into the surrounding table (measured against the "pastes clipboard table HTML as a nested table inside the cell" example) and flatten headings and blockquotes pasted into paragraphs.
Screenshots or screen recordings
Every row is staged the same way in real headless Chrome with the real clipboard: the document is typed into the rich text editor, the source text is selected with the keyboard and copied, the cursor is placed in the target and the content pasted. The five Unchanged rows are byte-identical between before and after.
Recordings
Three beats per side: the source text selected and copied, the cursor placed in the target, the paste settled. Frames 1 and 2 are byte-identical between the sides; only the paste beat differs, and in the unchanged story none does.
How to set up and validate locally
- Open any rich text editor surface, for example the Description field on the new issue page, and switch to rich text editing if in plain text mode.
- Type a bullet list with two items, for example
item oneanditem two. - Select the word
onein the first item and copy it. - Click at the end of
item twoand paste. - Before the fix: a nested bullet
oneappears underitem two. After the fix: the item readsitem twoone. - Repeat with the cursor at the start and in the middle of
item two, with an ordered list as the source, and with a task list as the target: the text lands at the cursor every time. - Regression checks: paste the same clipboard into an empty paragraph (still becomes a list), into the middle of a paragraph (still inserts a list block, as before), and paste plain text into a list item (still inline).
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.





































