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