@codemirror/view 6.18.1 → 6.20.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
@@ -579,7 +579,7 @@ function replaceRange(parent, fromI, fromOff, toI, toOff, insert, breakAtStart,
579
579
  if (toI < children.length) {
580
580
  let after = children[toI];
581
581
  // Make sure the end of the child after the update is preserved in `after`
582
- if (after && toOff < after.length) {
582
+ if (after && (toOff < after.length || after.breakAfter && (last === null || last === void 0 ? void 0 : last.breakAfter))) {
583
583
  // If we're splitting a child, separate part of it to avoid that
584
584
  // being mangled when updating the child before the update.
585
585
  if (fromI == toI) {
@@ -1091,6 +1091,236 @@ function getAttrs(dom) {
1091
1091
  return attrs;
1092
1092
  }
1093
1093
 
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;
1102
+ }
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;
1115
+ }
1116
+ split(at) {
1117
+ let end = new LineView;
1118
+ end.breakAfter = this.breakAfter;
1119
+ if (this.length == 0)
1120
+ return end;
1121
+ let { i, off } = this.childPos(at);
1122
+ if (off) {
1123
+ end.append(this.children[i].split(off), 0);
1124
+ this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
1125
+ i++;
1126
+ }
1127
+ for (let j = i; j < this.children.length; j++)
1128
+ end.append(this.children[j], 0);
1129
+ while (i > 0 && this.children[i - 1].length == 0)
1130
+ this.children[--i].destroy();
1131
+ this.children.length = i;
1132
+ this.markDirty();
1133
+ this.length = at;
1134
+ return end;
1135
+ }
1136
+ transferDOM(other) {
1137
+ if (!this.dom)
1138
+ return;
1139
+ this.markDirty();
1140
+ other.setDOM(this.dom);
1141
+ other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1142
+ this.prevAttrs = undefined;
1143
+ this.dom = null;
1144
+ }
1145
+ setDeco(attrs) {
1146
+ if (!attrsEq(this.attrs, attrs)) {
1147
+ if (this.dom) {
1148
+ this.prevAttrs = this.attrs;
1149
+ this.markDirty();
1150
+ }
1151
+ this.attrs = attrs;
1152
+ }
1153
+ }
1154
+ append(child, openStart) {
1155
+ joinInlineInto(this, child, openStart);
1156
+ }
1157
+ // Only called when building a line view in ContentBuilder
1158
+ addLineDeco(deco) {
1159
+ let attrs = deco.spec.attributes, cls = deco.spec.class;
1160
+ if (attrs)
1161
+ this.attrs = combineAttrs(attrs, this.attrs || {});
1162
+ if (cls)
1163
+ this.attrs = combineAttrs({ class: cls }, this.attrs || {});
1164
+ }
1165
+ domAtPos(pos) {
1166
+ return inlineDOMAtPos(this, pos);
1167
+ }
1168
+ reuseDOM(node) {
1169
+ if (node.nodeName == "DIV") {
1170
+ this.setDOM(node);
1171
+ this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */;
1172
+ }
1173
+ }
1174
+ sync(view, track) {
1175
+ var _a;
1176
+ if (!this.dom) {
1177
+ this.setDOM(document.createElement("div"));
1178
+ this.dom.className = "cm-line";
1179
+ this.prevAttrs = this.attrs ? null : undefined;
1180
+ }
1181
+ else if (this.flags & 4 /* ViewFlag.AttrsDirty */) {
1182
+ clearAttributes(this.dom);
1183
+ this.dom.className = "cm-line";
1184
+ this.prevAttrs = this.attrs ? null : undefined;
1185
+ }
1186
+ if (this.prevAttrs !== undefined) {
1187
+ updateAttrs(this.dom, this.prevAttrs, this.attrs);
1188
+ this.dom.classList.add("cm-line");
1189
+ this.prevAttrs = undefined;
1190
+ }
1191
+ super.sync(view, track);
1192
+ let last = this.dom.lastChild;
1193
+ while (last && ContentView.get(last) instanceof MarkView)
1194
+ last = last.lastChild;
1195
+ if (!last || !this.length ||
1196
+ last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
1197
+ (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
1198
+ let hack = document.createElement("BR");
1199
+ hack.cmIgnore = true;
1200
+ this.dom.appendChild(hack);
1201
+ }
1202
+ }
1203
+ measureTextSize() {
1204
+ if (this.children.length == 0 || this.length > 20)
1205
+ return null;
1206
+ let totalWidth = 0, textHeight;
1207
+ for (let child of this.children) {
1208
+ if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1209
+ return null;
1210
+ let rects = clientRectsFor(child.dom);
1211
+ if (rects.length != 1)
1212
+ return null;
1213
+ totalWidth += rects[0].width;
1214
+ textHeight = rects[0].height;
1215
+ }
1216
+ return !totalWidth ? null : {
1217
+ lineHeight: this.dom.getBoundingClientRect().height,
1218
+ charWidth: totalWidth / this.length,
1219
+ textHeight
1220
+ };
1221
+ }
1222
+ coordsAt(pos, side) {
1223
+ let rect = coordsInChildren(this, pos, side);
1224
+ // Correct rectangle height for empty lines when the returned
1225
+ // height is larger than the text height.
1226
+ if (!this.children.length && rect && this.parent) {
1227
+ let { heightOracle } = this.parent.view.viewState, height = rect.bottom - rect.top;
1228
+ if (Math.abs(height - heightOracle.lineHeight) < 2 && heightOracle.textHeight < height) {
1229
+ let dist = (height - heightOracle.textHeight) / 2;
1230
+ return { top: rect.top + dist, bottom: rect.bottom - dist, left: rect.left, right: rect.left };
1231
+ }
1232
+ }
1233
+ return rect;
1234
+ }
1235
+ become(_other) { return false; }
1236
+ covers() { return true; }
1237
+ static find(docView, pos) {
1238
+ for (let i = 0, off = 0; i < docView.children.length; i++) {
1239
+ let block = docView.children[i], end = off + block.length;
1240
+ if (end >= pos) {
1241
+ if (block instanceof LineView)
1242
+ return block;
1243
+ if (end > pos)
1244
+ break;
1245
+ }
1246
+ off = end + block.breakAfter;
1247
+ }
1248
+ return null;
1249
+ }
1250
+ }
1251
+ class BlockWidgetView extends ContentView {
1252
+ constructor(widget, length, deco) {
1253
+ super();
1254
+ this.widget = widget;
1255
+ this.length = length;
1256
+ this.deco = deco;
1257
+ this.breakAfter = 0;
1258
+ this.prevWidget = null;
1259
+ }
1260
+ merge(from, to, source, _takeDeco, openStart, openEnd) {
1261
+ if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) ||
1262
+ from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
1263
+ return false;
1264
+ this.length = from + (source ? source.length : 0) + (this.length - to);
1265
+ return true;
1266
+ }
1267
+ domAtPos(pos) {
1268
+ return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
1269
+ }
1270
+ split(at) {
1271
+ let len = this.length - at;
1272
+ this.length = at;
1273
+ let end = new BlockWidgetView(this.widget, len, this.deco);
1274
+ end.breakAfter = this.breakAfter;
1275
+ return end;
1276
+ }
1277
+ get children() { return noChildren; }
1278
+ sync(view) {
1279
+ if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
1280
+ if (this.dom && this.prevWidget)
1281
+ this.prevWidget.destroy(this.dom);
1282
+ this.prevWidget = null;
1283
+ this.setDOM(this.widget.toDOM(view));
1284
+ this.dom.contentEditable = "false";
1285
+ }
1286
+ }
1287
+ get overrideDOMText() {
1288
+ return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text.empty;
1289
+ }
1290
+ domBoundsAround() { return null; }
1291
+ become(other) {
1292
+ if (other instanceof BlockWidgetView &&
1293
+ other.widget.constructor == this.widget.constructor) {
1294
+ if (!other.widget.compare(this.widget))
1295
+ this.markDirty(true);
1296
+ if (this.dom && !this.prevWidget)
1297
+ this.prevWidget = this.widget;
1298
+ this.widget = other.widget;
1299
+ this.length = other.length;
1300
+ this.deco = other.deco;
1301
+ this.breakAfter = other.breakAfter;
1302
+ return true;
1303
+ }
1304
+ return false;
1305
+ }
1306
+ ignoreMutation() { return true; }
1307
+ ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1308
+ get isEditable() { return false; }
1309
+ get isWidget() { return true; }
1310
+ coordsAt(pos, side) {
1311
+ return this.widget.coordsAt(this.dom, pos, side);
1312
+ }
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
+
1094
1324
  /**
1095
1325
  Widgets added to the content are described by subclasses of this
1096
1326
  class. Using a description object like that makes it possible to
@@ -1256,346 +1486,120 @@ class Decoration extends RangeValue {
1256
1486
  startSide = (start ? (block ? -300000000 /* Side.BlockIncStart */ : -1 /* Side.InlineIncStart */) : 500000000 /* Side.NonIncStart */) - 1;
