@codemirror/view 0.19.27 → 0.19.31
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 +42 -0
- package/dist/index.cjs +1031 -872
- package/dist/index.d.ts +21 -1
- package/dist/index.js +1032 -874
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MapMode, Text as Text$1, Facet, StateEffect, ChangeSet, EditorSelection, CharCategory, EditorState, Transaction, Prec, combineConfig } from '@codemirror/state';
|
|
1
|
+
import { MapMode, Text as Text$1, Facet, StateEffect, ChangeSet, EditorSelection, CharCategory, EditorState, Transaction, Prec, combineConfig, StateField } from '@codemirror/state';
|
|
2
2
|
import { StyleModule } from 'style-mod';
|
|
3
3
|
import { RangeSet, RangeValue, RangeSetBuilder } from '@codemirror/rangeset';
|
|
4
4
|
export { Range } from '@codemirror/rangeset';
|
|
@@ -655,6 +655,7 @@ class TextView extends ContentView {
|
|
|
655
655
|
split(from) {
|
|
656
656
|
let result = new TextView(this.text.slice(from));
|
|
657
657
|
this.text = this.text.slice(0, from);
|
|
658
|
+
this.markDirty();
|
|
658
659
|
return result;
|
|
659
660
|
}
|
|
660
661
|
localPosFromDOM(node, offset) {
|
|
@@ -842,6 +843,9 @@ class CompositionView extends WidgetView {
|
|
|
842
843
|
coordsAt(pos, side) { return textCoords(this.widget.text, pos, side); }
|
|
843
844
|
get isEditable() { return true; }
|
|
844
845
|
}
|
|
846
|
+
// Use two characters on Android, to prevent Chrome from closing the
|
|
847
|
+
// virtual keyboard when backspacing after a widget (#602).
|
|
848
|
+
const ZeroWidthSpace = browser.android ? "\u200b\u200b" : "\u200b";
|
|
845
849
|
// These are drawn around uneditable widgets to avoid a number of
|
|
846
850
|
// browser bugs that show up when the cursor is directly next to
|
|
847
851
|
// uneditable inline content.
|
|
@@ -858,12 +862,13 @@ class WidgetBufferView extends ContentView {
|
|
|
858
862
|
split() { return new WidgetBufferView(this.side); }
|
|
859
863
|
sync() {
|
|
860
864
|
if (!this.dom)
|
|
861
|
-
this.setDOM(document.createTextNode(
|
|
862
|
-
else if (this.dirty && this.dom.nodeValue !=
|
|
863
|
-
this.dom.nodeValue =
|
|
865
|
+
this.setDOM(document.createTextNode(ZeroWidthSpace));
|
|
866
|
+
else if (this.dirty && this.dom.nodeValue != ZeroWidthSpace)
|
|
867
|
+
this.dom.nodeValue = ZeroWidthSpace;
|
|
864
868
|
}
|
|
865
869
|
getSide() { return this.side; }
|
|
866
870
|
domAtPos(pos) { return DOMPos.before(this.dom); }
|
|
871
|
+
localPosFromDOM() { return 0; }
|
|
867
872
|
domBoundsAround() { return null; }
|
|
868
873
|
coordsAt(pos) {
|
|
869
874
|
let rects = clientRectsFor(this.dom);
|
|
@@ -1934,797 +1939,873 @@ class ViewUpdate {
|
|
|
1934
1939
|
get empty() { return this.flags == 0 && this.transactions.length == 0; }
|
|
1935
1940
|
}
|
|
1936
1941
|
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1942
|
+
/**
|
|
1943
|
+
Used to indicate [text direction](https://codemirror.net/6/docs/ref/#view.EditorView.textDirection).
|
|
1944
|
+
*/
|
|
1945
|
+
var Direction = /*@__PURE__*/(function (Direction) {
|
|
1946
|
+
// (These are chosen to match the base levels, in bidi algorithm
|
|
1947
|
+
// terms, of spans in that direction.)
|
|
1948
|
+
/**
|
|
1949
|
+
Left-to-right.
|
|
1950
|
+
*/
|
|
1951
|
+
Direction[Direction["LTR"] = 0] = "LTR";
|
|
1952
|
+
/**
|
|
1953
|
+
Right-to-left.
|
|
1954
|
+
*/
|
|
1955
|
+
Direction[Direction["RTL"] = 1] = "RTL";
|
|
1956
|
+
return Direction})(Direction || (Direction = {}));
|
|
1957
|
+
const LTR = Direction.LTR, RTL = Direction.RTL;
|
|
1958
|
+
// Decode a string with each type encoded as log2(type)
|
|
1959
|
+
function dec(str) {
|
|
1960
|
+
let result = [];
|
|
1961
|
+
for (let i = 0; i < str.length; i++)
|
|
1962
|
+
result.push(1 << +str[i]);
|
|
1963
|
+
return result;
|
|
1964
|
+
}
|
|
1965
|
+
// Character types for codepoints 0 to 0xf8
|
|
1966
|
+
const LowTypes = /*@__PURE__*/dec("88888888888888888888888888888888888666888888787833333333337888888000000000000000000000000008888880000000000000000000000000088888888888888888888888888888888888887866668888088888663380888308888800000000000000000000000800000000000000000000000000000008");
|
|
1967
|
+
// Character types for codepoints 0x600 to 0x6f9
|
|
1968
|
+
const ArabicTypes = /*@__PURE__*/dec("4444448826627288999999999992222222222222222222222222222222222222222222222229999999999999999999994444444444644222822222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222999999949999999229989999223333333333");
|
|
1969
|
+
const Brackets = /*@__PURE__*/Object.create(null), BracketStack = [];
|
|
1970
|
+
// There's a lot more in
|
|
1971
|
+
// https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
|
|
1972
|
+
// which are left out to keep code size down.
|
|
1973
|
+
for (let p of ["()", "[]", "{}"]) {
|
|
1974
|
+
let l = /*@__PURE__*/p.charCodeAt(0), r = /*@__PURE__*/p.charCodeAt(1);
|
|
1975
|
+
Brackets[l] = r;
|
|
1976
|
+
Brackets[r] = -l;
|
|
1977
|
+
}
|
|
1978
|
+
function charType(ch) {
|
|
1979
|
+
return ch <= 0xf7 ? LowTypes[ch] :
|
|
1980
|
+
0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
|
|
1981
|
+
0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
|
|
1982
|
+
0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
|
|
1983
|
+
0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
|
|
1984
|
+
ch == 0x200c ? 256 /* NI */ : 1 /* L */;
|
|
1985
|
+
}
|
|
1986
|
+
const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
|
1987
|
+
/**
|
|
1988
|
+
Represents a contiguous range of text that has a single direction
|
|
1989
|
+
(as in left-to-right or right-to-left).
|
|
1990
|
+
*/
|
|
1991
|
+
class BidiSpan {
|
|
1992
|
+
/**
|
|
1993
|
+
@internal
|
|
1994
|
+
*/
|
|
1995
|
+
constructor(
|
|
1996
|
+
/**
|
|
1997
|
+
The start of the span (relative to the start of the line).
|
|
1998
|
+
*/
|
|
1999
|
+
from,
|
|
2000
|
+
/**
|
|
2001
|
+
The end of the span.
|
|
2002
|
+
*/
|
|
2003
|
+
to,
|
|
2004
|
+
/**
|
|
2005
|
+
The ["bidi
|
|
2006
|
+
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
|
|
2007
|
+
of the span (in this context, 0 means
|
|
2008
|
+
left-to-right, 1 means right-to-left, 2 means left-to-right
|
|
2009
|
+
number inside right-to-left text).
|
|
2010
|
+
*/
|
|
2011
|
+
level) {
|
|
2012
|
+
this.from = from;
|
|
2013
|
+
this.to = to;
|
|
2014
|
+
this.level = level;
|
|
1965
2015
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
2016
|
+
/**
|
|
2017
|
+
The direction of this span.
|
|
2018
|
+
*/
|
|
2019
|
+
get dir() { return this.level % 2 ? RTL : LTR; }
|
|
2020
|
+
/**
|
|
2021
|
+
@internal
|
|
2022
|
+
*/
|
|
2023
|
+
side(end, dir) { return (this.dir == dir) == end ? this.to : this.from; }
|
|
2024
|
+
/**
|
|
2025
|
+
@internal
|
|
2026
|
+
*/
|
|
2027
|
+
static find(order, index, level, assoc) {
|
|
2028
|
+
let maybe = -1;
|
|
2029
|
+
for (let i = 0; i < order.length; i++) {
|
|
2030
|
+
let span = order[i];
|
|
2031
|
+
if (span.from <= index && span.to >= index) {
|
|
2032
|
+
if (span.level == level)
|
|
2033
|
+
return i;
|
|
2034
|
+
// When multiple spans match, if assoc != 0, take the one that
|
|
2035
|
+
// covers that side, otherwise take the one with the minimum
|
|
2036
|
+
// level.
|
|
2037
|
+
if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
|
|
2038
|
+
maybe = i;
|
|
1982
2039
|
}
|
|
1983
2040
|
}
|
|
1984
|
-
if (
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
1988
|
-
// When the DOM nodes around the selection are moved to another
|
|
1989
|
-
// parent, Chrome sometimes reports a different selection through
|
|
1990
|
-
// getSelection than the one that it actually shows to the user.
|
|
1991
|
-
// This forces a selection update when lines are joined to work
|
|
1992
|
-
// around that. Issue #54
|
|
1993
|
-
if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
|
|
1994
|
-
update.state.doc.lines != update.startState.doc.lines)
|
|
1995
|
-
this.forceSelection = true;
|
|
1996
|
-
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
1997
|
-
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
1998
|
-
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
1999
|
-
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
2000
|
-
return false;
|
|
2001
|
-
}
|
|
2002
|
-
else {
|
|
2003
|
-
this.updateInner(changedRanges, deco, update.startState.doc.length);
|
|
2004
|
-
if (update.transactions.length)
|
|
2005
|
-
this.lastUpdate = Date.now();
|
|
2006
|
-
return true;
|
|
2007
|
-
}
|
|
2041
|
+
if (maybe < 0)
|
|
2042
|
+
throw new RangeError("Index out of range");
|
|
2043
|
+
return maybe;
|
|
2008
2044
|
}
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2045
|
+
}
|
|
2046
|
+
// Reused array of character types
|
|
2047
|
+
const types = [];
|
|
2048
|
+
function computeOrder(line, direction) {
|
|
2049
|
+
let len = line.length, outerType = direction == LTR ? 1 /* L */ : 2 /* R */, oppositeType = direction == LTR ? 2 /* R */ : 1 /* L */;
|
|
2050
|
+
if (!line || outerType == 1 /* L */ && !BidiRE.test(line))
|
|
2051
|
+
return trivialOrder(len);
|
|
2052
|
+
// W1. Examine each non-spacing mark (NSM) in the level run, and
|
|
2053
|
+
// change the type of the NSM to the type of the previous
|
|
2054
|
+
// character. If the NSM is at the start of the level run, it will
|
|
2055
|
+
// get the type of sor.
|
|
2056
|
+
// W2. Search backwards from each instance of a European number
|
|
2057
|
+
// until the first strong type (R, L, AL, or sor) is found. If an
|
|
2058
|
+
// AL is found, change the type of the European number to Arabic
|
|
2059
|
+
// number.
|
|
2060
|
+
// W3. Change all ALs to R.
|
|
2061
|
+
// (Left after this: L, R, EN, AN, ET, CS, NI)
|
|
2062
|
+
for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
|
|
2063
|
+
let type = charType(line.charCodeAt(i));
|
|
2064
|
+
if (type == 512 /* NSM */)
|
|
2065
|
+
type = prev;
|
|
2066
|
+
else if (type == 8 /* EN */ && prevStrong == 4 /* AL */)
|
|
2067
|
+
type = 16 /* AN */;
|
|
2068
|
+
types[i] = type == 4 /* AL */ ? 2 /* R */ : type;
|
|
2069
|
+
if (type & 7 /* Strong */)
|
|
2070
|
+
prevStrong = type;
|
|
2071
|
+
prev = type;
|
|
2072
|
+
}
|
|
2073
|
+
// W5. A sequence of European terminators adjacent to European
|
|
2074
|
+
// numbers changes to all European numbers.
|
|
2075
|
+
// W6. Otherwise, separators and terminators change to Other
|
|
2076
|
+
// Neutral.
|
|
2077
|
+
// W7. Search backwards from each instance of a European number
|
|
2078
|
+
// until the first strong type (R, L, or sor) is found. If an L is
|
|
2079
|
+
// found, then change the type of the European number to L.
|
|
2080
|
+
// (Left after this: L, R, EN+AN, NI)
|
|
2081
|
+
for (let i = 0, prev = outerType, prevStrong = outerType; i < len; i++) {
|
|
2082
|
+
let type = types[i];
|
|
2083
|
+
if (type == 128 /* CS */) {
|
|
2084
|
+
if (i < len - 1 && prev == types[i + 1] && (prev & 24 /* Num */))
|
|
2085
|
+
type = types[i] = prev;
|
|
2086
|
+
else
|
|
2087
|
+
types[i] = 256 /* NI */;
|
|
2014
2088
|
}
|
|
2015
|
-
else {
|
|
2016
|
-
|
|
2089
|
+
else if (type == 64 /* ET */) {
|
|
2090
|
+
let end = i + 1;
|
|
2091
|
+
while (end < len && types[end] == 64 /* ET */)
|
|
2092
|
+
end++;
|
|
2093
|
+
let replace = (i && prev == 8 /* EN */) || (end < len && types[end] == 8 /* EN */) ? (prevStrong == 1 /* L */ ? 1 /* L */ : 8 /* EN */) : 256 /* NI */;
|
|
2094
|
+
for (let j = i; j < end; j++)
|
|
2095
|
+
types[j] = replace;
|
|
2096
|
+
i = end - 1;
|
|
2017
2097
|
}
|
|
2098
|
+
else if (type == 8 /* EN */ && prevStrong == 1 /* L */) {
|
|
2099
|
+
types[i] = 1 /* L */;
|
|
2100
|
+
}
|
|
2101
|
+
prev = type;
|
|
2102
|
+
if (type & 7 /* Strong */)
|
|
2103
|
+
prevStrong = type;
|
|
2018
2104
|
}
|
|
2019
|
-
//
|
|
2020
|
-
//
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
this.dirty = 0 /* Not */;
|
|
2039
|
-
if (track && (track.written || observer.selectionRange.focusNode != track.node))
|
|
2040
|
-
this.forceSelection = true;
|
|
2041
|
-
this.dom.style.height = "";
|
|
2042
|
-
});
|
|
2043
|
-
let gaps = [];
|
|
2044
|
-
if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
|
|
2045
|
-
for (let child of this.children)
|
|
2046
|
-
if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
|
|
2047
|
-
gaps.push(child.dom);
|
|
2048
|
-
observer.updateGaps(gaps);
|
|
2049
|
-
}
|
|
2050
|
-
updateChildren(changes, deco, oldLength) {
|
|
2051
|
-
let cursor = this.childCursor(oldLength);
|
|
2052
|
-
for (let i = changes.length - 1;; i--) {
|
|
2053
|
-
let next = i >= 0 ? changes[i] : null;
|
|
2054
|
-
if (!next)
|
|
2055
|
-
break;
|
|
2056
|
-
let { fromA, toA, fromB, toB } = next;
|
|
2057
|
-
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
|
|
2058
|
-
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2059
|
-
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2060
|
-
replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
// Sync the DOM selection to this.state.selection
|
|
2064
|
-
updateSelection(mustRead = false, fromPointer = false) {
|
|
2065
|
-
if (mustRead)
|
|
2066
|
-
this.view.observer.readSelectionRange();
|
|
2067
|
-
if (!(fromPointer || this.mayControlSelection()) ||
|
|
2068
|
-
browser.ios && this.view.inputState.rapidCompositionStart)
|
|
2069
|
-
return;
|
|
2070
|
-
let force = this.forceSelection;
|
|
2071
|
-
this.forceSelection = false;
|
|
2072
|
-
let main = this.view.state.selection.main;
|
|
2073
|
-
// FIXME need to handle the case where the selection falls inside a block range
|
|
2074
|
-
let anchor = this.domAtPos(main.anchor);
|
|
2075
|
-
let head = main.empty ? anchor : this.domAtPos(main.head);
|
|
2076
|
-
// Always reset on Firefox when next to an uneditable node to
|
|
2077
|
-
// avoid invisible cursor bugs (#111)
|
|
2078
|
-
if (browser.gecko && main.empty && betweenUneditable(anchor)) {
|
|
2079
|
-
let dummy = document.createTextNode("");
|
|
2080
|
-
this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
|
|
2081
|
-
anchor = head = new DOMPos(dummy, 0);
|
|
2082
|
-
force = true;
|
|
2083
|
-
}
|
|
2084
|
-
let domSel = this.view.observer.selectionRange;
|
|
2085
|
-
// If the selection is already here, or in an equivalent position, don't touch it
|
|
2086
|
-
if (force || !domSel.focusNode ||
|
|
2087
|
-
!isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) ||
|
|
2088
|
-
!isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) {
|
|
2089
|
-
this.view.observer.ignore(() => {
|
|
2090
|
-
// Chrome Android will hide the virtual keyboard when tapping
|
|
2091
|
-
// inside an uneditable node, and not bring it back when we
|
|
2092
|
-
// move the cursor to its proper position. This tries to
|
|
2093
|
-
// restore the keyboard by cycling focus.
|
|
2094
|
-
if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) && inUneditable(domSel.focusNode, this.dom)) {
|
|
2095
|
-
this.dom.blur();
|
|
2096
|
-
this.dom.focus({ preventScroll: true });
|
|
2097
|
-
}
|
|
2098
|
-
let rawSel = getSelection(this.root);
|
|
2099
|
-
if (main.empty) {
|
|
2100
|
-
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
|
|
2101
|
-
if (browser.gecko) {
|
|
2102
|
-
let nextTo = nextToUneditable(anchor.node, anchor.offset);
|
|
2103
|
-
if (nextTo && nextTo != (1 /* Before */ | 2 /* After */)) {
|
|
2104
|
-
let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* Before */ ? 1 : -1);
|
|
2105
|
-
if (text)
|
|
2106
|
-
anchor = new DOMPos(text, nextTo == 1 /* Before */ ? 0 : text.nodeValue.length);
|
|
2107
|
-
}
|
|
2105
|
+
// N0. Process bracket pairs in an isolating run sequence
|
|
2106
|
+
// sequentially in the logical order of the text positions of the
|
|
2107
|
+
// opening paired brackets using the logic given below. Within this
|
|
2108
|
+
// scope, bidirectional types EN and AN are treated as R.
|
|
2109
|
+
for (let i = 0, sI = 0, context = 0, ch, br, type; i < len; i++) {
|
|
2110
|
+
// Keeps [startIndex, type, strongSeen] triples for each open
|
|
2111
|
+
// bracket on BracketStack.
|
|
2112
|
+
if (br = Brackets[ch = line.charCodeAt(i)]) {
|
|
2113
|
+
if (br < 0) { // Closing bracket
|
|
2114
|
+
for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
|
|
2115
|
+
if (BracketStack[sJ + 1] == -br) {
|
|
2116
|
+
let flags = BracketStack[sJ + 2];
|
|
2117
|
+
let type = (flags & 2 /* EmbedInside */) ? outerType :
|
|
2118
|
+
!(flags & 4 /* OppositeInside */) ? 0 :
|
|
2119
|
+
(flags & 1 /* OppositeBefore */) ? oppositeType : outerType;
|
|
2120
|
+
if (type)
|
|
2121
|
+
types[i] = types[BracketStack[sJ]] = type;
|
|
2122
|
+
sI = sJ;
|
|
2123
|
+
break;
|
|
2108
2124
|
}
|
|
2109
|
-
rawSel.collapse(anchor.node, anchor.offset);
|
|
2110
|
-
if (main.bidiLevel != null && domSel.cursorBidiLevel != null)
|
|
2111
|
-
domSel.cursorBidiLevel = main.bidiLevel;
|
|
2112
2125
|
}
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2126
|
+
}
|
|
2127
|
+
else if (BracketStack.length == 189 /* MaxDepth */) {
|
|
2128
|
+
break;
|
|
2129
|
+
}
|
|
2130
|
+
else {
|
|
2131
|
+
BracketStack[sI++] = i;
|
|
2132
|
+
BracketStack[sI++] = ch;
|
|
2133
|
+
BracketStack[sI++] = context;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
else if ((type = types[i]) == 2 /* R */ || type == 1 /* L */) {
|
|
2137
|
+
let embed = type == outerType;
|
|
2138
|
+
context = embed ? 0 : 1 /* OppositeBefore */;
|
|
2139
|
+
for (let sJ = sI - 3; sJ >= 0; sJ -= 3) {
|
|
2140
|
+
let cur = BracketStack[sJ + 2];
|
|
2141
|
+
if (cur & 2 /* EmbedInside */)
|
|
2142
|
+
break;
|
|
2143
|
+
if (embed) {
|
|
2144
|
+
BracketStack[sJ + 2] |= 2 /* EmbedInside */;
|
|
2119
2145
|
}
|
|
2120
2146
|
else {
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
[anchor, head] = [head, anchor];
|
|
2125
|
-
range.setEnd(head.node, head.offset);
|
|
2126
|
-
range.setStart(anchor.node, anchor.offset);
|
|
2127
|
-
rawSel.removeAllRanges();
|
|
2128
|
-
rawSel.addRange(range);
|
|
2147
|
+
if (cur & 4 /* OppositeInside */)
|
|
2148
|
+
break;
|
|
2149
|
+
BracketStack[sJ + 2] |= 4 /* OppositeInside */;
|
|
2129
2150
|
}
|
|
2130
|
-
}
|
|
2131
|
-
this.view.observer.setSelectionRange(anchor, head);
|
|
2151
|
+
}
|
|
2132
2152
|
}
|
|
2133
|
-
this.impreciseAnchor = anchor.precise ? null : new DOMPos(domSel.anchorNode, domSel.anchorOffset);
|
|
2134
|
-
this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
|
|
2135
|
-
}
|
|
2136
|
-
enforceCursorAssoc() {
|
|
2137
|
-
if (this.view.composing)
|
|
2138
|
-
return;
|
|
2139
|
-
let cursor = this.view.state.selection.main;
|
|
2140
|
-
let sel = getSelection(this.root);
|
|
2141
|
-
if (!cursor.empty || !cursor.assoc || !sel.modify)
|
|
2142
|
-
return;
|
|
2143
|
-
let line = LineView.find(this, cursor.head);
|
|
2144
|
-
if (!line)
|
|
2145
|
-
return;
|
|
2146
|
-
let lineStart = line.posAtStart;
|
|
2147
|
-
if (cursor.head == lineStart || cursor.head == lineStart + line.length)
|
|
2148
|
-
return;
|
|
2149
|
-
let before = this.coordsAt(cursor.head, -1), after = this.coordsAt(cursor.head, 1);
|
|
2150
|
-
if (!before || !after || before.bottom > after.top)
|
|
2151
|
-
return;
|
|
2152
|
-
let dom = this.domAtPos(cursor.head + cursor.assoc);
|
|
2153
|
-
sel.collapse(dom.node, dom.offset);
|
|
2154
|
-
sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
|
|
2155
|
-
}
|
|
2156
|
-
mayControlSelection() {
|
|
2157
|
-
return this.view.state.facet(editable) ? this.root.activeElement == this.dom
|
|
2158
|
-
: hasSelection(this.dom, this.view.observer.selectionRange);
|
|
2159
2153
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2154
|
+
// N1. A sequence of neutrals takes the direction of the
|
|
2155
|
+
// surrounding strong text if the text on both sides has the same
|
|
2156
|
+
// direction. European and Arabic numbers act as if they were R in
|
|
2157
|
+
// terms of their influence on neutrals. Start-of-level-run (sor)
|
|
2158
|
+
// and end-of-level-run (eor) are used at level run boundaries.
|
|
2159
|
+
// N2. Any remaining neutrals take the embedding direction.
|
|
2160
|
+
// (Left after this: L, R, EN+AN)
|
|
2161
|
+
for (let i = 0; i < len; i++) {
|
|
2162
|
+
if (types[i] == 256 /* NI */) {
|
|
2163
|
+
let end = i + 1;
|
|
2164
|
+
while (end < len && types[end] == 256 /* NI */)
|
|
2165
|
+
end++;
|
|
2166
|
+
let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
|
|
2167
|
+
let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
|
|
2168
|
+
let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
|
|
2169
|
+
for (let j = i; j < end; j++)
|
|
2170
|
+
types[j] = replace;
|
|
2171
|
+
i = end - 1;
|
|
2166
2172
|
}
|
|
2167
|
-
return null;
|
|
2168
2173
|
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
+
// Here we depart from the documented algorithm, in order to avoid
|
|
2175
|
+
// building up an actual levels array. Since there are only three
|
|
2176
|
+
// levels (0, 1, 2) in an implementation that doesn't take
|
|
2177
|
+
// explicit embedding into account, we can build up the order on
|
|
2178
|
+
// the fly, without following the level-based algorithm.
|
|
2179
|
+
let order = [];
|
|
2180
|
+
if (outerType == 1 /* L */) {
|
|
2181
|
+
for (let i = 0; i < len;) {
|
|
2182
|
+
let start = i, rtl = types[i++] != 1 /* L */;
|
|
2183
|
+
while (i < len && rtl == (types[i] != 1 /* L */))
|
|
2184
|
+
i++;
|
|
2185
|
+
if (rtl) {
|
|
2186
|
+
for (let j = i; j > start;) {
|
|
2187
|
+
let end = j, l = types[--j] != 2 /* R */;
|
|
2188
|
+
while (j > start && l == (types[j - 1] != 2 /* R */))
|
|
2189
|
+
j--;
|
|
2190
|
+
order.push(new BidiSpan(j, end, l ? 2 : 1));
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
else {
|
|
2194
|
+
order.push(new BidiSpan(start, i, 0));
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2174
2197
|
}
|
|
2175
|
-
|
|
2176
|
-
let
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
i++;
|
|
2182
|
-
off = 0;
|
|
2198
|
+
else {
|
|
2199
|
+
for (let i = 0; i < len;) {
|
|
2200
|
+
let start = i, rtl = types[i++] == 2 /* R */;
|
|
2201
|
+
while (i < len && rtl == (types[i] == 2 /* R */))
|
|
2202
|
+
i++;
|
|
2203
|
+
order.push(new BidiSpan(start, i, rtl ? 1 : 2));
|
|
2183
2204
|
}
|
|
2184
|
-
return this.children[i].domAtPos(off);
|
|
2185
2205
|
}
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2206
|
+
return order;
|
|
2207
|
+
}
|
|
2208
|
+
function trivialOrder(length) {
|
|
2209
|
+
return [new BidiSpan(0, length, 0)];
|
|
2210
|
+
}
|
|
2211
|
+
let movedOver = "";
|
|
2212
|
+
function moveVisually(line, order, dir, start, forward) {
|
|
2213
|
+
var _a;
|
|
2214
|
+
let startIndex = start.head - line.from, spanI = -1;
|
|
2215
|
+
if (startIndex == 0) {
|
|
2216
|
+
if (!forward || !line.length)
|
|
2217
|
+
return null;
|
|
2218
|
+
if (order[0].level != dir) {
|
|
2219
|
+
startIndex = order[0].side(false, dir);
|
|
2220
|
+
spanI = 0;
|
|
2195
2221
|
}
|
|
2196
2222
|
}
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
if (pos >= from) {
|
|
2205
|
-
result.push(child.dom.getBoundingClientRect().height);
|
|
2206
|
-
let width = child.dom.scrollWidth;
|
|
2207
|
-
if (width > minWidth) {
|
|
2208
|
-
this.minWidth = minWidth = width;
|
|
2209
|
-
this.minWidthFrom = pos;
|
|
2210
|
-
this.minWidthTo = end;
|
|
2211
|
-
}
|
|
2212
|
-
}
|
|
2213
|
-
pos = end + child.breakAfter;
|
|
2223
|
+
else if (startIndex == line.length) {
|
|
2224
|
+
if (forward)
|
|
2225
|
+
return null;
|
|
2226
|
+
let last = order[order.length - 1];
|
|
2227
|
+
if (last.level != dir) {
|
|
2228
|
+
startIndex = last.side(true, dir);
|
|
2229
|
+
spanI = order.length - 1;
|
|
2214
2230
|
}
|
|
2215
|
-
return result;
|
|
2216
2231
|
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
}
|
|
2225
|
-
// If no workable line exists, force a layout of a measurable element
|
|
2226
|
-
let dummy = document.createElement("div"), lineHeight, charWidth;
|
|
2227
|
-
dummy.className = "cm-line";
|
|
2228
|
-
dummy.textContent = "abc def ghi jkl mno pqr stu";
|
|
2229
|
-
this.view.observer.ignore(() => {
|
|
2230
|
-
this.dom.appendChild(dummy);
|
|
2231
|
-
let rect = clientRectsFor(dummy.firstChild)[0];
|
|
2232
|
-
lineHeight = dummy.getBoundingClientRect().height;
|
|
2233
|
-
charWidth = rect ? rect.width / 27 : 7;
|
|
2234
|
-
dummy.remove();
|
|
2235
|
-
});
|
|
2236
|
-
return { lineHeight, charWidth };
|
|
2232
|
+
if (spanI < 0)
|
|
2233
|
+
spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
|
|
2234
|
+
let span = order[spanI];
|
|
2235
|
+
// End of span. (But not end of line--that was checked for above.)
|
|
2236
|
+
if (startIndex == span.side(forward, dir)) {
|
|
2237
|
+
span = order[spanI += forward ? 1 : -1];
|
|
2238
|
+
startIndex = span.side(!forward, dir);
|
|
2237
2239
|
}
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
return
|
|
2240
|
+
let indexForward = forward == (span.dir == dir);
|
|
2241
|
+
let nextIndex = findClusterBreak(line.text, startIndex, indexForward);
|
|
2242
|
+
movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
|
|
2243
|
+
if (nextIndex != span.side(forward, dir))
|
|
2244
|
+
return EditorSelection.cursor(nextIndex + line.from, indexForward ? -1 : 1, span.level);
|
|
2245
|
+
let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
|
|
2246
|
+
if (!nextSpan && span.level != dir)
|
|
2247
|
+
return EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1, dir);
|
|
2248
|
+
if (nextSpan && nextSpan.level < span.level)
|
|
2249
|
+
return EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, forward ? 1 : -1, nextSpan.level);
|
|
2250
|
+
return EditorSelection.cursor(nextIndex + line.from, forward ? -1 : 1, span.level);
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
class DOMReader {
|
|
2254
|
+
constructor(points, view) {
|
|
2255
|
+
this.points = points;
|
|
2256
|
+
this.view = view;
|
|
2257
|
+
this.text = "";
|
|
2258
|
+
this.lineBreak = view.state.lineBreak;
|
|
2246
2259
|
}
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
if (!next)
|
|
2260
|
+
readRange(start, end) {
|
|
2261
|
+
if (!start)
|
|
2262
|
+
return this;
|
|
2263
|
+
let parent = start.parentNode;
|
|
2264
|
+
for (let cur = start;;) {
|
|
2265
|
+
this.findPointBefore(parent, cur);
|
|
2266
|
+
this.readNode(cur);
|
|
2267
|
+
let next = cur.nextSibling;
|
|
2268
|
+
if (next == end)
|
|
2257
2269
|
break;
|
|
2258
|
-
|
|
2270
|
+
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
2271
|
+
if (view && nextView ? view.breakAfter :
|
|
2272
|
+
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
2273
|
+
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
2274
|
+
this.text += this.lineBreak;
|
|
2275
|
+
cur = next;
|
|
2259
2276
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
updateDeco() {
|
|
2263
|
-
return this.decorations = [
|
|
2264
|
-
...this.view.pluginField(PluginField.decorations),
|
|
2265
|
-
...this.view.state.facet(decorations),
|
|
2266
|
-
this.compositionDeco,
|
|
2267
|
-
this.computeBlockGapDeco(),
|
|
2268
|
-
this.view.viewState.lineGapDeco
|
|
2269
|
-
];
|
|
2277
|
+
this.findPointBefore(parent, end);
|
|
2278
|
+
return this;
|
|
2270
2279
|
}
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
if (!rect)
|
|
2280
|
+
readNode(node) {
|
|
2281
|
+
if (node.cmIgnore)
|
|
2274
2282
|
return;
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
right: rect.right + mRight, bottom: rect.bottom + mBottom
|
|
2294
|
-
}, range.head < range.anchor ? -1 : 1, center);
|
|
2295
|
-
}
|
|
2296
|
-
}
|
|
2297
|
-
function betweenUneditable(pos) {
|
|
2298
|
-
return pos.node.nodeType == 1 && pos.node.firstChild &&
|
|
2299
|
-
(pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
|
|
2300
|
-
(pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
|
|
2301
|
-
}
|
|
2302
|
-
class BlockGapWidget extends WidgetType {
|
|
2303
|
-
constructor(height) {
|
|
2304
|
-
super();
|
|
2305
|
-
this.height = height;
|
|
2306
|
-
}
|
|
2307
|
-
toDOM() {
|
|
2308
|
-
let elt = document.createElement("div");
|
|
2309
|
-
this.updateDOM(elt);
|
|
2310
|
-
return elt;
|
|
2311
|
-
}
|
|
2312
|
-
eq(other) { return other.height == this.height; }
|
|
2313
|
-
updateDOM(elt) {
|
|
2314
|
-
elt.style.height = this.height + "px";
|
|
2315
|
-
return true;
|
|
2316
|
-
}
|
|
2317
|
-
get estimatedHeight() { return this.height; }
|
|
2318
|
-
}
|
|
2319
|
-
function computeCompositionDeco(view, changes) {
|
|
2320
|
-
let sel = view.observer.selectionRange;
|
|
2321
|
-
let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
|
|
2322
|
-
if (!textNode)
|
|
2323
|
-
return Decoration.none;
|
|
2324
|
-
let cView = view.docView.nearest(textNode);
|
|
2325
|
-
if (!cView)
|
|
2326
|
-
return Decoration.none;
|
|
2327
|
-
let from, to, topNode = textNode;
|
|
2328
|
-
if (cView instanceof LineView) {
|
|
2329
|
-
while (topNode.parentNode != cView.dom)
|
|
2330
|
-
topNode = topNode.parentNode;
|
|
2331
|
-
let prev = topNode.previousSibling;
|
|
2332
|
-
while (prev && !ContentView.get(prev))
|
|
2333
|
-
prev = prev.previousSibling;
|
|
2334
|
-
from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
|
|
2335
|
-
}
|
|
2336
|
-
else {
|
|
2337
|
-
for (;;) {
|
|
2338
|
-
let { parent } = cView;
|
|
2339
|
-
if (!parent)
|
|
2340
|
-
return Decoration.none;
|
|
2341
|
-
if (parent instanceof LineView)
|
|
2342
|
-
break;
|
|
2343
|
-
cView = parent;
|
|
2283
|
+
let view = ContentView.get(node);
|
|
2284
|
+
let fromView = view && view.overrideDOMText;
|
|
2285
|
+
let text;
|
|
2286
|
+
if (fromView != null)
|
|
2287
|
+
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
2288
|
+
else if (node.nodeType == 3)
|
|
2289
|
+
text = node.nodeValue;
|
|
2290
|
+
else if (node.nodeName == "BR")
|
|
2291
|
+
text = node.nextSibling ? this.lineBreak : "";
|
|
2292
|
+
else if (node.nodeType == 1)
|
|
2293
|
+
this.readRange(node.firstChild, null);
|
|
2294
|
+
if (text != null) {
|
|
2295
|
+
this.findPointIn(node, text.length);
|
|
2296
|
+
this.text += text;
|
|
2297
|
+
// Chrome inserts two newlines when pressing shift-enter at the
|
|
2298
|
+
// end of a line. This drops one of those.
|
|
2299
|
+
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
2300
|
+
this.text = this.text.slice(0, -1);
|
|
2344
2301
|
}
|
|
2345
|
-
from = cView.posAtStart;
|
|
2346
|
-
to = from + cView.length;
|
|
2347
|
-
topNode = cView.dom;
|
|
2348
|
-
}
|
|
2349
|
-
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2350
|
-
let text = textNode.nodeValue, { state } = view;
|
|
2351
|
-
if (newTo - newFrom < text.length) {
|
|
2352
|
-
if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
2353
|
-
newTo = newFrom + text.length;
|
|
2354
|
-
else if (state.sliceDoc(Math.max(0, newTo - text.length), newTo) == text)
|
|
2355
|
-
newFrom = newTo - text.length;
|
|
2356
|
-
else
|
|
2357
|
-
return Decoration.none;
|
|
2358
2302
|
}
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
}
|
|
2364
|
-
class CompositionWidget extends WidgetType {
|
|
2365
|
-
constructor(top, text) {
|
|
2366
|
-
super();
|
|
2367
|
-
this.top = top;
|
|
2368
|
-
this.text = text;
|
|
2303
|
+
findPointBefore(node, next) {
|
|
2304
|
+
for (let point of this.points)
|
|
2305
|
+
if (point.node == node && node.childNodes[point.offset] == next)
|
|
2306
|
+
point.pos = this.text.length;
|
|
2369
2307
|
}
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
}
|
|
2375
|
-
function nearbyTextNode(node, offset, side) {
|
|
2376
|
-
for (;;) {
|
|
2377
|
-
if (node.nodeType == 3)
|
|
2378
|
-
return node;
|
|
2379
|
-
if (node.nodeType == 1 && offset > 0 && side <= 0) {
|
|
2380
|
-
node = node.childNodes[offset - 1];
|
|
2381
|
-
offset = maxOffset(node);
|
|
2382
|
-
}
|
|
2383
|
-
else if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
|
|
2384
|
-
node = node.childNodes[offset];
|
|
2385
|
-
offset = 0;
|
|
2386
|
-
}
|
|
2387
|
-
else {
|
|
2388
|
-
return null;
|
|
2389
|
-
}
|
|
2308
|
+
findPointIn(node, maxLen) {
|
|
2309
|
+
for (let point of this.points)
|
|
2310
|
+
if (point.node == node)
|
|
2311
|
+
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
2390
2312
|
}
|
|
2391
2313
|
}
|
|
2392
|
-
function
|
|
2393
|
-
|
|
2394
|
-
return 0;
|
|
2395
|
-
return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* Before */ : 0) |
|
|
2396
|
-
(offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* After */ : 0);
|
|
2314
|
+
function isBlockElement(node) {
|
|
2315
|
+
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
2397
2316
|
}
|
|
2398
|
-
class
|
|
2399
|
-
constructor() {
|
|
2400
|
-
this.
|
|
2317
|
+
class DOMPoint {
|
|
2318
|
+
constructor(node, offset) {
|
|
2319
|
+
this.node = node;
|
|
2320
|
+
this.offset = offset;
|
|
2321
|
+
this.pos = -1;
|
|
2401
2322
|
}
|
|
2402
|
-
compareRange(from, to) { addRange(from, to, this.changes); }
|
|
2403
|
-
comparePoint(from, to) { addRange(from, to, this.changes); }
|
|
2404
|
-
}
|
|
2405
|
-
function findChangedDeco(a, b, diff) {
|
|
2406
|
-
let comp = new DecorationComparator$1;
|
|
2407
|
-
RangeSet.compare(a, b, diff, comp);
|
|
2408
|
-
return comp.changes;
|
|
2409
|
-
}
|
|
2410
|
-
function inUneditable(node, inside) {
|
|
2411
|
-
for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
|
|
2412
|
-
if (cur.nodeType == 1 && cur.contentEditable == 'false') {
|
|
2413
|
-
return true;
|
|
2414
|
-
}
|
|
2415
|
-
}
|
|
2416
|
-
return false;
|
|
2417
2323
|
}
|
|
2418
2324
|
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
//
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
// There's a lot more in
|
|
2448
|
-
// https://www.unicode.org/Public/UCD/latest/ucd/BidiBrackets.txt,
|
|
2449
|
-
// which are left out to keep code size down.
|
|
2450
|
-
for (let p of ["()", "[]", "{}"]) {
|
|
2451
|
-
let l = /*@__PURE__*/p.charCodeAt(0), r = /*@__PURE__*/p.charCodeAt(1);
|
|
2452
|
-
Brackets[l] = r;
|
|
2453
|
-
Brackets[r] = -l;
|
|
2454
|
-
}
|
|
2455
|
-
function charType(ch) {
|
|
2456
|
-
return ch <= 0xf7 ? LowTypes[ch] :
|
|
2457
|
-
0x590 <= ch && ch <= 0x5f4 ? 2 /* R */ :
|
|
2458
|
-
0x600 <= ch && ch <= 0x6f9 ? ArabicTypes[ch - 0x600] :
|
|
2459
|
-
0x6ee <= ch && ch <= 0x8ac ? 4 /* AL */ :
|
|
2460
|
-
0x2000 <= ch && ch <= 0x200b ? 256 /* NI */ :
|
|
2461
|
-
ch == 0x200c ? 256 /* NI */ : 1 /* L */;
|
|
2462
|
-
}
|
|
2463
|
-
const BidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/;
|
|
2464
|
-
/**
|
|
2465
|
-
Represents a contiguous range of text that has a single direction
|
|
2466
|
-
(as in left-to-right or right-to-left).
|
|
2467
|
-
*/
|
|
2468
|
-
class BidiSpan {
|
|
2469
|
-
/**
|
|
2470
|
-
@internal
|
|
2471
|
-
*/
|
|
2472
|
-
constructor(
|
|
2473
|
-
/**
|
|
2474
|
-
The start of the span (relative to the start of the line).
|
|
2475
|
-
*/
|
|
2476
|
-
from,
|
|
2477
|
-
/**
|
|
2478
|
-
The end of the span.
|
|
2479
|
-
*/
|
|
2480
|
-
to,
|
|
2481
|
-
/**
|
|
2482
|
-
The ["bidi
|
|
2483
|
-
level"](https://unicode.org/reports/tr9/#Basic_Display_Algorithm)
|
|
2484
|
-
of the span (in this context, 0 means
|
|
2485
|
-
left-to-right, 1 means right-to-left, 2 means left-to-right
|
|
2486
|
-
number inside right-to-left text).
|
|
2487
|
-
*/
|
|
2488
|
-
level) {
|
|
2489
|
-
this.from = from;
|
|
2490
|
-
this.to = to;
|
|
2491
|
-
this.level = level;
|
|
2325
|
+
class DocView extends ContentView {
|
|
2326
|
+
constructor(view) {
|
|
2327
|
+
super();
|
|
2328
|
+
this.view = view;
|
|
2329
|
+
this.compositionDeco = Decoration.none;
|
|
2330
|
+
this.decorations = [];
|
|
2331
|
+
// Track a minimum width for the editor. When measuring sizes in
|
|
2332
|
+
// measureVisibleLineHeights, this is updated to point at the width
|
|
2333
|
+
// of a given element and its extent in the document. When a change
|
|
2334
|
+
// happens in that range, these are reset. That way, once we've seen
|
|
2335
|
+
// a line/element of a given length, we keep the editor wide enough
|
|
2336
|
+
// to fit at least that element, until it is changed, at which point
|
|
2337
|
+
// we forget it again.
|
|
2338
|
+
this.minWidth = 0;
|
|
2339
|
+
this.minWidthFrom = 0;
|
|
2340
|
+
this.minWidthTo = 0;
|
|
2341
|
+
// Track whether the DOM selection was set in a lossy way, so that
|
|
2342
|
+
// we don't mess it up when reading it back it
|
|
2343
|
+
this.impreciseAnchor = null;
|
|
2344
|
+
this.impreciseHead = null;
|
|
2345
|
+
this.forceSelection = false;
|
|
2346
|
+
// Used by the resize observer to ignore resizes that we caused
|
|
2347
|
+
// ourselves
|
|
2348
|
+
this.lastUpdate = Date.now();
|
|
2349
|
+
this.setDOM(view.contentDOM);
|
|
2350
|
+
this.children = [new LineView];
|
|
2351
|
+
this.children[0].setParent(this);
|
|
2352
|
+
this.updateInner([new ChangedRange(0, 0, 0, view.state.doc.length)], this.updateDeco(), 0);
|
|
2492
2353
|
}
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
if (span.level == level)
|
|
2510
|
-
return i;
|
|
2511
|
-
// When multiple spans match, if assoc != 0, take the one that
|
|
2512
|
-
// covers that side, otherwise take the one with the minimum
|
|
2513
|
-
// level.
|
|
2514
|
-
if (maybe < 0 || (assoc != 0 ? (assoc < 0 ? span.from < index : span.to > index) : order[maybe].level > span.level))
|
|
2515
|
-
maybe = i;
|
|
2354
|
+
get root() { return this.view.root; }
|
|
2355
|
+
get editorView() { return this.view; }
|
|
2356
|
+
get length() { return this.view.state.doc.length; }
|
|
2357
|
+
// Update the document view to a given state. scrollIntoView can be
|
|
2358
|
+
// used as a hint to compute a new viewport that includes that
|
|
2359
|
+
// position, if we know the editor is going to scroll that position
|
|
2360
|
+
// into view.
|
|
2361
|
+
update(update) {
|
|
2362
|
+
let changedRanges = update.changedRanges;
|
|
2363
|
+
if (this.minWidth > 0 && changedRanges.length) {
|
|
2364
|
+
if (!changedRanges.every(({ fromA, toA }) => toA < this.minWidthFrom || fromA > this.minWidthTo)) {
|
|
2365
|
+
this.minWidth = this.minWidthFrom = this.minWidthTo = 0;
|
|
2366
|
+
}
|
|
2367
|
+
else {
|
|
2368
|
+
this.minWidthFrom = update.changes.mapPos(this.minWidthFrom, 1);
|
|
2369
|
+
this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
|
|
2516
2370
|
}
|
|
2517
2371
|
}
|
|
2518
|
-
if (
|
|
2519
|
-
|
|
2520
|
-
|
|
2372
|
+
if (this.view.inputState.composing < 0)
|
|
2373
|
+
this.compositionDeco = Decoration.none;
|
|
2374
|
+
else if (update.transactions.length || this.dirty)
|
|
2375
|
+
this.compositionDeco = computeCompositionDeco(this.view, update.changes);
|
|
2376
|
+
// When the DOM nodes around the selection are moved to another
|
|
2377
|
+
// parent, Chrome sometimes reports a different selection through
|
|
2378
|
+
// getSelection than the one that it actually shows to the user.
|
|
2379
|
+
// This forces a selection update when lines are joined to work
|
|
2380
|
+
// around that. Issue #54
|
|
2381
|
+
if ((browser.ie || browser.chrome) && !this.compositionDeco.size && update &&
|
|
2382
|
+
update.state.doc.lines != update.startState.doc.lines)
|
|
2383
|
+
this.forceSelection = true;
|
|
2384
|
+
let prevDeco = this.decorations, deco = this.updateDeco();
|
|
2385
|
+
let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
|
|
2386
|
+
changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
|
|
2387
|
+
if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
|
|
2388
|
+
return false;
|
|
2389
|
+
}
|
|
2390
|
+
else {
|
|
2391
|
+
this.updateInner(changedRanges, deco, update.startState.doc.length);
|
|
2392
|
+
if (update.transactions.length)
|
|
2393
|
+
this.lastUpdate = Date.now();
|
|
2394
|
+
return true;
|
|
2395
|
+
}
|
|
2521
2396
|
}
|
|
2522
|
-
|
|
2523
|
-
//
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2397
|
+
// Used by update and the constructor do perform the actual DOM
|
|
2398
|
+
// update
|
|
2399
|
+
updateInner(changes, deco, oldLength) {
|
|
2400
|
+
this.view.viewState.mustMeasureContent = true;
|
|
2401
|
+
this.updateChildren(changes, deco, oldLength);
|
|
2402
|
+
let { observer } = this.view;
|
|
2403
|
+
observer.ignore(() => {
|
|
2404
|
+
// Lock the height during redrawing, since Chrome sometimes
|
|
2405
|
+
// messes with the scroll position during DOM mutation (though
|
|
2406
|
+
// no relayout is triggered and I cannot imagine how it can
|
|
2407
|
+
// recompute the scroll position without a layout)
|
|
2408
|
+
this.dom.style.height = this.view.viewState.contentHeight + "px";
|
|
2409
|
+
this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
|
|
2410
|
+
// Chrome will sometimes, when DOM mutations occur directly
|
|
2411
|
+
// around the selection, get confused and report a different
|
|
2412
|
+
// selection from the one it displays (issue #218). This tries
|
|
2413
|
+
// to detect that situation.
|
|
2414
|
+
let track = browser.chrome || browser.ios ? { node: observer.selectionRange.focusNode, written: false } : undefined;
|
|
2415
|
+
this.sync(track);
|
|
2416
|
+
this.dirty = 0 /* Not */;
|
|
2417
|
+
if (track && (track.written || observer.selectionRange.focusNode != track.node))
|
|
2418
|
+
this.forceSelection = true;
|
|
2419
|
+
this.dom.style.height = "";
|
|
2420
|
+
});
|
|
2421
|
+
let gaps = [];
|
|
2422
|
+
if (this.view.viewport.from || this.view.viewport.to < this.view.state.doc.length)
|
|
2423
|
+
for (let child of this.children)
|
|
2424
|
+
if (child instanceof BlockWidgetView && child.widget instanceof BlockGapWidget)
|
|
2425
|
+
gaps.push(child.dom);
|
|
2426
|
+
observer.updateGaps(gaps);
|
|
2549
2427
|
}
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2428
|
+
updateChildren(changes, deco, oldLength) {
|
|
2429
|
+
let cursor = this.childCursor(oldLength);
|
|
2430
|
+
for (let i = changes.length - 1;; i--) {
|
|
2431
|
+
let next = i >= 0 ? changes[i] : null;
|
|
2432
|
+
if (!next)
|
|
2433
|
+
break;
|
|
2434
|
+
let { fromA, toA, fromB, toB } = next;
|
|
2435
|
+
let { content, breakAtStart, openStart, openEnd } = ContentBuilder.build(this.view.state.doc, fromB, toB, deco);
|
|
2436
|
+
let { i: toI, off: toOff } = cursor.findPos(toA, 1);
|
|
2437
|
+
let { i: fromI, off: fromOff } = cursor.findPos(fromA, -1);
|
|
2438
|
+
replaceRange(this, fromI, fromOff, toI, toOff, content, breakAtStart, openStart, openEnd);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
// Sync the DOM selection to this.state.selection
|
|
2442
|
+
updateSelection(mustRead = false, fromPointer = false) {
|
|
2443
|
+
if (mustRead)
|
|
2444
|
+
this.view.observer.readSelectionRange();
|
|
2445
|
+
if (!(fromPointer || this.mayControlSelection()) ||
|
|
2446
|
+
browser.ios && this.view.inputState.rapidCompositionStart)
|
|
2447
|
+
return;
|
|
2448
|
+
let force = this.forceSelection;
|
|
2449
|
+
this.forceSelection = false;
|
|
2450
|
+
let main = this.view.state.selection.main;
|
|
2451
|
+
// FIXME need to handle the case where the selection falls inside a block range
|
|
2452
|
+
let anchor = this.domAtPos(main.anchor);
|
|
2453
|
+
let head = main.empty ? anchor : this.domAtPos(main.head);
|
|
2454
|
+
// Always reset on Firefox when next to an uneditable node to
|
|
2455
|
+
// avoid invisible cursor bugs (#111)
|
|
2456
|
+
if (browser.gecko && main.empty && betweenUneditable(anchor)) {
|
|
2457
|
+
let dummy = document.createTextNode("");
|
|
2458
|
+
this.view.observer.ignore(() => anchor.node.insertBefore(dummy, anchor.node.childNodes[anchor.offset] || null));
|
|
2459
|
+
anchor = head = new DOMPos(dummy, 0);
|
|
2460
|
+
force = true;
|
|
2461
|
+
}
|
|
2462
|
+
let domSel = this.view.observer.selectionRange;
|
|
2463
|
+
// If the selection is already here, or in an equivalent position, don't touch it
|
|
2464
|
+
if (force || !domSel.focusNode ||
|
|
2465
|
+
!isEquivalentPosition(anchor.node, anchor.offset, domSel.anchorNode, domSel.anchorOffset) ||
|
|
2466
|
+
!isEquivalentPosition(head.node, head.offset, domSel.focusNode, domSel.focusOffset)) {
|
|
2467
|
+
this.view.observer.ignore(() => {
|
|
2468
|
+
// Chrome Android will hide the virtual keyboard when tapping
|
|
2469
|
+
// inside an uneditable node, and not bring it back when we
|
|
2470
|
+
// move the cursor to its proper position. This tries to
|
|
2471
|
+
// restore the keyboard by cycling focus.
|
|
2472
|
+
if (browser.android && browser.chrome && this.dom.contains(domSel.focusNode) &&
|
|
2473
|
+
inUneditable(domSel.focusNode, this.dom)) {
|
|
2474
|
+
this.dom.blur();
|
|
2475
|
+
this.dom.focus({ preventScroll: true });
|
|
2476
|
+
}
|
|
2477
|
+
let rawSel = getSelection(this.root);
|
|
2478
|
+
if (main.empty) {
|
|
2479
|
+
// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=1612076
|
|
2480
|
+
if (browser.gecko) {
|
|
2481
|
+
let nextTo = nextToUneditable(anchor.node, anchor.offset);
|
|
2482
|
+
if (nextTo && nextTo != (1 /* Before */ | 2 /* After */)) {
|
|
2483
|
+
let text = nearbyTextNode(anchor.node, anchor.offset, nextTo == 1 /* Before */ ? 1 : -1);
|
|
2484
|
+
if (text)
|
|
2485
|
+
anchor = new DOMPos(text, nextTo == 1 /* Before */ ? 0 : text.nodeValue.length);
|
|
2486
|
+
}
|
|
2487
|
+
}
|
|
2488
|
+
rawSel.collapse(anchor.node, anchor.offset);
|
|
2489
|
+
if (main.bidiLevel != null && domSel.cursorBidiLevel != null)
|
|
2490
|
+
domSel.cursorBidiLevel = main.bidiLevel;
|
|
2491
|
+
}
|
|
2492
|
+
else if (rawSel.extend) {
|
|
2493
|
+
// Selection.extend can be used to create an 'inverted' selection
|
|
2494
|
+
// (one where the focus is before the anchor), but not all
|
|
2495
|
+
// browsers support it yet.
|
|
2496
|
+
rawSel.collapse(anchor.node, anchor.offset);
|
|
2497
|
+
rawSel.extend(head.node, head.offset);
|
|
2498
|
+
}
|
|
2499
|
+
else {
|
|
2500
|
+
// Primitive (IE) way
|
|
2501
|
+
let range = document.createRange();
|
|
2502
|
+
if (main.anchor > main.head)
|
|
2503
|
+
[anchor, head] = [head, anchor];
|
|
2504
|
+
range.setEnd(head.node, head.offset);
|
|
2505
|
+
range.setStart(anchor.node, anchor.offset);
|
|
2506
|
+
rawSel.removeAllRanges();
|
|
2507
|
+
rawSel.addRange(range);
|
|
2508
|
+
}
|
|
2509
|
+
});
|
|
2510
|
+
this.view.observer.setSelectionRange(anchor, head);
|
|
2511
|
+
}
|
|
2512
|
+
this.impreciseAnchor = anchor.precise ? null : new DOMPos(domSel.anchorNode, domSel.anchorOffset);
|
|
2513
|
+
this.impreciseHead = head.precise ? null : new DOMPos(domSel.focusNode, domSel.focusOffset);
|
|
2514
|
+
}
|
|
2515
|
+
enforceCursorAssoc() {
|
|
2516
|
+
if (this.compositionDeco.size)
|
|
2517
|
+
return;
|
|
2518
|
+
let cursor = this.view.state.selection.main;
|
|
2519
|
+
let sel = getSelection(this.root);
|
|
2520
|
+
if (!cursor.empty || !cursor.assoc || !sel.modify)
|
|
2521
|
+
return;
|
|
2522
|
+
let line = LineView.find(this, cursor.head);
|
|
2523
|
+
if (!line)
|
|
2524
|
+
return;
|
|
2525
|
+
let lineStart = line.posAtStart;
|
|
2526
|
+
if (cursor.head == lineStart || cursor.head == lineStart + line.length)
|
|
2527
|
+
return;
|
|
2528
|
+
let before = this.coordsAt(cursor.head, -1), after = this.coordsAt(cursor.head, 1);
|
|
2529
|
+
if (!before || !after || before.bottom > after.top)
|
|
2530
|
+
return;
|
|
2531
|
+
let dom = this.domAtPos(cursor.head + cursor.assoc);
|
|
2532
|
+
sel.collapse(dom.node, dom.offset);
|
|
2533
|
+
sel.modify("move", cursor.assoc < 0 ? "forward" : "backward", "lineboundary");
|
|
2534
|
+
}
|
|
2535
|
+
mayControlSelection() {
|
|
2536
|
+
return this.view.state.facet(editable) ? this.root.activeElement == this.dom
|
|
2537
|
+
: hasSelection(this.dom, this.view.observer.selectionRange);
|
|
2538
|
+
}
|
|
2539
|
+
nearest(dom) {
|
|
2540
|
+
for (let cur = dom; cur;) {
|
|
2541
|
+
let domView = ContentView.get(cur);
|
|
2542
|
+
if (domView && domView.rootView == this)
|
|
2543
|
+
return domView;
|
|
2544
|
+
cur = cur.parentNode;
|
|
2565
2545
|
}
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2546
|
+
return null;
|
|
2547
|
+
}
|
|
2548
|
+
posFromDOM(node, offset) {
|
|
2549
|
+
let view = this.nearest(node);
|
|
2550
|
+
if (!view)
|
|
2551
|
+
throw new RangeError("Trying to find position for a DOM position outside of the document");
|
|
2552
|
+
return view.localPosFromDOM(node, offset) + view.posAtStart;
|
|
2553
|
+
}
|
|
2554
|
+
domAtPos(pos) {
|
|
2555
|
+
let { i, off } = this.childCursor().findPos(pos, -1);
|
|
2556
|
+
for (; i < this.children.length - 1;) {
|
|
2557
|
+
let child = this.children[i];
|
|
2558
|
+
if (off < child.length || child instanceof LineView)
|
|
2559
|
+
break;
|
|
2560
|
+
i++;
|
|
2561
|
+
off = 0;
|
|
2574
2562
|
}
|
|
2575
|
-
|
|
2576
|
-
|
|
2563
|
+
return this.children[i].domAtPos(off);
|
|
2564
|
+
}
|
|
2565
|
+
coordsAt(pos, side) {
|
|
2566
|
+
for (let off = this.length, i = this.children.length - 1;; i--) {
|
|
2567
|
+
let child = this.children[i], start = off - child.breakAfter - child.length;
|
|
2568
|
+
if (pos > start ||
|
|
2569
|
+
(pos == start && child.type != BlockType.WidgetBefore && child.type != BlockType.WidgetAfter &&
|
|
2570
|
+
(!i || side == 2 || this.children[i - 1].breakAfter ||
|
|
2571
|
+
(this.children[i - 1].type == BlockType.WidgetBefore && side > -2))))
|
|
2572
|
+
return child.coordsAt(pos - start, side);
|
|
2573
|
+
off = start;
|
|
2577
2574
|
}
|
|
2578
|
-
prev = type;
|
|
2579
|
-
if (type & 7 /* Strong */)
|
|
2580
|
-
prevStrong = type;
|
|
2581
2575
|
}
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2576
|
+
measureVisibleLineHeights() {
|
|
2577
|
+
let result = [], { from, to } = this.view.viewState.viewport;
|
|
2578
|
+
let contentWidth = this.view.contentDOM.clientWidth;
|
|
2579
|
+
let isWider = contentWidth > Math.max(this.view.scrollDOM.clientWidth, this.minWidth) + 1;
|
|
2580
|
+
let widest = -1;
|
|
2581
|
+
for (let pos = 0, i = 0; i < this.children.length; i++) {
|
|
2582
|
+
let child = this.children[i], end = pos + child.length;
|
|
2583
|
+
if (end > to)
|
|
2584
|
+
break;
|
|
2585
|
+
if (pos >= from) {
|
|
2586
|
+
let childRect = child.dom.getBoundingClientRect();
|
|
2587
|
+
result.push(childRect.height);
|
|
2588
|
+
if (isWider) {
|
|
2589
|
+
let last = child.dom.lastChild;
|
|
2590
|
+
let rects = last ? clientRectsFor(last) : [];
|
|
2591
|
+
if (rects.length) {
|
|
2592
|
+
let rect = rects[rects.length - 1];
|
|
2593
|
+
let width = this.view.textDirection == Direction.LTR ? rect.right - childRect.left
|
|
2594
|
+
: childRect.right - rect.left;
|
|
2595
|
+
if (width > widest) {
|
|
2596
|
+
widest = width;
|
|
2597
|
+
this.minWidth = contentWidth;
|
|
2598
|
+
this.minWidthFrom = pos;
|
|
2599
|
+
this.minWidthTo = end;
|
|
2600
|
+
}
|
|
2601
2601
|
}
|
|
2602
2602
|
}
|
|
2603
2603
|
}
|
|
2604
|
-
|
|
2605
|
-
break;
|
|
2606
|
-
}
|
|
2607
|
-
else {
|
|
2608
|
-
BracketStack[sI++] = i;
|
|
2609
|
-
BracketStack[sI++] = ch;
|
|
2610
|
-
BracketStack[sI++] = context;
|
|
2611
|
-
}
|
|
2604
|
+
pos = end + child.breakAfter;
|
|
2612
2605
|
}
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
BracketStack[sJ + 2] |= 2 /* EmbedInside */;
|
|
2622
|
-
}
|
|
2623
|
-
else {
|
|
2624
|
-
if (cur & 4 /* OppositeInside */)
|
|
2625
|
-
break;
|
|
2626
|
-
BracketStack[sJ + 2] |= 4 /* OppositeInside */;
|
|
2627
|
-
}
|
|
2606
|
+
return result;
|
|
2607
|
+
}
|
|
2608
|
+
measureTextSize() {
|
|
2609
|
+
for (let child of this.children) {
|
|
2610
|
+
if (child instanceof LineView) {
|
|
2611
|
+
let measure = child.measureTextSize();
|
|
2612
|
+
if (measure)
|
|
2613
|
+
return measure;
|
|
2628
2614
|
}
|
|
2629
2615
|
}
|
|
2616
|
+
// If no workable line exists, force a layout of a measurable element
|
|
2617
|
+
let dummy = document.createElement("div"), lineHeight, charWidth;
|
|
2618
|
+
dummy.className = "cm-line";
|
|
2619
|
+
dummy.textContent = "abc def ghi jkl mno pqr stu";
|
|
2620
|
+
this.view.observer.ignore(() => {
|
|
2621
|
+
this.dom.appendChild(dummy);
|
|
2622
|
+
let rect = clientRectsFor(dummy.firstChild)[0];
|
|
2623
|
+
lineHeight = dummy.getBoundingClientRect().height;
|
|
2624
|
+
charWidth = rect ? rect.width / 27 : 7;
|
|
2625
|
+
dummy.remove();
|
|
2626
|
+
});
|
|
2627
|
+
return { lineHeight, charWidth };
|
|
2630
2628
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
if (types[i] == 256 /* NI */) {
|
|
2640
|
-
let end = i + 1;
|
|
2641
|
-
while (end < len && types[end] == 256 /* NI */)
|
|
2642
|
-
end++;
|
|
2643
|
-
let beforeL = (i ? types[i - 1] : outerType) == 1 /* L */;
|
|
2644
|
-
let afterL = (end < len ? types[end] : outerType) == 1 /* L */;
|
|
2645
|
-
let replace = beforeL == afterL ? (beforeL ? 1 /* L */ : 2 /* R */) : outerType;
|
|
2646
|
-
for (let j = i; j < end; j++)
|
|
2647
|
-
types[j] = replace;
|
|
2648
|
-
i = end - 1;
|
|
2649
|
-
}
|
|
2629
|
+
childCursor(pos = this.length) {
|
|
2630
|
+
// Move back to start of last element when possible, so that
|
|
2631
|
+
// `ChildCursor.findPos` doesn't have to deal with the edge case
|
|
2632
|
+
// of being after the last element.
|
|
2633
|
+
let i = this.children.length;
|
|
2634
|
+
if (i)
|
|
2635
|
+
pos -= this.children[--i].length;
|
|
2636
|
+
return new ChildCursor(this.children, pos, i);
|
|
2650
2637
|
}
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
let start = i, rtl = types[i++] != 1 /* L */;
|
|
2660
|
-
while (i < len && rtl == (types[i] != 1 /* L */))
|
|
2661
|
-
i++;
|
|
2662
|
-
if (rtl) {
|
|
2663
|
-
for (let j = i; j > start;) {
|
|
2664
|
-
let end = j, l = types[--j] != 2 /* R */;
|
|
2665
|
-
while (j > start && l == (types[j - 1] != 2 /* R */))
|
|
2666
|
-
j--;
|
|
2667
|
-
order.push(new BidiSpan(j, end, l ? 2 : 1));
|
|
2668
|
-
}
|
|
2669
|
-
}
|
|
2670
|
-
else {
|
|
2671
|
-
order.push(new BidiSpan(start, i, 0));
|
|
2638
|
+
computeBlockGapDeco() {
|
|
2639
|
+
let deco = [], vs = this.view.viewState;
|
|
2640
|
+
for (let pos = 0, i = 0;; i++) {
|
|
2641
|
+
let next = i == vs.viewports.length ? null : vs.viewports[i];
|
|
2642
|
+
let end = next ? next.from - 1 : this.length;
|
|
2643
|
+
if (end > pos) {
|
|
2644
|
+
let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
|
|
2645
|
+
deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
|
|
2672
2646
|
}
|
|
2647
|
+
if (!next)
|
|
2648
|
+
break;
|
|
2649
|
+
pos = next.to + 1;
|
|
2673
2650
|
}
|
|
2651
|
+
return Decoration.set(deco);
|
|
2652
|
+
}
|
|
2653
|
+
updateDeco() {
|
|
2654
|
+
return this.decorations = [
|
|
2655
|
+
...this.view.pluginField(PluginField.decorations),
|
|
2656
|
+
...this.view.state.facet(decorations),
|
|
2657
|
+
this.compositionDeco,
|
|
2658
|
+
this.computeBlockGapDeco(),
|
|
2659
|
+
this.view.viewState.lineGapDeco
|
|
2660
|
+
];
|
|
2661
|
+
}
|
|
2662
|
+
scrollIntoView({ range, center }) {
|
|
2663
|
+
let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
|
|
2664
|
+
if (!rect)
|
|
2665
|
+
return;
|
|
2666
|
+
if (!range.empty && (other = this.coordsAt(range.anchor, range.anchor > range.head ? -1 : 1)))
|
|
2667
|
+
rect = { left: Math.min(rect.left, other.left), top: Math.min(rect.top, other.top),
|
|
2668
|
+
right: Math.max(rect.right, other.right), bottom: Math.max(rect.bottom, other.bottom) };
|
|
2669
|
+
let mLeft = 0, mRight = 0, mTop = 0, mBottom = 0;
|
|
2670
|
+
for (let margins of this.view.pluginField(PluginField.scrollMargins))
|
|
2671
|
+
if (margins) {
|
|
2672
|
+
let { left, right, top, bottom } = margins;
|
|
2673
|
+
if (left != null)
|
|
2674
|
+
mLeft = Math.max(mLeft, left);
|
|
2675
|
+
if (right != null)
|
|
2676
|
+
mRight = Math.max(mRight, right);
|
|
2677
|
+
if (top != null)
|
|
2678
|
+
mTop = Math.max(mTop, top);
|
|
2679
|
+
if (bottom != null)
|
|
2680
|
+
mBottom = Math.max(mBottom, bottom);
|
|
2681
|
+
}
|
|
2682
|
+
scrollRectIntoView(this.view.scrollDOM, {
|
|
2683
|
+
left: rect.left - mLeft, top: rect.top - mTop,
|
|
2684
|
+
right: rect.right + mRight, bottom: rect.bottom + mBottom
|
|
2685
|
+
}, range.head < range.anchor ? -1 : 1, center);
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
function betweenUneditable(pos) {
|
|
2689
|
+
return pos.node.nodeType == 1 && pos.node.firstChild &&
|
|
2690
|
+
(pos.offset == 0 || pos.node.childNodes[pos.offset - 1].contentEditable == "false") &&
|
|
2691
|
+
(pos.offset == pos.node.childNodes.length || pos.node.childNodes[pos.offset].contentEditable == "false");
|
|
2692
|
+
}
|
|
2693
|
+
class BlockGapWidget extends WidgetType {
|
|
2694
|
+
constructor(height) {
|
|
2695
|
+
super();
|
|
2696
|
+
this.height = height;
|
|
2697
|
+
}
|
|
2698
|
+
toDOM() {
|
|
2699
|
+
let elt = document.createElement("div");
|
|
2700
|
+
this.updateDOM(elt);
|
|
2701
|
+
return elt;
|
|
2702
|
+
}
|
|
2703
|
+
eq(other) { return other.height == this.height; }
|
|
2704
|
+
updateDOM(elt) {
|
|
2705
|
+
elt.style.height = this.height + "px";
|
|
2706
|
+
return true;
|
|
2707
|
+
}
|
|
2708
|
+
get estimatedHeight() { return this.height; }
|
|
2709
|
+
}
|
|
2710
|
+
function computeCompositionDeco(view, changes) {
|
|
2711
|
+
let sel = view.observer.selectionRange;
|
|
2712
|
+
let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
|
|
2713
|
+
if (!textNode)
|
|
2714
|
+
return Decoration.none;
|
|
2715
|
+
let cView = view.docView.nearest(textNode);
|
|
2716
|
+
if (!cView)
|
|
2717
|
+
return Decoration.none;
|
|
2718
|
+
let from, to, topNode = textNode;
|
|
2719
|
+
if (cView instanceof LineView) {
|
|
2720
|
+
while (topNode.parentNode != cView.dom)
|
|
2721
|
+
topNode = topNode.parentNode;
|
|
2722
|
+
let prev = topNode.previousSibling;
|
|
2723
|
+
while (prev && !ContentView.get(prev))
|
|
2724
|
+
prev = prev.previousSibling;
|
|
2725
|
+
from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
|
|
2674
2726
|
}
|
|
2675
2727
|
else {
|
|
2676
|
-
for (
|
|
2677
|
-
let
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2728
|
+
for (;;) {
|
|
2729
|
+
let { parent } = cView;
|
|
2730
|
+
if (!parent)
|
|
2731
|
+
return Decoration.none;
|
|
2732
|
+
if (parent instanceof LineView)
|
|
2733
|
+
break;
|
|
2734
|
+
cView = parent;
|
|
2681
2735
|
}
|
|
2736
|
+
from = cView.posAtStart;
|
|
2737
|
+
to = from + cView.length;
|
|
2738
|
+
topNode = cView.dom;
|
|
2682
2739
|
}
|
|
2683
|
-
|
|
2740
|
+
let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
|
|
2741
|
+
let { state } = view, text = topNode.nodeType == 3 ? topNode.nodeValue :
|
|
2742
|
+
new DOMReader([], view).readRange(topNode.firstChild, null).text;
|
|
2743
|
+
if (newTo - newFrom < text.length) {
|
|
2744
|
+
if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
|
|
2745
|
+
newTo = newFrom + text.length;
|
|
2746
|
+
else if (state.sliceDoc(Math.max(0, newTo - text.length), newTo) == text)
|
|
2747
|
+
newFrom = newTo - text.length;
|
|
2748
|
+
else
|
|
2749
|
+
return Decoration.none;
|
|
2750
|
+
}
|
|
2751
|
+
else if (state.sliceDoc(newFrom, newTo) != text) {
|
|
2752
|
+
return Decoration.none;
|
|
2753
|
+
}
|
|
2754
|
+
return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
|
|
2684
2755
|
}
|
|
2685
|
-
|
|
2686
|
-
|
|
2756
|
+
class CompositionWidget extends WidgetType {
|
|
2757
|
+
constructor(top, text) {
|
|
2758
|
+
super();
|
|
2759
|
+
this.top = top;
|
|
2760
|
+
this.text = text;
|
|
2761
|
+
}
|
|
2762
|
+
eq(other) { return this.top == other.top && this.text == other.text; }
|
|
2763
|
+
toDOM() { return this.top; }
|
|
2764
|
+
ignoreEvent() { return false; }
|
|
2765
|
+
get customView() { return CompositionView; }
|
|
2687
2766
|
}
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
if (order[0].level != dir) {
|
|
2696
|
-
startIndex = order[0].side(false, dir);
|
|
2697
|
-
spanI = 0;
|
|
2767
|
+
function nearbyTextNode(node, offset, side) {
|
|
2768
|
+
for (;;) {
|
|
2769
|
+
if (node.nodeType == 3)
|
|
2770
|
+
return node;
|
|
2771
|
+
if (node.nodeType == 1 && offset > 0 && side <= 0) {
|
|
2772
|
+
node = node.childNodes[offset - 1];
|
|
2773
|
+
offset = maxOffset(node);
|
|
2698
2774
|
}
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2775
|
+
else if (node.nodeType == 1 && offset < node.childNodes.length && side >= 0) {
|
|
2776
|
+
node = node.childNodes[offset];
|
|
2777
|
+
offset = 0;
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2702
2780
|
return null;
|
|
2703
|
-
let last = order[order.length - 1];
|
|
2704
|
-
if (last.level != dir) {
|
|
2705
|
-
startIndex = last.side(true, dir);
|
|
2706
|
-
spanI = order.length - 1;
|
|
2707
2781
|
}
|
|
2708
2782
|
}
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2783
|
+
}
|
|
2784
|
+
function nextToUneditable(node, offset) {
|
|
2785
|
+
if (node.nodeType != 1)
|
|
2786
|
+
return 0;
|
|
2787
|
+
return (offset && node.childNodes[offset - 1].contentEditable == "false" ? 1 /* Before */ : 0) |
|
|
2788
|
+
(offset < node.childNodes.length && node.childNodes[offset].contentEditable == "false" ? 2 /* After */ : 0);
|
|
2789
|
+
}
|
|
2790
|
+
class DecorationComparator$1 {
|
|
2791
|
+
constructor() {
|
|
2792
|
+
this.changes = [];
|
|
2716
2793
|
}
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2794
|
+
compareRange(from, to) { addRange(from, to, this.changes); }
|
|
2795
|
+
comparePoint(from, to) { addRange(from, to, this.changes); }
|
|
2796
|
+
}
|
|
2797
|
+
function findChangedDeco(a, b, diff) {
|
|
2798
|
+
let comp = new DecorationComparator$1;
|
|
2799
|
+
RangeSet.compare(a, b, diff, comp);
|
|
2800
|
+
return comp.changes;
|
|
2801
|
+
}
|
|
2802
|
+
function inUneditable(node, inside) {
|
|
2803
|
+
for (let cur = node; cur && cur != inside; cur = cur.assignedSlot || cur.parentNode) {
|
|
2804
|
+
if (cur.nodeType == 1 && cur.contentEditable == 'false') {
|
|
2805
|
+
return true;
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
return false;
|
|
2728
2809
|
}
|
|
2729
2810
|
|
|
2730
2811
|
function groupAt(state, pos, bias = 1) {
|
|
@@ -2862,21 +2943,29 @@ function domPosInText(node, x, y) {
|
|
|
2862
2943
|
function posAtCoords(view, { x, y }, precise, bias = -1) {
|
|
2863
2944
|
var _a;
|
|
2864
2945
|
let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
|
|
2865
|
-
let
|
|
2866
|
-
|
|
2867
|
-
|
|
2946
|
+
let block, yOffset = y - docTop, { docHeight } = view.viewState;
|
|
2947
|
+
if (yOffset < 0 || yOffset > docHeight) {
|
|
2948
|
+
if (precise)
|
|
2949
|
+
return null;
|
|
2950
|
+
yOffset = yOffset < 0 ? 0 : docHeight;
|
|
2951
|
+
}
|
|
2952
|
+
// Scan for a text block near the queried y position
|
|
2953
|
+
for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
|
|
2868
2954
|
block = view.elementAtHeight(yOffset);
|
|
2869
|
-
if (block.
|
|
2870
|
-
|
|
2871
|
-
|
|
2955
|
+
if (block.type == BlockType.Text)
|
|
2956
|
+
break;
|
|
2957
|
+
for (;;) {
|
|
2958
|
+
// Move the y position out of this block
|
|
2959
|
+
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2960
|
+
if (yOffset >= 0 && yOffset <= docHeight)
|
|
2961
|
+
break;
|
|
2962
|
+
// If the document consists entirely of replaced widgets, we
|
|
2963
|
+
// won't find a text block, so return 0
|
|
2872
2964
|
if (bounced)
|
|
2873
2965
|
return precise ? null : 0;
|
|
2874
|
-
|
|
2875
|
-
|
|
2966
|
+
bounced = true;
|
|
2967
|
+
bias = -bias;
|
|
2876
2968
|
}
|
|
2877
|
-
if (block.type == BlockType.Text)
|
|
2878
|
-
break;
|
|
2879
|
-
yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
|
|
2880
2969
|
}
|
|
2881
2970
|
y = docTop + yOffset;
|
|
2882
2971
|
let lineStart = block.from;
|
|
@@ -3034,14 +3123,6 @@ class InputState {
|
|
|
3034
3123
|
constructor(view) {
|
|
3035
3124
|
this.lastKeyCode = 0;
|
|
3036
3125
|
this.lastKeyTime = 0;
|
|
3037
|
-
// On Chrome Android, backspace near widgets is just completely
|
|
3038
|
-
// broken, and there are no key events, so we need to handle the
|
|
3039
|
-
// beforeinput event. Deleting stuff will often create a flurry of
|
|
3040
|
-
// events, and interrupting it before it is done just makes
|
|
3041
|
-
// subsequent events even more broken, so again, we hold off doing
|
|
3042
|
-
// anything until the browser is finished with whatever it is trying
|
|
3043
|
-
// to do.
|
|
3044
|
-
this.pendingAndroidKey = undefined;
|
|
3045
3126
|
// On iOS, some keys need to have their default behavior happen
|
|
3046
3127
|
// (after which we retroactively handle them and reset the DOM) to
|
|
3047
3128
|
// avoid messing up the virtual keyboard state.
|
|
@@ -3110,22 +3191,15 @@ class InputState {
|
|
|
3110
3191
|
}
|
|
3111
3192
|
runCustomHandlers(type, view, event) {
|
|
3112
3193
|
for (let set of this.customHandlers) {
|
|
3113
|
-
let handler = set.handlers[type]
|
|
3194
|
+
let handler = set.handlers[type];
|
|
3114
3195
|
if (handler) {
|
|
3115
3196
|
try {
|
|
3116
|
-
|
|
3197
|
+
if (handler.call(set.plugin, event, view))
|
|
3198
|
+
return true;
|
|
3117
3199
|
}
|
|
3118
3200
|
catch (e) {
|
|
3119
3201
|
logException(view.state, e);
|
|
3120
3202
|
}
|
|
3121
|
-
if (handled || event.defaultPrevented) {
|
|
3122
|
-
// Chrome for Android often applies a bunch of nonsensical
|
|
3123
|
-
// DOM changes after an enter press, even when
|
|
3124
|
-
// preventDefault-ed. This tries to ignore those.
|
|
3125
|
-
if (browser.android && type == "keydown" && event.keyCode == 13)
|
|
3126
|
-
view.observer.flushSoon();
|
|
3127
|
-
return true;
|
|
3128
|
-
}
|
|
3129
3203
|
}
|
|
3130
3204
|
}
|
|
3131
3205
|
return false;
|
|
@@ -3149,6 +3223,16 @@ class InputState {
|
|
|
3149
3223
|
this.lastKeyTime = Date.now();
|
|
3150
3224
|
if (this.screenKeyEvent(view, event))
|
|
3151
3225
|
return true;
|
|
3226
|
+
// Chrome for Android usually doesn't fire proper key events, but
|
|
3227
|
+
// occasionally does, usually surrounded by a bunch of complicated
|
|
3228
|
+
// composition changes. When an enter or backspace key event is
|
|
3229
|
+
// seen, hold off on handling DOM events for a bit, and then
|
|
3230
|
+
// dispatch it.
|
|
3231
|
+
if (browser.android && browser.chrome && !event.synthetic &&
|
|
3232
|
+
(event.keyCode == 13 || event.keyCode == 8)) {
|
|
3233
|
+
view.observer.delayAndroidKey(event.key, event.keyCode);
|
|
3234
|
+
return true;
|
|
3235
|
+
}
|
|
3152
3236
|
// Prevent the default behavior of Enter on iOS makes the
|
|
3153
3237
|
// virtual keyboard get stuck in the wrong (lowercase)
|
|
3154
3238
|
// state. So we let it go through, and then, in
|
|
@@ -3170,24 +3254,6 @@ class InputState {
|
|
|
3170
3254
|
this.pendingIOSKey = undefined;
|
|
3171
3255
|
return dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3172
3256
|
}
|
|
3173
|
-
// This causes the DOM observer to pause for a bit, and sets an
|
|
3174
|
-
// animation frame (which seems the most reliable way to detect
|
|
3175
|
-
// 'Chrome is done flailing about messing with the DOM') to fire a
|
|
3176
|
-
// fake key event and re-sync the view again.
|
|
3177
|
-
setPendingAndroidKey(view, pending) {
|
|
3178
|
-
this.pendingAndroidKey = pending;
|
|
3179
|
-
requestAnimationFrame(() => {
|
|
3180
|
-
let key = this.pendingAndroidKey;
|
|
3181
|
-
if (!key)
|
|
3182
|
-
return;
|
|
3183
|
-
this.pendingAndroidKey = undefined;
|
|
3184
|
-
view.observer.processRecords();
|
|
3185
|
-
let startState = view.state;
|
|
3186
|
-
dispatchKey(view.contentDOM, key.key, key.keyCode);
|
|
3187
|
-
if (view.state == startState)
|
|
3188
|
-
view.docView.reset(true);
|
|
3189
|
-
});
|
|
3190
|
-
}
|
|
3191
3257
|
ignoreDuringComposition(event) {
|
|
3192
3258
|
if (!/^key/.test(event.type))
|
|
3193
3259
|
return false;
|
|
@@ -3217,10 +3283,10 @@ class InputState {
|
|
|
3217
3283
|
return (event.type == "keydown" && event.keyCode != 229) ||
|
|
3218
3284
|
event.type == "compositionend" && !browser.ios;
|
|
3219
3285
|
}
|
|
3220
|
-
startMouseSelection(
|
|
3286
|
+
startMouseSelection(mouseSelection) {
|
|
3221
3287
|
if (this.mouseSelection)
|
|
3222
3288
|
this.mouseSelection.destroy();
|
|
3223
|
-
this.mouseSelection =
|
|
3289
|
+
this.mouseSelection = mouseSelection;
|
|
3224
3290
|
}
|
|
3225
3291
|
update(update) {
|
|
3226
3292
|
if (this.mouseSelection)
|
|
@@ -3241,10 +3307,10 @@ const PendingKeys = [
|
|
|
3241
3307
|
// Key codes for modifier keys
|
|
3242
3308
|
const modifierCodes = [16, 17, 18, 20, 91, 92, 224, 225];
|
|
3243
3309
|
class MouseSelection {
|
|
3244
|
-
constructor(
|
|
3245
|
-
this.inputState = inputState;
|
|
3310
|
+
constructor(view, startEvent, style, mustSelect) {
|
|
3246
3311
|
this.view = view;
|
|
3247
3312
|
this.style = style;
|
|
3313
|
+
this.mustSelect = mustSelect;
|
|
3248
3314
|
this.lastEvent = startEvent;
|
|
3249
3315
|
let doc = view.contentDOM.ownerDocument;
|
|
3250
3316
|
doc.addEventListener("mousemove", this.move = this.move.bind(this));
|
|
@@ -3278,16 +3344,18 @@ class MouseSelection {
|
|
|
3278
3344
|
let doc = this.view.contentDOM.ownerDocument;
|
|
3279
3345
|
doc.removeEventListener("mousemove", this.move);
|
|
3280
3346
|
doc.removeEventListener("mouseup", this.up);
|
|
3281
|
-
this.inputState.mouseSelection = null;
|
|
3347
|
+
this.view.inputState.mouseSelection = null;
|
|
3282
3348
|
}
|
|
3283
3349
|
select(event) {
|
|
3284
3350
|
let selection = this.style.get(event, this.extend, this.multiple);
|
|
3285
|
-
if (!selection.eq(this.view.state.selection) ||
|
|
3351
|
+
if (this.mustSelect || !selection.eq(this.view.state.selection) ||
|
|
3352
|
+
selection.main.assoc != this.view.state.selection.main.assoc)
|
|
3286
3353
|
this.view.dispatch({
|
|
3287
3354
|
selection,
|
|
3288
3355
|
userEvent: "select.pointer",
|
|
3289
3356
|
scrollIntoView: true
|
|
3290
3357
|
});
|
|
3358
|
+
this.mustSelect = false;
|
|
3291
3359
|
}
|
|
3292
3360
|
update(update) {
|
|
3293
3361
|
if (update.docChanged && this.dragging)
|
|
@@ -3406,9 +3474,10 @@ handlers.mousedown = (view, event) => {
|
|
|
3406
3474
|
if (!style && event.button == 0)
|
|
3407
3475
|
style = basicMouseSelection(view, event);
|
|
3408
3476
|
if (style) {
|
|
3409
|
-
|
|
3477
|
+
let mustFocus = view.root.activeElement != view.contentDOM;
|
|
3478
|
+
if (mustFocus)
|
|
3410
3479
|
view.observer.ignore(() => focusPreventScroll(view.contentDOM));
|
|
3411
|
-
view.inputState.startMouseSelection(view, event, style);
|
|
3480
|
+
view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
|
|
3412
3481
|
}
|
|
3413
3482
|
};
|
|
3414
3483
|
function rangeForClick(view, pos, bias, type) {
|
|
@@ -3663,12 +3732,12 @@ handlers.compositionstart = handlers.compositionupdate = view => {
|
|
|
3663
3732
|
if (view.inputState.compositionFirstChange == null)
|
|
3664
3733
|
view.inputState.compositionFirstChange = true;
|
|
3665
3734
|
if (view.inputState.composing < 0) {
|
|
3735
|
+
// FIXME possibly set a timeout to clear it again on Android
|
|
3736
|
+
view.inputState.composing = 0;
|
|
3666
3737
|
if (view.docView.compositionDeco.size) {
|
|
3667
3738
|
view.observer.flush();
|
|
3668
3739
|
forceClearComposition(view, true);
|
|
3669
3740
|
}
|
|
3670
|
-
// FIXME possibly set a timeout to clear it again on Android
|
|
3671
|
-
view.inputState.composing = 0;
|
|
3672
3741
|
}
|
|
3673
3742
|
};
|
|
3674
3743
|
handlers.compositionend = view => {
|
|
@@ -3694,7 +3763,7 @@ handlers.beforeinput = (view, event) => {
|
|
|
3694
3763
|
// seems to do nothing at all on Chrome).
|
|
3695
3764
|
let pending;
|
|
3696
3765
|
if (browser.chrome && browser.android && (pending = PendingKeys.find(key => key.inputType == event.inputType))) {
|
|
3697
|
-
view.
|
|
3766
|
+
view.observer.delayAndroidKey(pending.key, pending.keyCode);
|
|
3698
3767
|
if (pending.key == "Backspace" || pending.key == "Delete") {
|
|
3699
3768
|
let startViewHeight = ((_a = window.visualViewport) === null || _a === void 0 ? void 0 : _a.height) || 0;
|
|
3700
3769
|
setTimeout(() => {
|
|
@@ -4399,8 +4468,8 @@ function visiblePixelRange(dom, paddingTop) {
|
|
|
4399
4468
|
break;
|
|
4400
4469
|
}
|
|
4401
4470
|
}
|
|
4402
|
-
return { left: left - rect.left, right: right - rect.left,
|
|
4403
|
-
top: top - (rect.top + paddingTop), bottom: bottom - (rect.top + paddingTop) };
|
|
4471
|
+
return { left: left - rect.left, right: Math.max(left, right) - rect.left,
|
|
4472
|
+
top: top - (rect.top + paddingTop), bottom: Math.max(top, bottom) - (rect.top + paddingTop) };
|
|
4404
4473
|
}
|
|
4405
4474
|
// Line gaps are placeholder widgets used to hide pieces of overlong
|
|
4406
4475
|
// lines within the viewport, as a kludge to keep the editor
|
|
@@ -4627,7 +4696,7 @@ class ViewState {
|
|
|
4627
4696
|
let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
|
|
4628
4697
|
// If scrollTarget is given, make sure the viewport includes that position
|
|
4629
4698
|
if (scrollTarget) {
|
|
4630
|
-
let { head } = scrollTarget.range, viewHeight =
|
|
4699
|
+
let { head } = scrollTarget.range, viewHeight = this.editorHeight;
|
|
4631
4700
|
if (head < viewport.from || head > viewport.to) {
|
|
4632
4701
|
let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
|
|
4633
4702
|
if (scrollTarget.center)
|
|
@@ -4648,6 +4717,8 @@ class ViewState {
|
|
|
4648
4717
|
// Checks if a given viewport covers the visible part of the
|
|
4649
4718
|
// document and not too much beyond that.
|
|
4650
4719
|
viewportIsAppropriate({ from, to }, bias = 0) {
|
|
4720
|
+
if (!this.inView)
|
|
4721
|
+
return true;
|
|
4651
4722
|
let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4652
4723
|
let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0);
|
|
4653
4724
|
let { visibleTop, visibleBottom } = this;
|
|
@@ -4754,8 +4825,11 @@ class ViewState {
|
|
|
4754
4825
|
elementAtHeight(height) {
|
|
4755
4826
|
return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
|
|
4756
4827
|
}
|
|
4828
|
+
get docHeight() {
|
|
4829
|
+
return this.scaler.toDOM(this.heightMap.height);
|
|
4830
|
+
}
|
|
4757
4831
|
get contentHeight() {
|
|
4758
|
-
return this.
|
|
4832
|
+
return this.docHeight + this.paddingTop + this.paddingBottom;
|
|
4759
4833
|
}
|
|
4760
4834
|
}
|
|
4761
4835
|
class Viewport {
|
|
@@ -4985,11 +5059,13 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
4985
5059
|
// recomputation.
|
|
4986
5060
|
"@keyframes cm-blink": { "0%": {}, "50%": { visibility: "hidden" }, "100%": {} },
|
|
4987
5061
|
"@keyframes cm-blink2": { "0%": {}, "50%": { visibility: "hidden" }, "100%": {} },
|
|
4988
|
-
".cm-cursor": {
|
|
5062
|
+
".cm-cursor, .cm-dropCursor": {
|
|
4989
5063
|
position: "absolute",
|
|
4990
5064
|
borderLeft: "1.2px solid black",
|
|
4991
5065
|
marginLeft: "-0.6px",
|
|
4992
5066
|
pointerEvents: "none",
|
|
5067
|
+
},
|
|
5068
|
+
".cm-cursor": {
|
|
4993
5069
|
display: "none"
|
|
4994
5070
|
},
|
|
4995
5071
|
"&dark .cm-cursor": {
|
|
@@ -5009,7 +5085,8 @@ const baseTheme = /*@__PURE__*/buildTheme("." + baseThemeID, {
|
|
|
5009
5085
|
},
|
|
5010
5086
|
".cm-placeholder": {
|
|
5011
5087
|
color: "#888",
|
|
5012
|
-
display: "inline-block"
|
|
5088
|
+
display: "inline-block",
|
|
5089
|
+
verticalAlign: "top",
|
|
5013
5090
|
},
|
|
5014
5091
|
".cm-button": {
|
|
5015
5092
|
verticalAlign: "middle",
|
|
@@ -5076,6 +5153,7 @@ class DOMObserver {
|
|
|
5076
5153
|
this.delayedFlush = -1;
|
|
5077
5154
|
this.resizeTimeout = -1;
|
|
5078
5155
|
this.queue = [];
|
|
5156
|
+
this.delayedAndroidKey = null;
|
|
5079
5157
|
this.scrollTargets = [];
|
|
5080
5158
|
this.intersection = null;
|
|
5081
5159
|
this.resize = null;
|
|
@@ -5159,7 +5237,7 @@ class DOMObserver {
|
|
|
5159
5237
|
}
|
|
5160
5238
|
}
|
|
5161
5239
|
onSelectionChange(event) {
|
|
5162
|
-
if (!this.readSelectionRange())
|
|
5240
|
+
if (!this.readSelectionRange() || this.delayedAndroidKey)
|
|
5163
5241
|
return;
|
|
5164
5242
|
let { view } = this, sel = this.selectionRange;
|
|
5165
5243
|
if (view.state.facet(editable) ? view.root.activeElement != this.dom : !hasSelection(view.dom, sel))
|
|
@@ -5255,6 +5333,32 @@ class DOMObserver {
|
|
|
5255
5333
|
this.queue.length = 0;
|
|
5256
5334
|
this.selectionChanged = false;
|
|
5257
5335
|
}
|
|
5336
|
+
// Chrome Android, especially in combination with GBoard, not only
|
|
5337
|
+
// doesn't reliably fire regular key events, but also often
|
|
5338
|
+
// surrounds the effect of enter or backspace with a bunch of
|
|
5339
|
+
// composition events that, when interrupted, cause text duplication
|
|
5340
|
+
// or other kinds of corruption. This hack makes the editor back off
|
|
5341
|
+
// from handling DOM changes for a moment when such a key is
|
|
5342
|
+
// detected (via beforeinput or keydown), and then dispatches the
|
|
5343
|
+
// key event, throwing away the DOM changes if it gets handled.
|
|
5344
|
+
delayAndroidKey(key, keyCode) {
|
|
5345
|
+
if (!this.delayedAndroidKey)
|
|
5346
|
+
requestAnimationFrame(() => {
|
|
5347
|
+
let key = this.delayedAndroidKey;
|
|
5348
|
+
this.delayedAndroidKey = null;
|
|
5349
|
+
let startState = this.view.state;
|
|
5350
|
+
if (dispatchKey(this.view.contentDOM, key.key, key.keyCode))
|
|
5351
|
+
this.processRecords();
|
|
5352
|
+
else
|
|
5353
|
+
this.flush();
|
|
5354
|
+
if (this.view.state == startState)
|
|
5355
|
+
this.view.update([]);
|
|
5356
|
+
});
|
|
5357
|
+
// Since backspace beforeinput is sometimes signalled spuriously,
|
|
5358
|
+
// Enter always takes precedence.
|
|
5359
|
+
if (!this.delayedAndroidKey || key == "Enter")
|
|
5360
|
+
this.delayedAndroidKey = { key, keyCode };
|
|
5361
|
+
}
|
|
5258
5362
|
flushSoon() {
|
|
5259
5363
|
if (this.delayedFlush < 0)
|
|
5260
5364
|
this.delayedFlush = window.setTimeout(() => { this.delayedFlush = -1; this.flush(); }, 20);
|
|
@@ -5291,13 +5395,13 @@ class DOMObserver {
|
|
|
5291
5395
|
}
|
|
5292
5396
|
// Apply pending changes, if any
|
|
5293
5397
|
flush(readSelection = true) {
|
|
5294
|
-
if (readSelection)
|
|
5295
|
-
this.readSelectionRange();
|
|
5296
5398
|
// Completely hold off flushing when pending keys are set—the code
|
|
5297
5399
|
// managing those will make sure processRecords is called and the
|
|
5298
5400
|
// view is resynchronized after
|
|
5299
|
-
if (this.delayedFlush >= 0 || this.
|
|
5401
|
+
if (this.delayedFlush >= 0 || this.delayedAndroidKey)
|
|
5300
5402
|
return;
|
|
5403
|
+
if (readSelection)
|
|
5404
|
+
this.readSelectionRange();
|
|
5301
5405
|
let { from, to, typeOver } = this.processRecords();
|
|
5302
5406
|
let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
|
|
5303
5407
|
if (from < 0 && !newSel)
|
|
@@ -5307,7 +5411,7 @@ class DOMObserver {
|
|
|
5307
5411
|
this.onChange(from, to, typeOver);
|
|
5308
5412
|
// The view wasn't updated
|
|
5309
5413
|
if (this.view.state == startState)
|
|
5310
|
-
this.view.
|
|
5414
|
+
this.view.update([]);
|
|
5311
5415
|
}
|
|
5312
5416
|
readMutation(rec) {
|
|
5313
5417
|
let cView = this.view.docView.nearest(rec.target);
|
|
@@ -5438,11 +5542,22 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5438
5542
|
};
|
|
5439
5543
|
if (change) {
|
|
5440
5544
|
let startState = view.state;
|
|
5545
|
+
if (browser.ios && view.inputState.flushIOSKey(view))
|
|
5546
|
+
return;
|
|
5441
5547
|
// Android browsers don't fire reasonable key events for enter,
|
|
5442
5548
|
// backspace, or delete. So this detects changes that look like
|
|
5443
5549
|
// they're caused by those keys, and reinterprets them as key
|
|
5444
|
-
// events.
|
|
5445
|
-
|
|
5550
|
+
// events. (Some of these keys are also handled by beforeinput
|
|
5551
|
+
// events and the pendingAndroidKey mechanism, but that's not
|
|
5552
|
+
// reliable in all situations.)
|
|
5553
|
+
if (browser.android &&
|
|
5554
|
+
((change.from == sel.from && change.to == sel.to &&
|
|
5555
|
+
change.insert.length == 1 && change.insert.lines == 2 &&
|
|
5556
|
+
dispatchKey(view.contentDOM, "Enter", 13)) ||
|
|
5557
|
+
(change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 &&
|
|
5558
|
+
dispatchKey(view.contentDOM, "Backspace", 8)) ||
|
|
5559
|
+
(change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
|
|
5560
|
+
dispatchKey(view.contentDOM, "Delete", 46))))
|
|
5446
5561
|
return;
|
|
5447
5562
|
let text = change.insert.toString();
|
|
5448
5563
|
if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
|
|
@@ -5515,76 +5630,6 @@ function findDiff(a, b, preferredPos, preferredSide) {
|
|
|
5515
5630
|
}
|
|
5516
5631
|
return { from, toA, toB };
|
|
5517
5632
|
}
|
|
5518
|
-
class DOMReader {
|
|
5519
|
-
constructor(points, view) {
|
|
5520
|
-
this.points = points;
|
|
5521
|
-
this.view = view;
|
|
5522
|
-
this.text = "";
|
|
5523
|
-
this.lineBreak = view.state.lineBreak;
|
|
5524
|
-
}
|
|
5525
|
-
readRange(start, end) {
|
|
5526
|
-
if (!start)
|
|
5527
|
-
return;
|
|
5528
|
-
let parent = start.parentNode;
|
|
5529
|
-
for (let cur = start;;) {
|
|
5530
|
-
this.findPointBefore(parent, cur);
|
|
5531
|
-
this.readNode(cur);
|
|
5532
|
-
let next = cur.nextSibling;
|
|
5533
|
-
if (next == end)
|
|
5534
|
-
break;
|
|
5535
|
-
let view = ContentView.get(cur), nextView = ContentView.get(next);
|
|
5536
|
-
if (view && nextView ? view.breakAfter :
|
|
5537
|
-
(view ? view.breakAfter : isBlockElement(cur)) ||
|
|
5538
|
-
(isBlockElement(next) && (cur.nodeName != "BR" || cur.cmIgnore)))
|
|
5539
|
-
this.text += this.lineBreak;
|
|
5540
|
-
cur = next;
|
|
5541
|
-
}
|
|
5542
|
-
this.findPointBefore(parent, end);
|
|
5543
|
-
}
|
|
5544
|
-
readNode(node) {
|
|
5545
|
-
if (node.cmIgnore)
|
|
5546
|
-
return;
|
|
5547
|
-
let view = ContentView.get(node);
|
|
5548
|
-
let fromView = view && view.overrideDOMText;
|
|
5549
|
-
let text;
|
|
5550
|
-
if (fromView != null)
|
|
5551
|
-
text = fromView.sliceString(0, undefined, this.lineBreak);
|
|
5552
|
-
else if (node.nodeType == 3)
|
|
5553
|
-
text = node.nodeValue;
|
|
5554
|
-
else if (node.nodeName == "BR")
|
|
5555
|
-
text = node.nextSibling ? this.lineBreak : "";
|
|
5556
|
-
else if (node.nodeType == 1)
|
|
5557
|
-
this.readRange(node.firstChild, null);
|
|
5558
|
-
if (text != null) {
|
|
5559
|
-
this.findPointIn(node, text.length);
|
|
5560
|
-
this.text += text;
|
|
5561
|
-
// Chrome inserts two newlines when pressing shift-enter at the
|
|
5562
|
-
// end of a line. This drops one of those.
|
|
5563
|
-
if (browser.chrome && this.view.inputState.lastKeyCode == 13 && !node.nextSibling && /\n\n$/.test(this.text))
|
|
5564
|
-
this.text = this.text.slice(0, -1);
|
|
5565
|
-
}
|
|
5566
|
-
}
|
|
5567
|
-
findPointBefore(node, next) {
|
|
5568
|
-
for (let point of this.points)
|
|
5569
|
-
if (point.node == node && node.childNodes[point.offset] == next)
|
|
5570
|
-
point.pos = this.text.length;
|
|
5571
|
-
}
|
|
5572
|
-
findPointIn(node, maxLen) {
|
|
5573
|
-
for (let point of this.points)
|
|
5574
|
-
if (point.node == node)
|
|
5575
|
-
point.pos = this.text.length + Math.min(point.offset, maxLen);
|
|
5576
|
-
}
|
|
5577
|
-
}
|
|
5578
|
-
function isBlockElement(node) {
|
|
5579
|
-
return node.nodeType == 1 && /^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(node.nodeName);
|
|
5580
|
-
}
|
|
5581
|
-
class DOMPoint {
|
|
5582
|
-
constructor(node, offset) {
|
|
5583
|
-
this.node = node;
|
|
5584
|
-
this.offset = offset;
|
|
5585
|
-
this.pos = -1;
|
|
5586
|
-
}
|
|
5587
|
-
}
|
|
5588
5633
|
function selectionPoints(view) {
|
|
5589
5634
|
let result = [];
|
|
5590
5635
|
if (view.root.activeElement != view.contentDOM)
|
|
@@ -5870,7 +5915,9 @@ class EditorView {
|
|
|
5870
5915
|
if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
|
|
5871
5916
|
break;
|
|
5872
5917
|
if (i > 5) {
|
|
5873
|
-
console.warn(this.measureRequests.length
|
|
5918
|
+
console.warn(this.measureRequests.length
|
|
5919
|
+
? "Measure loop restarted more than 5 times"
|
|
5920
|
+
: "Viewport failed to stabilize");
|
|
5874
5921
|
break;
|
|
5875
5922
|
}
|
|
5876
5923
|
let measuring = [];
|
|
@@ -5916,7 +5963,8 @@ class EditorView {
|
|
|
5916
5963
|
}
|
|
5917
5964
|
if (redrawn)
|
|
5918
5965
|
this.docView.updateSelection(true);
|
|
5919
|
-
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5966
|
+
if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
|
|
5967
|
+
this.measureRequests.length == 0)
|
|
5920
5968
|
break;
|
|
5921
5969
|
}
|
|
5922
5970
|
}
|
|
@@ -6208,6 +6256,11 @@ class EditorView {
|
|
|
6208
6256
|
Find the DOM parent node and offset (child offset if `node` is
|
|
6209
6257
|
an element, character offset when it is a text node) at the
|
|
6210
6258
|
given document position.
|
|
6259
|
+
|
|
6260
|
+
Note that for positions that aren't currently in
|
|
6261
|
+
`visibleRanges`, the resulting DOM position isn't necessarily
|
|
6262
|
+
meaningful (it may just point before or after a placeholder
|
|
6263
|
+
element).
|
|
6211
6264
|
*/
|
|
6212
6265
|
domAtPos(pos) {
|
|
6213
6266
|
return this.docView.domAtPos(pos);
|
|
@@ -6868,7 +6921,7 @@ function measureRange(view, range) {
|
|
|
6868
6921
|
let between = [];
|
|
6869
6922
|
if ((visualStart || startBlock).to < (visualEnd || endBlock).from - 1)
|
|
6870
6923
|
between.push(piece(leftSide, top.bottom, rightSide, bottom.top));
|
|
6871
|
-
else if (top.bottom < bottom.top &&
|
|
6924
|
+
else if (top.bottom < bottom.top && view.elementAtHeight((top.bottom + bottom.top) / 2).type == BlockType.Text)
|
|
6872
6925
|
top.bottom = bottom.top = (top.bottom + bottom.top) / 2;
|
|
6873
6926
|
return pieces(top).concat(between).concat(pieces(bottom));
|
|
6874
6927
|
}
|
|
@@ -6933,6 +6986,94 @@ function measureCursor(view, cursor, primary) {
|
|
|
6933
6986
|
return new Piece(pos.left - base.left, pos.top - base.top, -1, pos.bottom - pos.top, primary ? "cm-cursor cm-cursor-primary" : "cm-cursor cm-cursor-secondary");
|
|
6934
6987
|
}
|
|
6935
6988
|
|
|
6989
|
+
const setDropCursorPos = /*@__PURE__*/StateEffect.define({
|
|
6990
|
+
map(pos, mapping) { return pos == null ? null : mapping.mapPos(pos); }
|
|
6991
|
+
});
|
|
6992
|
+
const dropCursorPos = /*@__PURE__*/StateField.define({
|
|
6993
|
+
create() { return null; },
|
|
6994
|
+
update(pos, tr) {
|
|
6995
|
+
if (pos != null)
|
|
6996
|
+
pos = tr.changes.mapPos(pos);
|
|
6997
|
+
return tr.effects.reduce((pos, e) => e.is(setDropCursorPos) ? e.value : pos, pos);
|
|
6998
|
+
}
|
|
6999
|
+
});
|
|
7000
|
+
const drawDropCursor = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
7001
|
+
constructor(view) {
|
|
7002
|
+
this.view = view;
|
|
7003
|
+
this.cursor = null;
|
|
7004
|
+
this.measureReq = { read: this.readPos.bind(this), write: this.drawCursor.bind(this) };
|
|
7005
|
+
}
|
|
7006
|
+
update(update) {
|
|
7007
|
+
var _a;
|
|
7008
|
+
let cursorPos = update.state.field(dropCursorPos);
|
|
7009
|
+
if (cursorPos == null) {
|
|
7010
|
+
if (this.cursor != null) {
|
|
7011
|
+
(_a = this.cursor) === null || _a === void 0 ? void 0 : _a.remove();
|
|
7012
|
+
this.cursor = null;
|
|
7013
|
+
}
|
|
7014
|
+
}
|
|
7015
|
+
else {
|
|
7016
|
+
if (!this.cursor) {
|
|
7017
|
+
this.cursor = this.view.scrollDOM.appendChild(document.createElement("div"));
|
|
7018
|
+
this.cursor.className = "cm-dropCursor";
|
|
7019
|
+
}
|
|
7020
|
+
if (update.startState.field(dropCursorPos) != cursorPos || update.docChanged || update.geometryChanged)
|
|
7021
|
+
this.view.requestMeasure(this.measureReq);
|
|
7022
|
+
}
|
|
7023
|
+
}
|
|
7024
|
+
readPos() {
|
|
7025
|
+
let pos = this.view.state.field(dropCursorPos);
|
|
7026
|
+
let rect = pos != null && this.view.coordsAtPos(pos);
|
|
7027
|
+
if (!rect)
|
|
7028
|
+
return null;
|
|
7029
|
+
let outer = this.view.scrollDOM.getBoundingClientRect();
|
|
7030
|
+
return { left: rect.left - outer.left, top: rect.top - outer.top, height: rect.bottom - rect.top };
|
|
7031
|
+
}
|
|
7032
|
+
drawCursor(pos) {
|
|
7033
|
+
if (this.cursor) {
|
|
7034
|
+
if (pos) {
|
|
7035
|
+
this.cursor.style.left = pos.left + "px";
|
|
7036
|
+
this.cursor.style.top = pos.top + "px";
|
|
7037
|
+
this.cursor.style.height = pos.height + "px";
|
|
7038
|
+
}
|
|
7039
|
+
else {
|
|
7040
|
+
this.cursor.style.left = "-100000px";
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
7043
|
+
}
|
|
7044
|
+
destroy() {
|
|
7045
|
+
if (this.cursor)
|
|
7046
|
+
this.cursor.remove();
|
|
7047
|
+
}
|
|
7048
|
+
setDropPos(pos) {
|
|
7049
|
+
if (this.view.state.field(dropCursorPos) != pos)
|
|
7050
|
+
this.view.dispatch({ effects: setDropCursorPos.of(pos) });
|
|
7051
|
+
}
|
|
7052
|
+
}, {
|
|
7053
|
+
eventHandlers: {
|
|
7054
|
+
dragover(event) {
|
|
7055
|
+
this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));
|
|
7056
|
+
},
|
|
7057
|
+
dragleave(event) {
|
|
7058
|
+
if (event.target == this.view.contentDOM || !this.view.contentDOM.contains(event.relatedTarget))
|
|
7059
|
+
this.setDropPos(null);
|
|
7060
|
+
},
|
|
7061
|
+
dragend() {
|
|
7062
|
+
this.setDropPos(null);
|
|
7063
|
+
},
|
|
7064
|
+
drop() {
|
|
7065
|
+
this.setDropPos(null);
|
|
7066
|
+
}
|
|
7067
|
+
}
|
|
7068
|
+
});
|
|
7069
|
+
/**
|
|
7070
|
+
Draws a cursor at the current drop position when something is
|
|
7071
|
+
dragged over the editor.
|
|
7072
|
+
*/
|
|
7073
|
+
function dropCursor() {
|
|
7074
|
+
return [dropCursorPos, drawDropCursor];
|
|
7075
|
+
}
|
|
7076
|
+
|
|
6936
7077
|
function iterMatches(doc, re, from, to, f) {
|
|
6937
7078
|
re.lastIndex = 0;
|
|
6938
7079
|
for (let cursor = doc.iterRange(from, to), pos = from, m; !cursor.next().done; pos += cursor.value.length) {
|
|
@@ -6941,6 +7082,22 @@ function iterMatches(doc, re, from, to, f) {
|
|
|
6941
7082
|
f(pos + m.index, pos + m.index + m[0].length, m);
|
|
6942
7083
|
}
|
|
6943
7084
|
}
|
|
7085
|
+
function matchRanges(view, maxLength) {
|
|
7086
|
+
let visible = view.visibleRanges;
|
|
7087
|
+
if (visible.length == 1 && visible[0].from == view.viewport.from &&
|
|
7088
|
+
visible[0].to == view.viewport.to)
|
|
7089
|
+
return visible;
|
|
7090
|
+
let result = [];
|
|
7091
|
+
for (let { from, to } of visible) {
|
|
7092
|
+
from = Math.max(view.state.doc.lineAt(from).from, from - maxLength);
|
|
7093
|
+
to = Math.min(view.state.doc.lineAt(to).to, to + maxLength);
|
|
7094
|
+
if (result.length && result[result.length - 1].to >= from)
|
|
7095
|
+
result[result.length - 1].to = to;
|
|
7096
|
+
else
|
|
7097
|
+
result.push({ from, to });
|
|
7098
|
+
}
|
|
7099
|
+
return result;
|
|
7100
|
+
}
|
|
6944
7101
|
/**
|
|
6945
7102
|
Helper class used to make it easier to maintain decorations on
|
|
6946
7103
|
visible code that matches a given regular expression. To be used
|
|
@@ -6952,12 +7109,13 @@ class MatchDecorator {
|
|
|
6952
7109
|
Create a decorator.
|
|
6953
7110
|
*/
|
|
6954
7111
|
constructor(config) {
|
|
6955
|
-
let { regexp, decoration, boundary } = config;
|
|
7112
|
+
let { regexp, decoration, boundary, maxLength = 1000 } = config;
|
|
6956
7113
|
if (!regexp.global)
|
|
6957
7114
|
throw new RangeError("The regular expression given to MatchDecorator should have its 'g' flag set");
|
|
6958
7115
|
this.regexp = regexp;
|
|
6959
7116
|
this.getDeco = typeof decoration == "function" ? decoration : () => decoration;
|
|
6960
7117
|
this.boundary = boundary;
|
|
7118
|
+
this.maxLength = maxLength;
|
|
6961
7119
|
}
|
|
6962
7120
|
/**
|
|
6963
7121
|
Compute the full set of decorations for matches in the given
|
|
@@ -6966,7 +7124,7 @@ class MatchDecorator {
|
|
|
6966
7124
|
*/
|
|
6967
7125
|
createDeco(view) {
|
|
6968
7126
|
let build = new RangeSetBuilder();
|
|
6969
|
-
for (let { from, to } of view.
|
|
7127
|
+
for (let { from, to } of matchRanges(view, this.maxLength))
|
|
6970
7128
|
iterMatches(view.state.doc, this.regexp, from, to, (a, b, m) => build.add(a, b, this.getDeco(m, view, a)));
|
|
6971
7129
|
return build.finish();
|
|
6972
7130
|
}
|
|
@@ -7267,4 +7425,4 @@ function placeholder(content) {
|
|
|
7267
7425
|
*/
|
|
7268
7426
|
const __test = { HeightMap, HeightOracle, MeasuredHeights, QueryType, ChangedRange, computeOrder, moveVisually };
|
|
7269
7427
|
|
|
7270
|
-
export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, MatchDecorator, PluginField, PluginFieldProvider, ViewPlugin, ViewUpdate, WidgetType, __test, drawSelection, highlightActiveLine, highlightSpecialChars, keymap, logException, placeholder, runScopeHandlers, scrollPastEnd };
|
|
7428
|
+
export { BidiSpan, BlockInfo, BlockType, Decoration, Direction, EditorView, MatchDecorator, PluginField, PluginFieldProvider, ViewPlugin, ViewUpdate, WidgetType, __test, drawSelection, dropCursor, highlightActiveLine, highlightSpecialChars, keymap, logException, placeholder, runScopeHandlers, scrollPastEnd };
|