@codemirror/view 6.39.16 → 6.40.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 +24 -0
- package/dist/index.cjs +83 -25
- package/dist/index.d.cts +7 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +83 -25
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## 6.40.0 (2026-03-12)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix a bug that caused Shift-Enter/Backspace/Delete on iOS to lose the shift modifier when delivered to key event handlers.
|
|
6
|
+
|
|
7
|
+
Fix an issue where `EditorView.moveVertically` could move to the wrong place in wrapped lines with a large line height.
|
|
8
|
+
|
|
9
|
+
Make sure the selection head associativity is properly set for mouse selections made with shift held down.
|
|
10
|
+
|
|
11
|
+
### New features
|
|
12
|
+
|
|
13
|
+
`WidgetType.updateDOM` is now called with the previous widget value as third argument.
|
|
14
|
+
|
|
15
|
+
## 6.39.17 (2026-03-10)
|
|
16
|
+
|
|
17
|
+
### Bug fixes
|
|
18
|
+
|
|
19
|
+
Improve touch tap-selection on line wrapping boundaries.
|
|
20
|
+
|
|
21
|
+
Make `drawSelection` draw our own selection handles on iOS.
|
|
22
|
+
|
|
23
|
+
Fix an issue where `posAtCoords`, when querying line wrapping points, got confused by extra empty client rectangles produced by Safari.
|
|
24
|
+
|
|
1
25
|
## 6.39.16 (2026-03-02)
|
|
2
26
|
|
|
3
27
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -134,7 +134,7 @@ class WidgetType {
|
|
|
134
134
|
couldn't (in which case the widget will be redrawn). The default
|
|
135
135
|
implementation just returns false.
|
|
136
136
|
*/
|
|
137
|
-
updateDOM(dom, view) { return false; }
|
|
137
|
+
updateDOM(dom, view, from) { return false; }
|
|
138
138
|
/**
|
|
139
139
|
@internal
|
|
140
140
|
*/
|
|
@@ -2539,7 +2539,7 @@ class TileCache {
|
|
|
2539
2539
|
let tile = widgets[i];
|
|
2540
2540
|
if (!this.reused.has(tile) &&
|
|
2541
2541
|
(pass == 0 ? tile.widget.compare(widget)
|
|
2542
|
-
: tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view))) {
|
|
2542
|
+
: tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view, tile.widget))) {
|
|
2543
2543
|
widgets.splice(i, 1);
|
|
2544
2544
|
if (i < this.index[0])
|
|
2545
2545
|
this.index[0]--;
|
|
@@ -3395,6 +3395,7 @@ class DocView {
|
|
|
3395
3395
|
this.blockWrappers = this.view.state.facet(blockWrappers).map(v => typeof v == "function" ? v(this.view) : v);
|
|
3396
3396
|
}
|
|
3397
3397
|
scrollIntoView(target) {
|
|
3398
|
+
var _a;
|
|
3398
3399
|
if (target.isSnapshot) {
|
|
3399
3400
|
let ref = this.view.viewState.lineBlockAt(target.range.head);
|
|
3400
3401
|
this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
|
|
@@ -3411,7 +3412,7 @@ class DocView {
|
|
|
3411
3412
|
}
|
|
3412
3413
|
}
|
|
3413
3414
|
let { range } = target;
|
|
3414
|
-
let rect = this.coordsAt(range.head, range.
|
|
3415
|
+
let rect = this.coordsAt(range.head, (_a = range.assoc) !== null && _a !== void 0 ? _a : (range.empty ? 0 : range.head > range.anchor ? -1 : 1)), other;
|
|
3415
3416
|
if (!rect)
|
|
3416
3417
|
return;
|
|
3417
3418
|
if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
|
|
@@ -3678,7 +3679,8 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3678
3679
|
return state.EditorSelection.cursor(startPos, start.assoc);
|
|
3679
3680
|
let goal = start.goalColumn, startY;
|
|
3680
3681
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3681
|
-
let startCoords = view.coordsAtPos(startPos, (start.empty ? start.
|
|
3682
|
+
let startCoords = view.coordsAtPos(startPos, start.assoc || ((start.empty ? forward : start.head == start.from) ? 1 : -1));
|
|
3683
|
+
let docTop = view.documentTop;
|
|
3682
3684
|
if (startCoords) {
|
|
3683
3685
|
if (goal == null)
|
|
3684
3686
|
goal = startCoords.left - rect.left;
|
|
@@ -3691,9 +3693,16 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3691
3693
|
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3692
3694
|
}
|
|
3693
3695
|
let resolvedGoal = rect.left + goal;
|
|
3694
|
-
let dist = distance !== null && distance !== void 0 ? distance :
|
|
3695
|
-
let
|
|
3696
|
-
|
|
3696
|
+
let halfText = view.viewState.heightOracle.textHeight >> 1, dist = distance !== null && distance !== void 0 ? distance : halfText;
|
|
3697
|
+
for (let scan = 0;; scan += halfText) {
|
|
3698
|
+
let y = startY + (dist + scan) * dir;
|
|
3699
|
+
let pos = posAtCoords(view, { x: resolvedGoal, y }, false, dir);
|
|
3700
|
+
if (forward ? y > rect.bottom : y < rect.top)
|
|
3701
|
+
return state.EditorSelection.cursor(pos.pos, pos.assoc);
|
|
3702
|
+
let posCoords = view.coordsAtPos(pos.pos, pos.assoc), mid = posCoords ? (posCoords.top + posCoords.bottom) / 2 : 0;
|
|
3703
|
+
if (!posCoords || (forward ? mid > startY : mid < startY))
|
|
3704
|
+
return state.EditorSelection.cursor(pos.pos, pos.assoc, undefined, goal);
|
|
3705
|
+
}
|
|
3697
3706
|
}
|
|
3698
3707
|
function skipAtomicRanges(atoms, pos, bias) {
|
|
3699
3708
|
for (;;) {
|
|
@@ -3863,6 +3872,9 @@ class InlineCoordsScan {
|
|
|
3863
3872
|
if (rects)
|
|
3864
3873
|
for (let i = 0; i < rects.length; i++) {
|
|
3865
3874
|
let rect = rects[i], side = 0;
|
|
3875
|
+
// Ignore empty rectangles when there are other rectangles
|
|
3876
|
+
if (rect.width == 0 && rects.length > 1)
|
|
3877
|
+
continue;
|
|
3866
3878
|
if (rect.bottom < this.y) {
|
|
3867
3879
|
if (!above || above.bottom < rect.bottom)
|
|
3868
3880
|
above = rect;
|
|
@@ -4086,7 +4098,7 @@ class DOMChange {
|
|
|
4086
4098
|
this.bounds = null;
|
|
4087
4099
|
this.text = "";
|
|
4088
4100
|
this.domChanged = start > -1;
|
|
4089
|
-
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
|
|
4101
|
+
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView, curSel = view.state.selection;
|
|
4090
4102
|
if (view.state.readOnly && start > -1) {
|
|
4091
4103
|
// Ignore changes when the editor is read-only
|
|
4092
4104
|
this.newSel = null;
|
|
@@ -4102,18 +4114,18 @@ class DOMChange {
|
|
|
4102
4114
|
let domSel = view.observer.selectionRange;
|
|
4103
4115
|
let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
|
|
4104
4116
|
!contains(view.contentDOM, domSel.focusNode)
|
|
4105
|
-
?
|
|
4117
|
+
? curSel.main.head
|
|
4106
4118
|
: view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
|
|
4107
4119
|
let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
|
|
4108
4120
|
!contains(view.contentDOM, domSel.anchorNode)
|
|
4109
|
-
?
|
|
4121
|
+
? curSel.main.anchor
|
|
4110
4122
|
: view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
|
|
4111
4123
|
// iOS will refuse to select the block gaps when doing
|
|
4112
4124
|
// select-all.
|
|
4113
4125
|
// Chrome will put the selection *inside* them, confusing
|
|
4114
4126
|
// posFromDOM
|
|
4115
4127
|
let vp = view.viewport;
|
|
4116
|
-
if ((browser.ios || browser.chrome) &&
|
|
4128
|
+
if ((browser.ios || browser.chrome) && curSel.main.empty && head != anchor &&
|
|
4117
4129
|
(vp.from > 0 || vp.to < view.state.doc.length)) {
|
|
4118
4130
|
let from = Math.min(head, anchor), to = Math.max(head, anchor);
|
|
4119
4131
|
let offFrom = vp.from - from, offTo = vp.to - to;
|
|
@@ -4122,10 +4134,22 @@ class DOMChange {
|
|
|
4122
4134
|
anchor = view.state.doc.length;
|
|
4123
4135
|
}
|
|
4124
4136
|
}
|
|
4125
|
-
if (view.inputState.composing > -1 &&
|
|
4126
|
-
this.newSel =
|
|
4127
|
-
|
|
4137
|
+
if (view.inputState.composing > -1 && curSel.ranges.length > 1) {
|
|
4138
|
+
this.newSel = curSel.replaceRange(state.EditorSelection.range(anchor, head));
|
|
4139
|
+
}
|
|
4140
|
+
else if (view.lineWrapping && anchor == head && !(curSel.main.empty && curSel.main.head == head) &&
|
|
4141
|
+
view.inputState.lastTouchTime > Date.now() - 100) {
|
|
4142
|
+
// If this is a cursor selection change in a line-wrapping
|
|
4143
|
+
// editor that may have been a touch, use the last touch
|
|
4144
|
+
// position to assign a side to the cursor.
|
|
4145
|
+
let before = view.coordsAtPos(head, -1), assoc = 0;
|
|
4146
|
+
if (before)
|
|
4147
|
+
assoc = view.inputState.lastTouchY <= before.bottom ? -1 : 1;
|
|
4148
|
+
this.newSel = state.EditorSelection.create([state.EditorSelection.cursor(head, assoc)]);
|
|
4149
|
+
}
|
|
4150
|
+
else {
|
|
4128
4151
|
this.newSel = state.EditorSelection.single(anchor, head);
|
|
4152
|
+
}
|
|
4129
4153
|
}
|
|
4130
4154
|
}
|
|
4131
4155
|
}
|
|
@@ -4412,6 +4436,8 @@ class InputState {
|
|
|
4412
4436
|
this.lastKeyCode = 0;
|
|
4413
4437
|
this.lastKeyTime = 0;
|
|
4414
4438
|
this.lastTouchTime = 0;
|
|
4439
|
+
this.lastTouchX = 0;
|
|
4440
|
+
this.lastTouchY = 0;
|
|
4415
4441
|
this.lastFocusTime = 0;
|
|
4416
4442
|
this.lastScrollTop = 0;
|
|
4417
4443
|
this.lastScrollLeft = 0;
|
|
@@ -4535,9 +4561,9 @@ class InputState {
|
|
|
4535
4561
|
// applyDOMChange, notify key handlers of it and reset to
|
|
4536
4562
|
// the state they produce.
|
|
4537
4563
|
let pending;
|
|
4538
|
-
if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey &&
|
|
4564
|
+
if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey && !event.shiftKey &&
|
|
4539
4565
|
((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
|
|
4540
|
-
EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey
|
|
4566
|
+
EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey)) {
|
|
4541
4567
|
this.pendingIOSKey = pending || event;
|
|
4542
4568
|
setTimeout(() => this.flushIOSKey(), 250);
|
|
4543
4569
|
return true;
|
|
@@ -4855,8 +4881,13 @@ handlers.keydown = (view, event) => {
|
|
|
4855
4881
|
return false;
|
|
4856
4882
|
};
|
|
4857
4883
|
observers.touchstart = (view, e) => {
|
|
4858
|
-
view.inputState
|
|
4859
|
-
|
|
4884
|
+
let iState = view.inputState, touch = e.targetTouches[0];
|
|
4885
|
+
iState.lastTouchTime = Date.now();
|
|
4886
|
+
if (touch) {
|
|
4887
|
+
iState.lastTouchX = touch.clientX;
|
|
4888
|
+
iState.lastTouchY = touch.clientY;
|
|
4889
|
+
}
|
|
4890
|
+
iState.setSelectionOrigin("select.pointer");
|
|
4860
4891
|
};
|
|
4861
4892
|
observers.touchmove = view => {
|
|
4862
4893
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
@@ -4936,10 +4967,10 @@ function basicMouseSelection(view, event) {
|
|
|
4936
4967
|
if (start.pos != cur.pos && !extend) {
|
|
4937
4968
|
let startRange = rangeForClick(view, start.pos, start.assoc, type);
|
|
4938
4969
|
let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to);
|
|
4939
|
-
range = from < range.from ? state.EditorSelection.range(from, to) : state.EditorSelection.range(to, from);
|
|
4970
|
+
range = from < range.from ? state.EditorSelection.range(from, to, range.assoc) : state.EditorSelection.range(to, from, range.assoc);
|
|
4940
4971
|
}
|
|
4941
4972
|
if (extend)
|
|
4942
|
-
return startSel.replaceRange(startSel.main.extend(range.from, range.to));
|
|
4973
|
+
return startSel.replaceRange(startSel.main.extend(range.from, range.to, range.assoc));
|
|
4943
4974
|
else if (multiple && type == 1 && startSel.ranges.length > 1 && (removed = removeRangeAround(startSel, cur.pos)))
|
|
4944
4975
|
return removed;
|
|
4945
4976
|
else if (multiple)
|
|
@@ -6798,6 +6829,21 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
|
|
|
6798
6829
|
"&dark .cm-cursor": {
|
|
6799
6830
|
borderLeftColor: "#ddd"
|
|
6800
6831
|
},
|
|
6832
|
+
".cm-selectionHandle": {
|
|
6833
|
+
backgroundColor: "currentColor",
|
|
6834
|
+
width: "1.5px"
|
|
6835
|
+
},
|
|
6836
|
+
".cm-selectionHandle-start::before, .cm-selectionHandle-end::before": {
|
|
6837
|
+
content: '""',
|
|
6838
|
+
backgroundColor: "inherit",
|
|
6839
|
+
borderRadius: "50%",
|
|
6840
|
+
width: "8px",
|
|
6841
|
+
height: "8px",
|
|
6842
|
+
position: "absolute",
|
|
6843
|
+
left: "-3.25px"
|
|
6844
|
+
},
|
|
6845
|
+
".cm-selectionHandle-start::before": { top: "-8px" },
|
|
6846
|
+
".cm-selectionHandle-end::before": { bottom: "-8px" },
|
|
6801
6847
|
".cm-dropCursor": {
|
|
6802
6848
|
position: "absolute"
|
|
6803
6849
|
},
|
|
@@ -9362,7 +9408,8 @@ const selectionConfig = state.Facet.define({
|
|
|
9362
9408
|
combine(configs) {
|
|
9363
9409
|
return state.combineConfig(configs, {
|
|
9364
9410
|
cursorBlinkRate: 1200,
|
|
9365
|
-
drawRangeCursor: true
|
|
9411
|
+
drawRangeCursor: true,
|
|
9412
|
+
iosSelectionHandles: true
|
|
9366
9413
|
}, {
|
|
9367
9414
|
cursorBlinkRate: (a, b) => Math.min(a, b),
|
|
9368
9415
|
drawRangeCursor: (a, b) => a || b
|
|
@@ -9414,9 +9461,9 @@ const cursorLayer = layer({
|
|
|
9414
9461
|
let cursors = [];
|
|
9415
9462
|
for (let r of state$1.selection.ranges) {
|
|
9416
9463
|
let prim = r == state$1.selection.main;
|
|
9417
|
-
if (r.empty || conf.drawRangeCursor) {
|
|
9464
|
+
if (r.empty || conf.drawRangeCursor && !(prim && browser.ios && conf.iosSelectionHandles)) {
|
|
9418
9465
|
let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
|
|
9419
|
-
let cursor = r.empty ? r : state.EditorSelection.cursor(r.head, r.
|
|
9466
|
+
let cursor = r.empty ? r : state.EditorSelection.cursor(r.head, r.assoc);
|
|
9420
9467
|
for (let piece of RectangleMarker.forRange(view, className, cursor))
|
|
9421
9468
|
cursors.push(piece);
|
|
9422
9469
|
}
|
|
@@ -9442,8 +9489,19 @@ function setBlinkRate(state, dom) {
|
|
|
9442
9489
|
const selectionLayer = layer({
|
|
9443
9490
|
above: false,
|
|
9444
9491
|
markers(view) {
|
|
9445
|
-
|
|
9446
|
-
|
|
9492
|
+
let markers = [], { main, ranges } = view.state.selection;
|
|
9493
|
+
for (let r of ranges)
|
|
9494
|
+
if (!r.empty) {
|
|
9495
|
+
for (let marker of RectangleMarker.forRange(view, "cm-selectionBackground", r))
|
|
9496
|
+
markers.push(marker);
|
|
9497
|
+
}
|
|
9498
|
+
if (browser.ios && !main.empty && view.state.facet(selectionConfig).iosSelectionHandles) {
|
|
9499
|
+
for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-start", state.EditorSelection.cursor(main.from, 1)))
|
|
9500
|
+
markers.push(piece);
|
|
9501
|
+
for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-end", state.EditorSelection.cursor(main.to, 1)))
|
|
9502
|
+
markers.push(piece);
|
|
9503
|
+
}
|
|
9504
|
+
return markers;
|
|
9447
9505
|
},
|
|
9448
9506
|
update(update, dom) {
|
|
9449
9507
|
return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
|
package/dist/index.d.cts
CHANGED
|
@@ -224,7 +224,7 @@ declare abstract class WidgetType {
|
|
|
224
224
|
couldn't (in which case the widget will be redrawn). The default
|
|
225
225
|
implementation just returns false.
|
|
226
226
|
*/
|
|
227
|
-
updateDOM(dom: HTMLElement, view: EditorView): boolean;
|
|
227
|
+
updateDOM(dom: HTMLElement, view: EditorView, from: this): boolean;
|
|
228
228
|
/**
|
|
229
229
|
The estimated height this widget will have, to be used when
|
|
230
230
|
estimating the height of content that hasn't been drawn. May
|
|
@@ -1573,6 +1573,12 @@ type SelectionConfig = {
|
|
|
1573
1573
|
true.
|
|
1574
1574
|
*/
|
|
1575
1575
|
drawRangeCursor?: boolean;
|
|
1576
|
+
/**
|
|
1577
|
+
Because hiding the cursor also hides the selection handles in
|
|
1578
|
+
the iOS browser, when this is enabled (the default), the
|
|
1579
|
+
extension draws handles on the side of the selection in iOS.
|
|
1580
|
+
*/
|
|
1581
|
+
iosSelectionHandles?: boolean;
|
|
1576
1582
|
};
|
|
1577
1583
|
/**
|
|
1578
1584
|
Returns an extension that hides the browser's native selection and
|
package/dist/index.d.ts
CHANGED
|
@@ -224,7 +224,7 @@ declare abstract class WidgetType {
|
|
|
224
224
|
couldn't (in which case the widget will be redrawn). The default
|
|
225
225
|
implementation just returns false.
|
|
226
226
|
*/
|
|
227
|
-
updateDOM(dom: HTMLElement, view: EditorView): boolean;
|
|
227
|
+
updateDOM(dom: HTMLElement, view: EditorView, from: this): boolean;
|
|
228
228
|
/**
|
|
229
229
|
The estimated height this widget will have, to be used when
|
|
230
230
|
estimating the height of content that hasn't been drawn. May
|
|
@@ -1573,6 +1573,12 @@ type SelectionConfig = {
|
|
|
1573
1573
|
true.
|
|
1574
1574
|
*/
|
|
1575
1575
|
drawRangeCursor?: boolean;
|
|
1576
|
+
/**
|
|
1577
|
+
Because hiding the cursor also hides the selection handles in
|
|
1578
|
+
the iOS browser, when this is enabled (the default), the
|
|
1579
|
+
extension draws handles on the side of the selection in iOS.
|
|
1580
|
+
*/
|
|
1581
|
+
iosSelectionHandles?: boolean;
|
|
1576
1582
|
};
|
|
1577
1583
|
/**
|
|
1578
1584
|
Returns an extension that hides the browser's native selection and
|
package/dist/index.js
CHANGED
|
@@ -132,7 +132,7 @@ class WidgetType {
|
|
|
132
132
|
couldn't (in which case the widget will be redrawn). The default
|
|
133
133
|
implementation just returns false.
|
|
134
134
|
*/
|
|
135
|
-
updateDOM(dom, view) { return false; }
|
|
135
|
+
updateDOM(dom, view, from) { return false; }
|
|
136
136
|
/**
|
|
137
137
|
@internal
|
|
138
138
|
*/
|
|
@@ -2535,7 +2535,7 @@ class TileCache {
|
|
|
2535
2535
|
let tile = widgets[i];
|
|
2536
2536
|
if (!this.reused.has(tile) &&
|
|
2537
2537
|
(pass == 0 ? tile.widget.compare(widget)
|
|
2538
|
-
: tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view))) {
|
|
2538
|
+
: tile.widget.constructor == widget.constructor && widget.updateDOM(tile.dom, this.view, tile.widget))) {
|
|
2539
2539
|
widgets.splice(i, 1);
|
|
2540
2540
|
if (i < this.index[0])
|
|
2541
2541
|
this.index[0]--;
|
|
@@ -3391,6 +3391,7 @@ class DocView {
|
|
|
3391
3391
|
this.blockWrappers = this.view.state.facet(blockWrappers).map(v => typeof v == "function" ? v(this.view) : v);
|
|
3392
3392
|
}
|
|
3393
3393
|
scrollIntoView(target) {
|
|
3394
|
+
var _a;
|
|
3394
3395
|
if (target.isSnapshot) {
|
|
3395
3396
|
let ref = this.view.viewState.lineBlockAt(target.range.head);
|
|
3396
3397
|
this.view.scrollDOM.scrollTop = ref.top - target.yMargin;
|
|
@@ -3407,7 +3408,7 @@ class DocView {
|
|
|
3407
3408
|
}
|
|
3408
3409
|
}
|
|
3409
3410
|
let { range } = target;
|
|
3410
|
-
let rect = this.coordsAt(range.head, range.
|
|
3411
|
+
let rect = this.coordsAt(range.head, (_a = range.assoc) !== null && _a !== void 0 ? _a : (range.empty ? 0 : range.head > range.anchor ? -1 : 1)), other;
|
|
3411
3412
|
if (!rect)
|
|
3412
3413
|
return;
|
|
3413
3414
|
if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
|
|
@@ -3674,7 +3675,8 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3674
3675
|
return EditorSelection.cursor(startPos, start.assoc);
|
|
3675
3676
|
let goal = start.goalColumn, startY;
|
|
3676
3677
|
let rect = view.contentDOM.getBoundingClientRect();
|
|
3677
|
-
let startCoords = view.coordsAtPos(startPos, (start.empty ? start.
|
|
3678
|
+
let startCoords = view.coordsAtPos(startPos, start.assoc || ((start.empty ? forward : start.head == start.from) ? 1 : -1));
|
|
3679
|
+
let docTop = view.documentTop;
|
|
3678
3680
|
if (startCoords) {
|
|
3679
3681
|
if (goal == null)
|
|
3680
3682
|
goal = startCoords.left - rect.left;
|
|
@@ -3687,9 +3689,16 @@ function moveVertically(view, start, forward, distance) {
|
|
|
3687
3689
|
startY = (dir < 0 ? line.top : line.bottom) + docTop;
|
|
3688
3690
|
}
|
|
3689
3691
|
let resolvedGoal = rect.left + goal;
|
|
3690
|
-
let dist = distance !== null && distance !== void 0 ? distance :
|
|
3691
|
-
let
|
|
3692
|
-
|
|
3692
|
+
let halfText = view.viewState.heightOracle.textHeight >> 1, dist = distance !== null && distance !== void 0 ? distance : halfText;
|
|
3693
|
+
for (let scan = 0;; scan += halfText) {
|
|
3694
|
+
let y = startY + (dist + scan) * dir;
|
|
3695
|
+
let pos = posAtCoords(view, { x: resolvedGoal, y }, false, dir);
|
|
3696
|
+
if (forward ? y > rect.bottom : y < rect.top)
|
|
3697
|
+
return EditorSelection.cursor(pos.pos, pos.assoc);
|
|
3698
|
+
let posCoords = view.coordsAtPos(pos.pos, pos.assoc), mid = posCoords ? (posCoords.top + posCoords.bottom) / 2 : 0;
|
|
3699
|
+
if (!posCoords || (forward ? mid > startY : mid < startY))
|
|
3700
|
+
return EditorSelection.cursor(pos.pos, pos.assoc, undefined, goal);
|
|
3701
|
+
}
|
|
3693
3702
|
}
|
|
3694
3703
|
function skipAtomicRanges(atoms, pos, bias) {
|
|
3695
3704
|
for (;;) {
|
|
@@ -3859,6 +3868,9 @@ class InlineCoordsScan {
|
|
|
3859
3868
|
if (rects)
|
|
3860
3869
|
for (let i = 0; i < rects.length; i++) {
|
|
3861
3870
|
let rect = rects[i], side = 0;
|
|
3871
|
+
// Ignore empty rectangles when there are other rectangles
|
|
3872
|
+
if (rect.width == 0 && rects.length > 1)
|
|
3873
|
+
continue;
|
|
3862
3874
|
if (rect.bottom < this.y) {
|
|
3863
3875
|
if (!above || above.bottom < rect.bottom)
|
|
3864
3876
|
above = rect;
|
|
@@ -4082,7 +4094,7 @@ class DOMChange {
|
|
|
4082
4094
|
this.bounds = null;
|
|
4083
4095
|
this.text = "";
|
|
4084
4096
|
this.domChanged = start > -1;
|
|
4085
|
-
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
|
|
4097
|
+
let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView, curSel = view.state.selection;
|
|
4086
4098
|
if (view.state.readOnly && start > -1) {
|
|
4087
4099
|
// Ignore changes when the editor is read-only
|
|
4088
4100
|
this.newSel = null;
|
|
@@ -4098,18 +4110,18 @@ class DOMChange {
|
|
|
4098
4110
|
let domSel = view.observer.selectionRange;
|
|
4099
4111
|
let head = iHead && iHead.node == domSel.focusNode && iHead.offset == domSel.focusOffset ||
|
|
4100
4112
|
!contains(view.contentDOM, domSel.focusNode)
|
|
4101
|
-
?
|
|
4113
|
+
? curSel.main.head
|
|
4102
4114
|
: view.docView.posFromDOM(domSel.focusNode, domSel.focusOffset);
|
|
4103
4115
|
let anchor = iAnchor && iAnchor.node == domSel.anchorNode && iAnchor.offset == domSel.anchorOffset ||
|
|
4104
4116
|
!contains(view.contentDOM, domSel.anchorNode)
|
|
4105
|
-
?
|
|
4117
|
+
? curSel.main.anchor
|
|
4106
4118
|
: view.docView.posFromDOM(domSel.anchorNode, domSel.anchorOffset);
|
|
4107
4119
|
// iOS will refuse to select the block gaps when doing
|
|
4108
4120
|
// select-all.
|
|
4109
4121
|
// Chrome will put the selection *inside* them, confusing
|
|
4110
4122
|
// posFromDOM
|
|
4111
4123
|
let vp = view.viewport;
|
|
4112
|
-
if ((browser.ios || browser.chrome) &&
|
|
4124
|
+
if ((browser.ios || browser.chrome) && curSel.main.empty && head != anchor &&
|
|
4113
4125
|
(vp.from > 0 || vp.to < view.state.doc.length)) {
|
|
4114
4126
|
let from = Math.min(head, anchor), to = Math.max(head, anchor);
|
|
4115
4127
|
let offFrom = vp.from - from, offTo = vp.to - to;
|
|
@@ -4118,10 +4130,22 @@ class DOMChange {
|
|
|
4118
4130
|
anchor = view.state.doc.length;
|
|
4119
4131
|
}
|
|
4120
4132
|
}
|
|
4121
|
-
if (view.inputState.composing > -1 &&
|
|
4122
|
-
this.newSel =
|
|
4123
|
-
|
|
4133
|
+
if (view.inputState.composing > -1 && curSel.ranges.length > 1) {
|
|
4134
|
+
this.newSel = curSel.replaceRange(EditorSelection.range(anchor, head));
|
|
4135
|
+
}
|
|
4136
|
+
else if (view.lineWrapping && anchor == head && !(curSel.main.empty && curSel.main.head == head) &&
|
|
4137
|
+
view.inputState.lastTouchTime > Date.now() - 100) {
|
|
4138
|
+
// If this is a cursor selection change in a line-wrapping
|
|
4139
|
+
// editor that may have been a touch, use the last touch
|
|
4140
|
+
// position to assign a side to the cursor.
|
|
4141
|
+
let before = view.coordsAtPos(head, -1), assoc = 0;
|
|
4142
|
+
if (before)
|
|
4143
|
+
assoc = view.inputState.lastTouchY <= before.bottom ? -1 : 1;
|
|
4144
|
+
this.newSel = EditorSelection.create([EditorSelection.cursor(head, assoc)]);
|
|
4145
|
+
}
|
|
4146
|
+
else {
|
|
4124
4147
|
this.newSel = EditorSelection.single(anchor, head);
|
|
4148
|
+
}
|
|
4125
4149
|
}
|
|
4126
4150
|
}
|
|
4127
4151
|
}
|
|
@@ -4408,6 +4432,8 @@ class InputState {
|
|
|
4408
4432
|
this.lastKeyCode = 0;
|
|
4409
4433
|
this.lastKeyTime = 0;
|
|
4410
4434
|
this.lastTouchTime = 0;
|
|
4435
|
+
this.lastTouchX = 0;
|
|
4436
|
+
this.lastTouchY = 0;
|
|
4411
4437
|
this.lastFocusTime = 0;
|
|
4412
4438
|
this.lastScrollTop = 0;
|
|
4413
4439
|
this.lastScrollLeft = 0;
|
|
@@ -4531,9 +4557,9 @@ class InputState {
|
|
|
4531
4557
|
// applyDOMChange, notify key handlers of it and reset to
|
|
4532
4558
|
// the state they produce.
|
|
4533
4559
|
let pending;
|
|
4534
|
-
if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey &&
|
|
4560
|
+
if (browser.ios && !event.synthetic && !event.altKey && !event.metaKey && !event.shiftKey &&
|
|
4535
4561
|
((pending = PendingKeys.find(key => key.keyCode == event.keyCode)) && !event.ctrlKey ||
|
|
4536
|
-
EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey
|
|
4562
|
+
EmacsyPendingKeys.indexOf(event.key) > -1 && event.ctrlKey)) {
|
|
4537
4563
|
this.pendingIOSKey = pending || event;
|
|
4538
4564
|
setTimeout(() => this.flushIOSKey(), 250);
|
|
4539
4565
|
return true;
|
|
@@ -4851,8 +4877,13 @@ handlers.keydown = (view, event) => {
|
|
|
4851
4877
|
return false;
|
|
4852
4878
|
};
|
|
4853
4879
|
observers.touchstart = (view, e) => {
|
|
4854
|
-
view.inputState
|
|
4855
|
-
|
|
4880
|
+
let iState = view.inputState, touch = e.targetTouches[0];
|
|
4881
|
+
iState.lastTouchTime = Date.now();
|
|
4882
|
+
if (touch) {
|
|
4883
|
+
iState.lastTouchX = touch.clientX;
|
|
4884
|
+
iState.lastTouchY = touch.clientY;
|
|
4885
|
+
}
|
|
4886
|
+
iState.setSelectionOrigin("select.pointer");
|
|
4856
4887
|
};
|
|
4857
4888
|
observers.touchmove = view => {
|
|
4858
4889
|
view.inputState.setSelectionOrigin("select.pointer");
|
|
@@ -4932,10 +4963,10 @@ function basicMouseSelection(view, event) {
|
|
|
4932
4963
|
if (start.pos != cur.pos && !extend) {
|
|
4933
4964
|
let startRange = rangeForClick(view, start.pos, start.assoc, type);
|
|
4934
4965
|
let from = Math.min(startRange.from, range.from), to = Math.max(startRange.to, range.to);
|
|
4935
|
-
range = from < range.from ? EditorSelection.range(from, to) : EditorSelection.range(to, from);
|
|
4966
|
+
range = from < range.from ? EditorSelection.range(from, to, range.assoc) : EditorSelection.range(to, from, range.assoc);
|
|
4936
4967
|
}
|
|
4937
4968
|
if (extend)
|
|
4938
|
-
return startSel.replaceRange(startSel.main.extend(range.from, range.to));
|
|
4969
|
+
return startSel.replaceRange(startSel.main.extend(range.from, range.to, range.assoc));
|
|
4939
4970
|
else if (multiple && type == 1 && startSel.ranges.length > 1 && (removed = removeRangeAround(startSel, cur.pos)))
|
|
4940
4971
|
return removed;
|
|
4941
4972
|
else if (multiple)
|
|
@@ -6793,6 +6824,21 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
6793
6824
|
"&dark .cm-cursor": {
|
|
6794
6825
|
borderLeftColor: "#ddd"
|
|
6795
6826
|
},
|
|
6827
|
+
".cm-selectionHandle": {
|
|
6828
|
+
backgroundColor: "currentColor",
|
|
6829
|
+
width: "1.5px"
|
|
6830
|
+
},
|
|
6831
|
+
".cm-selectionHandle-start::before, .cm-selectionHandle-end::before": {
|
|
6832
|
+
content: '""',
|
|
6833
|
+
backgroundColor: "inherit",
|
|
6834
|
+
borderRadius: "50%",
|
|
6835
|
+
width: "8px",
|
|
6836
|
+
height: "8px",
|
|
6837
|
+
position: "absolute",
|
|
6838
|
+
left: "-3.25px"
|
|
6839
|
+
},
|
|
6840
|
+
".cm-selectionHandle-start::before": { top: "-8px" },
|
|
6841
|
+
".cm-selectionHandle-end::before": { bottom: "-8px" },
|
|
6796
6842
|
".cm-dropCursor": {
|
|
6797
6843
|
position: "absolute"
|
|
6798
6844
|
},
|
|
@@ -9357,7 +9403,8 @@ const selectionConfig = /*@__PURE__*/Facet.define({
|
|
|
9357
9403
|
combine(configs) {
|
|
9358
9404
|
return combineConfig(configs, {
|
|
9359
9405
|
cursorBlinkRate: 1200,
|
|
9360
|
-
drawRangeCursor: true
|
|
9406
|
+
drawRangeCursor: true,
|
|
9407
|
+
iosSelectionHandles: true
|
|
9361
9408
|
}, {
|
|
9362
9409
|
cursorBlinkRate: (a, b) => Math.min(a, b),
|
|
9363
9410
|
drawRangeCursor: (a, b) => a || b
|
|
@@ -9409,9 +9456,9 @@ const cursorLayer = /*@__PURE__*/layer({
|
|
|
9409
9456
|
let cursors = [];
|
|
9410
9457
|
for (let r of state.selection.ranges) {
|
|
9411
9458
|
let prim = r == state.selection.main;
|
|
9412
|
-
if (r.empty || conf.drawRangeCursor) {
|
|
9459
|
+
if (r.empty || conf.drawRangeCursor && !(prim && browser.ios && conf.iosSelectionHandles)) {
|
|
9413
9460
|
let className = prim ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary";
|
|
9414
|
-
let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.
|
|
9461
|
+
let cursor = r.empty ? r : EditorSelection.cursor(r.head, r.assoc);
|
|
9415
9462
|
for (let piece of RectangleMarker.forRange(view, className, cursor))
|
|
9416
9463
|
cursors.push(piece);
|
|
9417
9464
|
}
|
|
@@ -9437,8 +9484,19 @@ function setBlinkRate(state, dom) {
|
|
|
9437
9484
|
const selectionLayer = /*@__PURE__*/layer({
|
|
9438
9485
|
above: false,
|
|
9439
9486
|
markers(view) {
|
|
9440
|
-
|
|
9441
|
-
|
|
9487
|
+
let markers = [], { main, ranges } = view.state.selection;
|
|
9488
|
+
for (let r of ranges)
|
|
9489
|
+
if (!r.empty) {
|
|
9490
|
+
for (let marker of RectangleMarker.forRange(view, "cm-selectionBackground", r))
|
|
9491
|
+
markers.push(marker);
|
|
9492
|
+
}
|
|
9493
|
+
if (browser.ios && !main.empty && view.state.facet(selectionConfig).iosSelectionHandles) {
|
|
9494
|
+
for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-start", EditorSelection.cursor(main.from, 1)))
|
|
9495
|
+
markers.push(piece);
|
|
9496
|
+
for (let piece of RectangleMarker.forRange(view, "cm-selectionHandle cm-selectionHandle-end", EditorSelection.cursor(main.to, 1)))
|
|
9497
|
+
markers.push(piece);
|
|
9498
|
+
}
|
|
9499
|
+
return markers;
|
|
9442
9500
|
},
|
|
9443
9501
|
update(update, dom) {
|
|
9444
9502
|
return update.docChanged || update.selectionSet || update.viewportChanged || configChanged(update);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemirror/view",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.40.0",
|
|
4
4
|
"description": "DOM view component for the CodeMirror code editor",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "cm-runtests",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"sideEffects": false,
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@codemirror/state": "^6.
|
|
29
|
+
"@codemirror/state": "^6.6.0",
|
|
30
30
|
"crelt": "^1.0.6",
|
|
31
31
|
"style-mod": "^4.1.0",
|
|
32
32
|
"w3c-keyname": "^2.2.4"
|