@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.cjs CHANGED
@@ -484,6 +484,8 @@ class ContentView {
484
484
  }
485
485
  }
486
486
  setDOM(dom) {
487
+ if (this.dom == dom)
488
+ return;
487
489
  if (this.dom)
488
490
  this.dom.cmView = null;
489
491
  this.dom = dom;
@@ -656,113 +658,6 @@ function mergeChildrenInto(parent, from, to, insert, openStart, openEnd) {
656
658
  replaceRange(parent, fromI, fromOff, toI, toOff, insert, 0, openStart, openEnd);
657
659
  }
658
660
 
659
- const LineBreakPlaceholder = "\uffff";
660
- class DOMReader {
661
- constructor(points, state$1) {
662
- this.points = points;
663
- this.text = "";
664
- this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
665
- }
666
- append(text) {
667
- this.text += text;
668
- }
669
- lineBreak() {
670
- this.text += LineBreakPlaceholder;
671
- }
672
- readRange(start, end) {
673
- if (!start)
674
- return this;
675
- let parent = start.parentNode;
676
- for (let cur = start;;) {
677
- this.findPointBefore(parent, cur);
678
- let oldLen = this.text.length;
679
- this.readNode(cur);
680
- let next = cur.nextSibling;
681
- if (next == end)
682
- break;
683
- let view = ContentView.get(cur), nextView = ContentView.get(next);
684
- if (view && nextView ? view.breakAfter :
685
- (view ? view.breakAfter : isBlockElement(cur)) ||
686
- (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
687
- this.lineBreak();
688
- cur = next;
689
- }
690
- this.findPointBefore(parent, end);
691
- return this;
692
- }
693
- readTextNode(node) {
694
- let text = node.nodeValue;
695
- for (let point of this.points)
696
- if (point.node == node)
697
- point.pos = this.text.length + Math.min(point.offset, text.length);
698
- for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
699
- let nextBreak = -1, breakSize = 1, m;
700
- if (this.lineSeparator) {
701
- nextBreak = text.indexOf(this.lineSeparator, off);
702
- breakSize = this.lineSeparator.length;
703
- }
704
- else if (m = re.exec(text)) {
705
- nextBreak = m.index;
706
- breakSize = m[0].length;
707
- }
708
- this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
709
- if (nextBreak < 0)
710
- break;
711
- this.lineBreak();
712
- if (breakSize > 1)
713
- for (let point of this.points)
714
- if (point.node == node && point.pos > this.text.length)
715
- point.pos -= breakSize - 1;
716
- off = nextBreak + breakSize;
717
- }
718
- }
719
- readNode(node) {
720
- if (node.cmIgnore)
721
- return;
722
- let view = ContentView.get(node);
723
- let fromView = view && view.overrideDOMText;
724
- if (fromView != null) {
725
- this.findPointInside(node, fromView.length);
726
- for (let i = fromView.iter(); !i.next().done;) {
727
- if (i.lineBreak)
728
- this.lineBreak();
729
- else
730
- this.append(i.value);
731
- }
732
- }
733
- else if (node.nodeType == 3) {
734
- this.readTextNode(node);
735
- }
736
- else if (node.nodeName == "BR") {
737
- if (node.nextSibling)
738
- this.lineBreak();
739
- }
740
- else if (node.nodeType == 1) {
741
- this.readRange(node.firstChild, null);
742
- }
743
- }
744
- findPointBefore(node, next) {
745
- for (let point of this.points)
746
- if (point.node == node && node.childNodes[point.offset] == next)
747
- point.pos = this.text.length;
748
- }
749
- findPointInside(node, maxLen) {
750
- for (let point of this.points)
751
- if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
752
- point.pos = this.text.length + Math.min(maxLen, point.offset);
753
- }
754
- }
755
- function isBlockElement(node) {
756
- return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
757
- }
758
- class DOMPoint {
759
- constructor(node, offset) {
760
- this.node = node;
761
- this.offset = offset;
762
- this.pos = -1;
763
- }
764
- }
765
-
766
661
  let nav = typeof navigator != "undefined" ? navigator : { userAgent: "", vendor: "", platform: "" };
767
662
  let doc = typeof document != "undefined" ? document : { documentElement: { style: {} } };
768
663
  const ie_edge = /Edge\/(\d+)/.exec(nav.userAgent);
@@ -1198,308 +1093,27 @@ function getAttrs(dom) {
1198
1093
  return attrs;
1199
1094
  }
1200
1095
 
1201
- /**
1202
- Widgets added to the content are described by subclasses of this
1203
- class. Using a description object like that makes it possible to
1204
- delay creating of the DOM structure for a widget until it is
1205
- needed, and to avoid redrawing widgets even if the decorations
1206
- that define them are recreated.
1207
- */
1208
- class WidgetType {
1209
- /**
1210
- Compare this instance to another instance of the same type.
1211
- (TypeScript can't express this, but only instances of the same
1212
- specific class will be passed to this method.) This is used to
1213
- avoid redrawing widgets when they are replaced by a new
1214
- decoration of the same type. The default implementation just
1215
- returns `false`, which will cause new instances of the widget to
1216
- always be redrawn.
1217
- */
1218
- eq(widget) { return false; }
1219
- /**
1220
- Update a DOM element created by a widget of the same type (but
1221
- different, non-`eq` content) to reflect this widget. May return
1222
- true to indicate that it could update, false to indicate it
1223
- couldn't (in which case the widget will be redrawn). The default
1224
- implementation just returns false.
1225
- */
1226
- updateDOM(dom, view) { return false; }
1227
- /**
1228
- @internal
1229
- */
1230
- compare(other) {
1231
- return this == other || this.constructor == other.constructor && this.eq(other);
1096
+ class LineView extends ContentView {
1097
+ constructor() {
1098
+ super(...arguments);
1099
+ this.children = [];
1100
+ this.length = 0;
1101
+ this.prevAttrs = undefined;
1102
+ this.attrs = null;
1103
+ this.breakAfter = 0;
1232
1104
  }
1233
- /**
1234
- The estimated height this widget will have, to be used when
1235
- estimating the height of content that hasn't been drawn. May
1236
- return -1 to indicate you don't know. The default implementation
1237
- returns -1.
1238
- */
1239
- get estimatedHeight() { return -1; }
1240
- /**
1241
- For inline widgets that are displayed inline (as opposed to
1242
- `inline-block`) and introduce line breaks (through `<br>` tags
1243
- or textual newlines), this must indicate the amount of line
1244
- breaks they introduce. Defaults to 0.
1245
- */
1246
- get lineBreaks() { return 0; }
1247
- /**
1248
- Can be used to configure which kinds of events inside the widget
1249
- should be ignored by the editor. The default is to ignore all
1250
- events.
1251
- */
1252
- ignoreEvent(event) { return true; }
1253
- /**
1254
- Override the way screen coordinates for positions at/in the
1255
- widget are found. `pos` will be the offset into the widget, and
1256
- `side` the side of the position that is being queried—less than
1257
- zero for before, greater than zero for after, and zero for
1258
- directly at that position.
1259
- */
1260
- coordsAt(dom, pos, side) { return null; }
1261
- /**
1262
- @internal
1263
- */
1264
- get isHidden() { return false; }
1265
- /**
1266
- This is called when the an instance of the widget is removed
1267
- from the editor view.
1268
- */
1269
- destroy(dom) { }
1270
- }
1271
- /**
1272
- The different types of blocks that can occur in an editor view.
1273
- */
1274
- exports.BlockType = void 0;
1275
- (function (BlockType) {
1276
- /**
1277
- A line of text.
1278
- */
1279
- BlockType[BlockType["Text"] = 0] = "Text";
1280
- /**
1281
- A block widget associated with the position after it.
1282
- */
1283
- BlockType[BlockType["WidgetBefore"] = 1] = "WidgetBefore";
1284
- /**
1285
- A block widget associated with the position before it.
1286
- */
1287
- BlockType[BlockType["WidgetAfter"] = 2] = "WidgetAfter";
1288
- /**
1289
- A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content.
1290
- */
1291
- BlockType[BlockType["WidgetRange"] = 3] = "WidgetRange";
1292
- })(exports.BlockType || (exports.BlockType = {}));
1293
- /**
1294
- A decoration provides information on how to draw or style a piece
1295
- of content. You'll usually use it wrapped in a
1296
- [`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
1297
- @nonabstract
1298
- */
1299
- class Decoration extends state.RangeValue {
1300
- constructor(
1301
- /**
1302
- @internal
1303
- */
1304
- startSide,
1305
- /**
1306
- @internal
1307
- */
1308
- endSide,
1309
- /**
1310
- @internal
1311
- */
1312
- widget,
1313
- /**
1314
- The config object used to create this decoration. You can
1315
- include additional properties in there to store metadata about
1316
- your decoration.
1317
- */
1318
- spec) {
1319
- super();
1320
- this.startSide = startSide;
1321
- this.endSide = endSide;
1322
- this.widget = widget;
1323
- this.spec = spec;
1324
- }
1325
- /**
1326
- @internal
1327
- */
1328
- get heightRelevant() { return false; }
1329
- /**
1330
- Create a mark decoration, which influences the styling of the
1331
- content in its range. Nested mark decorations will cause nested
1332
- DOM elements to be created. Nesting order is determined by
1333
- precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
1334
- the higher-precedence decorations creating the inner DOM nodes.
1335
- Such elements are split on line boundaries and on the boundaries
1336
- of lower-precedence decorations.
1337
- */
1338
- static mark(spec) {
1339
- return new MarkDecoration(spec);
1340
- }
1341
- /**
1342
- Create a widget decoration, which displays a DOM element at the
1343
- given position.
1344
- */
1345
- static widget(spec) {
1346
- let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
1347
- side += (block && !spec.inlineOrder)
1348
- ? (side > 0 ? 300000000 /* Side.BlockAfter */ : -400000000 /* Side.BlockBefore */)
1349
- : (side > 0 ? 100000000 /* Side.InlineAfter */ : -100000000 /* Side.InlineBefore */);
1350
- return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1351
- }
1352
- /**
1353
- Create a replace decoration which replaces the given range with
1354
- a widget, or simply hides it.
1355
- */
1356
- static replace(spec) {
1357
- let block = !!spec.block, startSide, endSide;
1358
- if (spec.isBlockGap) {
1359
- startSide = -500000000 /* Side.GapStart */;
1360
- endSide = 400000000 /* Side.GapEnd */;
1361
- }
1362
- else {
1363
- let { start, end } = getInclusive(spec, block);
1364
- startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
1365
- endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
1366
- }
1367
- return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1368
- }
1369
- /**
1370
- Create a line decoration, which can add DOM attributes to the
1371
- line starting at the given position.
1372
- */
1373
- static line(spec) {
1374
- return new LineDecoration(spec);
1375
- }
1376
- /**
1377
- Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
1378
- decorated range or ranges. If the ranges aren't already sorted,
1379
- pass `true` for `sort` to make the library sort them for you.
1380
- */
1381
- static set(of, sort = false) {
1382
- return state.RangeSet.of(of, sort);
1383
- }
1384
- /**
1385
- @internal
1386
- */
1387
- hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
1388
- }
1389
- /**
1390
- The empty set of decorations.
1391
- */
1392
- Decoration.none = state.RangeSet.empty;
1393
- class MarkDecoration extends Decoration {
1394
- constructor(spec) {
1395
- let { start, end } = getInclusive(spec);
1396
- super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
1397
- this.tagName = spec.tagName || "span";
1398
- this.class = spec.class || "";
1399
- this.attrs = spec.attributes || null;
1400
- }
1401
- eq(other) {
1402
- var _a, _b;
1403
- return this == other ||
1404
- other instanceof MarkDecoration &&
1405
- this.tagName == other.tagName &&
1406
- (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)) &&
1407
- attrsEq(this.attrs, other.attrs, "class");
1408
- }
1409
- range(from, to = from) {
1410
- if (from >= to)
1411
- throw new RangeError("Mark decorations may not be empty");
1412
- return super.range(from, to);
1413
- }
1414
- }
1415
- MarkDecoration.prototype.point = false;
1416
- class LineDecoration extends Decoration {
1417
- constructor(spec) {
1418
- super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
1419
- }
1420
- eq(other) {
1421
- return other instanceof LineDecoration &&
1422
- this.spec.class == other.spec.class &&
1423
- attrsEq(this.spec.attributes, other.spec.attributes);
1424
- }
1425
- range(from, to = from) {
1426
- if (to != from)
1427
- throw new RangeError("Line decoration ranges must be zero-length");
1428
- return super.range(from, to);
1429
- }
1430
- }
1431
- LineDecoration.prototype.mapMode = state.MapMode.TrackBefore;
1432
- LineDecoration.prototype.point = true;
1433
- class PointDecoration extends Decoration {
1434
- constructor(spec, startSide, endSide, block, widget, isReplace) {
1435
- super(startSide, endSide, widget, spec);
1436
- this.block = block;
1437
- this.isReplace = isReplace;
1438
- this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
1439
- }
1440
- // Only relevant when this.block == true
1441
- get type() {
1442
- return this.startSide < this.endSide ? exports.BlockType.WidgetRange
1443
- : this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
1444
- }
1445
- get heightRelevant() {
1446
- return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
1447
- }
1448
- eq(other) {
1449
- return other instanceof PointDecoration &&
1450
- widgetsEq(this.widget, other.widget) &&
1451
- this.block == other.block &&
1452
- this.startSide == other.startSide && this.endSide == other.endSide;
1453
- }
1454
- range(from, to = from) {
1455
- if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1456
- throw new RangeError("Invalid range for replacement decoration");
1457
- if (!this.isReplace && to != from)
1458
- throw new RangeError("Widget decorations can only have zero-length ranges");
1459
- return super.range(from, to);
1460
- }
1461
- }
1462
- PointDecoration.prototype.point = true;
1463
- function getInclusive(spec, block = false) {
1464
- let { inclusiveStart: start, inclusiveEnd: end } = spec;
1465
- if (start == null)
1466
- start = spec.inclusive;
1467
- if (end == null)
1468
- end = spec.inclusive;
1469
- return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
1470
- }
1471
- function widgetsEq(a, b) {
1472
- return a == b || !!(a && b && a.compare(b));
1473
- }
1474
- function addRange(from, to, ranges, margin = 0) {
1475
- let last = ranges.length - 1;
1476
- if (last >= 0 && ranges[last] + margin >= from)
1477
- ranges[last] = Math.max(ranges[last], to);
1478
- else
1479
- ranges.push(from, to);
1480
- }
1481
-
1482
- class LineView extends ContentView {
1483
- constructor() {
1484
- super(...arguments);
1485
- this.children = [];
1486
- this.length = 0;
1487
- this.prevAttrs = undefined;
1488
- this.attrs = null;
1489
- this.breakAfter = 0;
1490
- }
1491
- // Consumes source
1492
- merge(from, to, source, hasStart, openStart, openEnd) {
1493
- if (source) {
1494
- if (!(source instanceof LineView))
1495
- return false;
1496
- if (!this.dom)
1497
- source.transferDOM(this); // Reuse source.dom when appropriate
1498
- }
1499
- if (hasStart)
1500
- this.setDeco(source ? source.attrs : null);
1501
- mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1502
- return true;
1105
+ // Consumes source
1106
+ merge(from, to, source, hasStart, openStart, openEnd) {
1107
+ if (source) {
1108
+ if (!(source instanceof LineView))
1109
+ return false;
1110
+ if (!this.dom)
1111
+ source.transferDOM(this); // Reuse source.dom when appropriate
1112
+ }
1113
+ if (hasStart)
1114
+ this.setDeco(source ? source.attrs : null);
1115
+ mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1116
+ return true;
1503
1117
  }
1504
1118
  split(at) {
1505
1119
  let end = new LineView;
@@ -1621,7 +1235,7 @@ class LineView extends ContentView {
1621
1235
  return rect;
1622
1236
  }
1623
1237
  become(_other) { return false; }
1624
- get type() { return exports.BlockType.Text; }
1238
+ covers() { return true; }
1625
1239
  static find(docView, pos) {
1626
1240
  for (let i = 0, off = 0; i < docView.children.length; i++) {
1627
1241
  let block = docView.children[i], end = off + block.length;
@@ -1637,11 +1251,11 @@ class LineView extends ContentView {
1637
1251
  }
1638
1252
  }
1639
1253
  class BlockWidgetView extends ContentView {
1640
- constructor(widget, length, type) {
1254
+ constructor(widget, length, deco) {
1641
1255
  super();
1642
1256
  this.widget = widget;
1643
1257
  this.length = length;
1644
- this.type = type;
1258
+ this.deco = deco;
1645
1259
  this.breakAfter = 0;
1646
1260
  this.prevWidget = null;
1647
1261
  }
@@ -1658,7 +1272,7 @@ class BlockWidgetView extends ContentView {
1658
1272
  split(at) {
1659
1273
  let len = this.length - at;
1660
1274
  this.length = at;
1661
- let end = new BlockWidgetView(this.widget, len, this.type);
1275
+ let end = new BlockWidgetView(this.widget, len, this.deco);
1662
1276
  end.breakAfter = this.breakAfter;
1663
1277
  return end;
1664
1278
  }
@@ -1685,7 +1299,7 @@ class BlockWidgetView extends ContentView {
1685
1299
  this.prevWidget = this.widget;
1686
1300
  this.widget = other.widget;
1687
1301
  this.length = other.length;
1688
- this.type = other.type;
1302
+ this.deco = other.deco;
1689
1303
  this.breakAfter = other.breakAfter;
1690
1304
  return true;
1691
1305
  }
@@ -1698,12 +1312,297 @@ class BlockWidgetView extends ContentView {
1698
1312
  coordsAt(pos, side) {
1699
1313
  return this.widget.coordsAt(this.dom, pos, side);
1700
1314
  }
1701
- destroy() {
1702
- super.destroy();
1703
- if (this.dom)
1704
- this.widget.destroy(this.dom);
1315
+ destroy() {
1316
+ super.destroy();
1317
+ if (this.dom)
1318
+ this.widget.destroy(this.dom);
1319
+ }
1320
+ covers(side) {
1321
+ let { startSide, endSide } = this.deco;
1322
+ return startSide == endSide ? false : side < 0 ? startSide < 0 : endSide > 0;
1323
+ }
1324
+ }
1325
+
1326
+ /**
1327
+ Widgets added to the content are described by subclasses of this
1328
+ class. Using a description object like that makes it possible to
1329
+ delay creating of the DOM structure for a widget until it is
1330
+ needed, and to avoid redrawing widgets even if the decorations
1331
+ that define them are recreated.
1332
+ */
1333
+ class WidgetType {
1334
+ /**
1335
+ Compare this instance to another instance of the same type.
1336
+ (TypeScript can't express this, but only instances of the same
1337
+ specific class will be passed to this method.) This is used to
1338
+ avoid redrawing widgets when they are replaced by a new
1339
+ decoration of the same type. The default implementation just
1340
+ returns `false`, which will cause new instances of the widget to
1341
+ always be redrawn.
1342
+ */
1343
+ eq(widget) { return false; }
1344
+ /**
1345
+ Update a DOM element created by a widget of the same type (but
1346
+ different, non-`eq` content) to reflect this widget. May return
1347
+ true to indicate that it could update, false to indicate it
1348
+ couldn't (in which case the widget will be redrawn). The default
1349
+ implementation just returns false.
1350
+ */
1351
+ updateDOM(dom, view) { return false; }
1352
+ /**
1353
+ @internal
1354
+ */
1355
+ compare(other) {
1356
+ return this == other || this.constructor == other.constructor && this.eq(other);
1357
+ }
1358
+ /**
1359
+ The estimated height this widget will have, to be used when
1360
+ estimating the height of content that hasn't been drawn. May
1361
+ return -1 to indicate you don't know. The default implementation
1362
+ returns -1.
1363
+ */
1364
+ get estimatedHeight() { return -1; }
1365
+ /**
1366
+ For inline widgets that are displayed inline (as opposed to
1367
+ `inline-block`) and introduce line breaks (through `<br>` tags
1368
+ or textual newlines), this must indicate the amount of line
1369
+ breaks they introduce. Defaults to 0.
1370
+ */
1371
+ get lineBreaks() { return 0; }
1372
+ /**
1373
+ Can be used to configure which kinds of events inside the widget
1374
+ should be ignored by the editor. The default is to ignore all
1375
+ events.
1376
+ */
1377
+ ignoreEvent(event) { return true; }
1378
+ /**
1379
+ Override the way screen coordinates for positions at/in the
1380
+ widget are found. `pos` will be the offset into the widget, and
1381
+ `side` the side of the position that is being queried—less than
1382
+ zero for before, greater than zero for after, and zero for
1383
+ directly at that position.
1384
+ */
1385
+ coordsAt(dom, pos, side) { return null; }
1386
+ /**
1387
+ @internal
1388
+ */
1389
+ get isHidden() { return false; }
1390
+ /**
1391
+ This is called when the an instance of the widget is removed
1392
+ from the editor view.
1393
+ */
1394
+ destroy(dom) { }
1395
+ }
1396
+ /**
1397
+ The different types of blocks that can occur in an editor view.
1398
+ */
1399
+ exports.BlockType = void 0;
1400
+ (function (BlockType) {
1401
+ /**
1402
+ A line of text.
1403
+ */
1404
+ BlockType[BlockType["Text"] = 0] = "Text";
1405
+ /**
1406
+ A block widget associated with the position after it.
1407
+ */
1408
+ BlockType[BlockType["WidgetBefore"] = 1] = "WidgetBefore";
1409
+ /**
1410
+ A block widget associated with the position before it.
1411
+ */
1412
+ BlockType[BlockType["WidgetAfter"] = 2] = "WidgetAfter";
1413
+ /**
1414
+ A block widget [replacing](https://codemirror.net/6/docs/ref/#view.Decoration^replace) a range of content.
1415
+ */
1416
+ BlockType[BlockType["WidgetRange"] = 3] = "WidgetRange";
1417
+ })(exports.BlockType || (exports.BlockType = {}));
1418
+ /**
1419
+ A decoration provides information on how to draw or style a piece
1420
+ of content. You'll usually use it wrapped in a
1421
+ [`Range`](https://codemirror.net/6/docs/ref/#state.Range), which adds a start and end position.
1422
+ @nonabstract
1423
+ */
1424
+ class Decoration extends state.RangeValue {
1425
+ constructor(
1426
+ /**
1427
+ @internal
1428
+ */
1429
+ startSide,
1430
+ /**
1431
+ @internal
1432
+ */
1433
+ endSide,
1434
+ /**
1435
+ @internal
1436
+ */
1437
+ widget,
1438
+ /**
1439
+ The config object used to create this decoration. You can
1440
+ include additional properties in there to store metadata about
1441
+ your decoration.
1442
+ */
1443
+ spec) {
1444
+ super();
1445
+ this.startSide = startSide;
1446
+ this.endSide = endSide;
1447
+ this.widget = widget;
1448
+ this.spec = spec;
1449
+ }
1450
+ /**
1451
+ @internal
1452
+ */
1453
+ get heightRelevant() { return false; }
1454
+ /**
1455
+ Create a mark decoration, which influences the styling of the
1456
+ content in its range. Nested mark decorations will cause nested
1457
+ DOM elements to be created. Nesting order is determined by
1458
+ precedence of the [facet](https://codemirror.net/6/docs/ref/#view.EditorView^decorations), with
1459
+ the higher-precedence decorations creating the inner DOM nodes.
1460
+ Such elements are split on line boundaries and on the boundaries
1461
+ of lower-precedence decorations.
1462
+ */
1463
+ static mark(spec) {
1464
+ return new MarkDecoration(spec);
1465
+ }
1466
+ /**
1467
+ Create a widget decoration, which displays a DOM element at the
1468
+ given position.
1469
+ */
1470
+ static widget(spec) {
1471
+ let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
1472
+ side += (block && !spec.inlineOrder)
1473
+ ? (side > 0 ? 300000000 /* Side.BlockAfter */ : -400000000 /* Side.BlockBefore */)
1474
+ : (side > 0 ? 100000000 /* Side.InlineAfter */ : -100000000 /* Side.InlineBefore */);
1475
+ return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1476
+ }
1477
+ /**
1478
+ Create a replace decoration which replaces the given range with
1479
+ a widget, or simply hides it.
1480
+ */
1481
+ static replace(spec) {
1482
+ let block = !!spec.block, startSide, endSide;
1483
+ if (spec.isBlockGap) {
1484
+ startSide = -500000000 /* Side.GapStart */;
1485
+ endSide = 400000000 /* Side.GapEnd */;
1486
+ }
1487
+ else {
1488
+ let { start, end } = getInclusive(spec, block);
1489
+ startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
1490
+ endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
1491
+ }
1492
+ return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1493
+ }
1494
+ /**
1495
+ Create a line decoration, which can add DOM attributes to the
1496
+ line starting at the given position.
1497
+ */
1498
+ static line(spec) {
1499
+ return new LineDecoration(spec);
1500
+ }
1501
+ /**
1502
+ Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
1503
+ decorated range or ranges. If the ranges aren't already sorted,
1504
+ pass `true` for `sort` to make the library sort them for you.
1505
+ */
1506
+ static set(of, sort = false) {
1507
+ return state.RangeSet.of(of, sort);
1508
+ }
1509
+ /**
1510
+ @internal
1511
+ */
1512
+ hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
1513
+ }
1514
+ /**
1515
+ The empty set of decorations.
1516
+ */
1517
+ Decoration.none = state.RangeSet.empty;
1518
+ class MarkDecoration extends Decoration {
1519
+ constructor(spec) {
1520
+ let { start, end } = getInclusive(spec);
1521
+ super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
1522
+ this.tagName = spec.tagName || "span";
1523
+ this.class = spec.class || "";
1524
+ this.attrs = spec.attributes || null;
1525
+ }
1526
+ eq(other) {
1527
+ var _a, _b;
1528
+ return this == other ||
1529
+ other instanceof MarkDecoration &&
1530
+ this.tagName == other.tagName &&
1531
+ (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)) &&
1532
+ attrsEq(this.attrs, other.attrs, "class");
1533
+ }
1534
+ range(from, to = from) {
1535
+ if (from >= to)
1536
+ throw new RangeError("Mark decorations may not be empty");
1537
+ return super.range(from, to);
1538
+ }
1539
+ }
1540
+ MarkDecoration.prototype.point = false;
1541
+ class LineDecoration extends Decoration {
1542
+ constructor(spec) {
1543
+ super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
1544
+ }
1545
+ eq(other) {
1546
+ return other instanceof LineDecoration &&
1547
+ this.spec.class == other.spec.class &&
1548
+ attrsEq(this.spec.attributes, other.spec.attributes);
1549
+ }
1550
+ range(from, to = from) {
1551
+ if (to != from)
1552
+ throw new RangeError("Line decoration ranges must be zero-length");
1553
+ return super.range(from, to);
1554
+ }
1555
+ }
1556
+ LineDecoration.prototype.mapMode = state.MapMode.TrackBefore;
1557
+ LineDecoration.prototype.point = true;
1558
+ class PointDecoration extends Decoration {
1559
+ constructor(spec, startSide, endSide, block, widget, isReplace) {
1560
+ super(startSide, endSide, widget, spec);
1561
+ this.block = block;
1562
+ this.isReplace = isReplace;
1563
+ this.mapMode = !block ? state.MapMode.TrackDel : startSide <= 0 ? state.MapMode.TrackBefore : state.MapMode.TrackAfter;
1564
+ }
1565
+ // Only relevant when this.block == true
1566
+ get type() {
1567
+ return this.startSide != this.endSide ? exports.BlockType.WidgetRange
1568
+ : this.startSide <= 0 ? exports.BlockType.WidgetBefore : exports.BlockType.WidgetAfter;
1569
+ }
1570
+ get heightRelevant() {
1571
+ return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
1572
+ }
1573
+ eq(other) {
1574
+ return other instanceof PointDecoration &&
1575
+ widgetsEq(this.widget, other.widget) &&
1576
+ this.block == other.block &&
1577
+ this.startSide == other.startSide && this.endSide == other.endSide;
1578
+ }
1579
+ range(from, to = from) {
1580
+ if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1581
+ throw new RangeError("Invalid range for replacement decoration");
1582
+ if (!this.isReplace && to != from)
1583
+ throw new RangeError("Widget decorations can only have zero-length ranges");
1584
+ return super.range(from, to);
1705
1585
  }
1706
1586
  }
1587
+ PointDecoration.prototype.point = true;
1588
+ function getInclusive(spec, block = false) {
1589
+ let { inclusiveStart: start, inclusiveEnd: end } = spec;
1590
+ if (start == null)
1591
+ start = spec.inclusive;
1592
+ if (end == null)
1593
+ end = spec.inclusive;
1594
+ return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
1595
+ }
1596
+ function widgetsEq(a, b) {
1597
+ return a == b || !!(a && b && a.compare(b));
1598
+ }
1599
+ function addRange(from, to, ranges, margin = 0) {
1600
+ let last = ranges.length - 1;
1601
+ if (last >= 0 && ranges[last] + margin >= from)
1602
+ ranges[last] = Math.max(ranges[last], to);
1603
+ else
1604
+ ranges.push(from, to);
1605
+ }
1707
1606
 
1708
1607
  class ContentBuilder {
1709
1608
  constructor(doc, pos, end, disallowBlockEffectsFor) {
@@ -1729,7 +1628,7 @@ class ContentBuilder {
1729
1628
  if (this.content.length == 0)
1730
1629
  return !this.breakAtStart && this.doc.lineAt(this.pos).from != this.pos;
1731
1630
  let last = this.content[this.content.length - 1];
1732
- return !last.breakAfter && !(last instanceof BlockWidgetView && last.type == exports.BlockType.WidgetBefore);
1631
+ return !(last.breakAfter || last instanceof BlockWidgetView && last.deco.endSide < 0);
1733
1632
  }
1734
1633
  getLine() {
1735
1634
  if (!this.curLine) {
@@ -1754,7 +1653,7 @@ class ContentBuilder {
1754
1653
  this.flushBuffer();
1755
1654
  else
1756
1655
  this.pendingBuffer = 0 /* Buf.No */;
1757
- if (!this.posCovered())
1656
+ if (!openEnd && !this.posCovered())
1758
1657
  this.getLine();
1759
1658
  }
1760
1659
  buildText(length, active, openStart) {
@@ -1807,10 +1706,9 @@ class ContentBuilder {
1807
1706
  let len = to - from;
1808
1707
  if (deco instanceof PointDecoration) {
1809
1708
  if (deco.block) {
1810
- let { type } = deco;
1811
- if (type == exports.BlockType.WidgetAfter && !this.posCovered())
1709
+ if (deco.startSide > 0 && !this.posCovered())
1812
1710
  this.getLine();
1813
- this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
1711
+ this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, deco));
1814
1712
  }
1815
1713
  else {
1816
1714
  let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
@@ -1945,10 +1843,15 @@ class ViewPlugin {
1945
1843
  /**
1946
1844
  @internal
1947
1845
  */
1948
- domEventHandlers, buildExtensions) {
1846
+ domEventHandlers,
1847
+ /**
1848
+ @internal
1849
+ */
1850
+ domEventObservers, buildExtensions) {
1949
1851
  this.id = id;
1950
1852
  this.create = create;
1951
1853
  this.domEventHandlers = domEventHandlers;
1854
+ this.domEventObservers = domEventObservers;
1952
1855
  this.extension = buildExtensions(this);
1953
1856
  }
1954
1857
  /**
@@ -1956,8 +1859,8 @@ class ViewPlugin {
1956
1859
  plugin's value, given an editor view.
1957
1860
  */
1958
1861
  static define(create, spec) {
1959
- const { eventHandlers, provide, decorations: deco } = spec || {};
1960
- return new ViewPlugin(nextPluginID++, create, eventHandlers, plugin => {
1862
+ const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {};
1863
+ return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => {
1961
1864
  let ext = [viewPlugin.of(plugin)];
1962
1865
  if (deco)
1963
1866
  ext.push(decorations.of(view => {
@@ -2706,6 +2609,7 @@ class DocView extends ContentView {
2706
2609
  this.view = view;
2707
2610
  this.decorations = [];
2708
2611
  this.dynamicDecorationMap = [];
2612
+ this.domChanged = null;
2709
2613
  this.hasComposition = null;
2710
2614
  this.markedForComposition = new Set;
2711
2615
  // Track a minimum width for the editor. When measuring sizes in
@@ -2734,6 +2638,7 @@ class DocView extends ContentView {
2734
2638
  }
2735
2639
  // Update the document view to a given state.
2736
2640
  update(update) {
2641
+ var _a;
2737
2642
  let changedRanges = update.changedRanges;
2738
2643
  if (this.minWidth > 0 && changedRanges.length) {
2739
2644
  if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
@@ -2744,7 +2649,15 @@ class DocView extends ContentView {
2744
2649
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2745
2650
  }
2746
2651
  }
2747
- let composition = this.view.inputState.composing < 0 ? null : findCompositionRange(this.view, update.changes);
2652
+ let readCompositionAt = -1;
2653
+ if (this.view.inputState.composing >= 0) {
2654
+ if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel)
2655
+ readCompositionAt = this.domChanged.newSel.head;
2656
+ else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet)
2657
+ readCompositionAt = update.state.selection.main.head;
2658
+ }
2659
+ let composition = readCompositionAt > -1 ? findCompositionRange(this.view, update.changes, readCompositionAt) : null;
2660
+ this.domChanged = null;
2748
2661
  if (this.hasComposition) {
2749
2662
  this.markedForComposition.clear();
2750
2663
  let { from, to } = this.hasComposition;
@@ -2859,11 +2772,9 @@ class DocView extends ContentView {
2859
2772
  cView.flags |= 8 /* ViewFlag.Composition */ | (cView.children.some(c => c.flags & 7 /* ViewFlag.Dirty */) ? 1 /* ViewFlag.ChildDirty */ : 0);
2860
2773
  this.markedForComposition.add(cView);
2861
2774
  let prev = ContentView.get(dom);
2862
- if (prev != cView) {
2863
- if (prev)
2864
- prev.dom = null;
2865
- cView.setDOM(dom);
2866
- }
2775
+ if (prev && prev != cView)
2776
+ prev.dom = null;
2777
+ cView.setDOM(dom);
2867
2778
  };
2868
2779
  let pos = this.childPos(composition.range.fromB, 1);
2869
2780
  let cView = this.children[pos.i];
@@ -2886,9 +2797,8 @@ class DocView extends ContentView {
2886
2797
  let force = this.forceSelection;
2887
2798
  this.forceSelection = false;
2888
2799
  let main = this.view.state.selection.main;
2889
- // FIXME need to handle the case where the selection falls inside a block range
2890
- let anchor = this.domAtPos(main.anchor);
2891
- let head = main.empty ? anchor : this.domAtPos(main.head);
2800
+ let anchor = this.moveToLine(this.domAtPos(main.anchor));
2801
+ let head = main.empty ? anchor : this.moveToLine(this.domAtPos(main.head));
2892
2802
  // Always reset on Firefox when next to an uneditable node to
2893
2803
  // avoid invisible cursor bugs (#111)
2894
2804
  if (browser.gecko && main.empty && !this.hasComposition && betweenUneditable(anchor)) {
@@ -2921,12 +2831,12 @@ class DocView extends ContentView {
2921
2831
  if (nextTo && nextTo != (1 /* NextTo.Before */ | 2 /* NextTo.After */)) {
2922
2832
  let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* NextTo.Before */ ? 1 : -1);
2923
2833
  if (text)
2924
- anchor = new DOMPos(text, nextTo == 1 /* NextTo.Before */ ? 0 : text.nodeValue.length);
2834
+ anchor = new DOMPos(text.node, text.offset);
2925
2835
  }
2926
2836
  }
2927
2837
  rawSel.collapse(anchor.node, anchor.offset);
2928
- if (main.bidiLevel != null && domSel.caretBidiLevel != null)
2929
- domSel.caretBidiLevel = main.bidiLevel;
2838
+ if (main.bidiLevel != null && rawSel.caretBidiLevel !== undefined)
2839
+ rawSel.caretBidiLevel = main.bidiLevel;
2930
2840
  }
2931
2841
  else if (rawSel.extend) {
2932
2842
  // Selection.extend can be used to create an 'inverted' selection
@@ -2989,6 +2899,26 @@ class DocView extends ContentView {
2989
2899
  if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
2990
2900
  sel.collapse(anchorNode, anchorOffset);
2991
2901
  }
2902
+ // If a position is in/near a block widget, move it to a nearby text
2903
+ // line, since we don't want the cursor inside a block widget.
2904
+ moveToLine(pos) {
2905
+ // Block widgets will return positions before/after them, which
2906
+ // are thus directly in the document DOM element.
2907
+ let dom = this.dom, newPos;
2908
+ if (pos.node != dom)
2909
+ return pos;
2910
+ for (let i = pos.offset; !newPos && i < dom.childNodes.length; i++) {
2911
+ let view = ContentView.get(dom.childNodes[i]);
2912
+ if (view instanceof LineView)
2913
+ newPos = view.domAtPos(0);
2914
+ }
2915
+ for (let i = pos.offset - 1; !newPos && i >= 0; i--) {
2916
+ let view = ContentView.get(dom.childNodes[i]);
2917
+ if (view instanceof LineView)
2918
+ newPos = view.domAtPos(view.length);
2919
+ }
2920
+ return newPos ? new DOMPos(newPos.node, newPos.offset, true) : pos;
2921
+ }
2992
2922
  nearest(dom) {
2993
2923
  for (let cur = dom; cur;) {
2994
2924
  let domView = ContentView.get(cur);
@@ -3016,15 +2946,19 @@ class DocView extends ContentView {
3016
2946
  return this.children[i].domAtPos(off);
3017
2947
  }
3018
2948
  coordsAt(pos, side) {
3019
- for (let off = this.length, i = this.children.length - 1;; i--) {
3020
- let child = this.children[i], start = off - child.breakAfter - child.length;
3021
- if (pos > start ||
3022
- (pos == start && child.type != exports.BlockType.WidgetBefore && child.type != exports.BlockType.WidgetAfter &&
3023
- (!i || side == 2 || this.children[i - 1].breakAfter ||
3024
- (this.children[i - 1].type == exports.BlockType.WidgetBefore && side > -2))))
3025
- return child.coordsAt(pos - start, side);
2949
+ let best = null, bestPos = 0;
2950
+ for (let off = this.length, i = this.children.length - 1; i >= 0; i--) {
2951
+ let child = this.children[i], end = off - child.breakAfter, start = end - child.length;
2952
+ if (end < pos)
2953
+ break;
2954
+ if (start <= pos && (start < pos || child.covers(-1)) && (end > pos || child.covers(1)) &&
2955
+ (!best || child instanceof LineView && !(best instanceof LineView && side >= 0))) {
2956
+ best = child;
2957
+ bestPos = start;
2958
+ }
3026
2959
  off = start;
3027
2960
  }
2961
+ return best ? best.coordsAt(pos - bestPos, side) : null;
3028
2962
  }
3029
2963
  coordsForChar(pos) {
3030
2964
  let { i, off } = this.childPos(pos, 1), child = this.children[i];
@@ -3187,74 +3121,27 @@ class BlockGapWidget extends WidgetType {
3187
3121
  }
3188
3122
  get estimatedHeight() { return this.height; }
3189
3123
  }
3190
- function findCompositionNode(view, dLen) {
3124
+ function findCompositionNode(view, headPos) {
3191
3125
  let sel = view.observer.selectionRange;
3192
3126
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
3193
3127
  if (!textNode)
3194
3128
  return null;
3195
- let cView = ContentView.get(textNode);
3196
- let from, to;
3197
- if (cView instanceof TextView) {
3198
- from = cView.posAtStart;
3199
- to = from + cView.length;
3200
- }
3201
- else {
3202
- let oldLen = Math.max(0, textNode.nodeValue.length - dLen);
3203
- up: for (let offset = 0, node = textNode;;) {
3204
- for (let sibling = node.previousSibling, cView; sibling; sibling = sibling.previousSibling) {
3205
- if (cView = ContentView.get(sibling)) {
3206
- to = cView.posAtEnd + offset;
3207
- from = Math.max(0, to - oldLen);
3208
- break up;
3209
- }
3210
- let reader = new DOMReader([], view.state);
3211
- reader.readNode(sibling);
3212
- if (reader.text.indexOf(LineBreakPlaceholder) > -1)
3213
- return null;
3214
- offset += reader.text.length;
3215
- }
3216
- node = node.parentNode;
3217
- if (!node)
3218
- return null;
3219
- let parentView = ContentView.get(node);
3220
- if (parentView) {
3221
- from = parentView.posAtStart + offset;
3222
- to = from + oldLen;
3223
- break;
3224
- }
3225
- }
3226
- }
3227
- return { from, to: to, node: textNode };
3129
+ let from = headPos - textNode.offset;
3130
+ return { from, to: from + textNode.node.nodeValue.length, node: textNode.node };
3228
3131
  }
3229
- function findCompositionRange(view, changes) {
3230
- let found = findCompositionNode(view, changes.newLength - changes.length);
3132
+ function findCompositionRange(view, changes, headPos) {
3133
+ let found = findCompositionNode(view, headPos);
3231
3134
  if (!found)
3232
3135
  return null;
3233
- let { from: fromA, to: toA, node: textNode } = found;
3234
- let fromB = changes.mapPos(fromA, -1), toB = changes.mapPos(toA, 1);
3235
- let text = textNode.nodeValue;
3136
+ let { node: textNode, from, to } = found, text = textNode.nodeValue;
3236
3137
  // Don't try to preserve multi-line compositions
3237
3138
  if (/[\n\r]/.test(text))
3238
3139
  return null;
3239
- if (toB - fromB != text.length) {
3240
- // If there is a length mismatch, see if mapping non-inclusively helps
3241
- let fromB2 = changes.mapPos(fromA, 1), toB2 = changes.mapPos(toA, -1);
3242
- if (toB2 - fromB2 == text.length)
3243
- fromB = fromB2, toB = toB2;
3244
- // See if we can find an instance of the text at either side
3245
- else if (view.state.doc.sliceString(toB - text.length, toB) == text)
3246
- fromB = toB - text.length;
3247
- else if (view.state.doc.sliceString(fromB, fromB + text.length) == text)
3248
- toB = fromB + text.length;
3249
- // Not found
3250
- else
3251
- return null;
3252
- }
3253
- let { main } = view.state.selection;
3254
- if (view.state.doc.sliceString(fromB, toB) != text || fromB > main.head || toB < main.head)
3140
+ if (view.state.doc.sliceString(found.from, found.to) != text)
3255
3141
  return null;
3142
+ let inv = changes.invertedDesc;
3143
+ let range = new ChangedRange(inv.mapPos(from), inv.mapPos(to), from, to);
3256
3144
  let marks = [];
3257
- let range = new ChangedRange(fromA, toA, fromB, toB);
3258
3145
  for (let parent = textNode.parentNode;; parent = parent.parentNode) {
3259
3146
  let parentView = ContentView.get(parent);
3260
3147
  if (parentView instanceof MarkView)
@@ -3275,7 +3162,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3275
3162
  if (side <= 0)
3276
3163
  for (let node = startNode, offset = startOffset;;) {
3277
3164
  if (node.nodeType == 3)
3278
- return node;
3165
+ return { node: node, offset: offset };
3279
3166
  if (node.nodeType == 1 && offset > 0) {
3280
3167
  node = node.childNodes[offset - 1];
3281
3168
  offset = maxOffset(node);
@@ -3287,7 +3174,7 @@ function nearbyTextNode(startNode, startOffset, side) {
3287
3174
  if (side >= 0)
3288
3175
  for (let node = startNode, offset = startOffset;;) {
3289
3176
  if (node.nodeType == 3)
3290
- return node;
3177
+ return { node: node, offset: offset };
3291
3178
  if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
3292
3179
  node = node.childNodes[offset];
3293
3180
  offset = 0;
@@ -3324,6 +3211,15 @@ function inUneditable(node, inside) {
3324
3211
  }
3325
3212
  return false;
3326
3213
  }
3214
+ function touchesComposition(changes, composition) {
3215
+ let touched = false;
3216
+ if (composition)
3217
+ changes.iterChangedRanges((from, to) => {
3218
+ if (from < composition.to && to > composition.from)
3219
+ touched = true;
3220
+ });
3221
+ return touched;
3222
+ }
3327
3223
 
3328
3224
  function groupAt(state$1, pos, bias = 1) {
3329
3225
  let categorize = state$1.charCategorizer(pos);
@@ -3697,13 +3593,13 @@ class InputState {
3697
3593
  this.lastSelectionTime = Date.now();
3698
3594
  }
3699
3595
  constructor(view) {
3596
+ this.view = view;
3700
3597
  this.lastKeyCode = 0;
3701
3598
  this.lastKeyTime = 0;
3702
3599
  this.lastTouchTime = 0;
3703
3600
  this.lastFocusTime = 0;
3704
3601
  this.lastScrollTop = 0;
3705
3602
  this.lastScrollLeft = 0;
3706
- this.chromeScrollHack = -1;
3707
3603
  // On iOS, some keys need to have their default behavior happen
3708
3604
  // (after which we retroactively handle them and reset the DOM) to
3709
3605
  // avoid messing up the virtual keyboard state.
@@ -3713,8 +3609,7 @@ class InputState {
3713
3609
  this.lastEscPress = 0;
3714
3610
  this.lastContextMenu = 0;
3715
3611
  this.scrollHandlers = [];
3716
- this.registeredEvents = [];
3717
- this.customHandlers = [];
3612
+ this.handlers = Object.create(null);
3718
3613
  // -1 means not in a composition. Otherwise, this counts the number
3719
3614
  // of changes made during the composition. The count is used to
3720
3615
  // avoid treating the start state of the composition, before any
@@ -3735,29 +3630,10 @@ class InputState {
3735
3630
  // the mutation events fire shortly after the compositionend event
3736
3631
  this.compositionPendingChange = false;
3737
3632
  this.mouseSelection = null;
3738
- let handleEvent = (handler, event) => {
3739
- if (this.ignoreDuringComposition(event))
3740
- return;
3741
- if (event.type == "keydown" && this.keydown(view, event))
3742
- return;
3743
- if (this.mustFlushObserver(event))
3744
- view.observer.forceFlush();
3745
- if (this.runCustomHandlers(event.type, view, event))
3746
- event.preventDefault();
3747
- else
3748
- handler(view, event);
3749
- };
3750
- for (let type in handlers) {
3751
- let handler = handlers[type];
3752
- view.contentDOM.addEventListener(type, event => {
3753
- if (eventBelongsToEditor(view, event))
3754
- handleEvent(handler, event);
3755
- }, handlerOptions[type]);
3756
- this.registeredEvents.push(type);
3757
- }
3633
+ this.handleEvent = this.handleEvent.bind(this);
3758
3634
  view.scrollDOM.addEventListener("mousedown", (event) => {
3759
3635
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom) {
3760
- handleEvent(handlers.mousedown, event);
3636
+ this.handleEvent(event);
3761
3637
  if (!event.defaultPrevented && event.button == 2) {
3762
3638
  // Make sure the content covers the entire scroller height, in order
3763
3639
  // to catch a native context menu click below it
@@ -3769,23 +3645,8 @@ class InputState {
3769
3645
  });
3770
3646
  view.scrollDOM.addEventListener("drop", (event) => {
3771
3647
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
3772
- handleEvent(handlers.drop, event);
3648
+ this.handleEvent(event);
3773
3649
  });
3774
- if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3775
- // On Chrome 102, viewport updates somehow stop wheel-based
3776
- // scrolling. Turning off pointer events during the scroll seems
3777
- // to avoid the issue.
3778
- view.scrollDOM.addEventListener("wheel", () => {
3779
- if (this.chromeScrollHack < 0)
3780
- view.contentDOM.style.pointerEvents = "none";
3781
- else
3782
- window.clearTimeout(this.chromeScrollHack);
3783
- this.chromeScrollHack = setTimeout(() => {
3784
- this.chromeScrollHack = -1;
3785
- view.contentDOM.style.pointerEvents = "";
3786
- }, 100);
3787
- }, { passive: true });
3788
- }
3789
3650
  this.notifiedFocused = view.hasFocus;
3790
3651
  // On Safari adding an input event handler somehow prevents an
3791
3652
  // issue where the composition vanishes when you press enter.
@@ -3794,63 +3655,54 @@ class InputState {
3794
3655
  if (browser.gecko)
3795
3656
  firefoxCopyCutHack(view.contentDOM.ownerDocument);
3796
3657
  }
3797
- ensureHandlers(view, plugins) {
3798
- var _a;
3799
- let handlers;
3800
- this.customHandlers = [];
3801
- for (let plugin of plugins)
3802
- if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
3803
- this.customHandlers.push({ plugin: plugin.value, handlers });
3804
- for (let type in handlers)
3805
- if (this.registeredEvents.indexOf(type) < 0 && type != "scroll") {
3806
- this.registeredEvents.push(type);
3807
- view.contentDOM.addEventListener(type, (event) => {
3808
- if (!eventBelongsToEditor(view, event))
3809
- return;
3810
- if (this.runCustomHandlers(type, view, event))
3811
- event.preventDefault();
3812
- });
3813
- }
3814
- }
3815
- }
3816
- runCustomHandlers(type, view, event) {
3817
- for (let set of this.customHandlers) {
3818
- let handler = set.handlers[type];
3819
- if (handler) {
3820
- try {
3821
- if (handler.call(set.plugin, event, view) || event.defaultPrevented)
3822
- return true;
3823
- }
3824
- catch (e) {
3825
- logException(view.state, e);
3658
+ handleEvent(event) {
3659
+ if (!eventBelongsToEditor(this.view, event) || this.ignoreDuringComposition(event))
3660
+ return;
3661
+ if (event.type == "keydown" && this.keydown(event))
3662
+ return;
3663
+ this.runHandlers(event.type, event);
3664
+ }
3665
+ runHandlers(type, event) {
3666
+ let handlers = this.handlers[type];
3667
+ if (handlers) {
3668
+ for (let observer of handlers.observers)
3669
+ observer(this.view, event);
3670
+ for (let handler of handlers.handlers) {
3671
+ if (event.defaultPrevented)
3672
+ break;
3673
+ if (handler(this.view, event)) {
3674
+ event.preventDefault();
3675
+ break;
3826
3676
  }
3827
3677
  }
3828
3678
  }
3829
- return false;
3830
3679
  }
3831
- runScrollHandlers(view, event) {
3832
- this.lastScrollTop = view.scrollDOM.scrollTop;
3833
- this.lastScrollLeft = view.scrollDOM.scrollLeft;
3834
- for (let set of this.customHandlers) {
3835
- let handler = set.handlers.scroll;
3836
- if (handler) {
3837
- try {
3838
- handler.call(set.plugin, event, view);
3839
- }
3840
- catch (e) {
3841
- logException(view.state, e);
3680
+ ensureHandlers(plugins) {
3681
+ let handlers = computeHandlers(plugins), prev = this.handlers, dom = this.view.contentDOM;
3682
+ for (let type in handlers)
3683
+ if (type != "scroll") {
3684
+ let passive = !handlers[type].handlers.length;
3685
+ let exists = prev[type];
3686
+ if (exists && passive != !exists.handlers.length) {
3687
+ dom.removeEventListener(type, this.handleEvent);
3688
+ exists = null;
3842
3689
  }
3690
+ if (!exists)
3691
+ dom.addEventListener(type, this.handleEvent, { passive });
3843
3692
  }
3844
- }
3693
+ for (let type in prev)
3694
+ if (type != "scroll" && !handlers[type])
3695
+ dom.removeEventListener(type, this.handleEvent);
3696
+ this.handlers = handlers;
3845
3697
  }
3846
- keydown(view, event) {
3698
+ keydown(event) {
3847
3699
  // Must always run, even if a custom handler handled the event
3848
3700
  this.lastKeyCode = event.keyCode;
3849
3701
  this.lastKeyTime = Date.now();
3850
3702
  if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
3851
3703
  return true;
3852
3704
  if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3853
- view.inputState.lastEscPress = 0;
3705
+ this.view.inputState.lastEscPress = 0;
3854
3706
  // Chrome for Android usually doesn't fire proper key events, but
3855
3707
  // occasionally does, usually surrounded by a bunch of complicated
3856
3708
  // composition changes. When an enter or backspace key event is
@@ -3858,10 +3710,10 @@ class InputState {
3858
3710
  // dispatch it.
3859
3711
  if (browser.android && browser.chrome && !event.synthetic &&
3860
3712
  (event.keyCode == 13 || event.keyCode == 8)) {
3861
- view.observer.delayAndroidKey(event.key, event.keyCode);
3713
+ this.view.observer.delayAndroidKey(event.key, event.keyCode);
3862
3714
  return true;
3863
3715
  }
3864
- // Prevent the default behavior of Enter on iOS makes the
3716
+ // Preventing the default behavior of Enter on iOS makes the
3865
3717
  // virtual keyboard get stuck in the wrong (lowercase)
3866
3718
  // state. So we let it go through, and then, in
3867
3719
  // applyDOMChange, notify key handlers of it and reset to
@@ -3871,17 +3723,19 @@ class InputState {
3871
3723
  ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
3872
3724
  EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
3873
3725
  this.pendingIOSKey = pending || event;
3874
- setTimeout(() => this.flushIOSKey(view), 250);
3726
+ setTimeout(() => this.flushIOSKey(), 250);
3875
3727
  return true;
3876
3728
  }
3729
+ if (event.keyCode != 229)
3730
+ this.view.observer.forceFlush();
3877
3731
  return false;
3878
3732
  }
3879
- flushIOSKey(view) {
3733
+ flushIOSKey() {
3880
3734
  let key = this.pendingIOSKey;
3881
3735
  if (!key)
3882
3736
  return false;
3883
3737
  this.pendingIOSKey = undefined;
3884
- return dispatchKey(view.contentDOM, key.key, key.keyCode);
3738
+ return dispatchKey(this.view.contentDOM, key.key, key.keyCode);
3885
3739
  }
3886
3740
  ignoreDuringComposition(event) {
3887
3741
  if (!/^key/.test(event.type))
@@ -3900,9 +3754,6 @@ class InputState {
3900
3754
  }
3901
3755
  return false;
3902
3756
  }
3903
- mustFlushObserver(event) {
3904
- return event.type == "keydown" && event.keyCode != 229;
3905
- }
3906
3757
  startMouseSelection(mouseSelection) {
3907
3758
  if (this.mouseSelection)
3908
3759
  this.mouseSelection.destroy();
@@ -3919,6 +3770,42 @@ class InputState {
3919
3770
  this.mouseSelection.destroy();
3920
3771
  }
3921
3772
  }
3773
+ function bindHandler(plugin, handler) {
3774
+ return (view, event) => {
3775
+ try {
3776
+ return handler.call(plugin, event, view);
3777
+ }
3778
+ catch (e) {
3779
+ logException(view.state, e);
3780
+ }
3781
+ };
3782
+ }
3783
+ function computeHandlers(plugins) {
3784
+ let result = Object.create(null);
3785
+ function record(type) {
3786
+ return result[type] || (result[type] = { observers: [], handlers: [] });
3787
+ }
3788
+ for (let plugin of plugins) {
3789
+ let spec = plugin.spec;
3790
+ if (spec && spec.domEventHandlers)
3791
+ for (let type in spec.domEventHandlers) {
3792
+ let f = spec.domEventHandlers[type];
3793
+ if (f)
3794
+ record(type).handlers.push(bindHandler(plugin.value, f));
3795
+ }
3796
+ if (spec && spec.domEventObservers)
3797
+ for (let type in spec.domEventObservers) {
3798
+ let f = spec.domEventObservers[type];
3799
+ if (f)
3800
+ record(type).observers.push(bindHandler(plugin.value, f));
3801
+ }
3802
+ }
3803
+ for (let type in handlers)
3804
+ record(type).handlers.push(handlers[type]);
3805
+ for (let type in observers)
3806
+ record(type).observers.push(observers[type]);
3807
+ return result;
3808
+ }
3922
3809
  const PendingKeys = [
3923
3810
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3924
3811
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
@@ -3956,10 +3843,8 @@ class MouseSelection {
3956
3843
  start(event) {
3957
3844
  // When clicking outside of the selection, immediately apply the
3958
3845
  // effect of starting the selection
3959
- if (this.dragging === false) {
3960
- event.preventDefault();
3846
+ if (this.dragging === false)
3961
3847
  this.select(event);
3962
- }
3963
3848
  }
3964
3849
  move(event) {
3965
3850
  var _a;
@@ -4095,7 +3980,7 @@ function eventBelongsToEditor(view, event) {
4095
3980
  return true;
4096
3981
  }
4097
3982
  const handlers = Object.create(null);
4098
- const handlerOptions = Object.create(null);
3983
+ const observers = Object.create(null);
4099
3984
  // This is very crude, but unfortunately both these browsers _pretend_
4100
3985
  // that they have a clipboard API—all the objects and methods are
4101
3986
  // there, they just don't work, and they are hard to test.
@@ -4145,23 +4030,27 @@ function doPaste(view, input) {
4145
4030
  scrollIntoView: true
4146
4031
  });
4147
4032
  }
4033
+ observers.scroll = view => {
4034
+ view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
4035
+ view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
4036
+ };
4148
4037
  handlers.keydown = (view, event) => {
4149
4038
  view.inputState.setSelectionOrigin("select");
4150
4039
  if (event.keyCode == 27)
4151
4040
  view.inputState.lastEscPress = Date.now();
4041
+ return false;
4152
4042
  };
4153
- handlers.touchstart = (view, e) => {
4043
+ observers.touchstart = (view, e) => {
4154
4044
  view.inputState.lastTouchTime = Date.now();
4155
4045
  view.inputState.setSelectionOrigin("select.pointer");
4156
4046
  };
4157
- handlers.touchmove = view => {
4047
+ observers.touchmove = view => {
4158
4048
  view.inputState.setSelectionOrigin("select.pointer");
4159
4049
  };
4160
- handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
4161
4050
  handlers.mousedown = (view, event) => {
4162
4051
  view.observer.flush();
4163
4052
  if (view.inputState.lastTouchTime > Date.now() - 2000)
4164
- return; // Ignore touch interaction
4053
+ return false; // Ignore touch interaction
4165
4054
  let style = null;
4166
4055
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
4167
4056
  style = makeStyle(view, event);
@@ -4175,9 +4064,13 @@ handlers.mousedown = (view, event) => {
4175
4064
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
4176
4065
  if (mustFocus)
4177
4066
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
4178
- if (view.inputState.mouseSelection)
4179
- view.inputState.mouseSelection.start(event);
4067
+ let mouseSel = view.inputState.mouseSelection;
4068
+ if (mouseSel) {
4069
+ mouseSel.start(event);
4070
+ return !mouseSel.dragging;
4071
+ }
4180
4072
  }
4073
+ return false;
4181
4074
  };
4182
4075
  function rangeForClick(view, pos, bias, type) {
4183
4076
  if (type == 1) { // Single click
@@ -4281,12 +4174,12 @@ handlers.dragstart = (view, event) => {
4281
4174
  event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4282
4175
  event.dataTransfer.effectAllowed = "copyMove";
4283
4176
  }
4177
+ return false;
4284
4178
  };
4285
4179
  function dropText(view, event, text, direct) {
4286
4180
  if (!text)
4287
4181
  return;
4288
4182
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4289
- event.preventDefault();
4290
4183
  let { mouseSelection } = view.inputState;
4291
4184
  let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4292
4185
  { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
@@ -4301,12 +4194,11 @@ function dropText(view, event, text, direct) {
4301
4194
  }
4302
4195
  handlers.drop = (view, event) => {
4303
4196
  if (!event.dataTransfer)
4304
- return;
4197
+ return false;
4305
4198
  if (view.state.readOnly)
4306
- return event.preventDefault();
4199
+ return true;
4307
4200
  let files = event.dataTransfer.files;
4308
4201
  if (files && files.length) { // For a file drop, read the file's text.
4309
- event.preventDefault();
4310
4202
  let text = Array(files.length), read = 0;
4311
4203
  let finishFile = () => {
4312
4204
  if (++read == files.length)
@@ -4322,22 +4214,29 @@ handlers.drop = (view, event) => {
4322
4214
  };
4323
4215
  reader.readAsText(files[i]);
4324
4216
  }
4217
+ return true;
4325
4218
  }
4326
4219
  else {
4327
- dropText(view, event, event.dataTransfer.getData("Text"), true);
4220
+ let text = event.dataTransfer.getData("Text");
4221
+ if (text) {
4222
+ dropText(view, event, text, true);
4223
+ return true;
4224
+ }
4328
4225
  }
4226
+ return false;
4329
4227
  };
4330
4228
  handlers.paste = (view, event) => {
4331
4229
  if (view.state.readOnly)
4332
- return event.preventDefault();
4230
+ return true;
4333
4231
  view.observer.flush();
4334
4232
  let data = brokenClipboardAPI ? null : event.clipboardData;
4335
4233
  if (data) {
4336
4234
  doPaste(view, data.getData("text/plain") || data.getData("text/uri-text"));
4337
- event.preventDefault();
4235
+ return true;
4338
4236
  }
4339
4237
  else {
4340
4238
  capturePaste(view);
4239
+ return false;
4341
4240
  }
4342
4241
  };
4343
4242
  function captureCopy(view, text) {
@@ -4383,23 +4282,24 @@ let lastLinewiseCopy = null;
4383
4282
  handlers.copy = handlers.cut = (view, event) => {
4384
4283
  let { text, ranges, linewise } = copiedRange(view.state);
4385
4284
  if (!text && !linewise)
4386
- return;
4285
+ return false;
4387
4286
  lastLinewiseCopy = linewise ? text : null;
4287
+ if (event.type == "cut" && !view.state.readOnly)
4288
+ view.dispatch({
4289
+ changes: ranges,
4290
+ scrollIntoView: true,
4291
+ userEvent: "delete.cut"
4292
+ });
4388
4293
  let data = brokenClipboardAPI ? null : event.clipboardData;
4389
4294
  if (data) {
4390
- event.preventDefault();
4391
4295
  data.clearData();
4392
4296
  data.setData("text/plain", text);
4297
+ return true;
4393
4298
  }
4394
4299
  else {
4395
4300
  captureCopy(view, text);
4301
+ return false;
4396
4302
  }
4397
- if (event.type == "cut" && !view.state.readOnly)
4398
- view.dispatch({
4399
- changes: ranges,
4400
- scrollIntoView: true,
4401
- userEvent: "delete.cut"
4402
- });
4403
4303
  };
4404
4304
  const isFocusChange = state.Annotation.define();
4405
4305
  function focusChangeTransaction(state, focus) {
@@ -4423,7 +4323,7 @@ function updateForFocusChange(view) {
4423
4323
  }
4424
4324
  }, 10);
4425
4325
  }
4426
- handlers.focus = view => {
4326
+ observers.focus = view => {
4427
4327
  view.inputState.lastFocusTime = Date.now();
4428
4328
  // When focusing reset the scroll position, move it back to where it was
4429
4329
  if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
@@ -4432,11 +4332,11 @@ handlers.focus = view => {
4432
4332
  }
4433
4333
  updateForFocusChange(view);
4434
4334
  };
4435
- handlers.blur = view => {
4335
+ observers.blur = view => {
4436
4336
  view.observer.clearSelectionRange();
4437
4337
  updateForFocusChange(view);
4438
4338
  };
4439
- handlers.compositionstart = handlers.compositionupdate = view => {
4339
+ observers.compositionstart = observers.compositionupdate = view => {
4440
4340
  if (view.inputState.compositionFirstChange == null)
4441
4341
  view.inputState.compositionFirstChange = true;
4442
4342
  if (view.inputState.composing < 0) {
@@ -4444,7 +4344,7 @@ handlers.compositionstart = handlers.compositionupdate = view => {
4444
4344
  view.inputState.composing = 0;
4445
4345
  }
4446
4346
  };
4447
- handlers.compositionend = view => {
4347
+ observers.compositionend = view => {
4448
4348
  view.inputState.composing = -1;
4449
4349
  view.inputState.compositionEndedAt = Date.now();
4450
4350
  view.inputState.compositionPendingKey = true;
@@ -4468,7 +4368,7 @@ handlers.compositionend = view => {
4468
4368
  }, 50);
4469
4369
  }
4470
4370
  };
4471
- handlers.contextmenu = view => {
4371
+ observers.contextmenu = view => {
4472
4372
  view.inputState.lastContextMenu = Date.now();
4473
4373
  };
4474
4374
  handlers.beforeinput = (view, event) => {
@@ -4497,6 +4397,7 @@ handlers.beforeinput = (view, event) => {
4497
4397
  }, 100);
4498
4398
  }
4499
4399
  }
4400
+ return false;
4500
4401
  };
4501
4402
  const appliedFirefoxHack = new Set;
4502
4403
  // In Firefox, when cut/copy handlers are added to the document, that
@@ -5167,14 +5068,13 @@ class NodeBuilder {
5167
5068
  return line;
5168
5069
  }
5169
5070
  addBlock(block) {
5170
- var _a;
5171
5071
  this.enterLine();
5172
- let type = (_a = block.deco) === null || _a === void 0 ? void 0 : _a.type;
5173
- if (type == exports.BlockType.WidgetAfter && !this.isCovered)
5072
+ let deco = block.deco;
5073
+ if (deco && deco.startSide > 0 && !this.isCovered)
5174
5074
  this.ensureLine();
5175
5075
  this.nodes.push(block);
5176
5076
  this.writtenTo = this.pos = this.pos + block.length;
5177
- if (type != exports.BlockType.WidgetBefore)
5077
+ if (deco && deco.endSide > 0)
5178
5078
  this.covering = block;
5179
5079
  }
5180
5080
  addLineDeco(height, breaks, length) {
@@ -6082,6 +5982,113 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
6082
5982
  }
6083
5983
  }, lightDarkIDs);
6084
5984
 
5985
+ const LineBreakPlaceholder = "\uffff";
5986
+ class DOMReader {
5987
+ constructor(points, state$1) {
5988
+ this.points = points;
5989
+ this.text = "";
5990
+ this.lineSeparator = state$1.facet(state.EditorState.lineSeparator);
5991
+ }
5992
+ append(text) {
5993
+ this.text += text;
5994
+ }
5995
+ lineBreak() {
5996
+ this.text += LineBreakPlaceholder;
5997
+ }
5998
+ readRange(start, end) {
5999
+ if (!start)
6000
+ return this;
6001
+ let parent = start.parentNode;
6002
+ for (let cur = start;;) {
6003
+ this.findPointBefore(parent, cur);
6004
+ let oldLen = this.text.length;
6005
+ this.readNode(cur);
6006
+ let next = cur.nextSibling;
6007
+ if (next == end)
6008
+ break;
6009
+ let view = ContentView.get(cur), nextView = ContentView.get(next);
6010
+ if (view && nextView ? view.breakAfter :
6011
+ (view ? view.breakAfter : isBlockElement(cur)) ||
6012
+ (isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore) && this.text.length > oldLen))
6013
+ this.lineBreak();
6014
+ cur = next;
6015
+ }
6016
+ this.findPointBefore(parent, end);
6017
+ return this;
6018
+ }
6019
+ readTextNode(node) {
6020
+ let text = node.nodeValue;
6021
+ for (let point of this.points)
6022
+ if (point.node == node)
6023
+ point.pos = this.text.length + Math.min(point.offset, text.length);
6024
+ for (let off = 0, re = this.lineSeparator ? null : /\r\n?|\n/g;;) {
6025
+ let nextBreak = -1, breakSize = 1, m;
6026
+ if (this.lineSeparator) {
6027
+ nextBreak = text.indexOf(this.lineSeparator, off);
6028
+ breakSize = this.lineSeparator.length;
6029
+ }
6030
+ else if (m = re.exec(text)) {
6031
+ nextBreak = m.index;
6032
+ breakSize = m[0].length;
6033
+ }
6034
+ this.append(text.slice(off, nextBreak < 0 ? text.length : nextBreak));
6035
+ if (nextBreak < 0)
6036
+ break;
6037
+ this.lineBreak();
6038
+ if (breakSize > 1)
6039
+ for (let point of this.points)
6040
+ if (point.node == node && point.pos > this.text.length)
6041
+ point.pos -= breakSize - 1;
6042
+ off = nextBreak + breakSize;
6043
+ }
6044
+ }
6045
+ readNode(node) {
6046
+ if (node.cmIgnore)
6047
+ return;
6048
+ let view = ContentView.get(node);
6049
+ let fromView = view && view.overrideDOMText;
6050
+ if (fromView != null) {
6051
+ this.findPointInside(node, fromView.length);
6052
+ for (let i = fromView.iter(); !i.next().done;) {
6053
+ if (i.lineBreak)
6054
+ this.lineBreak();
6055
+ else
6056
+ this.append(i.value);
6057
+ }
6058
+ }
6059
+ else if (node.nodeType == 3) {
6060
+ this.readTextNode(node);
6061
+ }
6062
+ else if (node.nodeName == "BR") {
6063
+ if (node.nextSibling)
6064
+ this.lineBreak();
6065
+ }
6066
+ else if (node.nodeType == 1) {
6067
+ this.readRange(node.firstChild, null);
6068
+ }
6069
+ }
6070
+ findPointBefore(node, next) {
6071
+ for (let point of this.points)
6072
+ if (point.node == node && node.childNodes[point.offset] == next)
6073
+ point.pos = this.text.length;
6074
+ }
6075
+ findPointInside(node, maxLen) {
6076
+ for (let point of this.points)
6077
+ if (node.nodeType == 3 ? point.node == node : node.contains(point.node))
6078
+ point.pos = this.text.length + Math.min(maxLen, point.offset);
6079
+ }
6080
+ }
6081
+ function isBlockElement(node) {
6082
+ return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
6083
+ }
6084
+ class DOMPoint {
6085
+ constructor(node, offset) {
6086
+ this.node = node;
6087
+ this.offset = offset;
6088
+ this.pos = -1;
6089
+ }
6090
+ }
6091
+
6085
6092
  class DOMChange {
6086
6093
  constructor(view, start, end, typeOver) {
6087
6094
  this.typeOver = typeOver;
@@ -6175,7 +6182,7 @@ function applyDOMChange(view, domChange) {
6175
6182
  change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
6176
6183
  }
6177
6184
  if (change) {
6178
- if (browser.ios && view.inputState.flushIOSKey(view))
6185
+ if (browser.ios && view.inputState.flushIOSKey())
6179
6186
  return true;
6180
6187
  // Android browsers don't fire reasonable key events for enter,
6181
6188
  // backspace, or delete. So this detects changes that look like
@@ -6232,8 +6239,14 @@ function applyDefaultInsert(view, change, newSel) {
6232
6239
  if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
6233
6240
  change.to <= sel.to && change.to >= sel.to - 10) {
6234
6241
  let replaced = view.state.sliceDoc(change.from, change.to);
6235
- let composition = findCompositionNode(view, change.insert.length - (change.to - change.from)) ||
6236
- view.state.doc.lineAt(sel.head);
6242
+ let compositionRange, composition = newSel && findCompositionNode(view, newSel.main.head);
6243
+ if (composition) {
6244
+ let dLen = change.insert.length - (change.to - change.from);
6245
+ compositionRange = { from: composition.from, to: composition.to - dLen };
6246
+ }
6247
+ else {
6248
+ compositionRange = view.state.doc.lineAt(sel.head);
6249
+ }
6237
6250
  let offset = sel.to - change.to, size = sel.to - sel.from;
6238
6251
  tr = startState.changeByRange(range => {
6239
6252
  if (range.from == sel.from && range.to == sel.to)
@@ -6244,7 +6257,7 @@ function applyDefaultInsert(view, change, newSel) {
6244
6257
  // changes in the same node work without aborting
6245
6258
  // composition, so cursors in the composition range are
6246
6259
  // ignored.
6247
- composition && range.to >= composition.from && range.from <= composition.to)
6260
+ range.to >= compositionRange.from && range.from <= compositionRange.to)
6248
6261
  return { range };
6249
6262
  let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
6250
6263
  return {
@@ -6423,7 +6436,7 @@ class DOMObserver {
6423
6436
  this.readSelectionRange();
6424
6437
  }
6425
6438
  onScrollChanged(e) {
6426
- this.view.inputState.runScrollHandlers(this.view, e);
6439
+ this.view.inputState.runHandlers("scroll", e);
6427
6440
  if (this.intersecting)
6428
6441
  this.view.measure();
6429
6442
  }
@@ -6663,7 +6676,9 @@ class DOMObserver {
6663
6676
  this.lastChange = Date.now();
6664
6677
  this.view.inputState.lastFocusTime = 0;
6665
6678
  this.selectionChanged = false;
6666
- return new DOMChange(this.view, from, to, typeOver);
6679
+ let change = new DOMChange(this.view, from, to, typeOver);
6680
+ this.view.docView.domChanged = { newSel: change.newSel ? change.newSel.main : null };
6681
+ return change;
6667
6682
  }
6668
6683
  // Apply pending changes, if any
6669
6684
  flush(readSelection = true) {
@@ -6892,7 +6907,7 @@ class EditorView {
6892
6907
  plugin.update(this);
6893
6908
  this.observer = new DOMObserver(this);
6894
6909
  this.inputState = new InputState(this);
6895
- this.inputState.ensureHandlers(this, this.plugins);
6910
+ this.inputState.ensureHandlers(this.plugins);
6896
6911
  this.docView = new DocView(this);
6897
6912
  this.mountStyles();
6898
6913
  this.updateAttrs();
@@ -7034,7 +7049,7 @@ class EditorView {
7034
7049
  for (let plugin of this.plugins)
7035
7050
  plugin.update(this);
7036
7051
  this.docView = new DocView(this);
7037
- this.inputState.ensureHandlers(this, this.plugins);
7052
+ this.inputState.ensureHandlers(this.plugins);
7038
7053
  this.mountStyles();
7039
7054
  this.updateAttrs();
7040
7055
  this.bidiCache = [];
@@ -7066,7 +7081,7 @@ class EditorView {
7066
7081
  plugin.destroy(this);
7067
7082
  this.plugins = newPlugins;
7068
7083
  this.pluginMap.clear();
7069
- this.inputState.ensureHandlers(this, this.plugins);
7084
+ this.inputState.ensureHandlers(this.plugins);
7070
7085
  }
7071
7086
  else {
7072
7087
  for (let p of this.plugins)
@@ -7594,6 +7609,17 @@ class EditorView {
7594
7609
  return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
7595
7610
  }
7596
7611
  /**
7612
+ Create an extension that registers DOM event observers. Contrary
7613
+ to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
7614
+ observers can't be prevented from running by a higher-precedence
7615
+ handler returning true. They also don't prevent other handlers
7616
+ and observers from running when they return true, and should not
7617
+ call `preventDefault`.
7618
+ */
7619
+ static domEventObservers(observers) {
7620
+ return ViewPlugin.define(() => ({}), { eventObservers: observers });
7621
+ }
7622
+ /**
7597
7623
  Create a theme extension. The first argument can be a
7598
7624
  [`style-mod`](https://github.com/marijnh/style-mod#documentation)
7599
7625
  style spec providing the styles for the theme. These will be
@@ -8446,7 +8472,7 @@ const drawDropCursor = ViewPlugin.fromClass(class {
8446
8472
  this.view.dispatch({ effects: setDropCursorPos.of(pos) });
8447
8473
  }
8448
8474
  }, {
8449
- eventHandlers: {
8475
+ eventObservers: {
8450
8476
  dragover(event) {
8451
8477
  this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
8452
8478
  },
@@ -8956,7 +8982,7 @@ function crosshairCursor(options = {}) {
8956
8982
  }
8957
8983
  }
8958
8984
  }, {
8959
- eventHandlers: {
8985
+ eventObservers: {
8960
8986
  keydown(e) {
8961
8987
  this.set(e.keyCode == code || getter(e));
8962
8988
  },
@@ -9267,7 +9293,7 @@ const tooltipPlugin = ViewPlugin.fromClass(class {
9267
9293
  }
9268
9294
  }
9269
9295
  }, {
9270
- eventHandlers: {
9296
+ eventObservers: {
9271
9297
  scroll() { this.maybeMeasure(); }
9272
9298
  }
9273
9299
  });
@@ -9908,6 +9934,10 @@ const gutterView = ViewPlugin.fromClass(class {
9908
9934
  for (let cx of contexts)
9909
9935
  cx.line(this.view, line, classSet);
9910
9936
  }
9937
+ else if (line.widget) {
9938
+ for (let cx of contexts)
9939
+ cx.widget(this.view, line);
9940
+ }
9911
9941
  }
9912
9942
  for (let cx of contexts)
9913
9943
  cx.finish();