@codemirror/autocomplete 0.19.8 → 0.19.12

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,31 @@
1
+ ## 0.19.12 (2022-01-11)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix completion navigation with PageUp/Down when the completion tooltip isn't part of the view DOM.
6
+
7
+ ## 0.19.11 (2022-01-11)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix a bug that caused page up/down to only move the selection by two options in the completion tooltip.
12
+
13
+ ## 0.19.10 (2022-01-05)
14
+
15
+ ### Bug fixes
16
+
17
+ Make sure the info tooltip is hidden when the selected option is scrolled out of view.
18
+
19
+ Fix a bug in the completion ranking that would sometimes give options that match the input by word start chars higher scores than appropriate.
20
+
21
+ Options are now sorted (ascending) by length when their match score is otherwise identical.
22
+
23
+ ## 0.19.9 (2021-11-26)
24
+
25
+ ### Bug fixes
26
+
27
+ Fix an issue where info tooltips would be visible in an inappropriate position when there was no room to place them properly.
28
+
1
29
  ## 0.19.8 (2021-11-17)
2
30
 
3
31
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -246,7 +246,7 @@ class FuzzyMatcher {
246
246
  let byWordTo = 0, byWordFolded = false;
247
247
  // If we've found a partial adjacent match, these track its state
248
248
  let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
249
- let hasLower = /[a-z]/.test(word);
249
+ let hasLower = /[a-z]/.test(word), wordAdjacent = true;
250
250
  // Go over the option's text, scanning for the various kinds of matches
251
251
  for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
252
252
  let next = text.codePointAt(word, i);
@@ -268,26 +268,30 @@ class FuzzyMatcher {
268
268
  let ch, type = next < 0xff
269
269
  ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
270
270
  : ((ch = text.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
271
- if ((type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) &&
272
- (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true))))
273
- byWord[byWordTo++] = i;
271
+ if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
272
+ if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
273
+ byWord[byWordTo++] = i;
274
+ else if (byWord.length)
275
+ wordAdjacent = false;
276
+ }
274
277
  prevType = type;
275
278
  i += text.codePointSize(next);
276
279
  }
277
- if (byWordTo == len && byWord[0] == 0)
280
+ if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
278
281
  return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
279
282
  if (adjacentTo == len && adjacentStart == 0)
280
- return [-200 /* CaseFold */, 0, adjacentEnd];
283
+ return [-200 /* CaseFold */ - word.length, 0, adjacentEnd];
281
284
  if (direct > -1)
282
- return [-700 /* NotStart */, direct, direct + this.pattern.length];
285
+ return [-700 /* NotStart */ - word.length, direct, direct + this.pattern.length];
283
286
  if (adjacentTo == len)
284
- return [-200 /* CaseFold */ + -700 /* NotStart */, adjacentStart, adjacentEnd];
287
+ return [-200 /* CaseFold */ + -700 /* NotStart */ - word.length, adjacentStart, adjacentEnd];
285
288
  if (byWordTo == len)
286
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */, byWord, word);
289
+ return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
290
+ (wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
287
291
  return chars.length == 2 ? null : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
288
292
  }
