@codemirror/autocomplete 0.19.3 → 0.19.7

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,35 @@
1
+ ## 0.19.7 (2021-11-16)
2
+
3
+ ### Bug fixes
4
+
5
+ Make option deduplication less aggressive, so that options with different `type` or `apply` fields don't get merged.
6
+
7
+ ## 0.19.6 (2021-11-12)
8
+
9
+ ### Bug fixes
10
+
11
+ Fix an issue where parsing a snippet with a field that was labeled only by a number crashed.
12
+
13
+ ## 0.19.5 (2021-11-09)
14
+
15
+ ### Bug fixes
16
+
17
+ Make sure info tooltips don't stick out of the bottom of the page.
18
+
19
+ ### New features
20
+
21
+ The package exports a new function `selectedCompletion`, which can be used to find out which completion is currently selected.
22
+
23
+ Transactions created by picking a completion now have an annotation (`pickedCompletion`) holding the original completion.
24
+
25
+ ## 0.19.4 (2021-10-24)
26
+
27
+ ### Bug fixes
28
+
29
+ Don't rely on the platform's highlight colors for the active completion, since those are inconsistent and may not be appropriate for the theme.
30
+
31
+ Fix incorrect match underline for some kinds of matched completions.
32
+
1
33
  ## 0.19.3 (2021-08-31)
2
34
 
3
35
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -150,6 +150,11 @@ function ensureAnchor(expr, start) {
150
150
  return expr;
151
151
  return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : (expr.ignoreCase ? "i" : ""));
152
152
  }
