From ed199cd414854dcefd1ec348e5606bead8032b2b Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 21 Mar 2022 11:44:05 +0100 Subject: [PATCH] Fix a crash in DecorationSet.map when a node is lifted from multiple parent FIX: Fix a crash in `DecorationSet.map` that could occur with some kinds of lift transformations. See https://discuss.prosemirror.net/t/mapping-node-decorations-creates-rangeerror-when-deleting-text/4491 --- src/decoration.js | 14 ++++++++++---- test/test-decoration.js | 15 +++++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/decoration.js b/src/decoration.js index e45c9656..3a75d6b1 100644 --- a/src/decoration.js +++ b/src/decoration.js @@ -512,9 +512,10 @@ function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, op let shift = (oldStart, oldEnd, newStart, newEnd) => { for (let i = 0; i < children.length; i += 3) { let end = children[i + 1], dSize - if (end == -1 || oldStart > end + oldOffset) continue - if (oldEnd >= children[i] + oldOffset) { - children[i + 1] = -1 + if (end < 0 || oldStart > end + oldOffset) continue + let start = children[i] + oldOffset + if (oldEnd >= start) { + children[i + 1] = oldStart <= start ? -2 : -1 } else if (newStart >= offset && (dSize = (newEnd - newStart) - (oldEnd - oldStart))) { children[i] += dSize children[i + 1] += dSize @@ -526,7 +527,12 @@ function mapChildren(oldChildren, newLocal, mapping, node, offset, oldOffset, op // Find the child nodes that still correspond to a single node, // recursively call mapInner on them and update their positions. let mustRebuild = false - for (let i = 0; i < children.length; i += 3) if (children[i + 1] == -1) { // Touched nodes + for (let i = 0; i < children.length; i += 3) if (children[i + 1] < 0) { // Touched nodes + if (children[i + 1] == -2) { + mustRebuild = true + children[i + 1] = -1 + continue + } let from = mapping.map(oldChildren[i] + oldOffset), fromLocal = from - offset if (fromLocal < 0 || fromLocal >= node.content.size) { mustRebuild = true diff --git a/test/test-decoration.js b/test/test-decoration.js index d6858c5e..0473d17f 100644 --- a/test/test-decoration.js +++ b/test/test-decoration.js @@ -1,7 +1,7 @@ const ist = require("ist") const {schema, doc, p, h1, li, ul, blockquote} = require("prosemirror-test-builder") -const {Transform, ReplaceAroundStep} = require("prosemirror-transform") -const {Slice} = require("prosemirror-model") +const {Transform, ReplaceAroundStep, liftTarget} = require("prosemirror-transform") +const {Slice, NodeRange} = require("prosemirror-model") const {Decoration, DecorationSet} = require("..") @@ -236,6 +236,17 @@ describe("DecorationSet", () => { ist(mapped.from, 4) ist(mapped.to, 6) }) + + it("can handle nodes moving up multiple levels", () => { + let d = doc(ul(li(p()))) + let set = build(d, {node: true, from: 2, to: 4}) + let range = new NodeRange(d.resolve(2), d.resolve(4), 2) + let tr = new Transform(d).lift(range, liftTarget(range)) + let mapped = set.map(tr.mapping, tr.doc).find() + ist(mapped.length, 1) + ist(mapped[0].from, 0) + ist(mapped[0].to, 2) + }) }) describe("add", () => {