@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 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 > 4000 /* LG.DoubleMargin */)
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 > 4000 /* LG.DoubleMargin */)
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
- // This won't work at all in predominantly right-to-left text.
4986
- if (this.defaultTextDirection != exports.Direction.LTR)
4987
- return gaps;
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 < 4000 /* LG.DoubleMargin */)
5019
+ if (line.length < doubleMargin)
4990
5020
  continue;
4991
5021
  let structure = lineStructure(line.from, line.to, this.stateDeco);
4992
- if (structure.total < 4000 /* LG.DoubleMargin */)
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 (this.heightOracle.lineWrapping) {
4996
- let marginHeight = (2000 /* LG.Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
4997
- viewFrom = findPosition(structure, (this.visibleTop - line.top - marginHeight) / line.height);
4998
- viewTo = findPosition(structure, (this.visibleBottom - line.top + marginHeight) / line.height);
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 totalWidth = structure.total * this.heightOracle.charWidth;
5002
- let marginWidth = 2000 /* LG.Margin */ * this.heightOracle.charWidth;
5003
- viewFrom = findPosition(structure, (this.pixelViewport.left - marginWidth) / totalWidth);
5004
- viewTo = findPosition(structure, (this.pixelViewport.right + marginWidth) / totalWidth);
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
- outside.push({ from: line.from, to: viewFrom });
5056
+ addGap(line.from, viewFrom, line, structure);
5009
5057
  if (viewTo < line.to)
5010
- outside.push({ from: viewTo, to: line.to });
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
- // Heuristic to notice typing over a selected character
5906
- if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
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
- // If the change is inside the selection and covers most of it,
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
- // Detect insert-period-on-double-space Mac behavior, and transform
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
- commands: [(view) => {
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, commands: [] });
7115
- binding.commands.push(command);
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 b.scope ? b.scope.split(" ") : ["editor"]) {
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.commands)
7143
- if (cmd(view))
7144
- return true;
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: Command;
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 > 4000 /* LG.DoubleMargin */)
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 > 4000 /* LG.DoubleMargin */)
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
- // This won't work at all in predominantly right-to-left text.
4979
- if (this.defaultTextDirection != Direction.LTR)
4980
- return gaps;
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 < 4000 /* LG.DoubleMargin */)
5012
+ if (line.length < doubleMargin)
4983
5013
  continue;
4984
5014
  let structure = lineStructure(line.from, line.to, this.stateDeco);
4985
- if (structure.total < 4000 /* LG.DoubleMargin */)
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 (this.heightOracle.lineWrapping) {
4989
- let marginHeight = (2000 /* LG.Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
4990
- viewFrom = findPosition(structure, (this.visibleTop - line.top - marginHeight) / line.height);
4991
- viewTo = findPosition(structure, (this.visibleBottom - line.top + marginHeight) / line.height);
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 totalWidth = structure.total * this.heightOracle.charWidth;
4995
- let marginWidth = 2000 /* LG.Margin */ * this.heightOracle.charWidth;
4996
- viewFrom = findPosition(structure, (this.pixelViewport.left - marginWidth) / totalWidth);
4997
- viewTo = findPosition(structure, (this.pixelViewport.right + marginWidth) / totalWidth);
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
- outside.push({ from: line.from, to: viewFrom });
5049
+ addGap(line.from, viewFrom, line, structure);
5002
5050
  if (viewTo < line.to)
5003
- outside.push({ from: viewTo, to: line.to });
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
- // Heuristic to notice typing over a selected character
5899
- if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
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
- // If the change is inside the selection and covers most of it,
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
- // Detect insert-period-on-double-space Mac behavior, and transform
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
- commands: [(view) => {
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, commands: [] });
7108
- binding.commands.push(command);
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 b.scope ? b.scope.split(" ") : ["editor"]) {
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.commands)
7136
- if (cmd(view))
7137
- return true;
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.2.5",
3
+ "version": "6.3.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",