@codemirror/view 6.2.5 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/index.cjs +106 -62
- package/dist/index.d.ts +6 -1
- package/dist/index.js +106 -62
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## 6.3.0 (2022-09-28)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Reduce the amount of wrap-point jittering when scrolling through a very long wrapped line.
|
|
6
|
+
|
|
7
|
+
Fix an issue where scrolling to content that wasn't currently drawn due to being on a very long line would often fail to scroll to the right position.
|
|
8
|
+
|
|
9
|
+
Suppress double-space-adds-period behavior on Chrome Mac when it behaves weirdly next to widget.
|
|
10
|
+
|
|
11
|
+
### New features
|
|
12
|
+
|
|
13
|
+
Key binding objects with an `any` property will now add handlers that are called for any key, within the ordering of the keybindings.
|
|
14
|
+
|
|
1
15
|
## 6.2.5 (2022-09-24)
|
|
2
16
|
|
|
3
17
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -4827,7 +4827,7 @@ class ViewState {
|
|
|
4827
4827
|
this.updateForViewport();
|
|
4828
4828
|
if (updateLines)
|
|
4829
4829
|
this.updateViewportLines();
|
|
4830
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4830
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
|
|
4831
4831
|
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
4832
4832
|
update.flags |= this.computeVisibleRanges();
|
|
4833
4833
|
if (scrollTarget)
|
|
@@ -4908,8 +4908,8 @@ class ViewState {
|
|
|
4908
4908
|
this.updateForViewport();
|
|
4909
4909
|
if ((result & 2 /* UpdateFlag.Height */) || viewportChange)
|
|
4910
4910
|
this.updateViewportLines();
|
|
4911
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4912
|
-
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
4911
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
|
|
4912
|
+
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps, view));
|
|
4913
4913
|
result |= this.computeVisibleRanges();
|
|
4914
4914
|
if (this.mustEnforceCursorAssoc) {
|
|
4915
4915
|
this.mustEnforceCursorAssoc = false;
|
|
@@ -4980,46 +4980,82 @@ class ViewState {
|
|
|
4980
4980
|
// since actual DOM coordinates aren't always available and
|
|
4981
4981
|
// predictable. Relies on generous margins (see LG.Margin) to hide
|
|
4982
4982
|
// the artifacts this might produce from the user.
|
|
4983
|
-
ensureLineGaps(current) {
|
|
4983
|
+
ensureLineGaps(current, mayMeasure) {
|
|
4984
|
+
let wrapping = this.heightOracle.lineWrapping;
|
|
4985
|
+
let margin = wrapping ? 10000 /* LG.MarginWrap */ : 2000 /* LG.Margin */, halfMargin = margin >> 1, doubleMargin = margin << 1;
|
|
4986
|
+
// The non-wrapping logic won't work at all in predominantly right-to-left text.
|
|
4987
|
+
if (this.defaultTextDirection != exports.Direction.LTR && !wrapping)
|
|
4988
|
+
return [];
|
|
4984
4989
|
let gaps = [];
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4990
|
+
let addGap = (from, to, line, structure) => {
|
|
4991
|
+
if (to - from < halfMargin)
|
|
4992
|
+
return;
|
|
4993
|
+
let sel = this.state.selection.main, avoid = [sel.from];
|
|
4994
|
+
if (!sel.empty)
|
|
4995
|
+
avoid.push(sel.to);
|
|
4996
|
+
for (let pos of avoid) {
|
|
4997
|
+
if (pos > from && pos < to) {
|
|
4998
|
+
addGap(from, pos - 10 /* LG.SelectionMargin */, line, structure);
|
|
4999
|
+
addGap(pos + 10 /* LG.SelectionMargin */, to, line, structure);
|
|
5000
|
+
return;
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
let gap = find(current, gap => gap.from >= line.from && gap.to <= line.to &&
|
|
5004
|
+
Math.abs(gap.from - from) < halfMargin && Math.abs(gap.to - to) < halfMargin &&
|
|
5005
|
+
!avoid.some(pos => gap.from < pos && gap.to > pos));
|
|
5006
|
+
if (!gap) {
|
|
5007
|
+
// When scrolling down, snap gap ends to line starts to avoid shifts in wrapping
|
|
5008
|
+
if (to < line.to && mayMeasure && wrapping &&
|
|
5009
|
+
mayMeasure.visibleRanges.some(r => r.from <= to && r.to >= to)) {
|
|
5010
|
+
let lineStart = mayMeasure.moveToLineBoundary(state.EditorSelection.cursor(to), false, true).head;
|
|
5011
|
+
if (lineStart > from)
|
|
5012
|
+
to = lineStart;
|
|
5013
|
+
}
|
|
5014
|
+
gap = new LineGap(from, to, this.gapSize(line, from, to, structure));
|
|
5015
|
+
}
|
|
5016
|
+
gaps.push(gap);
|
|
5017
|
+
};
|
|
4988
5018
|
for (let line of this.viewportLines) {
|
|
4989
|
-
if (line.length <
|
|
5019
|
+
if (line.length < doubleMargin)
|
|
4990
5020
|
continue;
|
|
4991
5021
|
let structure = lineStructure(line.from, line.to, this.stateDeco);
|
|
4992
|
-
if (structure.total <
|
|
5022
|
+
if (structure.total < doubleMargin)
|
|
4993
5023
|
continue;
|
|
5024
|
+
let target = this.scrollTarget ? this.scrollTarget.range.head : null;
|
|
4994
5025
|
let viewFrom, viewTo;
|
|
4995
|
-
if (
|
|
4996
|
-
let marginHeight = (
|
|
4997
|
-
|
|
4998
|
-
|
|
5026
|
+
if (wrapping) {
|
|
5027
|
+
let marginHeight = (margin / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
5028
|
+
let top, bot;
|
|
5029
|
+
if (target != null) {
|
|
5030
|
+
top = Math.max(line.from, target - margin);
|
|
5031
|
+
bot = Math.min(line.to, target + margin);
|
|
5032
|
+
}
|
|
5033
|
+
else {
|
|
5034
|
+
top = (this.visibleTop - line.top - marginHeight) / line.height;
|
|
5035
|
+
bot = (this.visibleBottom - line.top + marginHeight) / line.height;
|
|
5036
|
+
}
|
|
5037
|
+
viewFrom = findPosition(structure, top);
|
|
5038
|
+
viewTo = findPosition(structure, bot);
|
|
4999
5039
|
}
|
|
5000
5040
|
else {
|
|
5001
|
-
let
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5041
|
+
let left, right;
|
|
5042
|
+
if (target != null) {
|
|
5043
|
+
left = Math.max(line.from, target - doubleMargin);
|
|
5044
|
+
right = Math.min(line.to, target + doubleMargin);
|
|
5045
|
+
}
|
|
5046
|
+
else {
|
|
5047
|
+
let totalWidth = structure.total * this.heightOracle.charWidth;
|
|
5048
|
+
let marginWidth = margin * this.heightOracle.charWidth;
|
|
5049
|
+
left = (this.pixelViewport.left - marginWidth) / totalWidth;
|
|
5050
|
+
right = (this.pixelViewport.right + marginWidth) / totalWidth;
|
|
5051
|
+
}
|
|
5052
|
+
viewFrom = findPosition(structure, left);
|
|
5053
|
+
viewTo = findPosition(structure, right);
|
|
5005
5054
|
}
|
|
5006
|
-
let outside = [];
|
|
5007
5055
|
if (viewFrom > line.from)
|
|
5008
|
-
|
|
5056
|
+
addGap(line.from, viewFrom, line, structure);
|
|
5009
5057
|
if (viewTo < line.to)
|
|
5010
|
-
|
|
5011
|
-
let sel = this.state.selection.main;
|
|
5012
|
-
// Make sure the gaps don't cover a selection end
|
|
5013
|
-
if (sel.from >= line.from && sel.from <= line.to)
|
|
5014
|
-
cutRange(outside, sel.from - 10 /* LG.SelectionMargin */, sel.from + 10 /* LG.SelectionMargin */);
|
|
5015
|
-
if (!sel.empty && sel.to >= line.from && sel.to <= line.to)
|
|
5016
|
-
cutRange(outside, sel.to - 10 /* LG.SelectionMargin */, sel.to + 10 /* LG.SelectionMargin */);
|
|
5017
|
-
for (let { from, to } of outside)
|
|
5018
|
-
if (to - from > 1000 /* LG.HalfMargin */) {
|
|
5019
|
-
gaps.push(find(current, gap => gap.from >= line.from && gap.to <= line.to &&
|
|
5020
|
-
Math.abs(gap.from - from) < 1000 /* LG.HalfMargin */ && Math.abs(gap.to - to) < 1000 /* LG.HalfMargin */) ||
|
|
5021
|
-
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
5022
|
-
}
|
|
5058
|
+
addGap(viewTo, line.to, line, structure);
|
|
5023
5059
|
}
|
|
5024
5060
|
return gaps;
|
|
5025
5061
|
}
|
|
@@ -5117,20 +5153,6 @@ function findFraction(structure, pos) {
|
|
|
5117
5153
|
}
|
|
5118
5154
|
return counted / structure.total;
|
|
5119
5155
|
}
|
|
5120
|
-
function cutRange(ranges, from, to) {
|
|
5121
|
-
for (let i = 0; i < ranges.length; i++) {
|
|
5122
|
-
let r = ranges[i];
|
|
5123
|
-
if (r.from < to && r.to > from) {
|
|
5124
|
-
let pieces = [];
|
|
5125
|
-
if (r.from < from)
|
|
5126
|
-
pieces.push({ from: r.from, to: from });
|
|
5127
|
-
if (r.to > to)
|
|
5128
|
-
pieces.push({ from: to, to: r.to });
|
|
5129
|
-
ranges.splice(i, 1, ...pieces);
|
|
5130
|
-
i += pieces.length - 1;
|
|
5131
|
-
}
|
|
5132
|
-
}
|
|
5133
|
-
}
|
|
5134
5156
|
function find(array, f) {
|
|
5135
5157
|
for (let val of array)
|
|
5136
5158
|
if (f(val))
|
|
@@ -5902,24 +5924,29 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5902
5924
|
}
|
|
5903
5925
|
if (!change && !newSel)
|
|
5904
5926
|
return false;
|
|
5905
|
-
|
|
5906
|
-
|
|
5927
|
+
if (!change && typeOver && !sel.empty && newSel && newSel.main.empty) {
|
|
5928
|
+
// Heuristic to notice typing over a selected character
|
|
5907
5929
|
change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
|
|
5908
|
-
|
|
5909
|
-
// assume it is a selection replace (with identical characters at
|
|
5910
|
-
// the start/end not included in the diff)
|
|
5930
|
+
}
|
|
5911
5931
|
else if (change && change.from >= sel.from && change.to <= sel.to &&
|
|
5912
5932
|
(change.from != sel.from || change.to != sel.to) &&
|
|
5913
|
-
(sel.to - sel.from) - (change.to - change.from) <= 4)
|
|
5933
|
+
(sel.to - sel.from) - (change.to - change.from) <= 4) {
|
|
5934
|
+
// If the change is inside the selection and covers most of it,
|
|
5935
|
+
// assume it is a selection replace (with identical characters at
|
|
5936
|
+
// the start/end not included in the diff)
|
|
5914
5937
|
change = {
|
|
5915
5938
|
from: sel.from, to: sel.to,
|
|
5916
5939
|
insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to))
|
|
5917
5940
|
};
|
|
5918
|
-
|
|
5919
|
-
// it into a regular space insert.
|
|
5941
|
+
}
|
|
5920
5942
|
else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
|
|
5921
|
-
change.insert.toString()
|
|
5943
|
+
/^\. ?$/.test(change.insert.toString())) {
|
|
5944
|
+
// Detect insert-period-on-double-space Mac and Android behavior,
|
|
5945
|
+
// and transform it into a regular space insert.
|
|
5946
|
+
if (newSel && change.insert.length == 2)
|
|
5947
|
+
newSel = state.EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
|
|
5922
5948
|
change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
|
|
5949
|
+
}
|
|
5923
5950
|
if (change) {
|
|
5924
5951
|
let startState = view.state;
|
|
5925
5952
|
if (browser.ios && view.inputState.flushIOSKey(view))
|
|
@@ -7093,6 +7120,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
|
|
|
7093
7120
|
throw new Error("Key binding " + name + " is used both as a regular binding and as a multi-stroke prefix");
|
|
7094
7121
|
};
|
|
7095
7122
|
let add = (scope, key, command, preventDefault) => {
|
|
7123
|
+
var _a, _b;
|
|
7096
7124
|
let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
|
|
7097
7125
|
let parts = key.split(/ (?!$)/).map(k => normalizeKeyName(k, platform));
|
|
7098
7126
|
for (let i = 1; i < parts.length; i++) {
|
|
@@ -7101,7 +7129,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
|
|
|
7101
7129
|
if (!scopeObj[prefix])
|
|
7102
7130
|
scopeObj[prefix] = {
|
|
7103
7131
|
preventDefault: true,
|
|
7104
|
-
|
|
7132
|
+
run: [(view) => {
|
|
7105
7133
|
let ourObj = storedPrefix = { view, prefix, scope };
|
|
7106
7134
|
setTimeout(() => { if (storedPrefix == ourObj)
|
|
7107
7135
|
storedPrefix = null; }, PrefixTimeout);
|
|
@@ -7111,16 +7139,26 @@ function buildKeymap(bindings, platform = currentPlatform) {
|
|
|
7111
7139
|
}
|
|
7112
7140
|
let full = parts.join(" ");
|
|
7113
7141
|
checkPrefix(full, false);
|
|
7114
|
-
let binding = scopeObj[full] || (scopeObj[full] = { preventDefault: false,
|
|
7115
|
-
|
|
7142
|
+
let binding = scopeObj[full] || (scopeObj[full] = { preventDefault: false, run: ((_b = (_a = scopeObj._any) === null || _a === void 0 ? void 0 : _a.run) === null || _b === void 0 ? void 0 : _b.slice()) || [] });
|
|
7143
|
+
if (command)
|
|
7144
|
+
binding.run.push(command);
|
|
7116
7145
|
if (preventDefault)
|
|
7117
7146
|
binding.preventDefault = true;
|
|
7118
7147
|
};
|
|
7119
7148
|
for (let b of bindings) {
|
|
7149
|
+
let scopes = b.scope ? b.scope.split(" ") : ["editor"];
|
|
7150
|
+
if (b.any)
|
|
7151
|
+
for (let scope of scopes) {
|
|
7152
|
+
let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
|
|
7153
|
+
if (!scopeObj._any)
|
|
7154
|
+
scopeObj._any = { preventDefault: false, run: [] };
|
|
7155
|
+
for (let key in scopeObj)
|
|
7156
|
+
scopeObj[key].run.push(b.any);
|
|
7157
|
+
}
|
|
7120
7158
|
let name = b[platform] || b.key;
|
|
7121
7159
|
if (!name)
|
|
7122
7160
|
continue;
|
|
7123
|
-
for (let scope of
|
|
7161
|
+
for (let scope of scopes) {
|
|
7124
7162
|
add(scope, name, b.run, b.preventDefault);
|
|
7125
7163
|
if (b.shift)
|
|
7126
7164
|
add(scope, "Shift-" + name, b.shift, b.preventDefault);
|
|
@@ -7137,11 +7175,15 @@ function runHandlers(map, event, view, scope) {
|
|
|
7137
7175
|
if (fallthrough = modifierCodes.indexOf(event.keyCode) < 0)
|
|
7138
7176
|
storedPrefix = null;
|
|
7139
7177
|
}
|
|
7178
|
+
let ran = new Set;
|
|
7140
7179
|
let runFor = (binding) => {
|
|
7141
7180
|
if (binding) {
|
|
7142
|
-
for (let cmd of binding.
|
|
7143
|
-
if (cmd
|
|
7144
|
-
|
|
7181
|
+
for (let cmd of binding.run)
|
|
7182
|
+
if (!ran.has(cmd)) {
|
|
7183
|
+
ran.add(cmd);
|
|
7184
|
+
if (cmd(view, event))
|
|
7185
|
+
return true;
|
|
7186
|
+
}
|
|
7145
7187
|
if (binding.preventDefault)
|
|
7146
7188
|
fallthrough = true;
|
|
7147
7189
|
}
|
|
@@ -7163,6 +7205,8 @@ function runHandlers(map, event, view, scope) {
|
|
|
7163
7205
|
if (runFor(scopeObj[prefix + modifiers(name, event, true)]))
|
|
7164
7206
|
return true;
|
|
7165
7207
|
}
|
|
7208
|
+
if (runFor(scopeObj._any))
|
|
7209
|
+
return true;
|
|
7166
7210
|
}
|
|
7167
7211
|
return fallthrough;
|
|
7168
7212
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1219,7 +1219,7 @@ interface KeyBinding {
|
|
|
1219
1219
|
command function returns `false`, further bindings will be tried
|
|
1220
1220
|
for the key.
|
|
1221
1221
|
*/
|
|
1222
|
-
run
|
|
1222
|
+
run?: Command;
|
|
1223
1223
|
/**
|
|
1224
1224
|
When given, this defines a second binding, using the (possibly
|
|
1225
1225
|
platform-specific) key name prefixed with `Shift-` to activate
|
|
@@ -1227,6 +1227,11 @@ interface KeyBinding {
|
|
|
1227
1227
|
*/
|
|
1228
1228
|
shift?: Command;
|
|
1229
1229
|
/**
|
|
1230
|
+
When this property is present, the function is called for every
|
|
1231
|
+
key that is not a multi-stroke prefix.
|
|
1232
|
+
*/
|
|
1233
|
+
any?: (view: EditorView, event: KeyboardEvent) => boolean;
|
|
1234
|
+
/**
|
|
1230
1235
|
By default, key bindings apply when focus is on the editor
|
|
1231
1236
|
content (the `"editor"` scope). Some extensions, mostly those
|
|
1232
1237
|
that define their own panels, might want to allow you to
|
package/dist/index.js
CHANGED
|
@@ -4820,7 +4820,7 @@ class ViewState {
|
|
|
4820
4820
|
this.updateForViewport();
|
|
4821
4821
|
if (updateLines)
|
|
4822
4822
|
this.updateViewportLines();
|
|
4823
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4823
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
|
|
4824
4824
|
this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
|
|
4825
4825
|
update.flags |= this.computeVisibleRanges();
|
|
4826
4826
|
if (scrollTarget)
|
|
@@ -4901,8 +4901,8 @@ class ViewState {
|
|
|
4901
4901
|
this.updateForViewport();
|
|
4902
4902
|
if ((result & 2 /* UpdateFlag.Height */) || viewportChange)
|
|
4903
4903
|
this.updateViewportLines();
|
|
4904
|
-
if (this.lineGaps.length || this.viewport.to - this.viewport.from >
|
|
4905
|
-
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
|
|
4904
|
+
if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
|
|
4905
|
+
this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps, view));
|
|
4906
4906
|
result |= this.computeVisibleRanges();
|
|
4907
4907
|
if (this.mustEnforceCursorAssoc) {
|
|
4908
4908
|
this.mustEnforceCursorAssoc = false;
|
|
@@ -4973,46 +4973,82 @@ class ViewState {
|
|
|
4973
4973
|
// since actual DOM coordinates aren't always available and
|
|
4974
4974
|
// predictable. Relies on generous margins (see LG.Margin) to hide
|
|
4975
4975
|
// the artifacts this might produce from the user.
|
|
4976
|
-
ensureLineGaps(current) {
|
|
4976
|
+
ensureLineGaps(current, mayMeasure) {
|
|
4977
|
+
let wrapping = this.heightOracle.lineWrapping;
|
|
4978
|
+
let margin = wrapping ? 10000 /* LG.MarginWrap */ : 2000 /* LG.Margin */, halfMargin = margin >> 1, doubleMargin = margin << 1;
|
|
4979
|
+
// The non-wrapping logic won't work at all in predominantly right-to-left text.
|
|
4980
|
+
if (this.defaultTextDirection != Direction.LTR && !wrapping)
|
|
4981
|
+
return [];
|
|
4977
4982
|
let gaps = [];
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4983
|
+
let addGap = (from, to, line, structure) => {
|
|
4984
|
+
if (to - from < halfMargin)
|
|
4985
|
+
return;
|
|
4986
|
+
let sel = this.state.selection.main, avoid = [sel.from];
|
|
4987
|
+
if (!sel.empty)
|
|
4988
|
+
avoid.push(sel.to);
|
|
4989
|
+
for (let pos of avoid) {
|
|
4990
|
+
if (pos > from && pos < to) {
|
|
4991
|
+
addGap(from, pos - 10 /* LG.SelectionMargin */, line, structure);
|
|
4992
|
+
addGap(pos + 10 /* LG.SelectionMargin */, to, line, structure);
|
|
4993
|
+
return;
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4996
|
+
let gap = find(current, gap => gap.from >= line.from && gap.to <= line.to &&
|
|
4997
|
+
Math.abs(gap.from - from) < halfMargin && Math.abs(gap.to - to) < halfMargin &&
|
|
4998
|
+
!avoid.some(pos => gap.from < pos && gap.to > pos));
|
|
4999
|
+
if (!gap) {
|
|
5000
|
+
// When scrolling down, snap gap ends to line starts to avoid shifts in wrapping
|
|
5001
|
+
if (to < line.to && mayMeasure && wrapping &&
|
|
5002
|
+
mayMeasure.visibleRanges.some(r => r.from <= to && r.to >= to)) {
|
|
5003
|
+
let lineStart = mayMeasure.moveToLineBoundary(EditorSelection.cursor(to), false, true).head;
|
|
5004
|
+
if (lineStart > from)
|
|
5005
|
+
to = lineStart;
|
|
5006
|
+
}
|
|
5007
|
+
gap = new LineGap(from, to, this.gapSize(line, from, to, structure));
|
|
5008
|
+
}
|
|
5009
|
+
gaps.push(gap);
|
|
5010
|
+
};
|
|
4981
5011
|
for (let line of this.viewportLines) {
|
|
4982
|
-
if (line.length <
|
|
5012
|
+
if (line.length < doubleMargin)
|
|
4983
5013
|
continue;
|
|
4984
5014
|
let structure = lineStructure(line.from, line.to, this.stateDeco);
|
|
4985
|
-
if (structure.total <
|
|
5015
|
+
if (structure.total < doubleMargin)
|
|
4986
5016
|
continue;
|
|
5017
|
+
let target = this.scrollTarget ? this.scrollTarget.range.head : null;
|
|
4987
5018
|
let viewFrom, viewTo;
|
|
4988
|
-
if (
|
|
4989
|
-
let marginHeight = (
|
|
4990
|
-
|
|
4991
|
-
|
|
5019
|
+
if (wrapping) {
|
|
5020
|
+
let marginHeight = (margin / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
|
|
5021
|
+
let top, bot;
|
|
5022
|
+
if (target != null) {
|
|
5023
|
+
top = Math.max(line.from, target - margin);
|
|
5024
|
+
bot = Math.min(line.to, target + margin);
|
|
5025
|
+
}
|
|
5026
|
+
else {
|
|
5027
|
+
top = (this.visibleTop - line.top - marginHeight) / line.height;
|
|
5028
|
+
bot = (this.visibleBottom - line.top + marginHeight) / line.height;
|
|
5029
|
+
}
|
|
5030
|
+
viewFrom = findPosition(structure, top);
|
|
5031
|
+
viewTo = findPosition(structure, bot);
|
|
4992
5032
|
}
|
|
4993
5033
|
else {
|
|
4994
|
-
let
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
5034
|
+
let left, right;
|
|
5035
|
+
if (target != null) {
|
|
5036
|
+
left = Math.max(line.from, target - doubleMargin);
|
|
5037
|
+
right = Math.min(line.to, target + doubleMargin);
|
|
5038
|
+
}
|
|
5039
|
+
else {
|
|
5040
|
+
let totalWidth = structure.total * this.heightOracle.charWidth;
|
|
5041
|
+
let marginWidth = margin * this.heightOracle.charWidth;
|
|
5042
|
+
left = (this.pixelViewport.left - marginWidth) / totalWidth;
|
|
5043
|
+
right = (this.pixelViewport.right + marginWidth) / totalWidth;
|
|
5044
|
+
}
|
|
5045
|
+
viewFrom = findPosition(structure, left);
|
|
5046
|
+
viewTo = findPosition(structure, right);
|
|
4998
5047
|
}
|
|
4999
|
-
let outside = [];
|
|
5000
5048
|
if (viewFrom > line.from)
|
|
5001
|
-
|
|
5049
|
+
addGap(line.from, viewFrom, line, structure);
|
|
5002
5050
|
if (viewTo < line.to)
|
|
5003
|
-
|
|
5004
|
-
let sel = this.state.selection.main;
|
|
5005
|
-
// Make sure the gaps don't cover a selection end
|
|
5006
|
-
if (sel.from >= line.from && sel.from <= line.to)
|
|
5007
|
-
cutRange(outside, sel.from - 10 /* LG.SelectionMargin */, sel.from + 10 /* LG.SelectionMargin */);
|
|
5008
|
-
if (!sel.empty && sel.to >= line.from && sel.to <= line.to)
|
|
5009
|
-
cutRange(outside, sel.to - 10 /* LG.SelectionMargin */, sel.to + 10 /* LG.SelectionMargin */);
|
|
5010
|
-
for (let { from, to } of outside)
|
|
5011
|
-
if (to - from > 1000 /* LG.HalfMargin */) {
|
|
5012
|
-
gaps.push(find(current, gap => gap.from >= line.from && gap.to <= line.to &&
|
|
5013
|
-
Math.abs(gap.from - from) < 1000 /* LG.HalfMargin */ && Math.abs(gap.to - to) < 1000 /* LG.HalfMargin */) ||
|
|
5014
|
-
new LineGap(from, to, this.gapSize(line, from, to, structure)));
|
|
5015
|
-
}
|
|
5051
|
+
addGap(viewTo, line.to, line, structure);
|
|
5016
5052
|
}
|
|
5017
5053
|
return gaps;
|
|
5018
5054
|
}
|
|
@@ -5110,20 +5146,6 @@ function findFraction(structure, pos) {
|
|
|
5110
5146
|
}
|
|
5111
5147
|
return counted / structure.total;
|
|
5112
5148
|
}
|
|
5113
|
-
function cutRange(ranges, from, to) {
|
|
5114
|
-
for (let i = 0; i < ranges.length; i++) {
|
|
5115
|
-
let r = ranges[i];
|
|
5116
|
-
if (r.from < to && r.to > from) {
|
|
5117
|
-
let pieces = [];
|
|
5118
|
-
if (r.from < from)
|
|
5119
|
-
pieces.push({ from: r.from, to: from });
|
|
5120
|
-
if (r.to > to)
|
|
5121
|
-
pieces.push({ from: to, to: r.to });
|
|
5122
|
-
ranges.splice(i, 1, ...pieces);
|
|
5123
|
-
i += pieces.length - 1;
|
|
5124
|
-
}
|
|
5125
|
-
}
|
|
5126
|
-
}
|
|
5127
5149
|
function find(array, f) {
|
|
5128
5150
|
for (let val of array)
|
|
5129
5151
|
if (f(val))
|
|
@@ -5895,24 +5917,29 @@ function applyDOMChange(view, start, end, typeOver) {
|
|
|
5895
5917
|
}
|
|
5896
5918
|
if (!change && !newSel)
|
|
5897
5919
|
return false;
|
|
5898
|
-
|
|
5899
|
-
|
|
5920
|
+
if (!change && typeOver && !sel.empty && newSel && newSel.main.empty) {
|
|
5921
|
+
// Heuristic to notice typing over a selected character
|
|
5900
5922
|
change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
|
|
5901
|
-
|
|
5902
|
-
// assume it is a selection replace (with identical characters at
|
|
5903
|
-
// the start/end not included in the diff)
|
|
5923
|
+
}
|
|
5904
5924
|
else if (change && change.from >= sel.from && change.to <= sel.to &&
|
|
5905
5925
|
(change.from != sel.from || change.to != sel.to) &&
|
|
5906
|
-
(sel.to - sel.from) - (change.to - change.from) <= 4)
|
|
5926
|
+
(sel.to - sel.from) - (change.to - change.from) <= 4) {
|
|
5927
|
+
// If the change is inside the selection and covers most of it,
|
|
5928
|
+
// assume it is a selection replace (with identical characters at
|
|
5929
|
+
// the start/end not included in the diff)
|
|
5907
5930
|
change = {
|
|
5908
5931
|
from: sel.from, to: sel.to,
|
|
5909
5932
|
insert: view.state.doc.slice(sel.from, change.from).append(change.insert).append(view.state.doc.slice(change.to, sel.to))
|
|
5910
5933
|
};
|
|
5911
|
-
|
|
5912
|
-
// it into a regular space insert.
|
|
5934
|
+
}
|
|
5913
5935
|
else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
|
|
5914
|
-
change.insert.toString()
|
|
5936
|
+
/^\. ?$/.test(change.insert.toString())) {
|
|
5937
|
+
// Detect insert-period-on-double-space Mac and Android behavior,
|
|
5938
|
+
// and transform it into a regular space insert.
|
|
5939
|
+
if (newSel && change.insert.length == 2)
|
|
5940
|
+
newSel = EditorSelection.single(newSel.main.anchor - 1, newSel.main.head - 1);
|
|
5915
5941
|
change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
|
|
5942
|
+
}
|
|
5916
5943
|
if (change) {
|
|
5917
5944
|
let startState = view.state;
|
|
5918
5945
|
if (browser.ios && view.inputState.flushIOSKey(view))
|
|
@@ -7086,6 +7113,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
|
|
|
7086
7113
|
throw new Error("Key binding " + name + " is used both as a regular binding and as a multi-stroke prefix");
|
|
7087
7114
|
};
|
|
7088
7115
|
let add = (scope, key, command, preventDefault) => {
|
|
7116
|
+
var _a, _b;
|
|
7089
7117
|
let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
|
|
7090
7118
|
let parts = key.split(/ (?!$)/).map(k => normalizeKeyName(k, platform));
|
|
7091
7119
|
for (let i = 1; i < parts.length; i++) {
|
|
@@ -7094,7 +7122,7 @@ function buildKeymap(bindings, platform = currentPlatform) {
|
|
|
7094
7122
|
if (!scopeObj[prefix])
|
|
7095
7123
|
scopeObj[prefix] = {
|
|
7096
7124
|
preventDefault: true,
|
|
7097
|
-
|
|
7125
|
+
run: [(view) => {
|
|
7098
7126
|
let ourObj = storedPrefix = { view, prefix, scope };
|
|
7099
7127
|
setTimeout(() => { if (storedPrefix == ourObj)
|
|
7100
7128
|
storedPrefix = null; }, PrefixTimeout);
|
|
@@ -7104,16 +7132,26 @@ function buildKeymap(bindings, platform = currentPlatform) {
|
|
|
7104
7132
|
}
|
|
7105
7133
|
let full = parts.join(" ");
|
|
7106
7134
|
checkPrefix(full, false);
|
|
7107
|
-
let binding = scopeObj[full] || (scopeObj[full] = { preventDefault: false,
|
|
7108
|
-
|
|
7135
|
+
let binding = scopeObj[full] || (scopeObj[full] = { preventDefault: false, run: ((_b = (_a = scopeObj._any) === null || _a === void 0 ? void 0 : _a.run) === null || _b === void 0 ? void 0 : _b.slice()) || [] });
|
|
7136
|
+
if (command)
|
|
7137
|
+
binding.run.push(command);
|
|
7109
7138
|
if (preventDefault)
|
|
7110
7139
|
binding.preventDefault = true;
|
|
7111
7140
|
};
|
|
7112
7141
|
for (let b of bindings) {
|
|
7142
|
+
let scopes = b.scope ? b.scope.split(" ") : ["editor"];
|
|
7143
|
+
if (b.any)
|
|
7144
|
+
for (let scope of scopes) {
|
|
7145
|
+
let scopeObj = bound[scope] || (bound[scope] = Object.create(null));
|
|
7146
|
+
if (!scopeObj._any)
|
|
7147
|
+
scopeObj._any = { preventDefault: false, run: [] };
|
|
7148
|
+
for (let key in scopeObj)
|
|
7149
|
+
scopeObj[key].run.push(b.any);
|
|
7150
|
+
}
|
|
7113
7151
|
let name = b[platform] || b.key;
|
|
7114
7152
|
if (!name)
|
|
7115
7153
|
continue;
|
|
7116
|
-
for (let scope of
|
|
7154
|
+
for (let scope of scopes) {
|
|
7117
7155
|
add(scope, name, b.run, b.preventDefault);
|
|
7118
7156
|
if (b.shift)
|
|
7119
7157
|
add(scope, "Shift-" + name, b.shift, b.preventDefault);
|
|
@@ -7130,11 +7168,15 @@ function runHandlers(map, event, view, scope) {
|
|
|
7130
7168
|
if (fallthrough = modifierCodes.indexOf(event.keyCode) < 0)
|
|
7131
7169
|
storedPrefix = null;
|
|
7132
7170
|
}
|
|
7171
|
+
let ran = new Set;
|
|
7133
7172
|
let runFor = (binding) => {
|
|
7134
7173
|
if (binding) {
|
|
7135
|
-
for (let cmd of binding.
|
|
7136
|
-
if (cmd
|
|
7137
|
-
|
|
7174
|
+
for (let cmd of binding.run)
|
|
7175
|
+
if (!ran.has(cmd)) {
|
|
7176
|
+
ran.add(cmd);
|
|
7177
|
+
if (cmd(view, event))
|
|
7178
|
+
return true;
|
|
7179
|
+
}
|
|
7138
7180
|
if (binding.preventDefault)
|
|
7139
7181
|
fallthrough = true;
|
|
7140
7182
|
}
|
|
@@ -7156,6 +7198,8 @@ function runHandlers(map, event, view, scope) {
|
|
|
7156
7198
|
if (runFor(scopeObj[prefix + modifiers(name, event, true)]))
|
|
7157
7199
|
return true;
|
|
7158
7200
|
}
|
|
7201
|
+
if (runFor(scopeObj._any))
|
|
7202
|
+
return true;
|
|
7159
7203
|
}
|
|
7160
7204
|
return fallthrough;
|
|
7161
7205
|
}
|