1257
1487
  endSide = (end ? (block ? 200000000 /* Side.BlockIncEnd */ : 1 /* Side.InlineIncEnd */) : -600000000 /* Side.NonIncEnd */) + 1;
1258
1488
  }
1259
- return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1260
- }
1261
- /**
1262
- Create a line decoration, which can add DOM attributes to the
1263
- line starting at the given position.
1264
- */
1265
- static line(spec) {
1266
- return new LineDecoration(spec);
1267
- }
1268
- /**
1269
- Build a [`DecorationSet`](https://codemirror.net/6/docs/ref/#view.DecorationSet) from the given
1270
- decorated range or ranges. If the ranges aren't already sorted,
1271
- pass `true` for `sort` to make the library sort them for you.
1272
- */
1273
- static set(of, sort = false) {
1274
- return RangeSet.of(of, sort);
1275
- }
1276
- /**
1277
- @internal
1278
- */
1279
- hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
1280
- }
1281
- /**
1282
- The empty set of decorations.
1283
- */
1284
- Decoration.none = RangeSet.empty;
1285
- class MarkDecoration extends Decoration {
1286
- constructor(spec) {
1287
- let { start, end } = getInclusive(spec);
1288
- super(start ? -1 /* Side.InlineIncStart */ : 500000000 /* Side.NonIncStart */, end ? 1 /* Side.InlineIncEnd */ : -600000000 /* Side.NonIncEnd */, null, spec);
1289
- this.tagName = spec.tagName || "span";
1290
- this.class = spec.class || "";
1291
- this.attrs = spec.attributes || null;
1292
- }
1293
- eq(other) {
1294
- var _a, _b;
1295
- return this == other ||
1296
- other instanceof MarkDecoration &&
1297
- this.tagName == other.tagName &&
1298
- (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)) &&
1299
- attrsEq(this.attrs, other.attrs, "class");
1300
- }
1301
- range(from, to = from) {
1302
- if (from >= to)
1303
- throw new RangeError("Mark decorations may not be empty");
1304
- return super.range(from, to);
1305
- }
1306
- }
1307
- MarkDecoration.prototype.point = false;
1308
- class LineDecoration extends Decoration {
1309
- constructor(spec) {
1310
- super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
1311
- }
1312
- eq(other) {
1313
- return other instanceof LineDecoration &&
1314
- this.spec.class == other.spec.class &&
1315
- attrsEq(this.spec.attributes, other.spec.attributes);
1316
- }
1317
- range(from, to = from) {
1318
- if (to != from)
1319
- throw new RangeError("Line decoration ranges must be zero-length");
1320
- return super.range(from, to);
1321
- }
1322
- }
1323
- LineDecoration.prototype.mapMode = MapMode.TrackBefore;
1324
- LineDecoration.prototype.point = true;
1325
- class PointDecoration extends Decoration {
1326
- constructor(spec, startSide, endSide, block, widget, isReplace) {
1327
- super(startSide, endSide, widget, spec);
1328
- this.block = block;
1329
- this.isReplace = isReplace;
1330
- this.mapMode = !block ? MapMode.TrackDel : startSide <= 0 ? MapMode.TrackBefore : MapMode.TrackAfter;
1331
- }
1332
- // Only relevant when this.block == true
1333
- get type() {
1334
- return this.startSide < this.endSide ? BlockType.WidgetRange
1335
- : this.startSide <= 0 ? BlockType.WidgetBefore : BlockType.WidgetAfter;
1336
- }
1337
- get heightRelevant() {
1338
- return this.block || !!this.widget && (this.widget.estimatedHeight >= 5 || this.widget.lineBreaks > 0);
1339
- }
1340
- eq(other) {
1341
- return other instanceof PointDecoration &&
1342
- widgetsEq(this.widget, other.widget) &&
1343
- this.block == other.block &&
1344
- this.startSide == other.startSide && this.endSide == other.endSide;
1345
- }
1346
- range(from, to = from) {
1347
- if (this.isReplace && (from > to || (from == to && this.startSide > 0 && this.endSide <= 0)))
1348
- throw new RangeError("Invalid range for replacement decoration");
1349
- if (!this.isReplace && to != from)
1350
- throw new RangeError("Widget decorations can only have zero-length ranges");
1351
- return super.range(from, to);
1352
- }
1353
- }
1354
- PointDecoration.prototype.point = true;
1355
- function getInclusive(spec, block = false) {
1356
- let { inclusiveStart: start, inclusiveEnd: end } = spec;
1357
- if (start == null)
1358
- start = spec.inclusive;
1359
- if (end == null)
1360
- end = spec.inclusive;
1361
- return { start: start !== null && start !== void 0 ? start : block, end: end !== null && end !== void 0 ? end : block };
1362
- }
1363
- function widgetsEq(a, b) {
1364
- return a == b || !!(a && b && a.compare(b));
1365
- }
1366
- function addRange(from, to, ranges, margin = 0) {
1367
- let last = ranges.length - 1;
1368
- if (last >= 0 && ranges[last] + margin >= from)
1369
- ranges[last] = Math.max(ranges[last], to);
1370
- else
1371
- ranges.push(from, to);
1372
- }
1373
-
1374
- class LineView extends ContentView {
1375
- constructor() {
1376
- super(...arguments);
1377
- this.children = [];
1378
- this.length = 0;
1379
- this.prevAttrs = undefined;
1380
- this.attrs = null;
1381
- this.breakAfter = 0;
1382
- }
1383
- // Consumes source
1384
- merge(from, to, source, hasStart, openStart, openEnd) {
1385
- if (source) {
1386
- if (!(source instanceof LineView))
1387
- return false;
1388
- if (!this.dom)
1389
- source.transferDOM(this); // Reuse source.dom when appropriate
1390
- }
1391
- if (hasStart)
1392
- this.setDeco(source ? source.attrs : null);
1393
- mergeChildrenInto(this, from, to, source ? source.children : [], openStart, openEnd);
1394
- return true;
1395
- }
1396
- split(at) {
1397
- let end = new LineView;
1398
- end.breakAfter = this.breakAfter;
1399
- if (this.length == 0)
1400
- return end;
1401
- let { i, off } = this.childPos(at);
1402
- if (off) {
1403
- end.append(this.children[i].split(off), 0);
1404
- this.children[i].merge(off, this.children[i].length, null, false, 0, 0);
1405
- i++;
1406
- }
1407
- for (let j = i; j < this.children.length; j++)
1408
- end.append(this.children[j], 0);
1409
- while (i > 0 && this.children[i - 1].length == 0)
1410
- this.children[--i].destroy();
1411
- this.children.length = i;
1412
- this.markDirty();
1413
- this.length = at;
1414
- return end;
1415
- }
1416
- transferDOM(other) {
1417
- if (!this.dom)
1418
- return;
1419
- this.markDirty();
1420
- other.setDOM(this.dom);
1421
- other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1422
- this.prevAttrs = undefined;
1423
- this.dom = null;
1424
- }
1425
- setDeco(attrs) {
1426
- if (!attrsEq(this.attrs, attrs)) {
1427
- if (this.dom) {
1428
- this.prevAttrs = this.attrs;
1429
- this.markDirty();
1430
- }
1431
- this.attrs = attrs;
1432
- }
1433
- }
1434
- append(child, openStart) {
1435
- joinInlineInto(this, child, openStart);
1436
- }
1437
- // Only called when building a line view in ContentBuilder
1438
- addLineDeco(deco) {
1439
- let attrs = deco.spec.attributes, cls = deco.spec.class;
1440
- if (attrs)
1441
- this.attrs = combineAttrs(attrs, this.attrs || {});
1442
- if (cls)
1443
- this.attrs = combineAttrs({ class: cls }, this.attrs || {});
1444
- }
1445
- domAtPos(pos) {
1446
- return inlineDOMAtPos(this, pos);
1447
- }
1448
- reuseDOM(node) {
1449
- if (node.nodeName == "DIV") {
1450
- this.setDOM(node);
1451
- this.flags |= 4 /* ViewFlag.AttrsDirty */ | 2 /* ViewFlag.NodeDirty */;
1452
- }
1453
- }
1454
- sync(view, track) {
1455
- var _a;
1456
- if (!this.dom) {
1457
- this.setDOM(document.createElement("div"));
1458
- this.dom.className = "cm-line";
1459
- this.prevAttrs = this.attrs ? null : undefined;
1460
- }
1461
- else if (this.flags & 4 /* ViewFlag.AttrsDirty */) {
1462
- clearAttributes(this.dom);
1463
- this.dom.className = "cm-line";
1464
- this.prevAttrs = this.attrs ? null : undefined;
1465
- }
1466
- if (this.prevAttrs !== undefined) {
1467
- updateAttrs(this.dom, this.prevAttrs, this.attrs);
1468
- this.dom.classList.add("cm-line");
1469
- this.prevAttrs = undefined;
1470
- }
1471
- super.sync(view, track);
1472
- let last = this.dom.lastChild;
1473
- while (last && ContentView.get(last) instanceof MarkView)
1474
- last = last.lastChild;
1475
- if (!last || !this.length ||
1476
- last.nodeName != "BR" && ((_a = ContentView.get(last)) === null || _a === void 0 ? void 0 : _a.isEditable) == false &&
1477
- (!browser.ios || !this.children.some(ch => ch instanceof TextView))) {
1478
- let hack = document.createElement("BR");
1479
- hack.cmIgnore = true;
1480
- this.dom.appendChild(hack);
1481
- }
1482
- }
1483
- measureTextSize() {
1484
- if (this.children.length == 0 || this.length > 20)
1485
- return null;
1486
- let totalWidth = 0, textHeight;
1487
- for (let child of this.children) {
1488
- if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1489
- return null;
1490
- let rects = clientRectsFor(child.dom);
1491
- if (rects.length != 1)
1492
- return null;
1493
- totalWidth += rects[0].width;
1494
- textHeight = rects[0].height;
1495
- }
1496
- return !totalWidth ? null : {
1497
- lineHeight: this.dom.getBoundingClientRect().height,
1498
- charWidth: totalWidth / this.length,
1499
- textHeight
1500
- };
1501
- }
1502
- coordsAt(pos, side) {
1503
- let rect = coordsInChildren(this, pos, side);
1504
- // Correct rectangle height for empty lines when the returned
1505
- // height is larger than the text height.
1506
- if (!this.children.length && rect && this.parent) {
1507
- let { heightOracle } = this.parent.view.viewState, height = rect.bottom - rect.top;
1508
- if (Math.abs(height - heightOracle.lineHeight) < 2 && heightOracle.textHeight < height) {
1509
- let dist = (height - heightOracle.textHeight) / 2;
1510
- return { top: rect.top + dist, bottom: rect.bottom - dist, left: rect.left, right: rect.left };
1511
- }
1512
- }
1513
- return rect;
1489
+ return new PointDecoration(spec, startSide, endSide, block, spec.widget || null, true);
1514
1490
  }
1515
- become(_other) { return false; }
1516
- get type() { return BlockType.Text; }
1517
- static find(docView, pos) {
1518
- for (let i = 0, off = 0; i < docView.children.length; i++) {
1519
- let block = docView.children[i], end = off + block.length;
1520
- if (end >= pos) {
1521
- if (block instanceof LineView)
1522
- return block;
1523
- if (end > pos)
1524
- break;
1525
- }
1526
- off = end + block.breakAfter;
1527
- }
1528
- return null;
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);
1529
1505
  }
