@codemirror/autocomplete 6.5.0 → 6.6.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,19 @@
1
+ ## 6.6.0 (2023-04-27)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug in `insertCompletionText` that caused it to replace the wrong range when a result set's `to` fell after the cursor.
6
+
7
+ ### New features
8
+
9
+ Functions returned by `snippet` can now be called without a completion object.
10
+
11
+ ## 6.5.1 (2023-04-13)
12
+
13
+ ### Bug fixes
14
+
15
+ Keep completions open when interaction with an info tooltip moves focus out of the editor.
16
+
1
17
  ## 6.5.0 (2023-04-13)
2
18
 
3
19
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -166,14 +166,14 @@ completion's text in the main selection range, and any other
166
166
  selection range that has the same text in front of it.
167
167
  */
168
168
  function insertCompletionText(state$1, text, from, to) {
169
- let { main } = state$1.selection, len = to - from;
169
+ let { main } = state$1.selection, fromOff = from - main.from, toOff = to - main.from;
170
170
  return Object.assign(Object.assign({}, state$1.changeByRange(range => {
171
- if (range != main && len &&
172
- state$1.sliceDoc(range.from - len, range.from + to - main.from) != state$1.sliceDoc(from, to))
171
+ if (range != main && from != to &&
172
+ state$1.sliceDoc(range.from + fromOff, range.from + toOff) != state$1.sliceDoc(from, to))
173
173
  return { range };
174
174
  return {
175
- changes: { from: range.from - len, to: to == main.from ? range.to : range.from + to - main.from, insert: text },
176
- range: state.EditorSelection.cursor(range.from - len + text.length)
175
+ changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
176
+ range: state.EditorSelection.cursor(range.from + fromOff + text.length)
177
177
  };
178
178
  })), { userEvent: "input.complete" });
179
179
  }
@@ -194,6 +194,8 @@ function asSource(source) {
194
194
  SourceCache.set(source, known = completeFromList(source));
195
195
  return known;
196
196
  }
197
+ const startCompletionEffect = state.StateEffect.define();
198
+ const closeCompletionEffect = state.StateEffect.define();
197
199
 
198
200
  // A pattern matcher for fuzzy completion matching. Create an instance
199
201
  // once for a pattern, and then use that to match any number of
@@ -475,6 +477,12 @@ class CompletionTooltip {
475
477
  }
476
478
  }
477
479
  });
480
+ this.dom.addEventListener("focusout", (e) => {
481
+ let state = view.state.field(this.stateField, false);
482
+ if (state && state.tooltip && view.state.facet(completionConfig).closeOnBlur &&
483
+ e.relatedTarget != view.contentDOM)
484
+ view.dispatch({ effects: closeCompletionEffect.of(null) });
485
+ });
478
486
  this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
