@codemirror/state 6.3.2 → 6.4.0

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,21 @@
1
+ ## 6.4.0 (2023-12-28)
2
+
3
+ ### Bug fixes
4
+
5
+ When multiple ranges in a single range set overlap, put the smaller ones inside the bigger ones, so that overlapping decorations don't break up each other's elements when coming from the same source.
6
+
7
+ ### New features
8
+
9
+ Selection and selection range `eq` methods now support an optional argument that makes them also compare by cursor associativity.
10
+
11
+ The `RangeSet.join` function can be used to join multiple range sets together.
12
+
13
+ ## 6.3.3 (2023-12-06)
14
+
15
+ ### Bug fixes
16
+
17
+ Fix an issue where `Text.slice` and `Text.replace` could return objects with incorrect `length` when the given `from`/`to` values were out of range for the text.
18
+
1
19
  ## 6.3.2 (2023-11-27)
2
20
 
3
21
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -24,6 +24,7 @@ class Text {
24
24
  Replace a range of the text with the given content.
25
25
  */
26
26
  replace(from, to, text) {
27
+ [from, to] = clip(this, from, to);
27
28
  let parts = [];
28
29
  this.decompose(0, from, parts, 2 /* Open.To */);
29
30
  if (text.length)
@@ -41,6 +42,7 @@ class Text {
41
42
  Retrieve the text between the given points.
42
43
  */
43
44
  slice(from, to = this.length) {
45
+ [from, to] = clip(this, from, to);
44
46
  let parts = [];
45
47
  this.decompose(from, to, parts, 0);
46
48
  return TextNode.from(parts, to - from);
@@ -167,6 +169,7 @@ class TextLeaf extends Text {
167
169
  replace(from, to, text) {
168
170
  if (!(text instanceof TextLeaf))
169
171
  return super.replace(from, to, text);
172
+ [from, to] = clip(this, from, to);
170
173
  let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to);
171
174
  let newLen = this.length + text.length - (to - from);
172
175
  if (lines.length <= 32 /* Tree.Branch */)
@@ -174,6 +177,7 @@ class TextLeaf extends Text {
174
177
  return TextNode.from(TextLeaf.split(lines, []), newLen);
175
178
  }
176
179
  sliceString(from, to = this.length, lineSep = "\n") {
180
+ [from, to] = clip(this, from, to);
177
181
  let result = "";
178
182
  for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) {
179
183
  let line = this.text[i], end = pos + line.length;
@@ -242,6 +246,7 @@ class TextNode extends Text {
242
246
  }
243
247
  }
244
248
  replace(from, to, text) {
249
+ [from, to] = clip(this, from, to);
245
250
  if (text.lines < this.lines)
246
251
  for (let i = 0, pos = 0; i < this.children.length; i++) {
247
252
  let child = this.children[i], end = pos + child.length;
@@ -264,6 +269,7 @@ class TextNode extends Text {
264
269
  return super.replace(from, to, text);
265
270
  }
266
271
  sliceString(from, to = this.length, lineSep = "\n") {
272
+ [from, to] = clip(this, from, to);
267
273
  let result = "";
268
274
  for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) {
269
275
  let child = this.children[i], end = pos + child.length;
@@ -551,6 +557,10 @@ class Line {
551
557
  */
552
558
  get length() { return this.to - this.from; }
553
559
  }
560
+ function clip(text, from, to) {
561
+ from = Math.max(0, Math.min(text.length, from));
562
+ return [from, Math.max(from, Math.min(text.length, to))];
563
+ }
554
564
 
555
565
  // Compressed representation of the Grapheme_Cluster_Break=Extend
556
566
  // information from
@@ -1381,8 +1391,9 @@ class SelectionRange {
1381
1391
  /**
1382
1392
  Compare this range to another range.
1383
1393
  */
1384
- eq(other) {
1385
- return this.anchor == other.anchor && this.head == other.head;
1394
+ eq(other, includeAssoc = false) {
1395
+ return this.anchor == other.anchor && this.head == other.head &&
1396
+ (!includeAssoc || !this.empty || this.assoc == other.assoc);
1386
1397
  }
1387
1398
  /**
1388
1399
  Return a JSON-serializable object representing the range.
@@ -1432,14 +1443,17 @@ class EditorSelection {
1432
1443
  return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex);
1433
1444
  }
1434
1445
  /**
1435
- Compare this selection to another selection.
1446
+ Compare this selection to another selection. By default, ranges
1447
+ are compared only by position. When `includeAssoc` is true,
1448
+ cursor ranges must also have the same
1449
+ [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value.
1436
1450
  */
1437
- eq(other) {
1451
+ eq(other, includeAssoc = false) {
1438
1452
  if (this.ranges.length != other.ranges.length ||
1439
1453
  this.mainIndex != other.mainIndex)
1440
1454
  return false;
1441
1455
  for (let i = 0; i < this.ranges.length; i++)
1442
- if (!this.ranges[i].eq(other.ranges[i]))
1456
+ if (!this.ranges[i].eq(other.ranges[i], includeAssoc))
1443
1457
  return false;
1444
1458
  return true;
1445
1459
  }
@@ -3409,6 +3423,19 @@ class RangeSet {
3409
3423
  build.add(range.from, range.to, range.value);
3410
3424
  return build.finish();
3411
3425
  }
3426
+ /**
3427
+ Join an array of range sets into a single set.
3428
+ */
3429
+ static join(sets) {
3430
+ if (!sets.length)
3431
+ return RangeSet.empty;
3432
+ let result = sets[sets.length - 1];
3433
+ for (let i = sets.length - 2; i >= 0; i--) {
3434
+ for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer)
3435
+ result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint));
3436
+ }
3437
+ return result;
3438
+ }
3412
3439
  }
3413
3440
  /**
3414
3441
  The empty set of ranges.
@@ -3726,7 +3753,8 @@ class SpanCursor {
3726
3753
  }
3727
3754
  addActive(trackOpen) {
3728
3755
  let i = 0, { value, to, rank } = this.cursor;
3729
- while (i < this.activeRank.length && this.activeRank[i] <= rank)
3756
+ // Organize active marks by rank first, then by size
3757
+ while (i < this.activeRank.length && (rank - this.activeRank[i] || to - this.activeTo[i]) > 0)
3730
3758
  i++;
3731
3759
  insert(this.active, i, value);
3732
3760
  insert(this.activeTo, i, to);
package/dist/index.d.cts CHANGED
@@ -400,7 +400,7 @@ declare class SelectionRange {
400
400
  /**
401
401
  Compare this range to another range.
402
402
  */
403
- eq(other: SelectionRange): boolean;
403
+ eq(other: SelectionRange, includeAssoc?: boolean): boolean;
404
404
  /**
405
405
  Return a JSON-serializable object representing the range.
406
406
  */
@@ -432,9 +432,12 @@ declare class EditorSelection {
432
432
  */
433
433
  map(change: ChangeDesc, assoc?: number): EditorSelection;
434
434
  /**
435
- Compare this selection to another selection.
435
+ Compare this selection to another selection. By default, ranges
436
+ are compared only by position. When `includeAssoc` is true,
437
+ cursor ranges must also have the same
438
+ [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value.
436
439
  */
437
- eq(other: EditorSelection): boolean;
440
+ eq(other: EditorSelection, includeAssoc?: boolean): boolean;
438
441
  /**
439
442
  Get the primary selection range. Usually, you should make sure
440
443
  your code applies to _all_ ranges, by using methods like
@@ -1604,6 +1607,10 @@ declare class RangeSet<T extends RangeValue> {
1604
1607
  */
1605
1608
  static of<T extends RangeValue>(ranges: readonly Range<T>[] | Range<T>, sort?: boolean): RangeSet<T>;
1606
1609
  /**
1610
+ Join an array of range sets into a single set.
1611
+ */
1612
+ static join<T extends RangeValue>(sets: readonly RangeSet<T>[]): RangeSet<T>;
1613
+ /**
1607
1614
  The empty set of ranges.
1608
1615
  */
1609
1616
  static empty: RangeSet<any>;
@@ -1683,4 +1690,4 @@ situation.
1683
1690
  */
1684
1691
  declare function findColumn(string: string, col: number, tabSize: number, strict?: boolean): number;
1685
1692
 
1686
- export { Annotation, AnnotationType, ChangeDesc, ChangeSet, ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, EditorStateConfig, Extension, Facet, FacetReader, Line, MapMode, Prec, Range, RangeComparator, RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, SpanIterator, StateCommand, StateEffect, StateEffectType, StateField, Text, TextIterator, Transaction, TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint };
1693
+ export { Annotation, AnnotationType, ChangeDesc, ChangeSet, type ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, type EditorStateConfig, type Extension, Facet, type FacetReader, Line, MapMode, Prec, Range, type RangeComparator, type RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, type SpanIterator, type StateCommand, StateEffect, StateEffectType, StateField, Text, type TextIterator, Transaction, type TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint };
package/dist/index.d.ts CHANGED
@@ -400,7 +400,7 @@ declare class SelectionRange {
400
400
  /**
401
401
  Compare this range to another range.
402
402
  */
403
- eq(other: SelectionRange): boolean;
403
+ eq(other: SelectionRange, includeAssoc?: boolean): boolean;
404
404
  /**
405
405
  Return a JSON-serializable object representing the range.
406
406
  */
@@ -432,9 +432,12 @@ declare class EditorSelection {
432
432
  */
433
433
  map(change: ChangeDesc, assoc?: number): EditorSelection;
434
434
  /**
435
- Compare this selection to another selection.
435
+ Compare this selection to another selection. By default, ranges
436
+ are compared only by position. When `includeAssoc` is true,
437
+ cursor ranges must also have the same
438
+ [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value.
436
439
  */
437
- eq(other: EditorSelection): boolean;
440
+ eq(other: EditorSelection, includeAssoc?: boolean): boolean;
438
441
  /**
439
442
  Get the primary selection range. Usually, you should make sure
440
443
  your code applies to _all_ ranges, by using methods like
@@ -1604,6 +1607,10 @@ declare class RangeSet<T extends RangeValue> {
1604
1607
  */
1605
1608
  static of<T extends RangeValue>(ranges: readonly Range<T>[] | Range<T>, sort?: boolean): RangeSet<T>;
1606
1609
  /**
1610
+ Join an array of range sets into a single set.
1611
+ */
1612
+ static join<T extends RangeValue>(sets: readonly RangeSet<T>[]): RangeSet<T>;
1613
+ /**
1607
1614
  The empty set of ranges.
1608
1615
  */
1609
1616
  static empty: RangeSet<any>;
@@ -1683,4 +1690,4 @@ situation.
1683
1690
  */
1684
1691
  declare function findColumn(string: string, col: number, tabSize: number, strict?: boolean): number;
1685
1692
 
1686
- export { Annotation, AnnotationType, ChangeDesc, ChangeSet, ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, EditorStateConfig, Extension, Facet, FacetReader, Line, MapMode, Prec, Range, RangeComparator, RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, SpanIterator, StateCommand, StateEffect, StateEffectType, StateField, Text, TextIterator, Transaction, TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint };
1693
+ export { Annotation, AnnotationType, ChangeDesc, ChangeSet, type ChangeSpec, CharCategory, Compartment, EditorSelection, EditorState, type EditorStateConfig, type Extension, Facet, type FacetReader, Line, MapMode, Prec, Range, type RangeComparator, type RangeCursor, RangeSet, RangeSetBuilder, RangeValue, SelectionRange, type SpanIterator, type StateCommand, StateEffect, StateEffectType, StateField, Text, type TextIterator, Transaction, type TransactionSpec, codePointAt, codePointSize, combineConfig, countColumn, findClusterBreak, findColumn, fromCodePoint };
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ class Text {
22
22
  Replace a range of the text with the given content.
23
23
  */
24
24
  replace(from, to, text) {
25
+ [from, to] = clip(this, from, to);
25
26
  let parts = [];
26
27
  this.decompose(0, from, parts, 2 /* Open.To */);
27
28
  if (text.length)
@@ -39,6 +40,7 @@ class Text {
39
40
  Retrieve the text between the given points.
40
41
  */
41
42
  slice(from, to = this.length) {
43
+ [from, to] = clip(this, from, to);
42
44
  let parts = [];
43
45
  this.decompose(from, to, parts, 0);
44
46
  return TextNode.from(parts, to - from);
@@ -165,6 +167,7 @@ class TextLeaf extends Text {
165
167
  replace(from, to, text) {
166
168
  if (!(text instanceof TextLeaf))
167
169
  return super.replace(from, to, text);
170
+ [from, to] = clip(this, from, to);
168
171
  let lines = appendText(this.text, appendText(text.text, sliceText(this.text, 0, from)), to);
169
172
  let newLen = this.length + text.length - (to - from);
170
173
  if (lines.length <= 32 /* Tree.Branch */)
@@ -172,6 +175,7 @@ class TextLeaf extends Text {
172
175
  return TextNode.from(TextLeaf.split(lines, []), newLen);
173
176
  }
174
177
  sliceString(from, to = this.length, lineSep = "\n") {
178
+ [from, to] = clip(this, from, to);
175
179
  let result = "";
176
180
  for (let pos = 0, i = 0; pos <= to && i < this.text.length; i++) {
177
181
  let line = this.text[i], end = pos + line.length;
@@ -240,6 +244,7 @@ class TextNode extends Text {
240
244
  }
241
245
  }
242
246
  replace(from, to, text) {
247
+ [from, to] = clip(this, from, to);
243
248
  if (text.lines < this.lines)
244
249
  for (let i = 0, pos = 0; i < this.children.length; i++) {
245
250
  let child = this.children[i], end = pos + child.length;
@@ -262,6 +267,7 @@ class TextNode extends Text {
262
267
  return super.replace(from, to, text);
263
268
  }
264
269
  sliceString(from, to = this.length, lineSep = "\n") {
270
+ [from, to] = clip(this, from, to);
265
271
  let result = "";
266
272
  for (let i = 0, pos = 0; i < this.children.length && pos <= to; i++) {
267
273
  let child = this.children[i], end = pos + child.length;
@@ -549,6 +555,10 @@ class Line {
549
555
  */
550
556
  get length() { return this.to - this.from; }
551
557
  }
558
+ function clip(text, from, to) {
559
+ from = Math.max(0, Math.min(text.length, from));
560
+ return [from, Math.max(from, Math.min(text.length, to))];
561
+ }
552
562
 
553
563
  // Compressed representation of the Grapheme_Cluster_Break=Extend
554
564
  // information from
@@ -1378,8 +1388,9 @@ class SelectionRange {
1378
1388
  /**
1379
1389
  Compare this range to another range.
1380
1390
  */
1381
- eq(other) {
1382
- return this.anchor == other.anchor && this.head == other.head;
1391
+ eq(other, includeAssoc = false) {
1392
+ return this.anchor == other.anchor && this.head == other.head &&
1393
+ (!includeAssoc || !this.empty || this.assoc == other.assoc);
1383
1394
  }
1384
1395
  /**
1385
1396
  Return a JSON-serializable object representing the range.
@@ -1429,14 +1440,17 @@ class EditorSelection {
1429
1440
  return EditorSelection.create(this.ranges.map(r => r.map(change, assoc)), this.mainIndex);
1430
1441
  }
1431
1442
  /**
1432
- Compare this selection to another selection.
1443
+ Compare this selection to another selection. By default, ranges
1444
+ are compared only by position. When `includeAssoc` is true,
1445
+ cursor ranges must also have the same
1446
+ [`assoc`](https://codemirror.net/6/docs/ref/#state.SelectionRange.assoc) value.
1433
1447
  */
1434
- eq(other) {
1448
+ eq(other, includeAssoc = false) {
1435
1449
  if (this.ranges.length != other.ranges.length ||
1436
1450
  this.mainIndex != other.mainIndex)
1437
1451
  return false;
1438
1452
  for (let i = 0; i < this.ranges.length; i++)
1439
- if (!this.ranges[i].eq(other.ranges[i]))
1453
+ if (!this.ranges[i].eq(other.ranges[i], includeAssoc))
1440
1454
  return false;
1441
1455
  return true;
1442
1456
  }
@@ -3405,6 +3419,19 @@ class RangeSet {
3405
3419
  build.add(range.from, range.to, range.value);
3406
3420
  return build.finish();
3407
3421
  }
3422
+ /**
3423
+ Join an array of range sets into a single set.
3424
+ */
3425
+ static join(sets) {
3426
+ if (!sets.length)
3427
+ return RangeSet.empty;
3428
+ let result = sets[sets.length - 1];
3429
+ for (let i = sets.length - 2; i >= 0; i--) {
3430
+ for (let layer = sets[i]; layer != RangeSet.empty; layer = layer.nextLayer)
3431
+ result = new RangeSet(layer.chunkPos, layer.chunk, result, Math.max(layer.maxPoint, result.maxPoint));
3432
+ }
3433
+ return result;
3434
+ }
3408
3435
  }
3409
3436
  /**
3410
3437
  The empty set of ranges.
@@ -3722,7 +3749,8 @@ class SpanCursor {
3722
3749
  }
3723
3750
  addActive(trackOpen) {
3724
3751
  let i = 0, { value, to, rank } = this.cursor;
3725
- while (i < this.activeRank.length && this.activeRank[i] <= rank)
3752
+ // Organize active marks by rank first, then by size
3753
+ while (i < this.activeRank.length && (rank - this.activeRank[i] || to - this.activeTo[i]) > 0)
3726
3754
  i++;
3727
3755
  insert(this.active, i, value);
3728
3756
  insert(this.activeTo, i, to);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/state",
3
- "version": "6.3.2",
3
+ "version": "6.4.0",
4
4
  "description": "Editor state data structures for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",