@codemirror/view 6.18.0 → 6.19.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/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { EditorState, Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, findColumn, CharCategory, Annotation, Transaction, Prec, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
1
+ import { Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, findColumn, CharCategory, Annotation, EditorState, Transaction, Prec, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
2
2
  import { StyleModule } from 'style-mod';
3
3
  import { keyName, base, shift } from 'w3c-keyname';
4
4
 
@@ -482,6 +482,8 @@ class ContentView {
482
482
  }
483
483
  }
484
484
  setDOM(dom) {
485
+ if (this.dom == dom)
486
+ return;
485
487
  if (this.dom)
486
488
  this.dom.cmView = null;
487
489
  this.dom = dom;
@@ -654,113 +656,6 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
654
656
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
655
657
  }
656
658
 
657
- const LineBreakPlaceholder = "\uffff";
658
- class DOMReader {
659
- constructor(points, state) {
660
- this.points = points;
661
- this.text = "";
662
- this.lineSeparator = state.facet(EditorState.lineSeparator);
663
- }
664
- append(text) {
665
- this.text += text;
666
- }
667
- lineBreak() {
668
- this.text += LineBreakPlaceholder;
669
- }
670
- readRange(start, end) {
671
- if (!start)
672
- return this;
673
- let parent = start.parentNode;
674
- for (let cur = start;;) {
675
- this.findPointBefore(parent, cur);
676
- let oldLen = this.text.length;
677
- this.readNode(cur);
678
- let next = cur.nextSibling;
679
- if (next == end)
680
- break;
681
- let view = ContentView.get(cur), nextView = ContentView.get(next);
682
- if (view && nextView ? view.breakAfter :
683
- (view ? view.breakAfter : isBlockElement(cur)) ||
684
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
685
- this.lineBreak();
686
- cur = next;
687
- }
688
- this.findPointBefore(parent, end);
689
- return this;
690
- }
691
- readTextNode(node) {
692
- let text = node.nodeValue;
693
- for (let point of this.points)
694
- if (point.node == node)
695
- point.pos = this.text.length + Math.min(point.offset, text.length);
696
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
697
- let nextBreak = -1, breakSize = 1, m;
698
- if (this.lineSeparator) {
699
- nextBreak = text.indexOf(this.lineSeparator, off);
700
- breakSize = this.lineSeparator.length;
701
- }
702
- else if (m = re.exec(text)) {
703
- nextBreak = m.index;
704
- breakSize = m[0].length;
705
- }
706
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
707
- if (nextBreak < 0)
708
- break;
709
- this.lineBreak();
710
- if (breakSize > 1)
711
- for (let point of this.points)
712
- if (point.node == node && point.pos > this.text.length)
713
- point.pos -= breakSize - 1;
714
- off = nextBreak + breakSize;
715
- }
716
- }
717
- readNode(node) {
718
- if (node.cmIgnore)
719
- return;
720
- let view = ContentView.get(node);
721
- let fromView = view && view.overrideDOMText;
722
- if (fromView != null) {
723
- this.findPointInside(node, fromView.length);
724
- for (let i = fromView.iter(); !i.next().done;) {
725
- if (i.lineBreak)
726
- this.lineBreak();
727
- else
728
- this.append(i.value);
729
- }
730
- }
731
- else if (node.nodeType == 3) {
732
- this.readTextNode(node);
733
- }
734
- else if (node.nodeName == "BR") {
735
- if (node.nextSibling)
736
- this.lineBreak();
737
- }
738
- else if (node.nodeType == 1) {
739
- this.readRange(node.firstChild, null);
740
- }
741
- }
742
- findPointBefore(node, next) {
743
- for (let point of this.points)
744
- if (point.node == node && node.childNodes[point.offset] == next)
745
- point.pos = this.text.length;
746
- }
747
- findPointInside(node, maxLen) {
748
- for (let point of this.points)
749
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
750
- point.pos = this.text.length + Math.min(maxLen, point.offset);
751
- }
752
- }
753
- function isBlockElement(node) {
754
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
755
- }
756
- class DOMPoint {
757
- constructor(node, offset) {
758
- this.node = node;
759
- this.offset = offset;
760
- this.pos = -1;
761
- }
762
- }
763
-
764
659
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
765
660
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
766
661
  const ie_edge = /*@__PURE__*//Edge\/(\d+)/.exec(nav.userAgent);
@@ -1196,307 +1091,27 @@ function getAttrs(dom) {
1196
1091
  return attrs;
1197
1092
  }
1198
1093
 
1199
- /**
1200
- Widgets added to the content are described by subclasses of this
1201
- class. Using a description object like that makes it possible to
1202
- delay creating of the DOM structure for a widget until it is
1203
- needed, and to avoid redrawing widgets even if the decorations
1204
- that define them are recreated.
1205
- */
1206
- class WidgetType {
1207
- /**
1208
- Compare this instance to another instance of the same type.
1209
- (TypeScript can't express this, but only instances of the same
1210
- specific class will be passed to this method.) This is used to
1211
- avoid redrawing widgets when they are replaced by a new
1212
- decoration of the same type. The default implementation just
1213
- returns `false`, which will cause new instances of the widget to
1214
- always be redrawn.
1215
- */
1216
- eq(widget) { return false; }
1217
- /**
1218
- Update a DOM element created by a widget of the same type (but
1219
- different, non-`eq` content) to reflect this widget. May return
1220
- true to indicate that it could update, false to indicate it
1221
- couldn't (in which case the widget will be redrawn). The default
1222
- implementation just returns false.
1223
- */
1224
- updateDOM(dom, view) { return false; }
1225
- /**
1226
- @internal
1227
- */
1228
- compare(other) {
1229
- return this == other || this.constructor == other.constructor && this.eq(other);
1094
+ class LineView extends ContentView {
1095
+ constructor() {
1096
+ super(...arguments);
1097
+ this.children = [];
1098
+ this.length = 0;
1099
+ this.prevAttrs = undefined;
1100
+ this.attrs = null;
1101
+ this.breakAfter = 0;
1230
1102
  }
1231
- /**
1232
- The estimated height this widget will have, to be used when
1233
- estimating the height of content that hasn't been drawn. May
1234
- return -1 to indicate you don't know. The default implementation
1235
- returns -1.
1236
- */
1237
- get estimatedHeight() { return -1; }
1238
- /**
1239
- For inline widgets that are displayed inline (as opposed to
1240
- `inline-block`) and introduce line breaks (through `<br>` tags
1241
- or textual newlines), this must indicate the amount of line
1242
- breaks they introduce. Defaults to 0.
1243
- */
1244
- get lineBreaks() { return 0; }
1245
- /**
1246
- Can be used to configure which kinds of events inside the widget
1247
- should be ignored by the editor. The default is to ignore all
1248
- events.
1249
- */
1250
- ignoreEvent(event) { return true; }
1251
- /**
1252
- Override the way screen coordinates for positions at/in the
1253
- widget are found. `pos` will be the offset into the widget, and
1254
- `side` the side of the position that is being queried—less than
1255
- zero for before, greater than zero for after, and zero for
1256
- directly at that position.
1257
- */
1258
- coordsAt(dom, pos, side) { return null; }
1259
- /**
1260
- @internal
1261
- */
1262
- get isHidden() { return false; }
1263
- /**
1264
- This is called when the an instance of the widget is removed
1265
- from the editor view.
1266
- */
1267
- destroy(dom) { }
1268
- }
1269
- /**
1270
- The different types of blocks that can occur in an editor view.
1271
- */
1272
- var BlockType = /*@__PURE__*/(function (BlockType) {
1273
- /**
1274
- A line of text.
1275
- */
1276
- BlockType[BlockType["Text"] = 0] = "Text";
1277
- /**
1278
- A block widget associated with the position after it.
1279
- */
1280
- BlockType[BlockType["WidgetBefore"] = 1] = "WidgetBefore";
1281
- /**
1282
- A block widget associated with the position before it.
1283
- */
1284
- BlockType[BlockType["WidgetAfter"] = 2] = "WidgetAfter";
1285
- /**
1286
- A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content.
1287
- */
1288
- BlockType[BlockType["WidgetRange"] = 3] = "WidgetRange";
1289
- return BlockType})(BlockType || (BlockType = {}));
1290
- /**
1291
- A decoration provides information on how to draw or style a piece
1292
- of content. You'll usually use it wrapped in a
1293
- [`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
1294
- @nonabstract
1295
- */
1296
- class Decoration extends RangeValue {
1297
- constructor(
1298
- /**
1299
- @internal
1300
- */
1301
- startSide,
1302
- /**
1303
- @internal
1304
- */
1305
- endSide,
1306
- /**
1307
- @internal
1308
- */
1309
- widget,
1310
- /**
1311
- The config object used to create this decoration. You can
1312
- include additional properties in there to store metadata about
1313
- your decoration.
1314
- */
1315
- spec) {
1316
- super();
1317
- this.startSide = startSide;
1318
- this.endSide = endSide;
1319
- this.widget = widget;
1320
- this.spec = spec;
1321
- }
1322
- /**
1323
- @internal
1324
- */
1325
- get heightRelevant() { return false; }
1326
- /**
1327
- Create a mark decoration, which influences the styling of the
1328
- content in its range. Nested mark decorations will cause nested
1329
- DOM elements to be created. Nesting order is determined by
1330
- precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
1331
- the higher-precedence decorations creating the inner DOM nodes.
1332
- Such elements are split on line boundaries and on the boundaries
1333
- of lower-precedence decorations.
1334
- */
1335
- static mark(spec) {
1336
- return new MarkDecoration(spec);
1337
- }
1338
- /**
1339
- Create a widget decoration, which displays a DOM element at the
1340
- given position.
1341
- */
1342
- static widget(spec) {
1343
- let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
1344
- side += (block && !spec.inlineOrder)
1345
- ? (side > 0 ? 300000000 /* Side.BlockAfter */ : -400000000 /* Side.BlockBefore */)
1346
- : (side > 0 ? 100000000 /* Side.InlineAfter */ : -100000000 /* Side.InlineBefore */);
1347
- return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1348
- }
1349
- /**
1350
- Create a replace decoration which replaces the given range with
1351
- a widget, or simply hides it.
1352
- */
1353
- static replace(spec) {
1354
- let block = !!spec.block, startSide, endSide;
1355
- if (spec.isBlockGap) {
1356
- startSide = -500000000 /* Side.GapStart */;
1357
- endSide = 400000000 /* Side.GapEnd */;
1358
- }
1359
- else {
1360
- let { start, end } = getInclusive(spec, block);
1361
- startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
1362
- endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
1363
- }
1364
- return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1365
- }
1366
- /**
1367
- Create a line decoration, which can add DOM attributes to the
1368
- line starting at the given position.
1369
- */
1370
- static line(spec) {
1371
- return new LineDecoration(spec);
1372
- }
1373
- /**
1374
- Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
1375
- decorated range or ranges. If the ranges aren't already sorted,
1376
- pass `true` for `sort` to make the library sort them for you.
1377
- */
1378
- static set(of, sort = false) {
1379
- return RangeSet.of(of, sort);
1380
- }
1381
- /**
1382
- @internal
1383
- */
1384
- hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
1385
- }
1386
- /**
1387
- The empty set of decorations.
1388
- */
1389
- Decoration.none = RangeSet.empty;
1390
- class MarkDecoration extends Decoration {
1391
- constructor(spec) {
1392
- let { start, end } = getInclusive(spec);
1393
- super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
1394
- this.tagName = spec.tagName || "span";
1395
- this.class = spec.class || "";
1396
- this.attrs = spec.attributes || null;
1397
- }
1398
- eq(other) {
1399
- var _a, _b;
1400
- return this == other ||
1401
- other instanceof MarkDecoration &&
1402
- this.tagName == other.tagName &&
1403
- (this.class || ((_a = this.attrs) === null || _a === void 0 ? void 0 : _a.class)) == (other.class || ((_b = other.attrs) === null || _b === void 0 ? void 0 : _b.class)) &&
1404
- attrsEq(this.attrs, other.attrs, "class");
1405
- }
1406
- range(from, to = from) {
1407
- if (from >= to)
1408
- throw new RangeError("Mark decorations may not be empty");
1409
- return super.range(from, to);
1410
- }
1411
- }
1412
- MarkDecoration.prototype.point = false;
1413
- class LineDecoration extends Decoration {
1414
- constructor(spec) {
1415
- super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
1416
- }
1417
- eq(other) {
1418
- return other instanceof LineDecoration &&
1419
- this.spec.class == other.spec.class &&
1420
- attrsEq(this.spec.attributes, other.spec.attributes);
1421
- }
1422
- range(from, to = from) {
1423
- if (to != from)
1424
- throw new RangeError("Line decoration ranges must be zero-length");
1425
- return super.range(from, to);
1426
- }
1427
- }
1428
- LineDecoration.prototype.mapMode = MapMode.TrackBefore;
1429
- LineDecoration.prototype.point = true;
1430
- class PointDecoration extends Decoration {
1431
- constructor(spec, startSide, endSide, block, widget, isReplace) {
1432
- super(startSide, endSide, widget, spec);
1433
- this.block = block;
1434
- this.isReplace = isReplace;
1435
- this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1436
- }
1437
- // Only relevant when this.block == true
1438
- get type() {
1439
- return this.startSide < this.endSide ? BlockType.WidgetRange
1440
- : this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1441
- }
1442
- get heightRelevant() {
1443
- return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
1444
- }
1445
- eq(other) {
1446
- return other instanceof PointDecoration &&
1447
- widgetsEq(this.widget, other.widget) &&
1448
- this.block == other.block &&
1449
- this.startSide == other.startSide && this.endSide == other.endSide;
1450
- }
1451
- range(from, to = from) {
1452
- if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1453
- throw new RangeError("Invalid range for replacement decoration");
1454
- if (!this.isReplace && to != from)
1455
- throw new RangeError("Widget decorations can only have zero-length ranges");
1456
- return super.range(from, to);
1457
- }
1458
- }
1459
- PointDecoration.prototype.point = true;
1460
- function getInclusive(spec, block = false) {
1461
- let { inclusiveStart: start, inclusiveEnd: end } = spec;
1462
- if (start == null)
1463
- start = spec.inclusive;
1464
- if (end == null)
1465
- end = spec.inclusive;
1466
- return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
1467
- }
1468
- function widgetsEq(a, b) {
1469
- return a == b || !!(a && b && a.compare(b));
1470
- }
1471
- function addRange(from, to, ranges, margin = 0) {
1472
- let last = ranges.length - 1;
1473
- if (last >= 0 && ranges[last] + margin >= from)
1474
- ranges[last] = Math.max(ranges[last], to);
1475
- else
1476
- ranges.push(from, to);
1477
- }
1478
-
1479
- class LineView extends ContentView {
1480
- constructor() {
1481
- super(...arguments);
1482
- this.children = [];
1483
- this.length = 0;
1484
- this.prevAttrs = undefined;
1485
- this.attrs = null;
1486
- this.breakAfter = 0;
1487
- }
1488
- // Consumes source
1489
- merge(from, to, source, hasStart, openStart, openEnd) {
1490
- if (source) {
1491
- if (!(source instanceof LineView))
1492
- return false;
1493
- if (!this.dom)
1494
- source.transferDOM(this); // Reuse source.dom when appropriate
1495
- }
1496
- if (hasStart)
1497
- this.setDeco(source ? source.attrs : null);
1498
- mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1499
- return true;
1103
+ // Consumes source
1104
+ merge(from, to, source, hasStart, openStart, openEnd) {
1105
+ if (source) {
1106
+ if (!(source instanceof LineView))
1107
+ return false;
1108
+ if (!this.dom)
1109
+ source.transferDOM(this); // Reuse source.dom when appropriate
1110
+ }
1111
+ if (hasStart)
1112
+ this.setDeco(source ? source.attrs : null);
1113
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1114
+ return true;
1500
1115
  }
1501
1116
  split(at) {
1502
1117
  let end = new LineView;
@@ -1618,7 +1233,7 @@ class LineView extends ContentView {
1618
1233
  return rect;
1619
1234
  }
1620
1235
  become(_other) { return false; }
1621
- get type() { return BlockType.Text; }
1236
+ covers() { return true; }
1622
1237
  static find(docView, pos) {
1623
1238
  for (let i = 0, off = 0; i < docView.children.length; i++) {
1624
1239
  let block = docView.children[i], end = off + block.length;
@@ -1634,11 +1249,11 @@ class LineView extends ContentView {
1634
1249
  }
1635
1250
  }
1636
1251
  class BlockWidgetView extends ContentView {
1637
- constructor(widget, length, type) {
1252
+ constructor(widget, length, deco) {
1638
1253
  super();
1639
1254
  this.widget = widget;
1640
1255
  this.length = length;
1641
- this.type = type;
1256
+ this.deco = deco;
1642
1257
  this.breakAfter = 0;
1643
1258
  this.prevWidget = null;
1644
1259
  }
@@ -1655,7 +1270,7 @@ class BlockWidgetView extends ContentView {
1655
1270
  split(at) {
1656
1271
  let len = this.length - at;
1657
1272
  this.length = at;
1658
- let end = new BlockWidgetView(this.widget, len, this.type);
1273
+ let end = new BlockWidgetView(this.widget, len, this.deco);
1659
1274
  end.breakAfter = this.breakAfter;
1660
1275
  return end;
1661
1276
  }
@@ -1682,7 +1297,7 @@ class BlockWidgetView extends ContentView {
1682
1297
  this.prevWidget = this.widget;
1683
1298
  this.widget = other.widget;
1684
1299
  this.length = other.length;
1685
- this.type = other.type;
1300
+ this.deco = other.deco;
1686
1301
  this.breakAfter = other.breakAfter;
1687
1302
  return true;
1688
1303
  }
@@ -1695,12 +1310,296 @@ class BlockWidgetView extends ContentView {
1695
1310
  coordsAt(pos, side) {
1696
1311
  return this.widget.coordsAt(this.dom, pos, side);
1697
1312
  }
1698
- destroy() {
1699
- super.destroy();
1700
- if (this.dom)
1701
- this.widget.destroy(this.dom);
1313
+ destroy() {
1314
+ super.destroy();
1315
+ if (this.dom)
1316
+ this.widget.destroy(this.dom);
1317
+ }
1318
+ covers(side) {
1319
+ let { startSide, endSide } = this.deco;
1320
+ return startSide == endSide ? false : side < 0 ? startSide < 0 : endSide > 0;
1321
+ }
1322
+ }
1323
+
1324
+ /**
1325
+ Widgets added to the content are described by subclasses of this
1326
+ class. Using a description object like that makes it possible to
1327
+ delay creating of the DOM structure for a widget until it is
1328
+ needed, and to avoid redrawing widgets even if the decorations
1329
+ that define them are recreated.
1330
+ */
1331
+ class WidgetType {
1332
+ /**
1333
+ Compare this instance to another instance of the same type.
1334
+ (TypeScript can't express this, but only instances of the same
1335
+ specific class will be passed to this method.) This is used to
1336
+ avoid redrawing widgets when they are replaced by a new
1337
+ decoration of the same type. The default implementation just
1338
+ returns `false`, which will cause new instances of the widget to
1339
+ always be redrawn.
1340
+ */
1341
+ eq(widget) { return false; }
1342
+ /**
1343
+ Update a DOM element created by a widget of the same type (but
1344
+ different, non-`eq` content) to reflect this widget. May return
1345
+ true to indicate that it could update, false to indicate it
1346
+ couldn't (in which case the widget will be redrawn). The default
1347
+ implementation just returns false.
1348
+ */
1349
+ updateDOM(dom, view) { return false; }
1350
+ /**
1351
+ @internal
1352
+ */
1353
+ compare(other) {
1354
+ return this == other || this.constructor == other.constructor && this.eq(other);
1355
+ }
1356
+ /**
1357
+ The estimated height this widget will have, to be used when
1358
+ estimating the height of content that hasn't been drawn. May
1359
+ return -1 to indicate you don't know. The default implementation
1360
+ returns -1.
1361
+ */
1362
+ get estimatedHeight() { return -1; }
1363
+ /**
1364
+ For inline widgets that are displayed inline (as opposed to
1365
+ `inline-block`) and introduce line breaks (through `<br>` tags
1366
+ or textual newlines), this must indicate the amount of line
1367
+ breaks they introduce. Defaults to 0.
1368
+ */
1369
+ get lineBreaks() { return 0; }
1370
+ /**
1371
+ Can be used to configure which kinds of events inside the widget
1372
+ should be ignored by the editor. The default is to ignore all
1373
+ events.
1374
+ */
1375
+ ignoreEvent(event) { return true; }
1376
+ /**
1377
+ Override the way screen coordinates for positions at/in the
1378
+ widget are found. `pos` will be the offset into the widget, and
1379
+ `side` the side of the position that is being queried—less than
1380
+ zero for before, greater than zero for after, and zero for
1381
+ directly at that position.
1382
+ */
1383
+ coordsAt(dom, pos, side) { return null; }
1384
+ /**
1385
+ @internal
1386
+ */
1387
+ get isHidden() { return false; }
1388
+ /**
1389
+ This is called when the an instance of the widget is removed
1390
+ from the editor view.
1391
+ */
1392
+ destroy(dom) { }
1393
+ }
1394
+ /**
1395
+ The different types of blocks that can occur in an editor view.
1396
+ */
1397
+ var BlockType = /*@__PURE__*/(function (BlockType) {
1398
+ /**
1399
+ A line of text.
1400
+ */
1401
+ BlockType[BlockType["Text"] = 0] = "Text";
1402
+ /**
1403
+ A block widget associated with the position after it.
1404
+ */
1405
+ BlockType[BlockType["WidgetBefore"] = 1] = "WidgetBefore";
1406
+ /**
1407
+ A block widget associated with the position before it.
1408
+ */
1409
+ BlockType[BlockType["WidgetAfter"] = 2] = "WidgetAfter";
1410
+ /**
1411
+ A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content.
1412
+ */
1413
+ BlockType[BlockType["WidgetRange"] = 3] = "WidgetRange";
1414
+ return BlockType})(BlockType || (BlockType = {}));
1415
+ /**
1416
+ A decoration provides information on how to draw or style a piece
1417
+ of content. You'll usually use it wrapped in a
1418
+ [`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
1419
+ @nonabstract
1420
+ */
1421
+ class Decoration extends RangeValue {
1422
+ constructor(
1423
+ /**
1424
+ @internal
1425
+ */
1426
+ startSide,
1427
+ /**
1428
+ @internal
1429
+ */
1430
+ endSide,
1431
+ /**
1432
+ @internal
1433
+ */
1434
+ widget,
1435
+ /**
1436
+ The config object used to create this decoration. You can
1437
+ include additional properties in there to store metadata about
1438
+ your decoration.
1439
+ */
1440
+ spec) {
1441
+ super();
1442
+ this.startSide = startSide;
1443
+ this.endSide = endSide;
1444
+ this.widget = widget;
1445
+ this.spec = spec;
1446
+ }
1447
+ /**
1448
+ @internal
1449
+ */
1450
+ get heightRelevant() { return false; }
1451
+ /**
1452
+ Create a mark decoration, which influences the styling of the
1453
+ content in its range. Nested mark decorations will cause nested
1454
+ DOM elements to be created. Nesting order is determined by
1455
+ precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
1456
+ the higher-precedence decorations creating the inner DOM nodes.
1457
+ Such elements are split on line boundaries and on the boundaries
1458
+ of lower-precedence decorations.
1459
+ */
1460
+ static mark(spec) {
1461
+ return new MarkDecoration(spec);
1462
+ }
1463
+ /**
1464
+ Create a widget decoration, which displays a DOM element at the
1465
+ given position.
1466
+ */
1467
+ static widget(spec) {
1468
+ let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
1469
+ side += (block && !spec.inlineOrder)
1470
+ ? (side > 0 ? 300000000 /* Side.BlockAfter */ : -400000000 /* Side.BlockBefore */)
1471
+ : (side > 0 ? 100000000 /* Side.InlineAfter */ : -100000000 /* Side.InlineBefore */);
1472
+ return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1473
+ }
1474
+ /**
1475
+ Create a replace decoration which replaces the given range with
1476
+ a widget, or simply hides it.
1477
+ */
1478
+ static replace(spec) {
1479
+ let block = !!spec.block, startSide, endSide;
1480
+ if (spec.isBlockGap) {
1481
+ startSide = -500000000 /* Side.GapStart */;
1482
+ endSide = 400000000 /* Side.GapEnd */;
1483
+ }
1484
+ else {
1485
+ let { start, end } = getInclusive(spec, block);
1486
+ startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
1487
+ endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
1488
+ }
1489
+ return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1490
+ }
1491
+ /**
1492
+ Create a line decoration, which can add DOM attributes to the
1493
+ line starting at the given position.
1494
+ */
1495
+ static line(spec) {
1496
+ return new LineDecoration(spec);
1497
+ }
1498
+ /**
1499
+ Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
1500
+ decorated range or ranges. If the ranges aren't already sorted,
1501
+ pass `true` for `sort` to make the library sort them for you.
1502
+ */
1503
+ static set(of, sort = false) {
1504
+ return RangeSet.of(of, sort);
1505
+ }
1506
+ /**
1507
+ @internal
1508
+ */
1509
+ hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
1510
+ }
1511
+ /**
1512
+ The empty set of decorations.
1513
+ */
1514
+ Decoration.none = RangeSet.empty;
1515
+ class MarkDecoration extends Decoration {
1516
+ constructor(spec) {
1517
+ let { start, end } = getInclusive(spec);
1518
+ super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
1519
+ this.tagName = spec.tagName || "span";
1520
+ this.class = spec.class || "";
1521
+ this.attrs = spec.attributes || null;
1522
+ }
1523
+ eq(other) {
1524
+ var _a, _b;
1525
+ return this == other ||
1526
+ other instanceof MarkDecoration &&
1527
+ this.tagName == other.tagName &&
1528
+ (this.class || ((_a = this.attrs) === null || _a === void 0 ? void 0 : _a.class)) == (other.class || ((_b = other.attrs) === null || _b === void 0 ? void 0 : _b.class)) &&
1529
+ attrsEq(this.attrs, other.attrs, "class");
1530
+ }
1531
+ range(from, to = from) {
1532
+ if (from >= to)
1533
+ throw new RangeError("Mark decorations may not be empty");
1534
+ return super.range(from, to);
1535
+ }
1536
+ }
1537
+ MarkDecoration.prototype.point = false;
1538
+ class LineDecoration extends Decoration {
1539
+ constructor(spec) {
1540
+ super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
1541
+ }
1542
+ eq(other) {
1543
+ return other instanceof LineDecoration &&
1544
+ this.spec.class == other.spec.class &&
1545
+ attrsEq(this.spec.attributes, other.spec.attributes);
1546
+ }
1547
+ range(from, to = from) {
1548
+ if (to != from)
1549
+ throw new RangeError("Line decoration ranges must be zero-length");
1550
+ return super.range(from, to);
1551
+ }
1552
+ }
1553
+ LineDecoration.prototype.mapMode = MapMode.TrackBefore;
1554
+ LineDecoration.prototype.point = true;
1555
+ class PointDecoration extends Decoration {
1556
+ constructor(spec, startSide, endSide, block, widget, isReplace) {
1557
+ super(startSide, endSide, widget, spec);
1558
+ this.block = block;
1559
+ this.isReplace = isReplace;
1560
+ this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1561
+ }
1562
+ // Only relevant when this.block == true
1563
+ get type() {
1564
+ return this.startSide != this.endSide ? BlockType.WidgetRange
1565
+ : this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1566
+ }
1567
+ get heightRelevant() {
1568
+ return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
1569
+ }
1570
+ eq(other) {
1571
+ return other instanceof PointDecoration &&
1572
+ widgetsEq(this.widget, other.widget) &&
1573
+ this.block == other.block &&
1574
+ this.startSide == other.startSide && this.endSide == other.endSide;
1575
+ }
1576
+ range(from, to = from) {
1577
+ if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1578
+ throw new RangeError("Invalid range for replacement decoration");
1579
+ if (!this.isReplace && to != from)
1580
+ throw new RangeError("Widget decorations can only have zero-length ranges");
1581
+ return super.range(from, to);
1702
1582
  }
1703
1583
  }
1584
+ PointDecoration.prototype.point = true;
1585
+ function getInclusive(spec, block = false) {
1586
+ let { inclusiveStart: start, inclusiveEnd: end } = spec;
1587
+ if (start == null)
1588
+ start = spec.inclusive;
1589
+ if (end == null)
1590
+ end = spec.inclusive;
1591
+ return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
1592
+ }
1593
+ function widgetsEq(a, b) {
1594
+ return a == b || !!(a && b && a.compare(b));
1595
+ }
1596
+ function addRange(from, to, ranges, margin = 0) {
1597
+ let last = ranges.length - 1;
1598
+ if (last >= 0 && ranges[last] + margin >= from)
1599
+ ranges[last] = Math.max(ranges[last], to);
1600
+ else
1601
+ ranges.push(from, to);
1602
+ }
1704
1603
 
1705
1604
  class ContentBuilder {
1706
1605
  constructor(doc, pos, end, disallowBlockEffectsFor) {
@@ -1726,7 +1625,7 @@ class ContentBuilder {
1726
1625
  if (this.content.length == 0)
1727
1626
  return !this.breakAtStart && this.doc.lineAt(this.pos).from != this.pos;
1728
1627
  let last = this.content[this.content.length - 1];
1729
- return !last.breakAfter && !(last instanceof BlockWidgetView && last.type == BlockType.WidgetBefore);
1628
+ return !(last.breakAfter || last instanceof BlockWidgetView && last.deco.endSide < 0);
1730
1629
  }
1731
1630
  getLine() {
1732
1631
  if (!this.curLine) {
@@ -1751,7 +1650,7 @@ class ContentBuilder {
1751
1650
  this.flushBuffer();
1752
1651
  else
1753
1652
  this.pendingBuffer = 0 /* Buf.No */;
1754
- if (!this.posCovered())
1653
+ if (!openEnd && !this.posCovered())
1755
1654
  this.getLine();
1756
1655
  }
1757
1656
  buildText(length, active, openStart) {
@@ -1804,10 +1703,9 @@ class ContentBuilder {
1804
1703
  let len = to - from;
1805
1704
  if (deco instanceof PointDecoration) {
1806
1705
  if (deco.block) {
1807
- let { type } = deco;
1808
- if (type == BlockType.WidgetAfter && !this.posCovered())
1706
+ if (deco.startSide > 0 && !this.posCovered())
1809
1707
  this.getLine();
1810
- this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
1708
+ this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, deco));
1811
1709
  }
1812
1710
  else {
1813
1711
  let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
@@ -1942,10 +1840,15 @@ class ViewPlugin {
1942
1840
  /**
1943
1841
  @internal
1944
1842
  */
1945
- domEventHandlers, buildExtensions) {
1843
+ domEventHandlers,
1844
+ /**
1845
+ @internal
1846
+ */
1847
+ domEventObservers, buildExtensions) {
1946
1848
  this.id = id;
1947
1849
  this.create = create;
1948
1850
  this.domEventHandlers = domEventHandlers;
1851
+ this.domEventObservers = domEventObservers;
1949
1852
  this.extension = buildExtensions(this);
1950
1853
  }
1951
1854
  /**
@@ -1953,8 +1856,8 @@ class ViewPlugin {
1953
1856
  plugin's value, given an editor view.
1954
1857
  */
1955
1858
  static define(create, spec) {
1956
- const { eventHandlers, provide, decorations: deco } = spec || {};
1957
- return new ViewPlugin(nextPluginID++, create, eventHandlers, plugin => {
1859
+ const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {};
1860
+ return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => {
1958
1861
  let ext = [viewPlugin.of(plugin)];
1959
1862
  if (deco)
1960
1863
  ext.push(decorations.of(view => {
@@ -2702,6 +2605,7 @@ class DocView extends ContentView {
2702
2605
  this.view = view;
2703
2606
  this.decorations = [];
2704
2607
  this.dynamicDecorationMap = [];
2608
+ this.domChanged = null;
2705
2609
  this.hasComposition = null;
2706
2610
  this.markedForComposition = new Set;
2707
2611
  // Track a minimum width for the editor. When measuring sizes in
@@ -2730,6 +2634,7 @@ class DocView extends ContentView {
2730
2634
  }
2731
2635
  // Update the document view to a given state.
2732
2636
  update(update) {
2637
+ var _a;
2733
2638
  let changedRanges = update.changedRanges;
2734
2639
  if (this.minWidth > 0 && changedRanges.length) {
2735
2640
  if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
@@ -2740,7 +2645,15 @@ class DocView extends ContentView {
2740
2645
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2741
2646
  }
2742
2647
  }
2743
- let composition = this.view.inputState.composing < 0 ? null : findCompositionRange(this.view, update.changes);
2648
+ let readCompositionAt = -1;
2649
+ if (this.view.inputState.composing >= 0) {
2650
+ if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel)
2651
+ readCompositionAt = this.domChanged.newSel.head;
2652
+ else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet)
2653
+ readCompositionAt = update.state.selection.main.head;
2654
+ }
2655
+ let composition = readCompositionAt > -1 ? findCompositionRange(this.view, update.changes, readCompositionAt) : null;
2656
+ this.domChanged = null;
2744
2657
  if (this.hasComposition) {
2745
2658
  this.markedForComposition.clear();
2746
2659
  let { from, to } = this.hasComposition;
@@ -2855,11 +2768,9 @@ class DocView extends ContentView {
2855
2768
  cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
2856
2769
  this.markedForComposition.add(cView);
2857
2770
  let prev = ContentView.get(dom);
2858
- if (prev != cView) {
2859
- if (prev)
2860
- prev.dom = null;
2861
- cView.setDOM(dom);
2862
- }
2771
+ if (prev && prev != cView)
2772
+ prev.dom = null;
2773
+ cView.setDOM(dom);
2863
2774
  };
2864
2775
  let pos = this.childPos(composition.range.fromB, 1);
2865
2776
  let cView = this.children[pos.i];
@@ -2882,9 +2793,8 @@ class DocView extends ContentView {
2882
2793
  let force = this.forceSelection;
2883
2794
  this.forceSelection = false;
2884
2795
  let main = this.view.state.selection.main;
2885
- // FIXME need to handle the case where the selection falls inside a block range
2886
- let anchor = this.domAtPos(main.anchor);
2887
- let head = main.empty ? anchor : this.domAtPos(main.head);
2796
+ let anchor = this.moveToLine(this.domAtPos(main.anchor));
2797
+ let head = main.empty ? anchor : this.moveToLine(this.domAtPos(main.head));
2888
2798
  // Always reset on Firefox when next to an uneditable node to
2889
2799
  // avoid invisible cursor bugs (#111)
2890
2800
  if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
@@ -2917,12 +2827,12 @@ class DocView extends ContentView {
2917
2827
  if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) {
2918
2828
  let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* NextTo.Before */ ? 1 : -1);
2919
2829
  if (text)
2920
- anchor = new DOMPos(text, nextTo == 1 /* NextTo.Before */ ? 0 : text.nodeValue.length);
2830
+ anchor = new DOMPos(text.node, text.offset);
2921
2831
  }
2922
2832
  }
2923
2833
  rawSel.collapse(anchor.node, anchor.offset);
2924
- if (main.bidiLevel != null && domSel.caretBidiLevel != null)
2925
- domSel.caretBidiLevel = main.bidiLevel;
2834
+ if (main.bidiLevel != null && rawSel.caretBidiLevel !== undefined)
2835
+ rawSel.caretBidiLevel = main.bidiLevel;
2926
2836
  }
2927
2837
  else if (rawSel.extend) {
2928
2838
  // Selection.extend can be used to create an 'inverted' selection
@@ -2985,6 +2895,26 @@ class DocView extends ContentView {
2985
2895
  if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
2986
2896
  sel.collapse(anchorNode, anchorOffset);
2987
2897
  }
2898
+ // If a position is in/near a block widget, move it to a nearby text
2899
+ // line, since we don't want the cursor inside a block widget.
2900
+ moveToLine(pos) {
2901
+ // Block widgets will return positions before/after them, which
2902
+ // are thus directly in the document DOM element.
2903
+ let dom = this.dom, newPos;
2904
+ if (pos.node != dom)
2905
+ return pos;
2906
+ for (let i = pos.offset; !newPos && i < dom.childNodes.length; i++) {
2907
+ let view = ContentView.get(dom.childNodes[i]);
2908
+ if (view instanceof LineView)
2909
+ newPos = view.domAtPos(0);
2910
+ }
2911
+ for (let i = pos.offset - 1; !newPos && i >= 0; i--) {
2912
+ let view = ContentView.get(dom.childNodes[i]);
2913
+ if (view instanceof LineView)
2914
+ newPos = view.domAtPos(view.length);
2915
+ }
2916
+ return newPos ? new DOMPos(newPos.node, newPos.offset, true) : pos;
2917
+ }
2988
2918
  nearest(dom) {
2989
2919
  for (let cur = dom; cur;) {
2990
2920
  let domView = ContentView.get(cur);
@@ -3012,15 +2942,19 @@ class DocView extends ContentView {
3012
2942
  return this.children[i].domAtPos(off);
3013
2943
  }
3014
2944
  coordsAt(pos, side) {
3015
- for (let off = this.length, i = this.children.length - 1;; i--) {
3016
- let child = this.children[i], start = off - child.breakAfter - child.length;
3017
- if (pos > start ||
3018
- (pos == start && child.type != BlockType.WidgetBefore && child.type != BlockType.WidgetAfter &&
3019
- (!i || side == 2 || this.children[i - 1].breakAfter ||
3020
- (this.children[i - 1].type == BlockType.WidgetBefore && side > -2))))
3021
- return child.coordsAt(pos - start, side);
2945
+ let best = null, bestPos = 0;
2946
+ for (let off = this.length, i = this.children.length - 1; i >= 0; i--) {
2947
+ let child = this.children[i], end = off - child.breakAfter, start = end - child.length;
2948
+ if (end < pos)
2949
+ break;
2950
+ if (start <= pos && (start < pos || child.covers(-1)) && (end > pos || child.covers(1)) &&
2951
+ (!best || child instanceof LineView && !(best instanceof LineView && side >= 0))) {
2952
+ best = child;
2953
+ bestPos = start;
2954
+ }
3022
2955
  off = start;
3023
2956
  }
2957
+ return best ? best.coordsAt(pos - bestPos, side) : null;
3024
2958
  }
3025
2959
  coordsForChar(pos) {
3026
2960
  let { i, off } = this.childPos(pos, 1), child = this.children[i];
@@ -3183,74 +3117,27 @@ class BlockGapWidget extends WidgetType {
3183
3117
  }
3184
3118
  get estimatedHeight() { return this.height; }
3185
3119
  }
3186
- function findCompositionNode(view, dLen) {
3120
+ function findCompositionNode(view, headPos) {
3187
3121
  let sel = view.observer.selectionRange;
3188
3122
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3189
3123
  if (!textNode)
3190
3124
  return null;
3191
- let cView = ContentView.get(textNode);
3192
- let from, to;
3193
- if (cView instanceof TextView) {
3194
- from = cView.posAtStart;
3195
- to = from + cView.length;
3196
- }
3197
- else {
3198
- let oldLen = Math.max(0, textNode.nodeValue.length - dLen);
3199
- up: for (let offset = 0, node = textNode;;) {
3200
- for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
3201
- if (cView = ContentView.get(sibling)) {
3202
- to = cView.posAtEnd + offset;
3203
- from = Math.max(0, to - oldLen);
3204
- break up;
3205
- }
3206
- let reader = new DOMReader([], view.state);
3207
- reader.readNode(sibling);
3208
- if (reader.text.indexOf(LineBreakPlaceholder) > -1)
3209
- return null;
3210
- offset += reader.text.length;
3211
- }
3212
- node = node.parentNode;
3213
- if (!node)
3214
- return null;
3215
- let parentView = ContentView.get(node);
3216
- if (parentView) {
3217
- from = parentView.posAtStart + offset;
3218
- to = from + oldLen;
3219
- break;
3220
- }
3221
- }
3222
- }
3223
- return { from, to: to, node: textNode };
3125
+ let from = headPos - textNode.offset;
3126
+ return { from, to: from + textNode.node.nodeValue.length, node: textNode.node };
3224
3127
  }
3225
- function findCompositionRange(view, changes) {
3226
- let found = findCompositionNode(view, changes.newLength - changes.length);
3128
+ function findCompositionRange(view, changes, headPos) {
3129
+ let found = findCompositionNode(view, headPos);
3227
3130
  if (!found)
3228
3131
  return null;
3229
- let { from: fromA, to: toA, node: textNode } = found;
3230
- let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
3231
- let text = textNode.nodeValue;
3132
+ let { node: textNode, from, to } = found, text = textNode.nodeValue;
3232
3133
  // Don't try to preserve multi-line compositions
3233
3134
  if (/[\n\r]/.test(text))
3234
3135
  return null;
3235
- if (toB - fromB != text.length) {
3236
- // If there is a length mismatch, see if mapping non-inclusively helps
3237
- let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
3238
- if (toB2 - fromB2 == text.length)
3239
- fromB = fromB2, toB = toB2;
3240
- // See if we can find an instance of the text at either side
3241
- else if (view.state.doc.sliceString(toB - text.length, toB) == text)
3242
- fromB = toB - text.length;
3243
- else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
3244
- toB = fromB + text.length;
3245
- // Not found
3246
- else
3247
- return null;
3248
- }
3249
- let { main } = view.state.selection;
3250
- if (view.state.doc.sliceString(fromB, toB) != text || fromB > main.head || toB < main.head)
3136
+ if (view.state.doc.sliceString(found.from, found.to) != text)
3251
3137
  return null;
3138
+ let inv = changes.invertedDesc;
3139
+ let range = new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to);
3252
3140
  let marks = [];
3253
- let range = new ChangedRange(fromA, toA, fromB, toB);
3254
3141
  for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3255
3142
  let parentView = ContentView.get(parent);
3256
3143
  if (parentView instanceof MarkView)
@@ -3271,7 +3158,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3271
3158
  if (side <= 0)
3272
3159
  for (let node = startNode, offset = startOffset;;) {
3273
3160
  if (node.nodeType == 3)
3274
- return node;
3161
+ return { node: node, offset: offset };
3275
3162
  if (node.nodeType == 1 && offset > 0) {
3276
3163
  node = node.childNodes[offset - 1];
3277
3164
  offset = maxOffset(node);
@@ -3283,7 +3170,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3283
3170
  if (side >= 0)
3284
3171
  for (let node = startNode, offset = startOffset;;) {
3285
3172
  if (node.nodeType == 3)
3286
- return node;
3173
+ return { node: node, offset: offset };
3287
3174
  if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
3288
3175
  node = node.childNodes[offset];
3289
3176
  offset = 0;
@@ -3320,6 +3207,15 @@ function inUneditable(node, inside) {
3320
3207
  }
3321
3208
  return false;
3322
3209
  }
3210
+ function touchesComposition(changes, composition) {
3211
+ let touched = false;
3212
+ if (composition)
3213
+ changes.iterChangedRanges((from, to) => {
3214
+ if (from < composition.to && to > composition.from)
3215
+ touched = true;
3216
+ });
3217
+ return touched;
3218
+ }
3323
3219
 
3324
3220
  function groupAt(state, pos, bias = 1) {
3325
3221
  let categorize = state.charCategorizer(pos);
@@ -3693,13 +3589,13 @@ class InputState {
3693
3589
  this.lastSelectionTime = Date.now();
3694
3590
  }
3695
3591
  constructor(view) {
3592
+ this.view = view;
3696
3593
  this.lastKeyCode = 0;
3697
3594
  this.lastKeyTime = 0;
3698
3595
  this.lastTouchTime = 0;
3699
3596
  this.lastFocusTime = 0;
3700
3597
  this.lastScrollTop = 0;
3701
3598
  this.lastScrollLeft = 0;
3702
- this.chromeScrollHack = -1;
3703
3599
  // On iOS, some keys need to have their default behavior happen
3704
3600
  // (after which we retroactively handle them and reset the DOM) to
3705
3601
  // avoid messing up the virtual keyboard state.
@@ -3709,8 +3605,7 @@ class InputState {
3709
3605
  this.lastEscPress = 0;
3710
3606
  this.lastContextMenu = 0;
3711
3607
  this.scrollHandlers = [];
3712
- this.registeredEvents = [];
3713
- this.customHandlers = [];
3608
+ this.handlers = Object.create(null);
3714
3609
  // -1 means not in a composition. Otherwise, this counts the number
3715
3610
  // of changes made during the composition. The count is used to
3716
3611
  // avoid treating the start state of the composition, before any
@@ -3731,29 +3626,10 @@ class InputState {
3731
3626
  // the mutation events fire shortly after the compositionend event
3732
3627
  this.compositionPendingChange = false;
3733
3628
  this.mouseSelection = null;
3734
- let handleEvent = (handler, event) => {
3735
- if (this.ignoreDuringComposition(event))
3736
- return;
3737
- if (event.type == "keydown" && this.keydown(view, event))
3738
- return;
3739
- if (this.mustFlushObserver(event))
3740
- view.observer.forceFlush();
3741
- if (this.runCustomHandlers(event.type, view, event))
3742
- event.preventDefault();
3743
- else
3744
- handler(view, event);
3745
- };
3746
- for (let type in handlers) {
3747
- let handler = handlers[type];
3748
- view.contentDOM.addEventListener(type, event => {
3749
- if (eventBelongsToEditor(view, event))
3750
- handleEvent(handler, event);
3751
- }, handlerOptions[type]);
3752
- this.registeredEvents.push(type);
3753
- }
3629
+ this.handleEvent = this.handleEvent.bind(this);
3754
3630
  view.scrollDOM.addEventListener("mousedown", (event) => {
3755
3631
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom) {
3756
- handleEvent(handlers.mousedown, event);
3632
+ this.handleEvent(event);
3757
3633
  if (!event.defaultPrevented && event.button == 2) {
3758
3634
  // Make sure the content covers the entire scroller height, in order
3759
3635
  // to catch a native context menu click below it
@@ -3765,23 +3641,8 @@ class InputState {
3765
3641
  });
3766
3642
  view.scrollDOM.addEventListener("drop", (event) => {
3767
3643
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
3768
- handleEvent(handlers.drop, event);
3644
+ this.handleEvent(event);
3769
3645
  });
3770
- if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3771
- // On Chrome 102, viewport updates somehow stop wheel-based
3772
- // scrolling. Turning off pointer events during the scroll seems
3773
- // to avoid the issue.
3774
- view.scrollDOM.addEventListener("wheel", () => {
3775
- if (this.chromeScrollHack < 0)
3776
- view.contentDOM.style.pointerEvents = "none";
3777
- else
3778
- window.clearTimeout(this.chromeScrollHack);
3779
- this.chromeScrollHack = setTimeout(() => {
3780
- this.chromeScrollHack = -1;
3781
- view.contentDOM.style.pointerEvents = "";
3782
- }, 100);
3783
- }, { passive: true });
3784
- }
3785
3646
  this.notifiedFocused = view.hasFocus;
3786
3647
  // On Safari adding an input event handler somehow prevents an
3787
3648
  // issue where the composition vanishes when you press enter.
@@ -3790,63 +3651,54 @@ class InputState {
3790
3651
  if (browser.gecko)
3791
3652
  firefoxCopyCutHack(view.contentDOM.ownerDocument);
3792
3653
  }
3793
- ensureHandlers(view, plugins) {
3794
- var _a;
3795
- let handlers;
3796
- this.customHandlers = [];
3797
- for (let plugin of plugins)
3798
- if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
3799
- this.customHandlers.push({ plugin: plugin.value, handlers });
3800
- for (let type in handlers)
3801
- if (this.registeredEvents.indexOf(type) < 0 && type != "scroll") {
3802
- this.registeredEvents.push(type);
3803
- view.contentDOM.addEventListener(type, (event) => {
3804
- if (!eventBelongsToEditor(view, event))
3805
- return;
3806
- if (this.runCustomHandlers(type, view, event))
3807
- event.preventDefault();
3808
- });
3809
- }
3810
- }
3811
- }
3812
- runCustomHandlers(type, view, event) {
3813
- for (let set of this.customHandlers) {
3814
- let handler = set.handlers[type];
3815
- if (handler) {
3816
- try {
3817
- if (handler.call(set.plugin, event, view) || event.defaultPrevented)
3818
- return true;
3819
- }
3820
- catch (e) {
3821
- logException(view.state, e);
3654
+ handleEvent(event) {
3655
+ if (!eventBelongsToEditor(this.view, event) || this.ignoreDuringComposition(event))
3656
+ return;
3657
+ if (event.type == "keydown" && this.keydown(event))
3658
+ return;
3659
+ this.runHandlers(event.type, event);
3660
+ }
3661
+ runHandlers(type, event) {
3662
+ let handlers = this.handlers[type];
3663
+ if (handlers) {
3664
+ for (let observer of handlers.observers)
3665
+ observer(this.view, event);
3666
+ for (let handler of handlers.handlers) {
3667
+ if (event.defaultPrevented)
3668
+ break;
3669
+ if (handler(this.view, event)) {
3670
+ event.preventDefault();
3671
+ break;
3822
3672
  }
3823
3673
  }
3824
3674
  }
3825
- return false;
3826
3675
  }
3827
- runScrollHandlers(view, event) {
3828
- this.lastScrollTop = view.scrollDOM.scrollTop;
3829
- this.lastScrollLeft = view.scrollDOM.scrollLeft;
3830
- for (let set of this.customHandlers) {
3831
- let handler = set.handlers.scroll;
3832
- if (handler) {
3833
- try {
3834
- handler.call(set.plugin, event, view);
3835
- }
3836
- catch (e) {
3837
- logException(view.state, e);
3676
+ ensureHandlers(plugins) {
3677
+ let handlers = computeHandlers(plugins), prev = this.handlers, dom = this.view.contentDOM;
3678
+ for (let type in handlers)
3679
+ if (type != "scroll") {
3680
+ let passive = !handlers[type].handlers.length;
3681
+ let exists = prev[type];
3682
+ if (exists && passive != !exists.handlers.length) {
3683
+ dom.removeEventListener(type, this.handleEvent);
3684
+ exists = null;
3838
3685
  }
3686
+ if (!exists)
3687
+ dom.addEventListener(type, this.handleEvent, { passive });
3839
3688
  }
3840
- }
3689
+ for (let type in prev)
3690
+ if (type != "scroll" && !handlers[type])
3691
+ dom.removeEventListener(type, this.handleEvent);
3692
+ this.handlers = handlers;
3841
3693
  }
3842
- keydown(view, event) {
3694
+ keydown(event) {
3843
3695
  // Must always run, even if a custom handler handled the event
3844
3696
  this.lastKeyCode = event.keyCode;
3845
3697
  this.lastKeyTime = Date.now();
3846
3698
  if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
3847
3699
  return true;
3848
3700
  if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3849
- view.inputState.lastEscPress = 0;
3701
+ this.view.inputState.lastEscPress = 0;
3850
3702
  // Chrome for Android usually doesn't fire proper key events, but
3851
3703
  // occasionally does, usually surrounded by a bunch of complicated
3852
3704
  // composition changes. When an enter or backspace key event is
@@ -3854,10 +3706,10 @@ class InputState {
3854
3706
  // dispatch it.
3855
3707
  if (browser.android && browser.chrome && !event.synthetic &&
3856
3708
  (event.keyCode == 13 || event.keyCode == 8)) {
3857
- view.observer.delayAndroidKey(event.key, event.keyCode);
3709
+ this.view.observer.delayAndroidKey(event.key, event.keyCode);
3858
3710
  return true;
3859
3711
  }
3860
- // Prevent the default behavior of Enter on iOS makes the
3712
+ // Preventing the default behavior of Enter on iOS makes the
3861
3713
  // virtual keyboard get stuck in the wrong (lowercase)
3862
3714
  // state. So we let it go through, and then, in
3863
3715
  // applyDOMChange, notify key handlers of it and reset to
@@ -3867,17 +3719,19 @@ class InputState {
3867
3719
  ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
3868
3720
  EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
3869
3721
  this.pendingIOSKey = pending || event;
3870
- setTimeout(() => this.flushIOSKey(view), 250);
3722
+ setTimeout(() => this.flushIOSKey(), 250);
3871
3723
  return true;
3872
3724
  }
3725
+ if (event.keyCode != 229)
3726
+ this.view.observer.forceFlush();
3873
3727
  return false;
3874
3728
  }
3875
- flushIOSKey(view) {
3729
+ flushIOSKey() {
3876
3730
  let key = this.pendingIOSKey;
3877
3731
  if (!key)
3878
3732
  return false;
3879
3733
  this.pendingIOSKey = undefined;
3880
- return dispatchKey(view.contentDOM, key.key, key.keyCode);
3734
+ return dispatchKey(this.view.contentDOM, key.key, key.keyCode);
3881
3735
  }
3882
3736
  ignoreDuringComposition(event) {
3883
3737
  if (!/^key/.test(event.type))
@@ -3896,9 +3750,6 @@ class InputState {
3896
3750
  }
3897
3751
  return false;
3898
3752
  }
3899
- mustFlushObserver(event) {
3900
- return event.type == "keydown" && event.keyCode != 229;
3901
- }
3902
3753
  startMouseSelection(mouseSelection) {
3903
3754
  if (this.mouseSelection)
3904
3755
  this.mouseSelection.destroy();
@@ -3915,6 +3766,42 @@ class InputState {
3915
3766
  this.mouseSelection.destroy();
3916
3767
  }
3917
3768
  }
3769
+ function bindHandler(plugin, handler) {
3770
+ return (view, event) => {
3771
+ try {
3772
+ return handler.call(plugin, event, view);
3773
+ }
3774
+ catch (e) {
3775
+ logException(view.state, e);
3776
+ }
3777
+ };
3778
+ }
3779
+ function computeHandlers(plugins) {
3780
+ let result = Object.create(null);
3781
+ function record(type) {
3782
+ return result[type] || (result[type] = { observers: [], handlers: [] });
3783
+ }
3784
+ for (let plugin of plugins) {
3785
+ let spec = plugin.spec;
3786
+ if (spec && spec.domEventHandlers)
3787
+ for (let type in spec.domEventHandlers) {
3788
+ let f = spec.domEventHandlers[type];
3789
+ if (f)
3790
+ record(type).handlers.push(bindHandler(plugin.value, f));
3791
+ }
3792
+ if (spec && spec.domEventObservers)
3793
+ for (let type in spec.domEventObservers) {
3794
+ let f = spec.domEventObservers[type];
3795
+ if (f)
3796
+ record(type).observers.push(bindHandler(plugin.value, f));
3797
+ }
3798
+ }
3799
+ for (let type in handlers)
3800
+ record(type).handlers.push(handlers[type]);
3801
+ for (let type in observers)
3802
+ record(type).observers.push(observers[type]);
3803
+ return result;
3804
+ }
3918
3805
  const PendingKeys = [
3919
3806
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3920
3807
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
@@ -3952,10 +3839,8 @@ class MouseSelection {
3952
3839
  start(event) {
3953
3840
  // When clicking outside of the selection, immediately apply the
3954
3841
  // effect of starting the selection
3955
- if (this.dragging === false) {
3956
- event.preventDefault();
3842
+ if (this.dragging === false)
3957
3843
  this.select(event);
3958
- }
3959
3844
  }
3960
3845
  move(event) {
3961
3846
  var _a;
@@ -4091,7 +3976,7 @@ function eventBelongsToEditor(view, event) {
4091
3976
  return true;
4092
3977
  }
4093
3978
  const handlers = /*@__PURE__*/Object.create(null);
4094
- const handlerOptions = /*@__PURE__*/Object.create(null);
3979
+ const observers = /*@__PURE__*/Object.create(null);
4095
3980
  // This is very crude, but unfortunately both these browsers _pretend_
4096
3981
  // that they have a clipboard API—all the objects and methods are
4097
3982
  // there, they just don't work, and they are hard to test.
@@ -4141,23 +4026,27 @@ function doPaste(view, input) {
4141
4026
  scrollIntoView: true
4142
4027
  });
4143
4028
  }
4029
+ observers.scroll = view => {
4030
+ view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
4031
+ view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
4032
+ };
4144
4033
  handlers.keydown = (view, event) => {
4145
4034
  view.inputState.setSelectionOrigin("select");
4146
4035
  if (event.keyCode == 27)
4147
4036
  view.inputState.lastEscPress = Date.now();
4037
+ return false;
4148
4038
  };
4149
- handlers.touchstart = (view, e) => {
4039
+ observers.touchstart = (view, e) => {
4150
4040
  view.inputState.lastTouchTime = Date.now();
4151
4041
  view.inputState.setSelectionOrigin("select.pointer");
4152
4042
  };
4153
- handlers.touchmove = view => {
4043
+ observers.touchmove = view => {
4154
4044
  view.inputState.setSelectionOrigin("select.pointer");
4155
4045
  };
4156
- handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
4157
4046
  handlers.mousedown = (view, event) => {
4158
4047
  view.observer.flush();
4159
4048
  if (view.inputState.lastTouchTime > Date.now() - 2000)
4160
- return; // Ignore touch interaction
4049
+ return false; // Ignore touch interaction
4161
4050
  let style = null;
4162
4051
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
4163
4052
  style = makeStyle(view, event);
@@ -4171,9 +4060,13 @@ handlers.mousedown = (view, event) => {
4171
4060
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
4172
4061
  if (mustFocus)
4173
4062
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
4174
- if (view.inputState.mouseSelection)
4175
- view.inputState.mouseSelection.start(event);
4063
+ let mouseSel = view.inputState.mouseSelection;
4064
+ if (mouseSel) {
4065
+ mouseSel.start(event);
4066
+ return !mouseSel.dragging;
4067
+ }
4176
4068
  }
4069
+ return false;
4177
4070
  };
4178
4071
  function rangeForClick(view, pos, bias, type) {
4179
4072
  if (type == 1) { // Single click
@@ -4277,12 +4170,12 @@ handlers.dragstart = (view, event) => {
4277
4170
  event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4278
4171
  event.dataTransfer.effectAllowed = "copyMove";
4279
4172
  }
4173
+ return false;
4280
4174
  };
4281
4175
  function dropText(view, event, text, direct) {
4282
4176
  if (!text)
4283
4177
  return;
4284
4178
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4285
- event.preventDefault();
4286
4179
  let { mouseSelection } = view.inputState;
4287
4180
  let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4288
4181
  { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
@@ -4297,12 +4190,11 @@ function dropText(view, event, text, direct) {
4297
4190
  }
4298
4191
  handlers.drop = (view, event) => {
4299
4192
  if (!event.dataTransfer)
4300
- return;
4193
+ return false;
4301
4194
  if (view.state.readOnly)
4302
- return event.preventDefault();
4195
+ return true;
4303
4196
  let files = event.dataTransfer.files;
4304
4197
  if (files && files.length) { // For a file drop, read the file's text.
4305
- event.preventDefault();
4306
4198
  let text = Array(files.length), read = 0;
4307
4199
  let finishFile = () => {
4308
4200
  if (++read == files.length)
@@ -4318,22 +4210,29 @@ handlers.drop = (view, event) => {
4318
4210
  };
4319
4211
  reader.readAsText(files[i]);
4320
4212
  }
4213
+ return true;
4321
4214
  }
4322
4215
  else {
4323
- dropText(view, event, event.dataTransfer.getData("Text"), true);
4216
+ let text = event.dataTransfer.getData("Text");
4217
+ if (text) {
4218
+ dropText(view, event, text, true);
4219
+ return true;
4220
+ }
4324
4221
  }
4222
+ return false;
4325
4223
  };
4326
4224
  handlers.paste = (view, event) => {
4327
4225
  if (view.state.readOnly)
4328
- return event.preventDefault();
4226
+ return true;
4329
4227
  view.observer.flush();
4330
4228
  let data = brokenClipboardAPI ? null : event.clipboardData;
4331
4229
  if (data) {
4332
4230
  doPaste(view, data.getData("text/plain") || data.getData("text/uri-text"));
4333
- event.preventDefault();
4231
+ return true;
4334
4232
  }
4335
4233
  else {
4336
4234
  capturePaste(view);
4235
+ return false;
4337
4236
  }
4338
4237
  };
4339
4238
  function captureCopy(view, text) {
@@ -4379,23 +4278,24 @@ let lastLinewiseCopy = null;
4379
4278
  handlers.copy = handlers.cut = (view, event) => {
4380
4279
  let { text, ranges, linewise } = copiedRange(view.state);
4381
4280
  if (!text && !linewise)
4382
- return;
4281
+ return false;
4383
4282
  lastLinewiseCopy = linewise ? text : null;
4283
+ if (event.type == "cut" && !view.state.readOnly)
4284
+ view.dispatch({
4285
+ changes: ranges,
4286
+ scrollIntoView: true,
4287
+ userEvent: "delete.cut"
4288
+ });
4384
4289
  let data = brokenClipboardAPI ? null : event.clipboardData;
4385
4290
  if (data) {
4386
- event.preventDefault();
4387
4291
  data.clearData();
4388
4292
  data.setData("text/plain", text);
4293
+ return true;
4389
4294
  }
4390
4295
  else {
4391
4296
  captureCopy(view, text);
4297
+ return false;
4392
4298
  }
4393
- if (event.type == "cut" && !view.state.readOnly)
4394
- view.dispatch({
4395
- changes: ranges,
4396
- scrollIntoView: true,
4397
- userEvent: "delete.cut"
4398
- });
4399
4299
  };
4400
4300
  const isFocusChange = /*@__PURE__*/Annotation.define();
4401
4301
  function focusChangeTransaction(state, focus) {
@@ -4419,7 +4319,7 @@ function updateForFocusChange(view) {
4419
4319
  }
4420
4320
  }, 10);
4421
4321
  }
4422
- handlers.focus = view => {
4322
+ observers.focus = view => {
4423
4323
  view.inputState.lastFocusTime = Date.now();
4424
4324
  // When focusing reset the scroll position, move it back to where it was
4425
4325
  if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
@@ -4428,11 +4328,11 @@ handlers.focus = view => {
4428
4328
  }
4429
4329
  updateForFocusChange(view);
4430
4330
  };
4431
- handlers.blur = view => {
4331
+ observers.blur = view => {
4432
4332
  view.observer.clearSelectionRange();
4433
4333
  updateForFocusChange(view);
4434
4334
  };
4435
- handlers.compositionstart = handlers.compositionupdate = view => {
4335
+ observers.compositionstart = observers.compositionupdate = view => {
4436
4336
  if (view.inputState.compositionFirstChange == null)
4437
4337
  view.inputState.compositionFirstChange = true;
4438
4338
  if (view.inputState.composing < 0) {
@@ -4440,7 +4340,7 @@ handlers.compositionstart = handlers.compositionupdate = view => {
4440
4340
  view.inputState.composing = 0;
4441
4341
  }
4442
4342
  };
4443
- handlers.compositionend = view => {
4343
+ observers.compositionend = view => {
4444
4344
  view.inputState.composing = -1;
4445
4345
  view.inputState.compositionEndedAt = Date.now();
4446
4346
  view.inputState.compositionPendingKey = true;
@@ -4464,7 +4364,7 @@ handlers.compositionend = view => {
4464
4364
  }, 50);
4465
4365
  }
4466
4366
  };
4467
- handlers.contextmenu = view => {
4367
+ observers.contextmenu = view => {
4468
4368
  view.inputState.lastContextMenu = Date.now();
4469
4369
  };
4470
4370
  handlers.beforeinput = (view, event) => {
@@ -4493,6 +4393,7 @@ handlers.beforeinput = (view, event) => {
4493
4393
  }, 100);
4494
4394
  }
4495
4395
  }
4396
+ return false;
4496
4397
  };
4497
4398
  const appliedFirefoxHack = /*@__PURE__*/new Set;
4498
4399
  // In Firefox, when cut/copy handlers are added to the document, that
@@ -5162,14 +5063,13 @@ class NodeBuilder {
5162
5063
  return line;
5163
5064
  }
5164
5065
  addBlock(block) {
5165
- var _a;
5166
5066
  this.enterLine();
5167
- let type = (_a = block.deco) === null || _a === void 0 ? void 0 : _a.type;
5168
- if (type == BlockType.WidgetAfter && !this.isCovered)
5067
+ let deco = block.deco;
5068
+ if (deco && deco.startSide > 0 && !this.isCovered)
5169
5069
  this.ensureLine();
5170
5070
  this.nodes.push(block);
5171
5071
  this.writtenTo = this.pos = this.pos + block.length;
5172
- if (type != BlockType.WidgetBefore)
5072
+ if (deco && deco.endSide > 0)
5173
5073
  this.covering = block;
5174
5074
  }
5175
5075
  addLineDeco(height, breaks, length) {
@@ -6077,6 +5977,113 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
6077
5977
  }
6078
5978
  }, lightDarkIDs);
6079
5979
 
5980
+ const LineBreakPlaceholder = "\uffff";
5981
+ class DOMReader {
5982
+ constructor(points, state) {
5983
+ this.points = points;
5984
+ this.text = "";
5985
+ this.lineSeparator = state.facet(EditorState.lineSeparator);
5986
+ }
5987
+ append(text) {
5988
+ this.text += text;
5989
+ }
5990
+ lineBreak() {
5991
+ this.text += LineBreakPlaceholder;
5992
+ }
5993
+ readRange(start, end) {
5994
+ if (!start)
5995
+ return this;
5996
+ let parent = start.parentNode;
5997
+ for (let cur = start;;) {
5998
+ this.findPointBefore(parent, cur);
5999
+ let oldLen = this.text.length;
6000
+ this.readNode(cur);
6001
+ let next = cur.nextSibling;
6002
+ if (next == end)
6003
+ break;
6004
+ let view = ContentView.get(cur), nextView = ContentView.get(next);
6005
+ if (view && nextView ? view.breakAfter :
6006
+ (view ? view.breakAfter : isBlockElement(cur)) ||
6007
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
6008
+ this.lineBreak();
6009
+ cur = next;
6010
+ }
6011
+ this.findPointBefore(parent, end);
6012
+ return this;
6013
+ }
6014
+ readTextNode(node) {
6015
+ let text = node.nodeValue;
6016
+ for (let point of this.points)
6017
+ if (point.node == node)
6018
+ point.pos = this.text.length + Math.min(point.offset, text.length);
6019
+ for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
6020
+ let nextBreak = -1, breakSize = 1, m;
6021
+ if (this.lineSeparator) {
6022
+ nextBreak = text.indexOf(this.lineSeparator, off);
6023
+ breakSize = this.lineSeparator.length;
6024
+ }
6025
+ else if (m = re.exec(text)) {
6026
+ nextBreak = m.index;
6027
+ breakSize = m[0].length;
6028
+ }
6029
+ this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
6030
+ if (nextBreak < 0)
6031
+ break;
6032
+ this.lineBreak();
6033
+ if (breakSize > 1)
6034
+ for (let point of this.points)
6035
+ if (point.node == node && point.pos > this.text.length)
6036
+ point.pos -= breakSize - 1;
6037
+ off = nextBreak + breakSize;
6038
+ }
6039
+ }
6040
+ readNode(node) {
6041
+ if (node.cmIgnore)
6042
+ return;
6043
+ let view = ContentView.get(node);
6044
+ let fromView = view && view.overrideDOMText;
6045
+ if (fromView != null) {
6046
+ this.findPointInside(node, fromView.length);
6047
+ for (let i = fromView.iter(); !i.next().done;) {
6048
+ if (i.lineBreak)
6049
+ this.lineBreak();
6050
+ else
6051
+ this.append(i.value);
6052
+ }
6053
+ }
6054
+ else if (node.nodeType == 3) {
6055
+ this.readTextNode(node);
6056
+ }
6057
+ else if (node.nodeName == "BR") {
6058
+ if (node.nextSibling)
6059
+ this.lineBreak();
6060
+ }
6061
+ else if (node.nodeType == 1) {
6062
+ this.readRange(node.firstChild, null);
6063
+ }
6064
+ }
6065
+ findPointBefore(node, next) {
6066
+ for (let point of this.points)
6067
+ if (point.node == node && node.childNodes[point.offset] == next)
6068
+ point.pos = this.text.length;
6069
+ }
6070
+ findPointInside(node, maxLen) {
6071
+ for (let point of this.points)
6072
+ if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
6073
+ point.pos = this.text.length + Math.min(maxLen, point.offset);
6074
+ }
6075
+ }
6076
+ function isBlockElement(node) {
6077
+ return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
6078
+ }
6079
+ class DOMPoint {
6080
+ constructor(node, offset) {
6081
+ this.node = node;
6082
+ this.offset = offset;
6083
+ this.pos = -1;
6084
+ }
6085
+ }
6086
+
6080
6087
  class DOMChange {
6081
6088
  constructor(view, start, end, typeOver) {
6082
6089
  this.typeOver = typeOver;
@@ -6170,7 +6177,7 @@ function applyDOMChange(view, domChange) {
6170
6177
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
6171
6178
  }
6172
6179
  if (change) {
6173
- if (browser.ios && view.inputState.flushIOSKey(view))
6180
+ if (browser.ios && view.inputState.flushIOSKey())
6174
6181
  return true;
6175
6182
  // Android browsers don't fire reasonable key events for enter,
6176
6183
  // backspace, or delete. So this detects changes that look like
@@ -6227,8 +6234,14 @@ function applyDefaultInsert(view, change, newSel) {
6227
6234
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
6228
6235
  change.to <= sel.to && change.to >= sel.to - 10) {
6229
6236
  let replaced = view.state.sliceDoc(change.from, change.to);
6230
- let composition = findCompositionNode(view, change.insert.length - (change.to - change.from)) ||
6231
- view.state.doc.lineAt(sel.head);
6237
+ let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head);
6238
+ if (composition) {
6239
+ let dLen = change.insert.length - (change.to - change.from);
6240
+ compositionRange = { from: composition.from, to: composition.to - dLen };
6241
+ }
6242
+ else {
6243
+ compositionRange = view.state.doc.lineAt(sel.head);
6244
+ }
6232
6245
  let offset = sel.to - change.to, size = sel.to - sel.from;
6233
6246
  tr = startState.changeByRange(range => {
6234
6247
  if (range.from == sel.from && range.to == sel.to)
@@ -6239,7 +6252,7 @@ function applyDefaultInsert(view, change, newSel) {
6239
6252
  // changes in the same node work without aborting
6240
6253
  // composition, so cursors in the composition range are
6241
6254
  // ignored.
6242
- composition && range.to >= composition.from && range.from <= composition.to)
6255
+ range.to >= compositionRange.from && range.from <= compositionRange.to)
6243
6256
  return { range };
6244
6257
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
6245
6258
  return {
@@ -6418,7 +6431,7 @@ class DOMObserver {
6418
6431
  this.readSelectionRange();
6419
6432
  }
6420
6433
  onScrollChanged(e) {
6421
- this.view.inputState.runScrollHandlers(this.view, e);
6434
+ this.view.inputState.runHandlers("scroll", e);
6422
6435
  if (this.intersecting)
6423
6436
  this.view.measure();
6424
6437
  }
@@ -6658,7 +6671,9 @@ class DOMObserver {
6658
6671
  this.lastChange = Date.now();
6659
6672
  this.view.inputState.lastFocusTime = 0;
6660
6673
  this.selectionChanged = false;
6661
- return new DOMChange(this.view, from, to, typeOver);
6674
+ let change = new DOMChange(this.view, from, to, typeOver);
6675
+ this.view.docView.domChanged = { newSel: change.newSel ? change.newSel.main : null };
6676
+ return change;
6662
6677
  }
6663
6678
  // Apply pending changes, if any
6664
6679
  flush(readSelection = true) {
@@ -6887,7 +6902,7 @@ class EditorView {
6887
6902
  plugin.update(this);
6888
6903
  this.observer = new DOMObserver(this);
6889
6904
  this.inputState = new InputState(this);
6890
- this.inputState.ensureHandlers(this, this.plugins);
6905
+ this.inputState.ensureHandlers(this.plugins);
6891
6906
  this.docView = new DocView(this);
6892
6907
  this.mountStyles();
6893
6908
  this.updateAttrs();
@@ -7029,7 +7044,7 @@ class EditorView {
7029
7044
  for (let plugin of this.plugins)
7030
7045
  plugin.update(this);
7031
7046
  this.docView = new DocView(this);
7032
- this.inputState.ensureHandlers(this, this.plugins);
7047
+ this.inputState.ensureHandlers(this.plugins);
7033
7048
  this.mountStyles();
7034
7049
  this.updateAttrs();
7035
7050
  this.bidiCache = [];
@@ -7061,7 +7076,7 @@ class EditorView {
7061
7076
  plugin.destroy(this);
7062
7077
  this.plugins = newPlugins;
7063
7078
  this.pluginMap.clear();
7064
- this.inputState.ensureHandlers(this, this.plugins);
7079
+ this.inputState.ensureHandlers(this.plugins);
7065
7080
  }
7066
7081
  else {
7067
7082
  for (let p of this.plugins)
@@ -7589,6 +7604,17 @@ class EditorView {
7589
7604
  return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
7590
7605
  }
7591
7606
  /**
7607
+ Create an extension that registers DOM event observers. Contrary
7608
+ to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
7609
+ observers can't be prevented from running by a higher-precedence
7610
+ handler returning true. They also don't prevent other handlers
7611
+ and observers from running when they return true, and should not
7612
+ call `preventDefault`.
7613
+ */
7614
+ static domEventObservers(observers) {
7615
+ return ViewPlugin.define(() => ({}), { eventObservers: observers });
7616
+ }
7617
+ /**
7592
7618
  Create a theme extension. The first argument can be a
7593
7619
  [`style-mod`](https://github.com/marijnh/style-mod#documentation)
7594
7620
  style spec providing the styles for the theme. These will be
@@ -8441,7 +8467,7 @@ const drawDropCursor = /*@__PURE__*/ViewPlugin.fromClass(class {
8441
8467
  this.view.dispatch({ effects: setDropCursorPos.of(pos) });
8442
8468
  }
8443
8469
  }, {
8444
- eventHandlers: {
8470
+ eventObservers: {
8445
8471
  dragover(event) {
8446
8472
  this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
8447
8473
  },
@@ -8951,7 +8977,7 @@ function crosshairCursor(options = {}) {
8951
8977
  }
8952
8978
  }
8953
8979
  }, {
8954
- eventHandlers: {
8980
+ eventObservers: {
8955
8981
  keydown(e) {
8956
8982
  this.set(e.keyCode == code || getter(e));
8957
8983
  },
@@ -9262,7 +9288,7 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9262
9288
  }
9263
9289
  }
9264
9290
  }, {
9265
- eventHandlers: {
9291
+ eventObservers: {
9266
9292
  scroll() { this.maybeMeasure(); }
9267
9293
  }
9268
9294
  });
@@ -9903,6 +9929,10 @@ const gutterView = /*@__PURE__*/ViewPlugin.fromClass(class {
9903
9929
  for (let cx of contexts)
9904
9930
  cx.line(this.view, line, classSet);
9905
9931
  }
9932
+ else if (line.widget) {
9933
+ for (let cx of contexts)
9934
+ cx.widget(this.view, line);
9935
+ }
9906
9936
  }
9907
9937
  for (let cx of contexts)
9908
9938
  cx.finish();