1506
+ /**
1507
+ @internal
1508
+ */
1509
+ hasHeight() { return this.widget ? this.widget.estimatedHeight > -1 : false; }
1530
1510
  }
1531
- class BlockWidgetView extends ContentView {
1532
- constructor(widget, length, type) {
1533
- super();
1534
- this.widget = widget;
1535
- this.length = length;
1536
- this.type = type;
1537
- this.breakAfter = 0;
1538
- this.prevWidget = null;
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;
1539
1522
  }
1540
- merge(from, to, source, _takeDeco, openStart, openEnd) {
1541
- if (source && (!(source instanceof BlockWidgetView) || !this.widget.compare(source.widget) ||
1542
- from > 0 && openStart <= 0 || to < this.length && openEnd <= 0))
1543
- return false;
1544
- this.length = from + (source ? source.length : 0) + (this.length - to);
1545
- return true;
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");
1546
1530
  }
1547
- domAtPos(pos) {
1548
- return pos == 0 ? DOMPos.before(this.dom) : DOMPos.after(this.dom, pos == this.length);
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);
1549
1535
  }
1550
- split(at) {
1551
- let len = this.length - at;
1552
- this.length = at;
1553
- let end = new BlockWidgetView(this.widget, len, this.type);
1554
- end.breakAfter = this.breakAfter;
1555
- return end;
1536
+ }
1537
+ MarkDecoration.prototype.point = false;
1538
+ class LineDecoration extends Decoration {
1539
+ constructor(spec) {
1540
+ super(-200000000 /* Side.Line */, -200000000 /* Side.Line */, null, spec);
1556
1541
  }
1557
- get children() { return noChildren; }
1558
- sync(view) {
1559
- if (!this.dom || !this.widget.updateDOM(this.dom, view)) {
1560
- if (this.dom && this.prevWidget)
1561
- this.prevWidget.destroy(this.dom);
1562
- this.prevWidget = null;
1563
- this.setDOM(this.widget.toDOM(view));
1564
- this.dom.contentEditable = "false";
1565
- }
1542
+ eq(other) {
1543
+ return other instanceof LineDecoration &&
1544
+ this.spec.class == other.spec.class &&
1545
+ attrsEq(this.spec.attributes, other.spec.attributes);
1566
1546
  }
1567
- get overrideDOMText() {
1568
- return this.parent ? this.parent.view.state.doc.slice(this.posAtStart, this.posAtEnd) : Text.empty;
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);
1569
1551
  }
1570
- domBoundsAround() { return null; }
1571
- become(other) {
1572
- if (other instanceof BlockWidgetView &&
1573
- other.widget.constructor == this.widget.constructor) {
1574
- if (!other.widget.compare(this.widget))
1575
- this.markDirty(true);
1576
- if (this.dom && !this.prevWidget)
1577
- this.prevWidget = this.widget;
1578
- this.widget = other.widget;
1579
- this.length = other.length;
1580
- this.type = other.type;
1581
- this.breakAfter = other.breakAfter;
1582
- return true;
1583
- }
1584
- return false;
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;
1585
1561
  }
1586
- ignoreMutation() { return true; }
1587
- ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1588
- get isEditable() { return false; }
1589
- get isWidget() { return true; }
1590
- coordsAt(pos, side) {
1591
- return this.widget.coordsAt(this.dom, pos, side);
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;
1592
1566
  }
1593
- destroy() {
1594
- super.destroy();
1595
- if (this.dom)
1596
- this.widget.destroy(this.dom);
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);
1597
1582
  }
