@codemirror/state 6.7.1 → 6.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 6.7.2 (2026-08-31)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where mapping through specific types of changes could end up violating ordering and overlapping invariants.
6
+
1
7
  ## 6.7.1 (2026-07-05)
2
8
 
3
9
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -3082,7 +3082,7 @@ class Chunk {
3082
3082
  this.value = value;
3083
3083
  this.maxPoint = maxPoint;
3084
3084
  }
3085
- get length() { return this.to[this.to.length - 1]; }
3085
+ get length() { return last(this.to); }
3086
3086
  // Find the index of the given position and side. Use the ranges'
3087
3087
  // `from` pos when `end == false`, `to` when `end == true`.
3088
3088
  findIndex(pos, side, end, startAt = 0) {
@@ -3105,9 +3105,9 @@ class Chunk {
3105
3105
  if (f(this.from[i] + offset, this.to[i] + offset, this.value[i]) === false)
3106
3106
  return false;
3107
3107
  }
3108
- map(offset, changes) {
3108
+ map(offset, changes, basePos, baseSide, spill) {
3109
3109
  let value = [], from = [], to = [], newPos = -1, maxPoint = -1;
3110
- for (let i = 0; i < this.value.length; i++) {
3110
+ iter: for (let i = 0; i < this.value.length; i++) {
3111
3111
  let val = this.value[i], curFrom = this.from[i] + offset, curTo = this.to[i] + offset, newFrom, newTo;
3112
3112
  if (curFrom == curTo) {
3113
3113
  let mapped = changes.mapPos(curFrom, val.startSide, val.mapMode);
@@ -3132,9 +3132,27 @@ class Chunk {
3132
3132
  newPos = newFrom;
3133
3133
  if (val.point)
3134
3134
  maxPoint = Math.max(maxPoint, newTo - newFrom);
3135
- value.push(val);
3136
- from.push(newFrom - newPos);
3137
- to.push(newTo - newPos);
3135
+ if ((newFrom - basePos || val.startSide - baseSide) >= 0) {
3136
+ value.push(val);
3137
+ from.push(newFrom - newPos);
3138
+ to.push(newTo - newPos);
3139
+ basePos = newTo;
3140
+ baseSide = val.endSide;
3141
+ }
3142
+ else {
3143
+ if (newFrom == newTo) { // Try to reorder points to fit in here
3144
+ for (let i = value.length - 1; i > 0; i--) {
3145
+ if ((newFrom - to[i - 1] || val.startSide - value[i - 1].endSide) <= 0) {
3146
+ value.splice(i, 0, val);
3147
+ from.splice(i, 0, newFrom);
3148
+ to.splice(i, 0, newTo);
3149
+ continue iter;
3150
+ }
3151
+ }
3152
+ }
3153
+ // Otherwise, spill into a new layer
3154
+ spill(newFrom, newTo, val);
3155
+ }
3138
3156
  }
3139
3157
  return { mapped: value.length ? new Chunk(from, to, value, maxPoint) : null, pos: newPos };
3140
3158
  }
@@ -3248,6 +3266,12 @@ class RangeSet {
3248
3266
  if (changes.empty || this.isEmpty)
3249
3267
  return this;
3250
3268
  let chunks = [], chunkPos = [], maxPoint = -1;
3269
+ let spilled;
3270
+ let spill = (from, to, value) => {
3271
+ if (!spilled)
3272
+ spilled = new RangeSetBuilder();
3273
+ spilled.add(from, to, value);
3274
+ };
3251
3275
  for (let i = 0; i < this.chunk.length; i++) {
3252
3276
  let start = this.chunkPos[i], chunk = this.chunk[i];
3253
3277
  let touch = changes.touchesRange(start, start + chunk.length);
@@ -3257,7 +3281,9 @@ class RangeSet {
3257
3281
  chunkPos.push(changes.mapPos(start));
3258
3282
  }
3259
3283
  else if (touch === true) {
3260
- let { mapped, pos } = chunk.map(start, changes);
3284
+ let [prevPos, prevSide] = !chunks.length ? [-1, -1]
3285
+ : [last(chunkPos) + last(chunks).length, last(last(chunks).value).endSide];
3286
+ let { mapped, pos } = chunk.map(start, changes, prevPos, prevSide, spill);
3261
3287
  if (mapped) {
3262
3288
  maxPoint = Math.max(maxPoint, mapped.maxPoint);
3263
3289
  chunks.push(mapped);
@@ -3266,6 +3292,8 @@ class RangeSet {
3266
3292
  }
3267
3293
  }
3268
3294
  let next = this.nextLayer.map(changes);
3295
+ if (spilled)
3296
+ next = spilled.finishInner(next);
3269
3297
  return chunks.length == 0 ? next : new RangeSet(chunkPos, chunks, next || RangeSet.empty, maxPoint);
3270
3298
  }
3271
3299
  /**
@@ -3407,7 +3435,7 @@ class RangeSet {
3407
3435
  static join(sets) {
3408
3436
  if (!sets.length)
3409
3437
  return RangeSet.empty;
3410
- let result = sets[sets.length - 1];
3438
+ let result = last(sets);
3411
3439
  for (let i = sets.length - 2; i >= 0; i--) {
3412
3440
  for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer)
3413
3441
  result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint));
@@ -3419,6 +3447,7 @@ class RangeSet {
3419
3447
  The empty set of ranges.
3420
3448
  */
3421
3449
  RangeSet.empty = new RangeSet([], [], null, -1);
3450
+ function last(arr) { return arr[arr.length - 1]; }
3422
3451
  function lazySort(ranges) {
3423
3452
  if (ranges.length > 1)
3424
3453
  for (let prev = ranges[0], i = 1; i < ranges.length; i++) {
package/dist/index.js CHANGED
@@ -3078,7 +3078,7 @@ class Chunk {
3078
3078
  this.value = value;
3079
3079
  this.maxPoint = maxPoint;
3080
3080
  }
3081
- get length() { return this.to[this.to.length - 1]; }
3081
+ get length() { return last(this.to); }
3082
3082
  // Find the index of the given position and side. Use the ranges'
3083
3083
  // `from` pos when `end == false`, `to` when `end == true`.
3084
3084
  findIndex(pos, side, end, startAt = 0) {
@@ -3101,9 +3101,9 @@ class Chunk {
3101
3101
  if (f(this.from[i] + offset, this.to[i] + offset, this.value[i]) === false)
3102
3102
  return false;
3103
3103
  }
3104
- map(offset, changes) {
3104
+ map(offset, changes, basePos, baseSide, spill) {
3105
3105
  let value = [], from = [], to = [], newPos = -1, maxPoint = -1;
3106
- for (let i = 0; i < this.value.length; i++) {
3106
+ iter: for (let i = 0; i < this.value.length; i++) {
3107
3107
  let val = this.value[i], curFrom = this.from[i] + offset, curTo = this.to[i] + offset, newFrom, newTo;
3108
3108
  if (curFrom == curTo) {
3109
3109
  let mapped = changes.mapPos(curFrom, val.startSide, val.mapMode);
@@ -3128,9 +3128,27 @@ class Chunk {
3128
3128
  newPos = newFrom;
3129
3129
  if (val.point)
3130
3130
  maxPoint = Math.max(maxPoint, newTo - newFrom);
3131
- value.push(val);
3132
- from.push(newFrom - newPos);
3133
- to.push(newTo - newPos);
3131
+ if ((newFrom - basePos || val.startSide - baseSide) >= 0) {
3132
+ value.push(val);
3133
+ from.push(newFrom - newPos);
3134
+ to.push(newTo - newPos);
3135
+ basePos = newTo;
3136
+ baseSide = val.endSide;
3137
+ }
3138
+ else {
3139
+ if (newFrom == newTo) { // Try to reorder points to fit in here
3140
+ for (let i = value.length - 1; i > 0; i--) {
3141
+ if ((newFrom - to[i - 1] || val.startSide - value[i - 1].endSide) <= 0) {
3142
+ value.splice(i, 0, val);
3143
+ from.splice(i, 0, newFrom);
3144
+ to.splice(i, 0, newTo);
3145
+ continue iter;
3146
+ }
3147
+ }
3148
+ }
3149
+ // Otherwise, spill into a new layer
3150
+ spill(newFrom, newTo, val);
3151
+ }
3134
3152
  }
3135
3153
  return { mapped: value.length ? new Chunk(from, to, value, maxPoint) : null, pos: newPos };
3136
3154
  }
@@ -3244,6 +3262,12 @@ class RangeSet {
3244
3262
  if (changes.empty || this.isEmpty)
3245
3263
  return this;
3246
3264
  let chunks = [], chunkPos = [], maxPoint = -1;
3265
+ let spilled;
3266
+ let spill = (from, to, value) => {
3267
+ if (!spilled)
3268
+ spilled = new RangeSetBuilder();
3269
+ spilled.add(from, to, value);
3270
+ };
3247
3271
  for (let i = 0; i < this.chunk.length; i++) {
3248
3272
  let start = this.chunkPos[i], chunk = this.chunk[i];
3249
3273
  let touch = changes.touchesRange(start, start + chunk.length);
@@ -3253,7 +3277,9 @@ class RangeSet {
3253
3277
  chunkPos.push(changes.mapPos(start));
3254
3278
  }
3255
3279
  else if (touch === true) {
3256
- let { mapped, pos } = chunk.map(start, changes);
3280
+ let [prevPos, prevSide] = !chunks.length ? [-1, -1]
3281
+ : [last(chunkPos) + last(chunks).length, last(last(chunks).value).endSide];
3282
+ let { mapped, pos } = chunk.map(start, changes, prevPos, prevSide, spill);
3257
3283
  if (mapped) {
3258
3284
  maxPoint = Math.max(maxPoint, mapped.maxPoint);
3259
3285
  chunks.push(mapped);
@@ -3262,6 +3288,8 @@ class RangeSet {
3262
3288
  }
3263
3289
  }
3264
3290
  let next = this.nextLayer.map(changes);
3291
+ if (spilled)
3292
+ next = spilled.finishInner(next);
3265
3293
  return chunks.length == 0 ? next : new RangeSet(chunkPos, chunks, next || RangeSet.empty, maxPoint);
3266
3294
  }
3267
3295
  /**
@@ -3403,7 +3431,7 @@ class RangeSet {
3403
3431
  static join(sets) {
3404
3432
  if (!sets.length)
3405
3433
  return RangeSet.empty;
3406
- let result = sets[sets.length - 1];
3434
+ let result = last(sets);
3407
3435
  for (let i = sets.length - 2; i >= 0; i--) {
3408
3436
  for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer)
3409
3437
  result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint));
@@ -3415,6 +3443,7 @@ class RangeSet {
3415
3443
  The empty set of ranges.
3416
3444
  */
3417
3445
  RangeSet.empty = /*@__PURE__*/new RangeSet([], [], null, -1);
3446
+ function last(arr) { return arr[arr.length - 1]; }
3418
3447
  function lazySort(ranges) {
3419
3448
  if (ranges.length > 1)
3420
3449
  for (let prev = ranges[0], i = 1; i < ranges.length; i++) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/state",
3
- "version": "6.7.1",
3
+ "version": "6.7.2",
4
4
  "description": "Editor state data structures for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",