Skip to content

Commit

Permalink
Fix a crash in DecorationSet.map when a node is lifted from multiple …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
marijnh committed Mar 21, 2022
1 parent 7c133ea commit ed199cd
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 6 deletions.
14 changes: 10 additions & 4 deletions src/decoration.js
Expand Up @@ -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
Expand All @@ -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
Expand Down
15 changes: 13 additions & 2 deletions 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("..")

Expand Down Expand Up @@ -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", () => {
Expand Down

0 comments on commit ed199cd

Please sign in to comment.