1598
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
+ }
1599
1603
 
1600
1604
  class ContentBuilder {
1601
1605
  constructor(doc, pos, end, disallowBlockEffectsFor) {
@@ -1621,7 +1625,7 @@ class ContentBuilder {
1621
1625
  if (this.content.length == 0)
1622
1626
  return !this.breakAtStart && this.doc.lineAt(this.pos).from != this.pos;
1623
1627
  let last = this.content[this.content.length - 1];
1624
- return !last.breakAfter && !(last instanceof BlockWidgetView && last.type == BlockType.WidgetBefore);
1628
+ return !(last.breakAfter || last instanceof BlockWidgetView && last.deco.endSide < 0);
1625
1629
  }
1626
1630
  getLine() {
1627
1631
  if (!this.curLine) {
@@ -1646,7 +1650,7 @@ class ContentBuilder {
1646
1650
  this.flushBuffer();
1647
1651
  else
1648
1652
  this.pendingBuffer = 0 /* Buf.No */;
1649
- if (!this.posCovered())
1653
+ if (!openEnd && !this.posCovered())
1650
1654
  this.getLine();
1651
1655
  }
1652
1656
  buildText(length, active, openStart) {
@@ -1699,10 +1703,9 @@ class ContentBuilder {
1699
1703
  let len = to - from;
1700
1704
  if (deco instanceof PointDecoration) {
1701
1705
  if (deco.block) {
1702
- let { type } = deco;
1703
- if (type == BlockType.WidgetAfter && !this.posCovered())
1706
+ if (deco.startSide > 0 && !this.posCovered())
1704
1707
  this.getLine();
1705
- this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, type));
1708
+ this.addBlockWidget(new BlockWidgetView(deco.widget || new NullWidget("div"), len, deco));
1706
1709
  }
1707
1710
  else {
1708
1711
  let view = WidgetView.create(deco.widget || new NullWidget("span"), len, len ? 0 : deco.startSide);
@@ -1837,10 +1840,15 @@ class ViewPlugin {
1837
1840
  /**
1838
1841
  @internal
1839
1842
  */
1840
- domEventHandlers, buildExtensions) {
1843
+ domEventHandlers,
1844
+ /**
1845
+ @internal
1846
+ */
1847
+ domEventObservers, buildExtensions) {
1841
1848
  this.id = id;
1842
1849
  this.create = create;
1843
1850
  this.domEventHandlers = domEventHandlers;
1851
+ this.domEventObservers = domEventObservers;
1844
1852
  this.extension = buildExtensions(this);
1845
1853
  }
1846
1854
  /**
@@ -1848,8 +1856,8 @@ class ViewPlugin {
1848
1856
  plugin's value, given an editor view.
1849
1857
  */
1850
1858
  static define(create, spec) {
1851
- const { eventHandlers, provide, decorations: deco } = spec || {};
1852
- return new ViewPlugin(nextPluginID++, create, eventHandlers, plugin => {
1859
+ const { eventHandlers, eventObservers, provide, decorations: deco } = spec || {};
1860
+ return new ViewPlugin(nextPluginID++, create, eventHandlers, eventObservers, plugin => {
1853
1861
  let ext = [viewPlugin.of(plugin)];
1854
1862
  if (deco)
1855
1863
  ext.push(decorations.of(view => {
@@ -2934,15 +2942,19 @@ class DocView extends ContentView {
2934
2942
  return this.children[i].domAtPos(off);
2935
2943
  }
2936
2944
  coordsAt(pos, side) {
2937
- for (let off = this.length, i = this.children.length - 1;; i--) {
2938
- let child = this.children[i], start = off - child.breakAfter - child.length;
2939
- if (pos > start ||
2940
- (pos == start && child.type != BlockType.WidgetBefore && child.type != BlockType.WidgetAfter &&
2941
- (!i || side == 2 || this.children[i - 1].breakAfter ||
2942
- (this.children[i - 1].type == BlockType.WidgetBefore && side > -2))))
2943
- 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
+ }
2944
2955
  off = start;
2945
2956
  }
2957
+ return best ? best.coordsAt(pos - bestPos, side) : null;
2946
2958
  }
2947
2959
  coordsForChar(pos) {
2948
2960
  let { i, off } = this.childPos(pos, 1), child = this.children[i];
@@ -3528,7 +3540,7 @@ function moveVertically(view, start, forward, distance) {
3528
3540
  return EditorSelection.cursor(startPos, start.assoc);
3529
3541
  let goal = start.goalColumn, startY;
3530
3542
  let rect = view.contentDOM.getBoundingClientRect();
3531
- let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
3543
+ let startCoords = view.coordsAtPos(startPos, start.assoc || -1), docTop = view.documentTop;
3532
3544
  if (startCoords) {
3533
3545
  if (goal == null)
3534
3546
  goal = startCoords.left - rect.left;
@@ -3545,8 +3557,11 @@ function moveVertically(view, start, forward, distance) {
3545
3557
  for (let extra = 0;; extra += 10) {
3546
3558
  let curY = startY + (dist + extra) * dir;
3547
3559
  let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
3548
- if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos))
3549
- return EditorSelection.cursor(pos, start.assoc, undefined, goal);
3560
+ if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos)) {
3561
+ let charRect = view.docView.coordsForChar(pos);
3562
+ let assoc = !charRect || curY < charRect.top ? -1 : 1;
3563
+ return EditorSelection.cursor(pos, assoc, undefined, goal);
3564
+ }
3550
3565
  }
3551
3566
  }
3552
3567
  function skipAtomicRanges(atoms, pos, bias) {
@@ -3577,13 +3592,13 @@ class InputState {
3577
3592
  this.lastSelectionTime = Date.now();
3578
3593
  }
3579
3594
  constructor(view) {
3595
+ this.view = view;
3580
3596
  this.lastKeyCode = 0;
3581
3597
  this.lastKeyTime = 0;
3582
3598
  this.lastTouchTime = 0;
3583
3599
  this.lastFocusTime = 0;
3584
3600
  this.lastScrollTop = 0;
3585
3601
  this.lastScrollLeft = 0;
3586
- this.chromeScrollHack = -1;
3587
3602
  // On iOS, some keys need to have their default behavior happen
3588
3603
  // (after which we retroactively handle them and reset the DOM) to
3589
3604
  // avoid messing up the virtual keyboard state.
@@ -3593,8 +3608,7 @@ class InputState {
3593
3608
  this.lastEscPress = 0;
3594
3609
  this.lastContextMenu = 0;
3595
3610
  this.scrollHandlers = [];
3596
- this.registeredEvents = [];
3597
- this.customHandlers = [];
3611
+ this.handlers = Object.create(null);
3598
3612
  // -1 means not in a composition. Otherwise, this counts the number
3599
3613
  // of changes made during the composition. The count is used to
3600
3614
  // avoid treating the start state of the composition, before any
@@ -3615,29 +3629,10 @@ class InputState {
3615
3629
  // the mutation events fire shortly after the compositionend event
3616
3630
  this.compositionPendingChange = false;
3617
3631
  this.mouseSelection = null;
3618
- let handleEvent = (handler, event) => {
3619
- if (this.ignoreDuringComposition(event))
3620
- return;
3621
- if (event.type == "keydown" && this.keydown(view, event))
3622
- return;
3623
- if (this.mustFlushObserver(event))
3624
- view.observer.forceFlush();
3625
- if (this.runCustomHandlers(event.type, view, event))
3626
- event.preventDefault();
3627
- else
3628
- handler(view, event);
3629
- };
3630
- for (let type in handlers) {
3631
- let handler = handlers[type];
3632
- view.contentDOM.addEventListener(type, event => {
3633
- if (eventBelongsToEditor(view, event))
3634
- handleEvent(handler, event);
3635
- }, handlerOptions[type]);
3636
- this.registeredEvents.push(type);
3637
- }
3632
+ this.handleEvent = this.handleEvent.bind(this);
3638
3633
  view.scrollDOM.addEventListener("mousedown", (event) => {
3639
3634
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom) {
3640
- handleEvent(handlers.mousedown, event);
3635
+ this.runHandlers("mousedown", event);
3641
3636
  if (!event.defaultPrevented && event.button == 2) {
3642
3637
  // Make sure the content covers the entire scroller height, in order
3643
3638
  // to catch a native context menu click below it
@@ -3649,23 +3644,8 @@ class InputState {
3649
3644
  });
3650
3645
  view.scrollDOM.addEventListener("drop", (event) => {
3651
3646
  if (event.target == view.scrollDOM && event.clientY > view.contentDOM.getBoundingClientRect().bottom)
3652
- handleEvent(handlers.drop, event);
3647
+ this.runHandlers("drop", event);
3653
3648
  });
3654
- if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3655
- // On Chrome 102, viewport updates somehow stop wheel-based
3656
- // scrolling. Turning off pointer events during the scroll seems
3657
- // to avoid the issue.
3658
- view.scrollDOM.addEventListener("wheel", () => {
3659
- if (this.chromeScrollHack < 0)
3660
- view.contentDOM.style.pointerEvents = "none";
3661
- else
3662
- window.clearTimeout(this.chromeScrollHack);
3663
- this.chromeScrollHack = setTimeout(() => {
3664
- this.chromeScrollHack = -1;
3665
- view.contentDOM.style.pointerEvents = "";
3666
- }, 100);
3667
- }, { passive: true });
3668
- }
3669
3649
  this.notifiedFocused = view.hasFocus;
3670
3650
  // On Safari adding an input event handler somehow prevents an
3671
3651
  // issue where the composition vanishes when you press enter.
@@ -3674,63 +3654,54 @@ class InputState {
3674
3654
  if (browser.gecko)
3675
3655
  firefoxCopyCutHack(view.contentDOM.ownerDocument);
3676
3656
  }
3677
- ensureHandlers(view, plugins) {
3678
- var _a;
3679
- let handlers;
3680
- this.customHandlers = [];
3681
- for (let plugin of plugins)
3682
- if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
3683
- this.customHandlers.push({ plugin: plugin.value, handlers });
3684
- for (let type in handlers)
3685
- if (this.registeredEvents.indexOf(type) < 0 && type != "scroll") {
3686
- this.registeredEvents.push(type);
3687
- view.contentDOM.addEventListener(type, (event) => {
3688
- if (!eventBelongsToEditor(view, event))
3689
- return;
3690
- if (this.runCustomHandlers(type, view, event))
3691
- event.preventDefault();
3692
- });
3693
- }
3694
- }
3695
- }
3696
- runCustomHandlers(type, view, event) {
3697
- for (let set of this.customHandlers) {
3698
- let handler = set.handlers[type];
3699
- if (handler) {
3700
- try {
3701
- if (handler.call(set.plugin, event, view) || event.defaultPrevented)
3702
- return true;
3703
- }
3704
- catch (e) {
3705
- logException(view.state, e);
3657
+ handleEvent(event) {
3658
+ if (!eventBelongsToEditor(this.view, event) || this.ignoreDuringComposition(event))
3659
+ return;
3660
+ if (event.type == "keydown" && this.keydown(event))
3661
+ return;
3662
+ this.runHandlers(event.type, event);
3663
+ }
3664
+ runHandlers(type, event) {
3665
+ let handlers = this.handlers[type];
3666
+ if (handlers) {
3667
+ for (let observer of handlers.observers)
3668
+ observer(this.view, event);
3669
+ for (let handler of handlers.handlers) {
3670
+ if (event.defaultPrevented)
3671
+ break;
3672
+ if (handler(this.view, event)) {
3673
+ event.preventDefault();
3674
+ break;
3706
3675
  }
3707
3676
  }
3708
3677
  }
3709
- return false;
3710
3678
  }
3711
- runScrollHandlers(view, event) {
3712
- this.lastScrollTop = view.scrollDOM.scrollTop;
3713
- this.lastScrollLeft = view.scrollDOM.scrollLeft;
3714
- for (let set of this.customHandlers) {
3715
- let handler = set.handlers.scroll;
3716
- if (handler) {
3717
- try {
3718
- handler.call(set.plugin, event, view);
3719
- }
3720
- catch (e) {
3721
- logException(view.state, e);
3679
+ ensureHandlers(plugins) {
3680
+ let handlers = computeHandlers(plugins), prev = this.handlers, dom = this.view.contentDOM;
3681
+ for (let type in handlers)
3682
+ if (type != "scroll") {
3683
+ let passive = !handlers[type].handlers.length;
3684
+ let exists = prev[type];
3685
+ if (exists && passive != !exists.handlers.length) {
3686
+ dom.removeEventListener(type, this.handleEvent);
3687
+ exists = null;
3722
3688
  }
3689
+ if (!exists)
3690
+ dom.addEventListener(type, this.handleEvent, { passive });
3723
3691
  }
3724
- }
3692
+ for (let type in prev)
3693
+ if (type != "scroll" && !handlers[type])
3694
+ dom.removeEventListener(type, this.handleEvent);
3695
+ this.handlers = handlers;
3725
3696
  }
3726
- keydown(view, event) {
3697
+ keydown(event) {
3727
3698
  // Must always run, even if a custom handler handled the event
3728
3699
  this.lastKeyCode = event.keyCode;
3729
3700
  this.lastKeyTime = Date.now();
3730
3701
  if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
3731
3702
  return true;
3732
3703
  if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3733
- view.inputState.lastEscPress = 0;
3704
+ this.view.inputState.lastEscPress = 0;
3734
3705
  // Chrome for Android usually doesn't fire proper key events, but
3735
3706
  // occasionally does, usually surrounded by a bunch of complicated
3736
3707
  // composition changes. When an enter or backspace key event is
@@ -3738,10 +3709,10 @@ class InputState {
3738
3709
  // dispatch it.
3739
3710
  if (browser.android && browser.chrome && !event.synthetic &&
3740
3711
  (event.keyCode == 13 || event.keyCode == 8)) {
3741
- view.observer.delayAndroidKey(event.key, event.keyCode);
3712
+ this.view.observer.delayAndroidKey(event.key, event.keyCode);
3742
3713
  return true;
3743
3714
  }
3744
- // Prevent the default behavior of Enter on iOS makes the
3715
+ // Preventing the default behavior of Enter on iOS makes the
3745
3716
  // virtual keyboard get stuck in the wrong (lowercase)
3746
3717
  // state. So we let it go through, and then, in
3747
3718
  // applyDOMChange, notify key handlers of it and reset to
@@ -3751,17 +3722,19 @@ class InputState {
3751
3722
  ((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
3752
3723
  EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey && !event.shiftKey)) {
3753
3724
  this.pendingIOSKey = pending || event;
3754
- setTimeout(() => this.flushIOSKey(view), 250);
3725
+ setTimeout(() => this.flushIOSKey(), 250);
3755
3726
  return true;
3756
3727
  }
3728
+ if (event.keyCode != 229)
3729
+ this.view.observer.forceFlush();
3757
3730
  return false;
3758
3731
  }
3759
- flushIOSKey(view) {
3732
+ flushIOSKey() {
3760
3733
  let key = this.pendingIOSKey;
3761
3734
  if (!key)
3762
3735
  return false;
3763
3736
  this.pendingIOSKey = undefined;
3764
- return dispatchKey(view.contentDOM, key.key, key.keyCode);
3737
+ return dispatchKey(this.view.contentDOM, key.key, key.keyCode);
3765
3738
  }
3766
3739
  ignoreDuringComposition(event) {
3767
3740
  if (!/^key/.test(event.type))
@@ -3780,9 +3753,6 @@ class InputState {
3780
3753
  }
3781
3754
  return false;
3782
3755
  }
3783
- mustFlushObserver(event) {
3784
- return event.type == "keydown" && event.keyCode != 229;
3785
- }
3786
3756
  startMouseSelection(mouseSelection) {
3787
3757
  if (this.mouseSelection)
3788
3758
  this.mouseSelection.destroy();
@@ -3799,6 +3769,42 @@ class InputState {
3799
3769
  this.mouseSelection.destroy();
3800
3770
  }
3801
3771
  }
3772
+ function bindHandler(plugin, handler) {
3773
+ return (view, event) => {
3774
+ try {
3775
+ return handler.call(plugin, event, view);
3776
+ }
3777
+ catch (e) {
3778
+ logException(view.state, e);
3779
+ }
3780
+ };
3781
+ }
3782
+ function computeHandlers(plugins) {
3783
+ let result = Object.create(null);
3784
+ function record(type) {
3785
+ return result[type] || (result[type] = { observers: [], handlers: [] });
3786
+ }
3787
+ for (let plugin of plugins) {
3788
+ let spec = plugin.spec;
3789
+ if (spec && spec.domEventHandlers)
3790
+ for (let type in spec.domEventHandlers) {
3791
+ let f = spec.domEventHandlers[type];
3792
+ if (f)
3793
+ record(type).handlers.push(bindHandler(plugin.value, f));
3794
+ }
3795
+ if (spec && spec.domEventObservers)
3796
+ for (let type in spec.domEventObservers) {
3797
+ let f = spec.domEventObservers[type];
3798
+ if (f)
3799
+ record(type).observers.push(bindHandler(plugin.value, f));
3800
+ }
3801
+ }
3802
+ for (let type in handlers)
3803
+ record(type).handlers.push(handlers[type]);
3804
+ for (let type in observers)
3805
+ record(type).observers.push(observers[type]);
3806
+ return result;
3807
+ }
3802
3808
  const PendingKeys = [
3803
3809
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3804
3810
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
@@ -3836,10 +3842,8 @@ class MouseSelection {
3836
3842
  start(event) {
3837
3843
  // When clicking outside of the selection, immediately apply the
3838
3844
  // effect of starting the selection
3839
- if (this.dragging === false) {
3840
- event.preventDefault();
3845
+ if (this.dragging === false)
3841
3846
  this.select(event);
3842
- }
3843
3847
  }
3844
3848
  move(event) {
3845
3849
  var _a;
@@ -3975,7 +3979,7 @@ function eventBelongsToEditor(view, event) {
3975
3979
  return true;
3976
3980
  }
3977
3981
  const handlers = /*@__PURE__*/Object.create(null);
3978
- const handlerOptions = /*@__PURE__*/Object.create(null);
3982
+ const observers = /*@__PURE__*/Object.create(null);
3979
3983
  // This is very crude, but unfortunately both these browsers _pretend_
3980
3984
  // that they have a clipboard API—all the objects and methods are
3981
3985
  // there, they just don't work, and they are hard to test.
@@ -4025,23 +4029,27 @@ function doPaste(view, input) {
4025
4029
  scrollIntoView: true
4026
4030
  });
4027
4031
  }
4032
+ observers.scroll = view => {
4033
+ view.inputState.lastScrollTop = view.scrollDOM.scrollTop;
4034
+ view.inputState.lastScrollLeft = view.scrollDOM.scrollLeft;
4035
+ };
4028
4036
  handlers.keydown = (view, event) => {
4029
4037
  view.inputState.setSelectionOrigin("select");
4030
4038
  if (event.keyCode == 27)
4031
4039
  view.inputState.lastEscPress = Date.now();
4040
+ return false;
4032
4041
  };
4033
- handlers.touchstart = (view, e) => {
4042
+ observers.touchstart = (view, e) => {
4034
4043
  view.inputState.lastTouchTime = Date.now();
4035
4044
  view.inputState.setSelectionOrigin("select.pointer");
4036
4045
  };
4037
- handlers.touchmove = view => {
4046
+ observers.touchmove = view => {
4038
4047
  view.inputState.setSelectionOrigin("select.pointer");
4039
4048
  };
4040
- handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
4041
4049
  handlers.mousedown = (view, event) => {
4042
4050
  view.observer.flush();
4043
4051
  if (view.inputState.lastTouchTime > Date.now() - 2000)
4044
- return; // Ignore touch interaction
4052
+ return false; // Ignore touch interaction
4045
4053
  let style = null;
4046
4054
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
4047
4055
  style = makeStyle(view, event);
@@ -4055,9 +4063,13 @@ handlers.mousedown = (view, event) => {
4055
4063
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
4056
4064
  if (mustFocus)
4057
4065
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
4058
- if (view.inputState.mouseSelection)
4059
- view.inputState.mouseSelection.start(event);
4066
+ let mouseSel = view.inputState.mouseSelection;
4067
+ if (mouseSel) {
4068
+ mouseSel.start(event);
4069
+ return !mouseSel.dragging;
4070
+ }
4060
4071
  }
4072
+ return false;
4061
4073
  };
4062
4074
  function rangeForClick(view, pos, bias, type) {
4063
4075
  if (type == 1) { // Single click
@@ -4161,12 +4173,12 @@ handlers.dragstart = (view, event) => {
4161
4173
  event.dataTransfer.setData("Text", view.state.sliceDoc(main.from, main.to));
4162
4174
  event.dataTransfer.effectAllowed = "copyMove";
4163
4175
  }
4176
+ return false;
4164
4177
  };
4165
4178
  function dropText(view, event, text, direct) {
4166
4179
  if (!text)
4167
4180
  return;
4168
4181
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4169
- event.preventDefault();
4170
4182
  let { mouseSelection } = view.inputState;
4171
4183
  let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4172
4184
  { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
@@ -4181,12 +4193,11 @@ function dropText(view, event, text, direct) {
4181
4193
  }
4182
4194
  handlers.drop = (view, event) => {
4183
4195
  if (!event.dataTransfer)
4184
- return;
4196
+ return false;
4185
4197
  if (view.state.readOnly)
4186
- return event.preventDefault();
4198
+ return true;
4187
4199
  let files = event.dataTransfer.files;
4188
4200
  if (files && files.length) { // For a file drop, read the file's text.
4189
- event.preventDefault();
4190
4201
  let text = Array(files.length), read = 0;
4191
4202
  let finishFile = () => {
4192
4203
  if (++read == files.length)
@@ -4202,22 +4213,29 @@ handlers.drop = (view, event) => {
4202
4213
  };
4203
4214
  reader.readAsText(files[i]);
4204
4215
  }
4216
+ return true;
4205
4217
  }
4206
4218
  else {
4207
- dropText(view, event, event.dataTransfer.getData("Text"), true);
4219
+ let text = event.dataTransfer.getData("Text");
4220
+ if (text) {
4221
+ dropText(view, event, text, true);
4222
+ return true;
4223
+ }
4208
4224
  }
4225
+ return false;
4209
4226
  };
4210
4227
  handlers.paste = (view, event) => {
4211
4228
  if (view.state.readOnly)
4212
- return event.preventDefault();
4229
+ return true;
4213
4230
  view.observer.flush();
4214
4231
  let data = brokenClipboardAPI ? null : event.clipboardData;
4215
4232
  if (data) {
4216
4233
  doPaste(view, data.getData("text/plain") || data.getData("text/uri-text"));
4217
- event.preventDefault();
4234
+ return true;
4218
4235
  }
4219
4236
  else {
4220
4237
  capturePaste(view);
4238
+ return false;
4221
4239
  }
4222
4240
  };
4223
4241
  function captureCopy(view, text) {
@@ -4263,23 +4281,24 @@ let lastLinewiseCopy = null;
4263
4281
  handlers.copy = handlers.cut = (view, event) => {
4264
4282
  let { text, ranges, linewise } = copiedRange(view.state);
4265
4283
  if (!text && !linewise)
4266
- return;
4284
+ return false;
4267
4285
  lastLinewiseCopy = linewise ? text : null;
4286
+ if (event.type == "cut" && !view.state.readOnly)
4287
+ view.dispatch({
4288
+ changes: ranges,
4289
+ scrollIntoView: true,
4290
+ userEvent: "delete.cut"
4291
+ });
4268
4292
  let data = brokenClipboardAPI ? null : event.clipboardData;
4269
4293
  if (data) {
4270
- event.preventDefault();
4271
4294
  data.clearData();
4272
4295
  data.setData("text/plain", text);
4296
+ return true;
4273
4297
  }
4274
4298
  else {
4275
4299
  captureCopy(view, text);
4300
+ return false;
4276
4301
  }
4277
- if (event.type == "cut" && !view.state.readOnly)
4278
- view.dispatch({
4279
- changes: ranges,
4280
- scrollIntoView: true,
4281
- userEvent: "delete.cut"
4282
- });
4283
4302
  };
4284
4303
  const isFocusChange = /*@__PURE__*/Annotation.define();
4285
4304
  function focusChangeTransaction(state, focus) {
@@ -4303,7 +4322,7 @@ function updateForFocusChange(view) {
4303
4322
  }
4304
4323
  }, 10);
4305
4324
  }
4306
- handlers.focus = view => {
4325
+ observers.focus = view => {
4307
4326
  view.inputState.lastFocusTime = Date.now();
4308
4327
  // When focusing reset the scroll position, move it back to where it was
4309
4328
  if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
@@ -4312,11 +4331,11 @@ handlers.focus = view => {
4312
4331
  }
4313
4332
  updateForFocusChange(view);
4314
4333
  };
4315
- handlers.blur = view => {
4334
+ observers.blur = view => {
4316
4335
  view.observer.clearSelectionRange();
4317
4336
  updateForFocusChange(view);
4318
4337
  };
4319
- handlers.compositionstart = handlers.compositionupdate = view => {
4338
+ observers.compositionstart = observers.compositionupdate = view => {
4320
4339
  if (view.inputState.compositionFirstChange == null)
4321
4340
  view.inputState.compositionFirstChange = true;
4322
4341
  if (view.inputState.composing < 0) {
@@ -4324,7 +4343,7 @@ handlers.compositionstart = handlers.compositionupdate = view => {
4324
4343
  view.inputState.composing = 0;
4325
4344
  }
4326
4345
  };
4327
- handlers.compositionend = view => {
4346
+ observers.compositionend = view => {
4328
4347
  view.inputState.composing = -1;
4329
4348
  view.inputState.compositionEndedAt = Date.now();
4330
4349
  view.inputState.compositionPendingKey = true;
@@ -4348,7 +4367,7 @@ handlers.compositionend = view => {
4348
4367
  }, 50);
4349
4368
  }
4350
4369
  };
4351
- handlers.contextmenu = view => {
4370
+ observers.contextmenu = view => {
4352
4371
  view.inputState.lastContextMenu = Date.now();
4353
4372
  };
4354
4373
  handlers.beforeinput = (view, event) => {
@@ -4377,6 +4396,7 @@ handlers.beforeinput = (view, event) => {
4377
4396
  }, 100);
4378
4397
  }
4379
4398
  }
4399
+ return false;
4380
4400
  };
4381
4401
  const appliedFirefoxHack = /*@__PURE__*/new Set;
4382
4402
  // In Firefox, when cut/copy handlers are added to the document, that
@@ -5046,14 +5066,13 @@ class NodeBuilder {
5046
5066
  return line;
5047
5067
  }
5048
5068
  addBlock(block) {
5049
- var _a;
5050
5069
  this.enterLine();
5051
- let type = (_a = block.deco) === null || _a === void 0 ? void 0 : _a.type;
5052
- if (type == BlockType.WidgetAfter && !this.isCovered)
5070
+ let deco = block.deco;
5071
+ if (deco && deco.startSide > 0 && !this.isCovered)
5053
5072
  this.ensureLine();
5054
5073
  this.nodes.push(block);
5055
5074
  this.writtenTo = this.pos = this.pos + block.length;
5056
- if (type != BlockType.WidgetBefore)
5075
+ if (deco && deco.endSide > 0)
5057
5076
  this.covering = block;
5058
5077
  }
5059
5078
  addLineDeco(height, breaks, length) {
@@ -6161,7 +6180,7 @@ function applyDOMChange(view, domChange) {
6161
6180
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
6162
6181
  }
6163
6182
  if (change) {
6164
- if (browser.ios && view.inputState.flushIOSKey(view))
6183
+ if (browser.ios && view.inputState.flushIOSKey())
6165
6184
  return true;
6166
6185
  // Android browsers don't fire reasonable key events for enter,
6167
6186
  // backspace, or delete. So this detects changes that look like
@@ -6415,7 +6434,7 @@ class DOMObserver {
6415
6434
  this.readSelectionRange();
6416
6435
  }
6417
6436
  onScrollChanged(e) {
6418
- this.view.inputState.runScrollHandlers(this.view, e);
6437
+ this.view.inputState.runHandlers("scroll", e);
6419
6438
  if (this.intersecting)
6420
6439
  this.view.measure();
6421
6440
  }
@@ -6886,7 +6905,7 @@ class EditorView {
6886
6905
  plugin.update(this);
6887
6906
  this.observer = new DOMObserver(this);
6888
6907
  this.inputState = new InputState(this);
6889
- this.inputState.ensureHandlers(this, this.plugins);
6908
+ this.inputState.ensureHandlers(this.plugins);
6890
6909
  this.docView = new DocView(this);
6891
6910
  this.mountStyles();
6892
6911
  this.updateAttrs();
@@ -7028,7 +7047,7 @@ class EditorView {
7028
7047
  for (let plugin of this.plugins)
7029
7048
  plugin.update(this);
7030
7049
  this.docView = new DocView(this);
7031
- this.inputState.ensureHandlers(this, this.plugins);
7050
+ this.inputState.ensureHandlers(this.plugins);
7032
7051
  this.mountStyles();
7033
7052
  this.updateAttrs();
7034
7053
  this.bidiCache = [];
@@ -7060,7 +7079,7 @@ class EditorView {
7060
7079
  plugin.destroy(this);
7061
7080
  this.plugins = newPlugins;
7062
7081
  this.pluginMap.clear();
7063
- this.inputState.ensureHandlers(this, this.plugins);
7082
+ this.inputState.ensureHandlers(this.plugins);
7064
7083
  }
7065
7084
  else {
7066
7085
  for (let p of this.plugins)
@@ -7588,6 +7607,17 @@ class EditorView {
7588
7607
  return ViewPlugin.define(() => ({}), { eventHandlers: handlers });
7589
7608
  }
7590
7609
  /**
7610
+ Create an extension that registers DOM event observers. Contrary
7611
+ to event [handlers](https://codemirror.net/6/docs/ref/#view.EditorView^domEventHandlers),
7612
+ observers can't be prevented from running by a higher-precedence
7613
+ handler returning true. They also don't prevent other handlers
7614
+ and observers from running when they return true, and should not
7615
+ call `preventDefault`.
7616
+ */
7617
+ static domEventObservers(observers) {
7618
+ return ViewPlugin.define(() => ({}), { eventObservers: observers });
7619
+ }
7620
+ /**
7591
7621
  Create a theme extension. The first argument can be a
7592
7622
  [`style-mod`](https://github.com/marijnh/style-mod#documentation)
7593
7623
  style spec providing the styles for the theme. These will be
@@ -8314,6 +8344,14 @@ function drawSelection(config = {}) {
8314
8344
  nativeSelectionHidden.of(true)
8315
8345
  ];
8316
8346
  }
8347
+ /**
8348
+ Retrieve the [`drawSelection`](https://codemirror.net/6/docs/ref/#view.drawSelection) configuration
8349
+ for this state. (Note that this will return a set of defaults even
8350
+ if `drawSelection` isn't enabled.)
8351
+ */
8352
+ function getDrawSelectionConfig(state) {
8353
+ return state.facet(selectionConfig);
8354
+ }
8317
8355
  function configChanged(update) {
8318
8356
  return update.startState.facet(selectionConfig) != update.state.facet(selectionConfig);
8319
8357
  }
@@ -8440,7 +8478,7 @@ const drawDropCursor = /*@__PURE__*/ViewPlugin.fromClass(class {
8440
8478
  this.view.dispatch({ effects: setDropCursorPos.of(pos) });
8441
8479
  }
8442
8480
  }, {
8443
- eventHandlers: {
8481
+ eventObservers: {
8444
8482
  dragover(event) {
8445
8483
  this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
8446
8484
  },
@@ -8950,7 +8988,7 @@ function crosshairCursor(options = {}) {
8950
8988
  }
8951
8989
  }
8952
8990
  }, {
8953
- eventHandlers: {
8991
+ eventObservers: {
8954
8992
  keydown(e) {
8955
8993
  this.set(e.keyCode == code || getter(e));
8956
8994
  },
@@ -9138,6 +9176,8 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9138
9176
  tooltipView.dom.remove();
9139
9177
  (_a = tooltipView.destroy) === null || _a === void 0 ? void 0 : _a.call(tooltipView);
9140
9178
  }
9179
+ if (this.parent)
9180
+ this.container.remove();
9141
9181
  (_b = this.intersectionObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
9142
9182
  clearTimeout(this.measureTimeout);
9143
9183
  }
@@ -9261,7 +9301,7 @@ const tooltipPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
9261
9301
  }
9262
9302
  }
9263
9303
  }, {
9264
- eventHandlers: {
9304
+ eventObservers: {
9265
9305
  scroll() { this.maybeMeasure(); }
9266
9306
  }
9267
9307
  });
@@ -9579,8 +9619,9 @@ re-positioning or CSS change affecting the editor) that could
9579
9619
  invalidate the existing tooltip positions.
9580
9620
  */
9581
9621
  function repositionTooltips(view) {
9582
- var _a;
9583
- (_a = view.plugin(tooltipPlugin)) === null || _a === void 0 ? void 0 : _a.maybeMeasure();
9622
+ let plugin = view.plugin(tooltipPlugin);
9623
+ if (plugin)
9624
+ plugin.maybeMeasure();
9584
9625
  }
9585
9626
 
9586
9627
  const panelConfig = /*@__PURE__*/Facet.define({
@@ -10283,4 +10324,4 @@ function highlightTrailingWhitespace() {
10283
10324
  */
10284
10325
  const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
10285
10326
 
10286
- export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, RectangleMarker, ViewPlugin, ViewUpdate, WidgetType, __test, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };
10327
+ export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, GutterMarker, MatchDecorator, RectangleMarker, ViewPlugin, ViewUpdate, WidgetType, __test, closeHoverTooltips, crosshairCursor, drawSelection, dropCursor, getDrawSelectionConfig, getPanel, getTooltip, gutter, gutterLineClass, gutters, hasHoverTooltips, highlightActiveLine, highlightActiveLineGutter, highlightSpecialChars, highlightTrailingWhitespace, highlightWhitespace, hoverTooltip, keymap, layer, lineNumberMarkers, lineNumbers, logException, panels, placeholder, rectangularSelection, repositionTooltips, runScopeHandlers, scrollPastEnd, showPanel, showTooltip, tooltips };