289
293
  result(score, positions, word) {
290
- let result = [score], i = 1;
294
+ let result = [score - word.length], i = 1;
291
295
  for (let pos of positions) {
292
296
  let to = pos + (this.astral ? text.codePointSize(text.codePointAt(word, pos)) : 1);
293
297
  if (i > 1 && result[i - 1] == pos)
@@ -483,12 +487,14 @@ class CompletionTooltip {
483
487
  let sel = this.dom.querySelector("[aria-selected]");
484
488
  if (!sel || !this.info)
485
489
  return null;
486
- let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
487
- let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
488
- if (top < 0 || top > this.list.clientHeight - 10)
490
+ let listRect = this.dom.getBoundingClientRect();
491
+ let infoRect = this.info.getBoundingClientRect();
492
+ let selRect = sel.getBoundingClientRect();
493
+ if (selRect.top > Math.min(innerHeight, listRect.bottom) - 10 || selRect.bottom < Math.max(0, listRect.top) + 10)
489
494
  return null;
495
+ let top = Math.max(0, Math.min(selRect.top, innerHeight - infoRect.height)) - listRect.top;
490
496
  let left = this.view.textDirection == view.Direction.RTL;
491
- let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
497
+ let spaceLeft = listRect.left, spaceRight = innerWidth - listRect.right;
492
498
  if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
493
499
  left = false;
494
500
  else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
@@ -496,10 +502,12 @@ class CompletionTooltip {
496
502
  return { top, left };
497
503
  }
498
504
  positionInfo(pos) {
499
- if (this.info && pos) {
500
- this.info.style.top = pos.top + "px";
501
- this.info.classList.toggle("cm-completionInfo-left", pos.left);
502
- this.info.classList.toggle("cm-completionInfo-right", !pos.left);
505
+ if (this.info) {
506
+ this.info.style.top = (pos ? pos.top : -1e6) + "px";
507
+ if (pos) {
508
+ this.info.classList.toggle("cm-completionInfo-left", pos.left);
509
+ this.info.classList.toggle("cm-completionInfo-right", !pos.left);
510
+ }
503
511
  }
504
512
  }
505
513
  createListBox(options, id, range) {
@@ -773,9 +781,10 @@ function moveCompletionSelection(forward, by = "option") {
773
781
  let cState = view.state.field(completionState, false);
774
782
  if (!cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
775
783
  return false;
776
- let step = 1, tooltip;
777
- if (by == "page" && (tooltip = view.dom.querySelector(".cm-tooltip-autocomplete")))
778
- step = Math.max(2, Math.floor(tooltip.offsetHeight / tooltip.firstChild.offsetHeight));
784
+ let step = 1, tooltip$1;
785
+ if (by == "page" && (tooltip$1 = tooltip.getTooltip(view, cState.open.tooltip)))
786
+ step = Math.max(2, Math.floor(tooltip$1.dom.offsetHeight /
787
+ tooltip$1.dom.querySelector("li").offsetHeight) - 1);
779
788
  let selected = cState.open.selected + step * (forward ? 1 : -1), { length } = cState.open.options;
780
789
  if (selected < 0)
781
790
  selected = by == "page" ? 0 : length - 1;
@@ -989,7 +998,7 @@ const baseTheme = view.EditorView.baseTheme({
989
998
  }
990
999
  },
991
1000
  "&light .cm-tooltip-autocomplete ul li[aria-selected]": {
992
- background: "#39e",
1001
+ background: "#17c",
993
1002
  color: "white",
994
1003
  },
995
1004
  "&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Annotation, Facet, combineConfig, StateEffect, StateField, Prec, EditorSelection, Text } from '@codemirror/state';
2
2
  import { Direction, logException, EditorView, ViewPlugin, Decoration, WidgetType, keymap } from '@codemirror/view';
3
- import { showTooltip } from '@codemirror/tooltip';
3
+ import { showTooltip, getTooltip } from '@codemirror/tooltip';
4
4
  import { syntaxTree, indentUnit } from '@codemirror/language';
5
5
  import { codePointAt, codePointSize, fromCodePoint } from '@codemirror/text';
6
6
 
@@ -242,7 +242,7 @@ class FuzzyMatcher {
242
242
  let byWordTo = 0, byWordFolded = false;
243
243
  // If we've found a partial adjacent match, these track its state
244
244
  let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
245
- let hasLower = /[a-z]/.test(word);
245
+ let hasLower = /[a-z]/.test(word), wordAdjacent = true;
246
246
  // Go over the option's text, scanning for the various kinds of matches
247
247
  for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
248
248
  let next = codePointAt(word, i);
@@ -264,26 +264,30 @@ class FuzzyMatcher {
264
264
  let ch, type = next < 0xff
265
265
  ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
266
266
  : ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
267
- if ((type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) &&
268
- (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true))))
269
- byWord[byWordTo++] = i;
267
+ if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
268
+ if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
269
+ byWord[byWordTo++] = i;
270
+ else if (byWord.length)
271
+ wordAdjacent = false;
272
+ }
270
273
  prevType = type;
271
274
  i += codePointSize(next);
272
275
  }
273
- if (byWordTo == len && byWord[0] == 0)
276
+ if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
274
277
  return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
275
278
  if (adjacentTo == len && adjacentStart == 0)
276
- return [-200 /* CaseFold */, 0, adjacentEnd];
279
+ return [-200 /* CaseFold */ - word.length, 0, adjacentEnd];
277
280
  if (direct > -1)
278
- return [-700 /* NotStart */, direct, direct + this.pattern.length];
281
+ return [-700 /* NotStart */ - word.length, direct, direct + this.pattern.length];
279
282
  if (adjacentTo == len)
280
- return [-200 /* CaseFold */ + -700 /* NotStart */, adjacentStart, adjacentEnd];
283
+ return [-200 /* CaseFold */ + -700 /* NotStart */ - word.length, adjacentStart, adjacentEnd];
281
284
  if (byWordTo == len)
282
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */, byWord, word);
285
+ return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
286
+ (wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
283
287
  return chars.length == 2 ? null : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
284
288
  }
285
289
  result(score, positions, word) {
286
- let result = [score], i = 1;
290
+ let result = [score - word.length], i = 1;
287
291
  for (let pos of positions) {
288
292
  let to = pos + (this.astral ? codePointSize(codePointAt(word, pos)) : 1);
289
293
  if (i > 1 && result[i - 1] == pos)
@@ -479,12 +483,14 @@ class CompletionTooltip {
479
483
  let sel = this.dom.querySelector("[aria-selected]");
480
484
  if (!sel || !this.info)
481
485
  return null;
482
- let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
483
- let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
484
- if (top < 0 || top > this.list.clientHeight - 10)
486
+ let listRect = this.dom.getBoundingClientRect();
487
+ let infoRect = this.info.getBoundingClientRect();
488
+ let selRect = sel.getBoundingClientRect();
489
+ if (selRect.top > Math.min(innerHeight, listRect.bottom) - 10 || selRect.bottom < Math.max(0, listRect.top) + 10)
485
490
  return null;
491
+ let top = Math.max(0, Math.min(selRect.top, innerHeight - infoRect.height)) - listRect.top;
486
492
  let left = this.view.textDirection == Direction.RTL;
487
- let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
493
+ let spaceLeft = listRect.left, spaceRight = innerWidth - listRect.right;
488
494
  if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
489
495
  left = false;
490
496
  else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
@@ -492,10 +498,12 @@ class CompletionTooltip {
492
498
  return { top, left };
493
499
  }
494
500
  positionInfo(pos) {
495
- if (this.info && pos) {
496
- this.info.style.top = pos.top + "px";
497
- this.info.classList.toggle("cm-completionInfo-left", pos.left);
498
- this.info.classList.toggle("cm-completionInfo-right", !pos.left);
501
+ if (this.info) {
502
+ this.info.style.top = (pos ? pos.top : -1e6) + "px";
503
+ if (pos) {
504
+ this.info.classList.toggle("cm-completionInfo-left", pos.left);
505
+ this.info.classList.toggle("cm-completionInfo-right", !pos.left);
506
+ }
499
507
  }
500
508
  }
501
509
  createListBox(options, id, range) {
@@ -770,8 +778,9 @@ function moveCompletionSelection(forward, by = "option") {
770
778
  if (!cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
771
779
  return false;
772
780
  let step = 1, tooltip;
773
- if (by == "page" && (tooltip = view.dom.querySelector(".cm-tooltip-autocomplete")))
774
- step = Math.max(2, Math.floor(tooltip.offsetHeight / tooltip.firstChild.offsetHeight));
781
+ if (by == "page" && (tooltip = getTooltip(view, cState.open.tooltip)))
782
+ step = Math.max(2, Math.floor(tooltip.dom.offsetHeight /
783
+ tooltip.dom.querySelector("li").offsetHeight) - 1);
775
784
  let selected = cState.open.selected + step * (forward ? 1 : -1), { length } = cState.open.options;
776
785
  if (selected < 0)
777
786
  selected = by == "page" ? 0 : length - 1;
@@ -985,7 +994,7 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
985
994
  }
986
995
  },
987
996
  "&light .cm-tooltip-autocomplete ul li[aria-selected]": {
988
- background: "#39e",
997
+ background: "#17c",
989
998
  color: "white",
990
999
  },
991
1000
  "&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "0.19.8",
3
+ "version": "0.19.12",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -29,7 +29,7 @@
29
29
  "@codemirror/language": "^0.19.0",
30
30
  "@codemirror/state": "^0.19.4",
31
31
  "@codemirror/text": "^0.19.2",
32
- "@codemirror/tooltip": "^0.19.0",
32
+ "@codemirror/tooltip": "^0.19.12",
33
33
  "@codemirror/view": "^0.19.0",
34
34
  "@lezer/common": "^0.15.0"
35
35
  },