479
487
  this.list.addEventListener("scroll", () => {
480
488
  if (this.info)
@@ -905,8 +913,6 @@ function checkValid(validFor, state, from, to) {
905
913
  let text = state.sliceDoc(from, to);
906
914
  return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
907
915
  }
908
- const startCompletionEffect = state.StateEffect.define();
909
- const closeCompletionEffect = state.StateEffect.define();
910
916
  const setActiveEffect = state.StateEffect.define({
911
917
  map(sources, mapping) { return sources.map(s => s.map(mapping)); }
912
918
  });
@@ -1113,10 +1119,13 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1113
1119
  }
1114
1120
  }, {
1115
1121
  eventHandlers: {
1116
- blur() {
1122
+ blur(event) {
1117
1123
  let state = this.view.state.field(completionState, false);
1118
- if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur)
1119
- this.view.dispatch({ effects: closeCompletionEffect.of(null) });
1124
+ if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
1125
+ let dialog = state.open && view.getTooltip(this.view, state.open.tooltip);
1126
+ if (!dialog || !dialog.dom.contains(event.relatedTarget))
1127
+ this.view.dispatch({ effects: closeCompletionEffect.of(null) });
1128
+ }
1120
1129
  },
1121
1130
  compositionstart() {
1122
1131
  this.composing = 1 /* CompositionState.Started */;
@@ -1423,7 +1432,7 @@ function snippet(template) {
1423
1432
  let spec = {
1424
1433
  changes: { from, to, insert: state.Text.of(text) },
1425
1434
  scrollIntoView: true,
1426
- annotations: pickedCompletion.of(completion)
1435
+ annotations: completion ? pickedCompletion.of(completion) : undefined
1427
1436
  };
1428
1437
  if (ranges.length)
1429
1438
  spec.selection = fieldSelection(ranges, 0);
package/dist/index.d.ts CHANGED
@@ -384,7 +384,7 @@ interpreted as indicating a placeholder.
384
384
  declare function snippet(template: string): (editor: {
385
385
  state: EditorState;
386
386
  dispatch: (tr: Transaction) => void;
387
- }, completion: Completion, from: number, to: number) => void;
387
+ }, completion: Completion | null, from: number, to: number) => void;
388
388
  /**
389
389
  A command that clears the active snippet, if any.
390
390
  */
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Annotation, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateEffect, StateField, Prec, Text, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
1
+ import { Annotation, StateEffect, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateField, Prec, Text, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
2
2
  import { Direction, logException, showTooltip, EditorView, ViewPlugin, getTooltip, Decoration, WidgetType, keymap } from '@codemirror/view';
3
3
  import { syntaxTree, indentUnit } from '@codemirror/language';
4
4
 
@@ -162,14 +162,14 @@ completion's text in the main selection range, and any other
162
162
  selection range that has the same text in front of it.
163
163
  */
164
164
  function insertCompletionText(state, text, from, to) {
165
- let { main } = state.selection, len = to - from;
165
+ let { main } = state.selection, fromOff = from - main.from, toOff = to - main.from;
166
166
  return Object.assign(Object.assign({}, state.changeByRange(range => {
167
- if (range != main && len &&
168
- state.sliceDoc(range.from - len, range.from + to - main.from) != state.sliceDoc(from, to))
167
+ if (range != main && from != to &&
168
+ state.sliceDoc(range.from + fromOff, range.from + toOff) != state.sliceDoc(from, to))
169
169
  return { range };
170
170
  return {
171
- changes: { from: range.from - len, to: to == main.from ? range.to : range.from + to - main.from, insert: text },
172
- range: EditorSelection.cursor(range.from - len + text.length)
171
+ changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
172
+ range: EditorSelection.cursor(range.from + fromOff + text.length)
173
173
  };
174
174
  })), { userEvent: "input.complete" });
175
175
  }
@@ -190,6 +190,8 @@ function asSource(source) {
190
190
  SourceCache.set(source, known = completeFromList(source));
191
191
  return known;
192
192
  }
193
+ const startCompletionEffect = /*@__PURE__*/StateEffect.define();
194
+ const closeCompletionEffect = /*@__PURE__*/StateEffect.define();
193
195
 
194
196
  // A pattern matcher for fuzzy completion matching. Create an instance
195
197
  // once for a pattern, and then use that to match any number of
@@ -471,6 +473,12 @@ class CompletionTooltip {
471
473
  }
472
474
  }
473
475
  });
476
+ this.dom.addEventListener("focusout", (e) => {
477
+ let state = view.state.field(this.stateField, false);
478
+ if (state && state.tooltip && view.state.facet(completionConfig).closeOnBlur &&
479
+ e.relatedTarget != view.contentDOM)
480
+ view.dispatch({ effects: closeCompletionEffect.of(null) });
481
+ });
474
482
  this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
475
483
  this.list.addEventListener("scroll", () => {
476
484
  if (this.info)
@@ -901,8 +909,6 @@ function checkValid(validFor, state, from, to) {
901
909
  let text = state.sliceDoc(from, to);
902
910
  return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
903
911
  }
904
- const startCompletionEffect = /*@__PURE__*/StateEffect.define();
905
- const closeCompletionEffect = /*@__PURE__*/StateEffect.define();
906
912
  const setActiveEffect = /*@__PURE__*/StateEffect.define({
907
913
  map(sources, mapping) { return sources.map(s => s.map(mapping)); }
908
914
  });
@@ -1109,10 +1115,13 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1109
1115
  }
1110
1116
  }, {
1111
1117
  eventHandlers: {
1112
- blur() {
1118
+ blur(event) {
1113
1119
  let state = this.view.state.field(completionState, false);
1114
- if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur)
1115
- this.view.dispatch({ effects: closeCompletionEffect.of(null) });
1120
+ if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
1121
+ let dialog = state.open && getTooltip(this.view, state.open.tooltip);
1122
+ if (!dialog || !dialog.dom.contains(event.relatedTarget))
1123
+ this.view.dispatch({ effects: closeCompletionEffect.of(null) });
1124
+ }
1116
1125
  },
1117
1126
  compositionstart() {
1118
1127
  this.composing = 1 /* CompositionState.Started */;
@@ -1419,7 +1428,7 @@ function snippet(template) {
1419
1428
  let spec = {
1420
1429
  changes: { from, to, insert: Text.of(text) },
1421
1430
  scrollIntoView: true,
1422
- annotations: pickedCompletion.of(completion)
1431
+ annotations: completion ? pickedCompletion.of(completion) : undefined
1423
1432
  };
1424
1433
  if (ranges.length)
1425
1434
  spec.selection = fieldSelection(ranges, 0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "6.5.0",
3
+ "version": "6.6.0",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",