@codemirror/autocomplete 0.19.5 → 0.19.9

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.9 (2021-11-26)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where info tooltips would be visible in an inappropriate position when there was no room to place them properly.
6
+
7
+ ## 0.19.8 (2021-11-17)
8
+
9
+ ### Bug fixes
10
+
11
+ Give the completion tooltip a minimal width, and show ellipsis when completions overflow the tooltip width.
12
+
13
+ ### New features
14
+
15
+ `autocompletion` now accepts an `aboveCursor` option to make the completion tooltip show up above the cursor.
16
+
17
+ ## 0.19.7 (2021-11-16)
18
+
19
+ ### Bug fixes
20
+
21
+ Make option deduplication less aggressive, so that options with different `type` or `apply` fields don't get merged.
22
+
23
+ ## 0.19.6 (2021-11-12)
24
+
25
+ ### Bug fixes
26
+
27
+ Fix an issue where parsing a snippet with a field that was labeled only by a number crashed.
28
+
1
29
  ## 0.19.5 (2021-11-09)
2
30
 
3
31
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -309,6 +309,7 @@ const completionConfig = state.Facet.define({
309
309
  maxRenderedOptions: 100,
310
310
  defaultKeymap: true,
311
311
  optionClass: () => "",
312
+ aboveCursor: false,
312
313
  icons: true,
313
314
  addToOptions: []
314
315
  }, {
@@ -483,9 +484,9 @@ class CompletionTooltip {
483
484
  if (!sel || !this.info)
484
485
  return null;
485
486
  let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
486
- let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
487
- if (top < 0 || top > this.list.clientHeight - 10)
487
+ if (rect.top > innerHeight - 10 || rect.bottom < 10)
488
488
  return null;
489
+ let top = Math.max(0, Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height)) - rect.top;
489
490
  let left = this.view.textDirection == view.Direction.RTL;
490
491
  let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
491
492
  if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
@@ -495,10 +496,12 @@ class CompletionTooltip {
495
496
  return { top, left };
496
497
  }
497
498
  positionInfo(pos) {
498
- if (this.info && pos) {
499
- this.info.style.top = pos.top + "px";
500
- this.info.classList.toggle("cm-completionInfo-left", pos.left);
501
- this.info.classList.toggle("cm-completionInfo-right", !pos.left);
499
+ if (this.info) {
500
+ this.info.style.top = (pos ? pos.top : -1e6) + "px";
501
+ if (pos) {
502
+ this.info.classList.toggle("cm-completionInfo-left", pos.left);
503
+ this.info.classList.toggle("cm-completionInfo-right", !pos.left);
504
+ }
502
505
  }
503
506
  }
504
507
  createListBox(options, id, range) {
@@ -570,7 +573,8 @@ function sortOptions(active, state) {
570
573
  for (let opt of options.sort(cmpOption)) {
571
574
  if (result.length == MaxOptions)
572
575
  break;
573
- if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail)
576
+ if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
577
+ prev.type != opt.completion.type || prev.apply != opt.completion.apply)
574
578
  result.push(opt);
575
579
  else if (score(opt.completion) > score(prev))
576
580
  result[result.length - 1] = opt;
@@ -590,7 +594,7 @@ class CompletionDialog {
590
594
  return selected == this.selected || selected >= this.options.length ? this
591
595
  : new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected);
592
596
  }
593
- static build(active, state, id, prev) {
597
+ static build(active, state, id, prev, conf) {
594
598
  let options = sortOptions(active, state);
595
599
  if (!options.length)
596
600
  return null;
@@ -604,7 +608,8 @@ class CompletionDialog {
604
608
  }
605
609
  return new CompletionDialog(options, makeAttrs(id, selected), {
606
610
  pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
607
- create: completionTooltip(completionState)
611
+ create: completionTooltip(completionState),
612
+ above: conf.aboveCursor,
608
613
  }, prev ? prev.timestamp : Date.now(), selected);
609
614
  }
610
615
  map(changes) {
@@ -632,7 +637,7 @@ class CompletionState {
632
637
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
633
638
  active = this.active;
634
639
  let open = tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
635
- !sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open)
640
+ !sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open, conf)
636
641
  : this.open && tr.docChanged ? this.open.map(tr.changes) : this.open;
637
642
  if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
638
643
  active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
@@ -968,16 +973,19 @@ const baseTheme = view.EditorView.baseTheme({
968
973
  "& > ul": {
969
974
  fontFamily: "monospace",
970
975
  whiteSpace: "nowrap",
971
- overflow: "auto",
976
+ overflow: "hidden auto",
972
977
  maxWidth_fallback: "700px",
973
978
  maxWidth: "min(700px, 95vw)",
979
+ minWidth: "250px",
974
980
  maxHeight: "10em",
975
981
  listStyle: "none",
976
982
  margin: 0,
977
983
  padding: 0,
978
984
  "& > li": {
985
+ overflowX: "hidden",
986
+ textOverflow: "ellipsis",
979
987
  cursor: "pointer",
980
- padding: "1px 1em 1px 3px",
988
+ padding: "1px 3px",
981
989
  lineHeight: 1.2
982
990
  },
983
991
  }
@@ -1108,7 +1116,7 @@ class Snippet {
1108
1116
  let lines = [], positions = [], m;
1109
1117
  for (let line of template.split(/\r\n?|\n/)) {
1110
1118
  while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
1111
- let seq = m[1] ? +m[1] : null, name = m[2] || m[3], found = -1;
1119
+ let seq = m[1] ? +m[1] : null, name = m[2] || m[3] || "", found = -1;
1112
1120
  for (let i = 0; i < fields.length; i++) {
1113
1121
  if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
1114
1122
  found = i;
@@ -1117,7 +1125,7 @@ class Snippet {
1117
1125
  let i = 0;
1118
1126
  while (i < fields.length && (seq == null || (fields[i].seq != null && fields[i].seq < seq)))
1119
1127
  i++;
1120
- fields.splice(i, 0, { seq, name: name || null });
1128
+ fields.splice(i, 0, { seq, name });
1121
1129
  found = i;
1122
1130
  for (let pos of positions)
1123
1131
  if (pos.field >= found)
package/dist/index.d.ts CHANGED
@@ -30,6 +30,12 @@ interface CompletionConfig {
30
30
  */
31
31
  defaultKeymap?: boolean;
32
32
  /**
33
+ By default, completions are shown below the cursor when there is
34
+ space. Setting this to true will make the extension put the
35
+ completions above the cursor when possible.
36
+ */
37
+ aboveCursor?: boolean;
38
+ /**
33
39
  This can be used to add additional CSS classes to completion
34
40
  options.
35
41
  */
package/dist/index.js CHANGED
@@ -305,6 +305,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
305
305
  maxRenderedOptions: 100,
306
306
  defaultKeymap: true,
307
307
  optionClass: () => "",
308
+ aboveCursor: false,
308
309
  icons: true,
309
310
  addToOptions: []
310
311
  }, {
@@ -479,9 +480,9 @@ class CompletionTooltip {
479
480
  if (!sel || !this.info)
480
481
  return null;
481
482
  let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
482
- let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
483
- if (top < 0 || top > this.list.clientHeight - 10)
483
+ if (rect.top > innerHeight - 10 || rect.bottom < 10)
484
484
  return null;
485
+ let top = Math.max(0, Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height)) - rect.top;
485
486
  let left = this.view.textDirection == Direction.RTL;
486
487
  let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
487
488
  if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
@@ -491,10 +492,12 @@ class CompletionTooltip {
491
492
  return { top, left };
492
493
  }
493
494
  positionInfo(pos) {
494
- if (this.info && pos) {
495
- this.info.style.top = pos.top + "px";
496
- this.info.classList.toggle("cm-completionInfo-left", pos.left);
497
- this.info.classList.toggle("cm-completionInfo-right", !pos.left);
495
+ if (this.info) {
496
+ this.info.style.top = (pos ? pos.top : -1e6) + "px";
497
+ if (pos) {
498
+ this.info.classList.toggle("cm-completionInfo-left", pos.left);
499
+ this.info.classList.toggle("cm-completionInfo-right", !pos.left);
500
+ }
498
501
  }
499
502
  }
500
503
  createListBox(options, id, range) {
@@ -566,7 +569,8 @@ function sortOptions(active, state) {
566
569
  for (let opt of options.sort(cmpOption)) {
567
570
  if (result.length == MaxOptions)
568
571
  break;
569
- if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail)
572
+ if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
573
+ prev.type != opt.completion.type || prev.apply != opt.completion.apply)
570
574
  result.push(opt);
571
575
  else if (score(opt.completion) > score(prev))
572
576
  result[result.length - 1] = opt;
@@ -586,7 +590,7 @@ class CompletionDialog {
586
590
  return selected == this.selected || selected >= this.options.length ? this
587
591
  : new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected);
588
592
  }
589
- static build(active, state, id, prev) {
593
+ static build(active, state, id, prev, conf) {
590
594
  let options = sortOptions(active, state);
591
595
  if (!options.length)
592
596
  return null;
@@ -600,7 +604,8 @@ class CompletionDialog {
600
604
  }
601
605
  return new CompletionDialog(options, makeAttrs(id, selected), {
602
606
  pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
603
- create: completionTooltip(completionState)
607
+ create: completionTooltip(completionState),
608
+ above: conf.aboveCursor,
604
609
  }, prev ? prev.timestamp : Date.now(), selected);
605
610
  }
606
611
  map(changes) {
@@ -628,7 +633,7 @@ class CompletionState {
628
633
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
629
634
  active = this.active;
630
635
  let open = tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
631
- !sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open)
636
+ !sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open, conf)
632
637
  : this.open && tr.docChanged ? this.open.map(tr.changes) : this.open;
633
638
  if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
634
639
  active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
@@ -964,16 +969,19 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
964
969
  "& > ul": {
965
970
  fontFamily: "monospace",
966
971
  whiteSpace: "nowrap",
967
- overflow: "auto",
972
+ overflow: "hidden auto",
968
973
  maxWidth_fallback: "700px",
969
974
  maxWidth: "min(700px, 95vw)",
975
+ minWidth: "250px",
970
976
  maxHeight: "10em",
971
977
  listStyle: "none",
972
978
  margin: 0,
973
979
  padding: 0,
974
980
  "& > li": {
981
+ overflowX: "hidden",
982
+ textOverflow: "ellipsis",
975
983
  cursor: "pointer",
976
- padding: "1px 1em 1px 3px",
984
+ padding: "1px 3px",
977
985
  lineHeight: 1.2
978
986
  },
979
987
  }
@@ -1104,7 +1112,7 @@ class Snippet {
1104
1112
  let lines = [], positions = [], m;
1105
1113
  for (let line of template.split(/\r\n?|\n/)) {
1106
1114
  while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
1107
- let seq = m[1] ? +m[1] : null, name = m[2] || m[3], found = -1;
1115
+ let seq = m[1] ? +m[1] : null, name = m[2] || m[3] || "", found = -1;
1108
1116
  for (let i = 0; i < fields.length; i++) {
1109
1117
  if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
1110
1118
  found = i;
@@ -1113,7 +1121,7 @@ class Snippet {
1113
1121
  let i = 0;
1114
1122
  while (i < fields.length && (seq == null || (fields[i].seq != null && fields[i].seq < seq)))
1115
1123
  i++;
1116
- fields.splice(i, 0, { seq, name: name || null });
1124
+ fields.splice(i, 0, { seq, name });
1117
1125
  found = i;
1118
1126
  for (let pos of positions)
1119
1127
  if (pos.field >= found)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "0.19.5",
3
+ "version": "0.19.9",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",