153
+ /**
154
+ This annotation is added to transactions that are produced by
155
+ picking a completion.
156
+ */
157
+ const pickedCompletion = state.Annotation.define();
153
158
  function applyCompletion(view, option) {
154
159
  let apply = option.completion.apply || option.completion.label;
155
160
  let result = option.source;
@@ -157,7 +162,8 @@ function applyCompletion(view, option) {
157
162
  view.dispatch({
158
163
  changes: { from: result.from, to: result.to, insert: apply },
159
164
  selection: { anchor: result.from + apply.length },
160
- userEvent: "input.complete"
165
+ userEvent: "input.complete",
166
+ annotations: pickedCompletion.of(option.completion)
161
167
  });
162
168
  }
163
169
  else {
@@ -251,7 +257,7 @@ class FuzzyMatcher {
251
257
  if (next == chars[adjacentTo] || next == folded[adjacentTo]) {
252
258
  if (adjacentTo == 0)
253
259
  adjacentStart = i;
254
- adjacentEnd = i;
260
+ adjacentEnd = i + 1;
255
261
  adjacentTo++;
256
262
  }
257
263
  else {
@@ -317,105 +323,6 @@ function joinClass(a, b) {
317
323
  return a ? b ? a + " " + b : a : b;
318
324
  }
319
325
 
320
- const MaxInfoWidth = 300;
321
- const baseTheme = view.EditorView.baseTheme({
322
- ".cm-tooltip.cm-tooltip-autocomplete": {
323
- "& > ul": {
324
- fontFamily: "monospace",
325
- whiteSpace: "nowrap",
326
- overflow: "auto",
327
- maxWidth_fallback: "700px",
328
- maxWidth: "min(700px, 95vw)",
329
- maxHeight: "10em",
330
- listStyle: "none",
331
- margin: 0,
332
- padding: 0,
333
- "& > li": {
334
- cursor: "pointer",
335
- padding: "1px 1em 1px 3px",
336
- lineHeight: 1.2
337
- },
338
- "& > li[aria-selected]": {
339
- background_fallback: "#bdf",
340
- backgroundColor: "Highlight",
341
- color_fallback: "white",
342
- color: "HighlightText"
343
- }
344
- }
345
- },
346
- ".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
347
- content: '"···"',
348
- opacity: 0.5,
349
- display: "block",
350
- textAlign: "center"
351
- },
352
- ".cm-tooltip.cm-completionInfo": {
353
- position: "absolute",
354
- padding: "3px 9px",
355
- width: "max-content",
356
- maxWidth: MaxInfoWidth + "px",
357
- },
358
- ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
359
- ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
360
- "&light .cm-snippetField": { backgroundColor: "#00000022" },
361
- "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
362
- ".cm-snippetFieldPosition": {
363
- verticalAlign: "text-top",
364
- width: 0,
365
- height: "1.15em",
366
- margin: "0 -0.7px -.7em",
367
- borderLeft: "1.4px dotted #888"
368
- },
369
- ".cm-completionMatchedText": {
370
- textDecoration: "underline"
371
- },
372
- ".cm-completionDetail": {
373
- marginLeft: "0.5em",
374
- fontStyle: "italic"
375
- },
376
- ".cm-completionIcon": {
377
- fontSize: "90%",
378
- width: ".8em",
379
- display: "inline-block",
380
- textAlign: "center",
381
- paddingRight: ".6em",
382
- opacity: "0.6"
383
- },
384
- ".cm-completionIcon-function, .cm-completionIcon-method": {
385
- "&:after": { content: "'ƒ'" }
386
- },
387
- ".cm-completionIcon-class": {
388
- "&:after": { content: "'○'" }
389
- },
390
- ".cm-completionIcon-interface": {
391
- "&:after": { content: "'◌'" }
392
- },
393
- ".cm-completionIcon-variable": {
394
- "&:after": { content: "'𝑥'" }
395
- },
396
- ".cm-completionIcon-constant": {
397
- "&:after": { content: "'𝐶'" }
398
- },
399
- ".cm-completionIcon-type": {
400
- "&:after": { content: "'𝑡'" }
401
- },
402
- ".cm-completionIcon-enum": {
403
- "&:after": { content: "'∪'" }
404
- },
405
- ".cm-completionIcon-property": {
406
- "&:after": { content: "'□'" }
407
- },
408
- ".cm-completionIcon-keyword": {
409
- "&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
410
- },
411
- ".cm-completionIcon-namespace": {
412
- "&:after": { content: "'▢'" }
413
- },
414
- ".cm-completionIcon-text": {
415
- "&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
416
- }
417
- });
418
-
419
326
  function optionContent(config) {
420
327
  let content = config.addToOptions.slice();
421
328
  if (config.icons)
@@ -573,17 +480,17 @@ class CompletionTooltip {
573
480
  }
574
481
  measureInfo() {
575
482
  let sel = this.dom.querySelector("[aria-selected]");
576
- if (!sel)
483
+ if (!sel || !this.info)
577
484
  return null;
578
- let rect = this.dom.getBoundingClientRect();
579
- let top = sel.getBoundingClientRect().top - rect.top;
485
+ let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
486
+ let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
580
487
  if (top < 0 || top > this.list.clientHeight - 10)
581
488
  return null;
582
489
  let left = this.view.textDirection == view.Direction.RTL;
583
490
  let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
584
- if (left && spaceLeft < Math.min(MaxInfoWidth, spaceRight))
491
+ if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
585
492
  left = false;
586
- else if (!left && spaceRight < Math.min(MaxInfoWidth, spaceLeft))
493
+ else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
587
494
  left = true;
588
495
  return { top, left };
589
496
  }
@@ -663,7 +570,8 @@ function sortOptions(active, state) {
663
570
  for (let opt of options.sort(cmpOption)) {
664
571
  if (result.length == MaxOptions)
665
572
  break;
666
- if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail)
573
+ if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
574
+ prev.type != opt.completion.type || prev.apply != opt.completion.apply)
667
575
  result.push(opt);
668
576
  else if (score(opt.completion) > score(prev))
669
577
  result[result.length - 1] = opt;
@@ -880,7 +788,7 @@ Accept the current completion.
880
788
  */
881
789
  const acceptCompletion = (view) => {
882
790
  let cState = view.state.field(completionState, false);
883
- if (!cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
791
+ if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
884
792
  return false;
885
793
  applyCompletion(view, cState.open.options[cState.open.selected]);
886
794
  return true;
@@ -1056,6 +964,106 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1056
964
  }
1057
965
  });
1058
966
 
967
+ const baseTheme = view.EditorView.baseTheme({
968
+ ".cm-tooltip.cm-tooltip-autocomplete": {
969
+ "& > ul": {
970
+ fontFamily: "monospace",
971
+ whiteSpace: "nowrap",
972
+ overflow: "auto",
973
+ maxWidth_fallback: "700px",
974
+ maxWidth: "min(700px, 95vw)",
975
+ maxHeight: "10em",
976
+ listStyle: "none",
977
+ margin: 0,
978
+ padding: 0,
979
+ "& > li": {
980
+ cursor: "pointer",
981
+ padding: "1px 1em 1px 3px",
982
+ lineHeight: 1.2
983
+ },
984
+ }
985
+ },
986
+ "&light .cm-tooltip-autocomplete ul li[aria-selected]": {
987
+ background: "#39e",
988
+ color: "white",
989
+ },
990
+ "&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
991
+ background: "#347",
992
+ color: "white",
993
+ },
994
+ ".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
995
+ content: '"···"',
996
+ opacity: 0.5,
997
+ display: "block",
998
+ textAlign: "center"
999
+ },
1000
+ ".cm-tooltip.cm-completionInfo": {
1001
+ position: "absolute",
1002
+ padding: "3px 9px",
1003
+ width: "max-content",
1004
+ maxWidth: "300px",
1005
+ },
1006
+ ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1007
+ ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1008
+ "&light .cm-snippetField": { backgroundColor: "#00000022" },
1009
+ "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1010
+ ".cm-snippetFieldPosition": {
1011
+ verticalAlign: "text-top",
1012
+ width: 0,
1013
+ height: "1.15em",
1014
+ margin: "0 -0.7px -.7em",
1015
+ borderLeft: "1.4px dotted #888"
1016
+ },
1017
+ ".cm-completionMatchedText": {
1018
+ textDecoration: "underline"
1019
+ },
1020
+ ".cm-completionDetail": {
1021
+ marginLeft: "0.5em",
1022
+ fontStyle: "italic"
1023
+ },
1024
+ ".cm-completionIcon": {
1025
+ fontSize: "90%",
1026
+ width: ".8em",
1027
+ display: "inline-block",
1028
+ textAlign: "center",
1029
+ paddingRight: ".6em",
1030
+ opacity: "0.6"
1031
+ },
1032
+ ".cm-completionIcon-function, .cm-completionIcon-method": {
1033
+ "&:after": { content: "'ƒ'" }
1034
+ },
1035
+ ".cm-completionIcon-class": {
1036
+ "&:after": { content: "'○'" }
1037
+ },
1038
+ ".cm-completionIcon-interface": {
1039
+ "&:after": { content: "'◌'" }
1040
+ },
1041
+ ".cm-completionIcon-variable": {
1042
+ "&:after": { content: "'𝑥'" }
1043
+ },
1044
+ ".cm-completionIcon-constant": {
1045
+ "&:after": { content: "'𝐶'" }
1046
+ },
1047
+ ".cm-completionIcon-type": {
1048
+ "&:after": { content: "'𝑡'" }
1049
+ },
1050
+ ".cm-completionIcon-enum": {
1051
+ "&:after": { content: "'∪'" }
1052
+ },
1053
+ ".cm-completionIcon-property": {
1054
+ "&:after": { content: "'□'" }
1055
+ },
1056
+ ".cm-completionIcon-keyword": {
1057
+ "&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
1058
+ },
1059
+ ".cm-completionIcon-namespace": {
1060
+ "&:after": { content: "'▢'" }
1061
+ },
1062
+ ".cm-completionIcon-text": {
1063
+ "&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
1064
+ }
1065
+ });
1066
+
1059
1067
  class FieldPos {
1060
1068
  constructor(field, line, from, to) {
1061
1069
  this.field = field;
@@ -1101,7 +1109,7 @@ class Snippet {
1101
1109
  let lines = [], positions = [], m;
1102
1110
  for (let line of template.split(/\r\n?|\n/)) {
1103
1111
  while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
1104
- let seq = m[1] ? +m[1] : null, name = m[2] || m[3], found = -1;
1112
+ let seq = m[1] ? +m[1] : null, name = m[2] || m[3] || "", found = -1;
1105
1113
  for (let i = 0; i < fields.length; i++) {
1106
1114
  if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
1107
1115
  found = i;
@@ -1110,7 +1118,7 @@ class Snippet {
1110
1118
  let i = 0;
1111
1119
  while (i < fields.length && (seq == null || (fields[i].seq != null && fields[i].seq < seq)))
1112
1120
  i++;
1113
- fields.splice(i, 0, { seq, name: name || null });
1121
+ fields.splice(i, 0, { seq, name });
1114
1122
  found = i;
1115
1123
  for (let pos of positions)
1116
1124
  if (pos.field >= found)
@@ -1206,8 +1214,7 @@ function snippet(template) {
1206
1214
  let active = new ActiveSnippet(ranges, 0);
1207
1215
  let effects = spec.effects = [setActive.of(active)];
1208
1216
  if (editor.state.field(snippetState, false) === undefined)
1209
- effects.push(state.StateEffect.appendConfig.of([snippetState.init(() => active), addSnippetKeymap,
1210
- snippetPointerHandler, baseTheme]));
1217
+ effects.push(state.StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
1211
1218
  }
1212
1219
  editor.dispatch(editor.state.update(spec));
1213
1220
  };
@@ -1257,7 +1264,7 @@ to [`clearSnippet`](https://codemirror.net/6/docs/ref/#autocomplete.clearSnippet
1257
1264
  const snippetKeymap = state.Facet.define({
1258
1265
  combine(maps) { return maps.length ? maps[0] : defaultSnippetKeymap; }
1259
1266
  });
1260
- const addSnippetKeymap = state.Prec.override(view.keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
1267
+ const addSnippetKeymap = state.Prec.highest(view.keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
1261
1268
  /**
1262
1269
  Create a completion from a snippet. Returns an object with the
1263
1270
  properties from `completion`, plus an `apply` function that
@@ -1391,7 +1398,7 @@ const completionKeymap = [
1391
1398
  { key: "PageUp", run: moveCompletionSelection(false, "page") },
1392
1399
  { key: "Enter", run: acceptCompletion }
1393
1400
  ];
1394
- const completionKeymapExt = state.Prec.override(view.keymap.computeN([completionConfig], state => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
1401
+ const completionKeymapExt = state.Prec.highest(view.keymap.computeN([completionConfig], state => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
1395
1402
  /**
1396
1403
  Get the current completion status. When completions are available,
1397
1404
  this will return `"active"`. When completions are pending (in the
@@ -1411,6 +1418,14 @@ function currentCompletions(state) {
1411
1418
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1412
1419
  return open ? open.options.map(o => o.completion) : [];
1413
1420
  }
1421
+ /**
1422
+ Return the currently selected completion, if any.
1423
+ */
1424
+ function selectedCompletion(state) {
1425
+ var _a;
1426
+ let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1427
+ return open ? open.options[open.selected].completion : null;
1428
+ }
1414
1429
 
1415
1430
  exports.CompletionContext = CompletionContext;
1416
1431
  exports.acceptCompletion = acceptCompletion;
@@ -1426,7 +1441,9 @@ exports.ifIn = ifIn;
1426
1441
  exports.ifNotIn = ifNotIn;
1427
1442
  exports.moveCompletionSelection = moveCompletionSelection;
1428
1443
  exports.nextSnippetField = nextSnippetField;
1444
+ exports.pickedCompletion = pickedCompletion;
1429
1445
  exports.prevSnippetField = prevSnippetField;
1446
+ exports.selectedCompletion = selectedCompletion;
1430
1447
  exports.snippet = snippet;
1431
1448
  exports.snippetCompletion = snippetCompletion;
1432
1449
  exports.snippetKeymap = snippetKeymap;
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as _codemirror_state from '@codemirror/state';
1
2
  import { EditorState, Transaction, StateCommand, Facet, Extension } from '@codemirror/state';
2
3
  import { EditorView, KeyBinding, Command } from '@codemirror/view';
3
4
  import * as _lezer_common from '@lezer/common';
@@ -12,7 +13,8 @@ interface CompletionConfig {
12
13
  Override the completion sources used. By default, they will be
13
14
  taken from the `"autocomplete"` [language
14
15
  data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) (which should hold
15
- [completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource)).
16
+ [completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource) or arrays
17
+ of [completions](https://codemirror.net/6/docs/ref/#autocomplete.Completion)).
16
18
  */
17
19
  override?: readonly CompletionSource[] | null;
18
20
  /**
@@ -79,7 +81,9 @@ interface Completion {
79
81
  its [label](https://codemirror.net/6/docs/ref/#autocomplete.Completion.label). When this holds a
80
82
  string, the completion range is replaced by that string. When it
81
83
  is a function, that function is called to perform the
82
- completion.
84
+ completion. If it fires a transaction, it is responsible for
85
+ adding the [`pickedCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.pickedCompletion)
86
+ annotation to it.
83
87
  */
84
88
  apply?: string | ((view: EditorView, completion: Completion, from: number, to: number) => void);
85
89
  /**
@@ -234,6 +238,11 @@ interface CompletionResult {
234
238
  */
235
239
  filter?: boolean;
236
240
  }
241
+ /**
242
+ This annotation is added to transactions that are produced by
243
+ picking a completion.
244
+ */
245
+ declare const pickedCompletion: _codemirror_state.AnnotationType<Completion>;
237
246
 
238
247
  /**
239
248
  Convert a snippet template to a function that can apply it.
@@ -343,5 +352,9 @@ declare function completionStatus(state: EditorState): null | "active" | "pendin
343
352
  Returns the available completions as an array.
344
353
  */
345
354
  declare function currentCompletions(state: EditorState): readonly Completion[];
355
+ /**
356
+ Return the currently selected completion, if any.
357
+ */
358
+ declare function selectedCompletion(state: EditorState): Completion | null;
346
359
 
347
- export { Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
360
+ export { Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { Facet, combineConfig, StateEffect, StateField, Text, EditorSelection, Prec } from '@codemirror/state';
2
- import { EditorView, Direction, logException, ViewPlugin, Decoration, WidgetType, keymap } from '@codemirror/view';
1
+ import { Annotation, Facet, combineConfig, StateEffect, StateField, Prec, EditorSelection, Text } from '@codemirror/state';
2
+ import { Direction, logException, EditorView, ViewPlugin, Decoration, WidgetType, keymap } from '@codemirror/view';
3
3
  import { showTooltip } from '@codemirror/tooltip';
4
4
  import { syntaxTree, indentUnit } from '@codemirror/language';
5
5
  import { codePointAt, codePointSize, fromCodePoint } from '@codemirror/text';
@@ -146,6 +146,11 @@ function ensureAnchor(expr, start) {
146
146
  return expr;
147
147
  return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : (expr.ignoreCase ? "i" : ""));
148
148
  }
149
+ /**
150
+ This annotation is added to transactions that are produced by
151
+ picking a completion.
152
+ */
153
+ const pickedCompletion = /*@__PURE__*/Annotation.define();
149
154
  function applyCompletion(view, option) {
150
155
  let apply = option.completion.apply || option.completion.label;
151
156
  let result = option.source;
@@ -153,7 +158,8 @@ function applyCompletion(view, option) {
153
158
  view.dispatch({
154
159
  changes: { from: result.from, to: result.to, insert: apply },
155
160
  selection: { anchor: result.from + apply.length },
156
- userEvent: "input.complete"
161
+ userEvent: "input.complete",
162
+ annotations: pickedCompletion.of(option.completion)
157
163
  });
158
164
  }
159
165
  else {
@@ -247,7 +253,7 @@ class FuzzyMatcher {
247
253
  if (next == chars[adjacentTo] || next == folded[adjacentTo]) {
248
254
  if (adjacentTo == 0)
249
255
  adjacentStart = i;
250
- adjacentEnd = i;
256
+ adjacentEnd = i + 1;
251
257
  adjacentTo++;
252
258
  }
253
259
  else {
@@ -313,105 +319,6 @@ function joinClass(a, b) {
313
319
  return a ? b ? a + " " + b : a : b;
314
320
  }
315
321
 
316
- const MaxInfoWidth = 300;
317
- const baseTheme = /*@__PURE__*/EditorView.baseTheme({
318
- ".cm-tooltip.cm-tooltip-autocomplete": {
319
- "& > ul": {
320
- fontFamily: "monospace",
321
- whiteSpace: "nowrap",
322
- overflow: "auto",
323
- maxWidth_fallback: "700px",
324
- maxWidth: "min(700px, 95vw)",
325
- maxHeight: "10em",
326
- listStyle: "none",
327
- margin: 0,
328
- padding: 0,
329
- "& > li": {
330
- cursor: "pointer",
331
- padding: "1px 1em 1px 3px",
332
- lineHeight: 1.2
333
- },
334
- "& > li[aria-selected]": {
335
- background_fallback: "#bdf",
336
- backgroundColor: "Highlight",
337
- color_fallback: "white",
338
- color: "HighlightText"
339
- }
340
- }
341
- },
342
- ".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
343
- content: '"···"',
344
- opacity: 0.5,
345
- display: "block",
346
- textAlign: "center"
347
- },
348
- ".cm-tooltip.cm-completionInfo": {
349
- position: "absolute",
350
- padding: "3px 9px",
351
- width: "max-content",
352
- maxWidth: MaxInfoWidth + "px",
353
- },
354
- ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
355
- ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
356
- "&light .cm-snippetField": { backgroundColor: "#00000022" },
357
- "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
358
- ".cm-snippetFieldPosition": {
359
- verticalAlign: "text-top",
360
- width: 0,
361
- height: "1.15em",
362
- margin: "0 -0.7px -.7em",
363
- borderLeft: "1.4px dotted #888"
364
- },
365
- ".cm-completionMatchedText": {
366
- textDecoration: "underline"
367
- },
368
- ".cm-completionDetail": {
369
- marginLeft: "0.5em",
370
- fontStyle: "italic"
371
- },
372
- ".cm-completionIcon": {
373
- fontSize: "90%",
374
- width: ".8em",
375
- display: "inline-block",
376
- textAlign: "center",
377
- paddingRight: ".6em",
378
- opacity: "0.6"
379
- },
380
- ".cm-completionIcon-function, .cm-completionIcon-method": {
381
- "&:after": { content: "'ƒ'" }
382
- },
383
- ".cm-completionIcon-class": {
384
- "&:after": { content: "'○'" }
385
- },
386
- ".cm-completionIcon-interface": {
387
- "&:after": { content: "'◌'" }
388
- },
389
- ".cm-completionIcon-variable": {
390
- "&:after": { content: "'𝑥'" }
391
- },
392
- ".cm-completionIcon-constant": {
393
- "&:after": { content: "'𝐶'" }
394
- },
395
- ".cm-completionIcon-type": {
396
- "&:after": { content: "'𝑡'" }
397
- },
398
- ".cm-completionIcon-enum": {
399
- "&:after": { content: "'∪'" }
400
- },
401
- ".cm-completionIcon-property": {
402
- "&:after": { content: "'□'" }
403
- },
404
- ".cm-completionIcon-keyword": {
405
- "&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
406
- },
407
- ".cm-completionIcon-namespace": {
408
- "&:after": { content: "'▢'" }
409
- },
410
- ".cm-completionIcon-text": {
411
- "&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
412
- }
413
- });
414
-
415
322
  function optionContent(config) {
416
323
  let content = config.addToOptions.slice();
417
324
  if (config.icons)
@@ -569,17 +476,17 @@ class CompletionTooltip {
569
476
  }
570
477
  measureInfo() {
571
478
  let sel = this.dom.querySelector("[aria-selected]");
572
- if (!sel)
479
+ if (!sel || !this.info)
573
480
  return null;
574
- let rect = this.dom.getBoundingClientRect();
575
- let top = sel.getBoundingClientRect().top - rect.top;
481
+ let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
482
+ let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
576
483
  if (top < 0 || top > this.list.clientHeight - 10)
577
484
  return null;
578
485
  let left = this.view.textDirection == Direction.RTL;
579
486
  let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
580
- if (left && spaceLeft < Math.min(MaxInfoWidth, spaceRight))
487
+ if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
581
488
  left = false;
582
- else if (!left && spaceRight < Math.min(MaxInfoWidth, spaceLeft))
489
+ else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
583
490
  left = true;
584
491
  return { top, left };
585
492
  }
@@ -659,7 +566,8 @@ function sortOptions(active, state) {
659
566
  for (let opt of options.sort(cmpOption)) {
660
567
  if (result.length == MaxOptions)
661
568
  break;
662
- if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail)
569
+ if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
570
+ prev.type != opt.completion.type || prev.apply != opt.completion.apply)
663
571
  result.push(opt);
664
572
  else if (score(opt.completion) > score(prev))
665
573
  result[result.length - 1] = opt;
@@ -876,7 +784,7 @@ Accept the current completion.
876
784
  */
877
785
  const acceptCompletion = (view) => {
878
786
  let cState = view.state.field(completionState, false);
879
- if (!cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
787
+ if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
880
788
  return false;
881
789
  applyCompletion(view, cState.open.options[cState.open.selected]);
882
790
  return true;
@@ -1052,6 +960,106 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1052
960
  }
1053
961
  });
1054
962
 
963
+ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
964
+ ".cm-tooltip.cm-tooltip-autocomplete": {
965
+ "& > ul": {
966
+ fontFamily: "monospace",
967
+ whiteSpace: "nowrap",
968
+ overflow: "auto",
969
+ maxWidth_fallback: "700px",
970
+ maxWidth: "min(700px, 95vw)",
971
+ maxHeight: "10em",
972
+ listStyle: "none",
973
+ margin: 0,
974
+ padding: 0,
975
+ "& > li": {
976
+ cursor: "pointer",
977
+ padding: "1px 1em 1px 3px",
978
+ lineHeight: 1.2
979
+ },
980
+ }
981
+ },
982
+ "&light .cm-tooltip-autocomplete ul li[aria-selected]": {
983
+ background: "#39e",
984
+ color: "white",
985
+ },
986
+ "&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
987
+ background: "#347",
988
+ color: "white",
989
+ },
990
+ ".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
991
+ content: '"···"',
992
+ opacity: 0.5,
993
+ display: "block",
994
+ textAlign: "center"
995
+ },
996
+ ".cm-tooltip.cm-completionInfo": {
997
+ position: "absolute",
998
+ padding: "3px 9px",
999
+ width: "max-content",
1000
+ maxWidth: "300px",
1001
+ },
1002
+ ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1003
+ ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1004
+ "&light .cm-snippetField": { backgroundColor: "#00000022" },
1005
+ "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1006
+ ".cm-snippetFieldPosition": {
1007
+ verticalAlign: "text-top",
1008
+ width: 0,
1009
+ height: "1.15em",
1010
+ margin: "0 -0.7px -.7em",
1011
+ borderLeft: "1.4px dotted #888"
1012
+ },
1013
+ ".cm-completionMatchedText": {
1014
+ textDecoration: "underline"
1015
+ },
1016
+ ".cm-completionDetail": {
1017
+ marginLeft: "0.5em",
1018
+ fontStyle: "italic"
1019
+ },
1020
+ ".cm-completionIcon": {
1021
+ fontSize: "90%",
1022
+ width: ".8em",
1023
+ display: "inline-block",
1024
+ textAlign: "center",
1025
+ paddingRight: ".6em",
1026
+ opacity: "0.6"
1027
+ },
1028
+ ".cm-completionIcon-function, .cm-completionIcon-method": {
1029
+ "&:after": { content: "'ƒ'" }
1030
+ },
1031
+ ".cm-completionIcon-class": {
1032
+ "&:after": { content: "'○'" }
1033
+ },
1034
+ ".cm-completionIcon-interface": {
1035
+ "&:after": { content: "'◌'" }
1036
+ },
1037
+ ".cm-completionIcon-variable": {
1038
+ "&:after": { content: "'𝑥'" }
1039
+ },
1040
+ ".cm-completionIcon-constant": {
1041
+ "&:after": { content: "'𝐶'" }
1042
+ },
1043
+ ".cm-completionIcon-type": {
1044
+ "&:after": { content: "'𝑡'" }
1045
+ },
1046
+ ".cm-completionIcon-enum": {
1047
+ "&:after": { content: "'∪'" }
1048
+ },
1049
+ ".cm-completionIcon-property": {
1050
+ "&:after": { content: "'□'" }
1051
+ },
1052
+ ".cm-completionIcon-keyword": {
1053
+ "&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
1054
+ },
1055
+ ".cm-completionIcon-namespace": {
1056
+ "&:after": { content: "'▢'" }
1057
+ },
1058
+ ".cm-completionIcon-text": {
1059
+ "&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
1060
+ }
1061
+ });
1062
+
1055
1063
  class FieldPos {
1056
1064
  constructor(field, line, from, to) {
1057
1065
  this.field = field;
@@ -1097,7 +1105,7 @@ class Snippet {
1097
1105
  let lines = [], positions = [], m;
1098
1106
  for (let line of template.split(/\r\n?|\n/)) {
1099
1107
  while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
1100
- let seq = m[1] ? +m[1] : null, name = m[2] || m[3], found = -1;
1108
+ let seq = m[1] ? +m[1] : null, name = m[2] || m[3] || "", found = -1;
1101
1109
  for (let i = 0; i < fields.length; i++) {
1102
1110
  if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
1103
1111
  found = i;
@@ -1106,7 +1114,7 @@ class Snippet {
1106
1114
  let i = 0;
1107
1115
  while (i < fields.length && (seq == null || (fields[i].seq != null && fields[i].seq < seq)))
1108
1116
  i++;
1109
- fields.splice(i, 0, { seq, name: name || null });
1117
+ fields.splice(i, 0, { seq, name });
1110
1118
  found = i;
1111
1119
  for (let pos of positions)
1112
1120
  if (pos.field >= found)
@@ -1202,8 +1210,7 @@ function snippet(template) {
1202
1210
  let active = new ActiveSnippet(ranges, 0);
1203
1211
  let effects = spec.effects = [setActive.of(active)];
1204
1212
  if (editor.state.field(snippetState, false) === undefined)
1205
- effects.push(StateEffect.appendConfig.of([snippetState.init(() => active), addSnippetKeymap,
1206
- snippetPointerHandler, baseTheme]));
1213
+ effects.push(StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
1207
1214
  }
1208
1215
  editor.dispatch(editor.state.update(spec));
1209
1216
  };
@@ -1253,7 +1260,7 @@ to [`clearSnippet`](https://codemirror.net/6/docs/ref/#autocomplete.clearSnippet
1253
1260
  const snippetKeymap = /*@__PURE__*/Facet.define({
1254
1261
  combine(maps) { return maps.length ? maps[0] : defaultSnippetKeymap; }
1255
1262
  });
1256
- const addSnippetKeymap = /*@__PURE__*/Prec.override(/*@__PURE__*/keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
1263
+ const addSnippetKeymap = /*@__PURE__*/Prec.highest(/*@__PURE__*/keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
1257
1264
  /**
1258
1265
  Create a completion from a snippet. Returns an object with the
1259
1266
  properties from `completion`, plus an `apply` function that
@@ -1387,7 +1394,7 @@ const completionKeymap = [
1387
1394
  { key: "PageUp", run: /*@__PURE__*/moveCompletionSelection(false, "page") },
1388
1395
  { key: "Enter", run: acceptCompletion }
1389
1396
  ];
1390
- const completionKeymapExt = /*@__PURE__*/Prec.override(/*@__PURE__*/keymap.computeN([completionConfig], state => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
1397
+ const completionKeymapExt = /*@__PURE__*/Prec.highest(/*@__PURE__*/keymap.computeN([completionConfig], state => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
1391
1398
  /**
1392
1399
  Get the current completion status. When completions are available,
1393
1400
  this will return `"active"`. When completions are pending (in the
@@ -1407,5 +1414,13 @@ function currentCompletions(state) {
1407
1414
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1408
1415
  return open ? open.options.map(o => o.completion) : [];
1409
1416
  }
1417
+ /**
1418
+ Return the currently selected completion, if any.
1419
+ */
1420
+ function selectedCompletion(state) {
1421
+ var _a;
1422
+ let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1423
+ return open ? open.options[open.selected].completion : null;
1424
+ }
1410
1425
 
1411
- export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
1426
+ export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "0.19.3",
3
+ "version": "0.19.7",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -27,7 +27,7 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@codemirror/language": "^0.19.0",
30
- "@codemirror/state": "^0.19.0",
30
+ "@codemirror/state": "^0.19.4",
31
31
  "@codemirror/text": "^0.19.2",
32
32
  "@codemirror/tooltip": "^0.19.0",
33
33
  "@codemirror/view": "^0.19.0",