@codemirror/autocomplete 6.5.1 → 6.6.1
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 +16 -0
- package/dist/index.cjs +234 -231
- package/dist/index.d.ts +104 -104
- package/dist/index.js +235 -232
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 6.6.1 (2023-05-03)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix a bug that made the editor use the completion's original position, rather than its current position, when changes happened in the document while a result was active.
|
|
6
|
+
|
|
7
|
+
## 6.6.0 (2023-04-27)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
Fix a bug in `insertCompletionText` that caused it to replace the wrong range when a result set's `to` fell after the cursor.
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
Functions returned by `snippet` can now be called without a completion object.
|
|
16
|
+
|
|
1
17
|
## 6.5.1 (2023-04-13)
|
|
2
18
|
|
|
3
19
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -166,25 +166,17 @@ 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,
|
|
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 &&
|
|
172
|
-
state$1.sliceDoc(range.from
|
|
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
|
|
176
|
-
range: state.EditorSelection.cursor(range.from
|
|
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
|
}
|
|
180
|
-
function applyCompletion(view, option) {
|
|
181
|
-
const apply = option.completion.apply || option.completion.label;
|
|
182
|
-
let result = option.source;
|
|
183
|
-
if (typeof apply == "string")
|
|
184
|
-
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
|
|
185
|
-
else
|
|
186
|
-
apply(view, option.completion, result.from, result.to);
|
|
187
|
-
}
|
|
188
180
|
const SourceCache = new WeakMap();
|
|
189
181
|
function asSource(source) {
|
|
190
182
|
if (!Array.isArray(source))
|
|
@@ -388,6 +380,232 @@ function defaultPositionInfo(view$1, list, option, info, space) {
|
|
|
388
380
|
};
|
|
389
381
|
}
|
|
390
382
|
|
|
383
|
+
/**
|
|
384
|
+
Returns a command that moves the completion selection forward or
|
|
385
|
+
backward by the given amount.
|
|
386
|
+
*/
|
|
387
|
+
function moveCompletionSelection(forward, by = "option") {
|
|
388
|
+
return (view$1) => {
|
|
389
|
+
let cState = view$1.state.field(completionState, false);
|
|
390
|
+
if (!cState || !cState.open || cState.open.disabled ||
|
|
391
|
+
Date.now() - cState.open.timestamp < view$1.state.facet(completionConfig).interactionDelay)
|
|
392
|
+
return false;
|
|
393
|
+
let step = 1, tooltip;
|
|
394
|
+
if (by == "page" && (tooltip = view.getTooltip(view$1, cState.open.tooltip)))
|
|
395
|
+
step = Math.max(2, Math.floor(tooltip.dom.offsetHeight /
|
|
396
|
+
tooltip.dom.querySelector("li").offsetHeight) - 1);
|
|
397
|
+
let { length } = cState.open.options;
|
|
398
|
+
let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
|
|
399
|
+
if (selected < 0)
|
|
400
|
+
selected = by == "page" ? 0 : length - 1;
|
|
401
|
+
else if (selected >= length)
|
|
402
|
+
selected = by == "page" ? length - 1 : 0;
|
|
403
|
+
view$1.dispatch({ effects: setSelectedEffect.of(selected) });
|
|
404
|
+
return true;
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
Accept the current completion.
|
|
409
|
+
*/
|
|
410
|
+
const acceptCompletion = (view) => {
|
|
411
|
+
let cState = view.state.field(completionState, false);
|
|
412
|
+
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
|
413
|
+
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
414
|
+
return false;
|
|
415
|
+
if (!cState.open.disabled)
|
|
416
|
+
return applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
417
|
+
return true;
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
Explicitly start autocompletion.
|
|
421
|
+
*/
|
|
422
|
+
const startCompletion = (view) => {
|
|
423
|
+
let cState = view.state.field(completionState, false);
|
|
424
|
+
if (!cState)
|
|
425
|
+
return false;
|
|
426
|
+
view.dispatch({ effects: startCompletionEffect.of(true) });
|
|
427
|
+
return true;
|
|
428
|
+
};
|
|
429
|
+
/**
|
|
430
|
+
Close the currently active completion.
|
|
431
|
+
*/
|
|
432
|
+
const closeCompletion = (view) => {
|
|
433
|
+
let cState = view.state.field(completionState, false);
|
|
434
|
+
if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
|
|
435
|
+
return false;
|
|
436
|
+
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
437
|
+
return true;
|
|
438
|
+
};
|
|
439
|
+
class RunningQuery {
|
|
440
|
+
constructor(active, context) {
|
|
441
|
+
this.active = active;
|
|
442
|
+
this.context = context;
|
|
443
|
+
this.time = Date.now();
|
|
444
|
+
this.updates = [];
|
|
445
|
+
// Note that 'undefined' means 'not done yet', whereas 'null' means
|
|
446
|
+
// 'query returned null'.
|
|
447
|
+
this.done = undefined;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
const DebounceTime = 50, MaxUpdateCount = 50, MinAbortTime = 1000;
|
|
451
|
+
const completionPlugin = view.ViewPlugin.fromClass(class {
|
|
452
|
+
constructor(view) {
|
|
453
|
+
this.view = view;
|
|
454
|
+
this.debounceUpdate = -1;
|
|
455
|
+
this.running = [];
|
|
456
|
+
this.debounceAccept = -1;
|
|
457
|
+
this.composing = 0 /* CompositionState.None */;
|
|
458
|
+
for (let active of view.state.field(completionState).active)
|
|
459
|
+
if (active.state == 1 /* State.Pending */)
|
|
460
|
+
this.startQuery(active);
|
|
461
|
+
}
|
|
462
|
+
update(update) {
|
|
463
|
+
let cState = update.state.field(completionState);
|
|
464
|
+
if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState)
|
|
465
|
+
return;
|
|
466
|
+
let doesReset = update.transactions.some(tr => {
|
|
467
|
+
return (tr.selection || tr.docChanged) && !getUserEvent(tr);
|
|
468
|
+
});
|
|
469
|
+
for (let i = 0; i < this.running.length; i++) {
|
|
470
|
+
let query = this.running[i];
|
|
471
|
+
if (doesReset ||
|
|
472
|
+
query.updates.length + update.transactions.length > MaxUpdateCount && Date.now() - query.time > MinAbortTime) {
|
|
473
|
+
for (let handler of query.context.abortListeners) {
|
|
474
|
+
try {
|
|
475
|
+
handler();
|
|
476
|
+
}
|
|
477
|
+
catch (e) {
|
|
478
|
+
view.logException(this.view.state, e);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
query.context.abortListeners = null;
|
|
482
|
+
this.running.splice(i--, 1);
|
|
483
|
+
}
|
|
484
|
+
else {
|
|
485
|
+
query.updates.push(...update.transactions);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (this.debounceUpdate > -1)
|
|
489
|
+
clearTimeout(this.debounceUpdate);
|
|
490
|
+
this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
|
|
491
|
+
? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
|
|
492
|
+
if (this.composing != 0 /* CompositionState.None */)
|
|
493
|
+
for (let tr of update.transactions) {
|
|
494
|
+
if (getUserEvent(tr) == "input")
|
|
495
|
+
this.composing = 2 /* CompositionState.Changed */;
|
|
496
|
+
else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
|
|
497
|
+
this.composing = 3 /* CompositionState.ChangedAndMoved */;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
startUpdate() {
|
|
501
|
+
this.debounceUpdate = -1;
|
|
502
|
+
let { state } = this.view, cState = state.field(completionState);
|
|
503
|
+
for (let active of cState.active) {
|
|
504
|
+
if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
|
|
505
|
+
this.startQuery(active);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
startQuery(active) {
|
|
509
|
+
let { state } = this.view, pos = cur(state);
|
|
510
|
+
let context = new CompletionContext(state, pos, active.explicitPos == pos);
|
|
511
|
+
let pending = new RunningQuery(active, context);
|
|
512
|
+
this.running.push(pending);
|
|
513
|
+
Promise.resolve(active.source(context)).then(result => {
|
|
514
|
+
if (!pending.context.aborted) {
|
|
515
|
+
pending.done = result || null;
|
|
516
|
+
this.scheduleAccept();
|
|
517
|
+
}
|
|
518
|
+
}, err => {
|
|
519
|
+
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
520
|
+
view.logException(this.view.state, err);
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
scheduleAccept() {
|
|
524
|
+
if (this.running.every(q => q.done !== undefined))
|
|
525
|
+
this.accept();
|
|
526
|
+
else if (this.debounceAccept < 0)
|
|
527
|
+
this.debounceAccept = setTimeout(() => this.accept(), DebounceTime);
|
|
528
|
+
}
|
|
529
|
+
// For each finished query in this.running, try to create a result
|
|
530
|
+
// or, if appropriate, restart the query.
|
|
531
|
+
accept() {
|
|
532
|
+
var _a;
|
|
533
|
+
if (this.debounceAccept > -1)
|
|
534
|
+
clearTimeout(this.debounceAccept);
|
|
535
|
+
this.debounceAccept = -1;
|
|
536
|
+
let updated = [];
|
|
537
|
+
let conf = this.view.state.facet(completionConfig);
|
|
538
|
+
for (let i = 0; i < this.running.length; i++) {
|
|
539
|
+
let query = this.running[i];
|
|
540
|
+
if (query.done === undefined)
|
|
541
|
+
continue;
|
|
542
|
+
this.running.splice(i--, 1);
|
|
543
|
+
if (query.done) {
|
|
544
|
+
let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state));
|
|
545
|
+
// Replay the transactions that happened since the start of
|
|
546
|
+
// the request and see if that preserves the result
|
|
547
|
+
for (let tr of query.updates)
|
|
548
|
+
active = active.update(tr, conf);
|
|
549
|
+
if (active.hasResult()) {
|
|
550
|
+
updated.push(active);
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
|
|
555
|
+
if (current && current.state == 1 /* State.Pending */) {
|
|
556
|
+
if (query.done == null) {
|
|
557
|
+
// Explicitly failed. Should clear the pending status if it
|
|
558
|
+
// hasn't been re-set in the meantime.
|
|
559
|
+
let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
|
|
560
|
+
for (let tr of query.updates)
|
|
561
|
+
active = active.update(tr, conf);
|
|
562
|
+
if (active.state != 1 /* State.Pending */)
|
|
563
|
+
updated.push(active);
|
|
564
|
+
}
|
|
565
|
+
else {
|
|
566
|
+
// Cleared by subsequent transactions. Restart.
|
|
567
|
+
this.startQuery(current);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (updated.length)
|
|
572
|
+
this.view.dispatch({ effects: setActiveEffect.of(updated) });
|
|
573
|
+
}
|
|
574
|
+
}, {
|
|
575
|
+
eventHandlers: {
|
|
576
|
+
blur(event) {
|
|
577
|
+
let state = this.view.state.field(completionState, false);
|
|
578
|
+
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
|
|
579
|
+
let dialog = state.open && view.getTooltip(this.view, state.open.tooltip);
|
|
580
|
+
if (!dialog || !dialog.dom.contains(event.relatedTarget))
|
|
581
|
+
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
compositionstart() {
|
|
585
|
+
this.composing = 1 /* CompositionState.Started */;
|
|
586
|
+
},
|
|
587
|
+
compositionend() {
|
|
588
|
+
if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
|
|
589
|
+
// Safari fires compositionend events synchronously, possibly
|
|
590
|
+
// from inside an update, so dispatch asynchronously to avoid reentrancy
|
|
591
|
+
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
|
|
592
|
+
}
|
|
593
|
+
this.composing = 0 /* CompositionState.None */;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
function applyCompletion(view, option) {
|
|
598
|
+
const apply = option.completion.apply || option.completion.label;
|
|
599
|
+
let result = view.state.field(completionState).active.find(a => a.source == option.source);
|
|
600
|
+
if (!(result instanceof ActiveResult))
|
|
601
|
+
return false;
|
|
602
|
+
if (typeof apply == "string")
|
|
603
|
+
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
|
|
604
|
+
else
|
|
605
|
+
apply(view, option.completion, result.from, result.to);
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
|
|
391
609
|
function optionContent(config) {
|
|
392
610
|
let content = config.addToOptions.slice();
|
|
393
611
|
if (config.icons)
|
|
@@ -693,14 +911,14 @@ function sortOptions(active, state) {
|
|
|
693
911
|
if (getMatch)
|
|
694
912
|
for (let n of getMatch(option))
|
|
695
913
|
match.push(n);
|
|
696
|
-
addOption(new Option(option, a, match, match[0]));
|
|
914
|
+
addOption(new Option(option, a.source, match, match[0]));
|
|
697
915
|
}
|
|
698
916
|
}
|
|
699
917
|
else {
|
|
700
918
|
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
|
|
701
919
|
for (let option of a.result.options)
|
|
702
920
|
if (match = matcher.match(option.label)) {
|
|
703
|
-
addOption(new Option(option, a, match, match[0] + (option.boost || 0)));
|
|
921
|
+
addOption(new Option(option, a.source, match, match[0] + (option.boost || 0)));
|
|
704
922
|
}
|
|
705
923
|
}
|
|
706
924
|
}
|
|
@@ -926,221 +1144,6 @@ const completionState = state.StateField.define({
|
|
|
926
1144
|
]
|
|
927
1145
|
});
|
|
928
1146
|
|
|
929
|
-
/**
|
|
930
|
-
Returns a command that moves the completion selection forward or
|
|
931
|
-
backward by the given amount.
|
|
932
|
-
*/
|
|
933
|
-
function moveCompletionSelection(forward, by = "option") {
|
|
934
|
-
return (view$1) => {
|
|
935
|
-
let cState = view$1.state.field(completionState, false);
|
|
936
|
-
if (!cState || !cState.open || cState.open.disabled ||
|
|
937
|
-
Date.now() - cState.open.timestamp < view$1.state.facet(completionConfig).interactionDelay)
|
|
938
|
-
return false;
|
|
939
|
-
let step = 1, tooltip;
|
|
940
|
-
if (by == "page" && (tooltip = view.getTooltip(view$1, cState.open.tooltip)))
|
|
941
|
-
step = Math.max(2, Math.floor(tooltip.dom.offsetHeight /
|
|
942
|
-
tooltip.dom.querySelector("li").offsetHeight) - 1);
|
|
943
|
-
let { length } = cState.open.options;
|
|
944
|
-
let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
|
|
945
|
-
if (selected < 0)
|
|
946
|
-
selected = by == "page" ? 0 : length - 1;
|
|
947
|
-
else if (selected >= length)
|
|
948
|
-
selected = by == "page" ? length - 1 : 0;
|
|
949
|
-
view$1.dispatch({ effects: setSelectedEffect.of(selected) });
|
|
950
|
-
return true;
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
/**
|
|
954
|
-
Accept the current completion.
|
|
955
|
-
*/
|
|
956
|
-
const acceptCompletion = (view) => {
|
|
957
|
-
let cState = view.state.field(completionState, false);
|
|
958
|
-
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
|
959
|
-
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
960
|
-
return false;
|
|
961
|
-
if (!cState.open.disabled)
|
|
962
|
-
applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
963
|
-
return true;
|
|
964
|
-
};
|
|
965
|
-
/**
|
|
966
|
-
Explicitly start autocompletion.
|
|
967
|
-
*/
|
|
968
|
-
const startCompletion = (view) => {
|
|
969
|
-
let cState = view.state.field(completionState, false);
|
|
970
|
-
if (!cState)
|
|
971
|
-
return false;
|
|
972
|
-
view.dispatch({ effects: startCompletionEffect.of(true) });
|
|
973
|
-
return true;
|
|
974
|
-
};
|
|
975
|
-
/**
|
|
976
|
-
Close the currently active completion.
|
|
977
|
-
*/
|
|
978
|
-
const closeCompletion = (view) => {
|
|
979
|
-
let cState = view.state.field(completionState, false);
|
|
980
|
-
if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
|
|
981
|
-
return false;
|
|
982
|
-
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
983
|
-
return true;
|
|
984
|
-
};
|
|
985
|
-
class RunningQuery {
|
|
986
|
-
constructor(active, context) {
|
|
987
|
-
this.active = active;
|
|
988
|
-
this.context = context;
|
|
989
|
-
this.time = Date.now();
|
|
990
|
-
this.updates = [];
|
|
991
|
-
// Note that 'undefined' means 'not done yet', whereas 'null' means
|
|
992
|
-
// 'query returned null'.
|
|
993
|
-
this.done = undefined;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
const DebounceTime = 50, MaxUpdateCount = 50, MinAbortTime = 1000;
|
|
997
|
-
const completionPlugin = view.ViewPlugin.fromClass(class {
|
|
998
|
-
constructor(view) {
|
|
999
|
-
this.view = view;
|
|
1000
|
-
this.debounceUpdate = -1;
|
|
1001
|
-
this.running = [];
|
|
1002
|
-
this.debounceAccept = -1;
|
|
1003
|
-
this.composing = 0 /* CompositionState.None */;
|
|
1004
|
-
for (let active of view.state.field(completionState).active)
|
|
1005
|
-
if (active.state == 1 /* State.Pending */)
|
|
1006
|
-
this.startQuery(active);
|
|
1007
|
-
}
|
|
1008
|
-
update(update) {
|
|
1009
|
-
let cState = update.state.field(completionState);
|
|
1010
|
-
if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState)
|
|
1011
|
-
return;
|
|
1012
|
-
let doesReset = update.transactions.some(tr => {
|
|
1013
|
-
return (tr.selection || tr.docChanged) && !getUserEvent(tr);
|
|
1014
|
-
});
|
|
1015
|
-
for (let i = 0; i < this.running.length; i++) {
|
|
1016
|
-
let query = this.running[i];
|
|
1017
|
-
if (doesReset ||
|
|
1018
|
-
query.updates.length + update.transactions.length > MaxUpdateCount && Date.now() - query.time > MinAbortTime) {
|
|
1019
|
-
for (let handler of query.context.abortListeners) {
|
|
1020
|
-
try {
|
|
1021
|
-
handler();
|
|
1022
|
-
}
|
|
1023
|
-
catch (e) {
|
|
1024
|
-
view.logException(this.view.state, e);
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
query.context.abortListeners = null;
|
|
1028
|
-
this.running.splice(i--, 1);
|
|
1029
|
-
}
|
|
1030
|
-
else {
|
|
1031
|
-
query.updates.push(...update.transactions);
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
if (this.debounceUpdate > -1)
|
|
1035
|
-
clearTimeout(this.debounceUpdate);
|
|
1036
|
-
this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
|
|
1037
|
-
? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
|
|
1038
|
-
if (this.composing != 0 /* CompositionState.None */)
|
|
1039
|
-
for (let tr of update.transactions) {
|
|
1040
|
-
if (getUserEvent(tr) == "input")
|
|
1041
|
-
this.composing = 2 /* CompositionState.Changed */;
|
|
1042
|
-
else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
|
|
1043
|
-
this.composing = 3 /* CompositionState.ChangedAndMoved */;
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
startUpdate() {
|
|
1047
|
-
this.debounceUpdate = -1;
|
|
1048
|
-
let { state } = this.view, cState = state.field(completionState);
|
|
1049
|
-
for (let active of cState.active) {
|
|
1050
|
-
if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
|
|
1051
|
-
this.startQuery(active);
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
startQuery(active) {
|
|
1055
|
-
let { state } = this.view, pos = cur(state);
|
|
1056
|
-
let context = new CompletionContext(state, pos, active.explicitPos == pos);
|
|
1057
|
-
let pending = new RunningQuery(active, context);
|
|
1058
|
-
this.running.push(pending);
|
|
1059
|
-
Promise.resolve(active.source(context)).then(result => {
|
|
1060
|
-
if (!pending.context.aborted) {
|
|
1061
|
-
pending.done = result || null;
|
|
1062
|
-
this.scheduleAccept();
|
|
1063
|
-
}
|
|
1064
|
-
}, err => {
|
|
1065
|
-
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
1066
|
-
view.logException(this.view.state, err);
|
|
1067
|
-
});
|
|
1068
|
-
}
|
|
1069
|
-
scheduleAccept() {
|
|
1070
|
-
if (this.running.every(q => q.done !== undefined))
|
|
1071
|
-
this.accept();
|
|
1072
|
-
else if (this.debounceAccept < 0)
|
|
1073
|
-
this.debounceAccept = setTimeout(() => this.accept(), DebounceTime);
|
|
1074
|
-
}
|
|
1075
|
-
// For each finished query in this.running, try to create a result
|
|
1076
|
-
// or, if appropriate, restart the query.
|
|
1077
|
-
accept() {
|
|
1078
|
-
var _a;
|
|
1079
|
-
if (this.debounceAccept > -1)
|
|
1080
|
-
clearTimeout(this.debounceAccept);
|
|
1081
|
-
this.debounceAccept = -1;
|
|
1082
|
-
let updated = [];
|
|
1083
|
-
let conf = this.view.state.facet(completionConfig);
|
|
1084
|
-
for (let i = 0; i < this.running.length; i++) {
|
|
1085
|
-
let query = this.running[i];
|
|
1086
|
-
if (query.done === undefined)
|
|
1087
|
-
continue;
|
|
1088
|
-
this.running.splice(i--, 1);
|
|
1089
|
-
if (query.done) {
|
|
1090
|
-
let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state));
|
|
1091
|
-
// Replay the transactions that happened since the start of
|
|
1092
|
-
// the request and see if that preserves the result
|
|
1093
|
-
for (let tr of query.updates)
|
|
1094
|
-
active = active.update(tr, conf);
|
|
1095
|
-
if (active.hasResult()) {
|
|
1096
|
-
updated.push(active);
|
|
1097
|
-
continue;
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
|
|
1101
|
-
if (current && current.state == 1 /* State.Pending */) {
|
|
1102
|
-
if (query.done == null) {
|
|
1103
|
-
// Explicitly failed. Should clear the pending status if it
|
|
1104
|
-
// hasn't been re-set in the meantime.
|
|
1105
|
-
let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
|
|
1106
|
-
for (let tr of query.updates)
|
|
1107
|
-
active = active.update(tr, conf);
|
|
1108
|
-
if (active.state != 1 /* State.Pending */)
|
|
1109
|
-
updated.push(active);
|
|
1110
|
-
}
|
|
1111
|
-
else {
|
|
1112
|
-
// Cleared by subsequent transactions. Restart.
|
|
1113
|
-
this.startQuery(current);
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
if (updated.length)
|
|
1118
|
-
this.view.dispatch({ effects: setActiveEffect.of(updated) });
|
|
1119
|
-
}
|
|
1120
|
-
}, {
|
|
1121
|
-
eventHandlers: {
|
|
1122
|
-
blur(event) {
|
|
1123
|
-
let state = this.view.state.field(completionState, false);
|
|
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
|
-
}
|
|
1129
|
-
},
|
|
1130
|
-
compositionstart() {
|
|
1131
|
-
this.composing = 1 /* CompositionState.Started */;
|
|
1132
|
-
},
|
|
1133
|
-
compositionend() {
|
|
1134
|
-
if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
|
|
1135
|
-
// Safari fires compositionend events synchronously, possibly
|
|
1136
|
-
// from inside an update, so dispatch asynchronously to avoid reentrancy
|
|
1137
|
-
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
|
|
1138
|
-
}
|
|
1139
|
-
this.composing = 0 /* CompositionState.None */;
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
1142
|
-
});
|
|
1143
|
-
|
|
1144
1147
|
const baseTheme = view.EditorView.baseTheme({
|
|
1145
1148
|
".cm-tooltip.cm-tooltip-autocomplete": {
|
|
1146
1149
|
"& > ul": {
|
|
@@ -1432,7 +1435,7 @@ function snippet(template) {
|
|
|
1432
1435
|
let spec = {
|
|
1433
1436
|
changes: { from, to, insert: state.Text.of(text) },
|
|
1434
1437
|
scrollIntoView: true,
|
|
1435
|
-
annotations: pickedCompletion.of(completion)
|
|
1438
|
+
annotations: completion ? pickedCompletion.of(completion) : undefined
|
|
1436
1439
|
};
|
|
1437
1440
|
if (ranges.length)
|
|
1438
1441
|
spec.selection = fieldSelection(ranges, 0);
|
package/dist/index.d.ts
CHANGED
|
@@ -3,109 +3,6 @@ import { EditorState, TransactionSpec, Transaction, StateCommand, Facet, Extensi
|
|
|
3
3
|
import { EditorView, Rect, KeyBinding, Command } from '@codemirror/view';
|
|
4
4
|
import * as _lezer_common from '@lezer/common';
|
|
5
5
|
|
|
6
|
-
interface CompletionConfig {
|
|
7
|
-
/**
|
|
8
|
-
When enabled (defaults to true), autocompletion will start
|
|
9
|
-
whenever the user types something that can be completed.
|
|
10
|
-
*/
|
|
11
|
-
activateOnTyping?: boolean;
|
|
12
|
-
/**
|
|
13
|
-
By default, when completion opens, the first option is selected
|
|
14
|
-
and can be confirmed with
|
|
15
|
-
[`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion). When this
|
|
16
|
-
is set to false, the completion widget starts with no completion
|
|
17
|
-
selected, and the user has to explicitly move to a completion
|
|
18
|
-
before you can confirm one.
|
|
19
|
-
*/
|
|
20
|
-
selectOnOpen?: boolean;
|
|
21
|
-
/**
|
|
22
|
-
Override the completion sources used. By default, they will be
|
|
23
|
-
taken from the `"autocomplete"` [language
|
|
24
|
-
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) (which should hold
|
|
25
|
-
[completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource) or arrays
|
|
26
|
-
of [completions](https://codemirror.net/6/docs/ref/#autocomplete.Completion)).
|
|
27
|
-
*/
|
|
28
|
-
override?: readonly CompletionSource[] | null;
|
|
29
|
-
/**
|
|
30
|
-
Determines whether the completion tooltip is closed when the
|
|
31
|
-
editor loses focus. Defaults to true.
|
|
32
|
-
*/
|
|
33
|
-
closeOnBlur?: boolean;
|
|
34
|
-
/**
|
|
35
|
-
The maximum number of options to render to the DOM.
|
|
36
|
-
*/
|
|
37
|
-
maxRenderedOptions?: number;
|
|
38
|
-
/**
|
|
39
|
-
Set this to false to disable the [default completion
|
|
40
|
-
keymap](https://codemirror.net/6/docs/ref/#autocomplete.completionKeymap). (This requires you to
|
|
41
|
-
add bindings to control completion yourself. The bindings should
|
|
42
|
-
probably have a higher precedence than other bindings for the
|
|
43
|
-
same keys.)
|
|
44
|
-
*/
|
|
45
|
-
defaultKeymap?: boolean;
|
|
46
|
-
/**
|
|
47
|
-
By default, completions are shown below the cursor when there is
|
|
48
|
-
space. Setting this to true will make the extension put the
|
|
49
|
-
completions above the cursor when possible.
|
|
50
|
-
*/
|
|
51
|
-
aboveCursor?: boolean;
|
|
52
|
-
/**
|
|
53
|
-
When given, this may return an additional CSS class to add to
|
|
54
|
-
the completion dialog element.
|
|
55
|
-
*/
|
|
56
|
-
tooltipClass?: (state: EditorState) => string;
|
|
57
|
-
/**
|
|
58
|
-
This can be used to add additional CSS classes to completion
|
|
59
|
-
options.
|
|
60
|
-
*/
|
|
61
|
-
optionClass?: (completion: Completion) => string;
|
|
62
|
-
/**
|
|
63
|
-
By default, the library will render icons based on the
|
|
64
|
-
completion's [type](https://codemirror.net/6/docs/ref/#autocomplete.Completion.type) in front of
|
|
65
|
-
each option. Set this to false to turn that off.
|
|
66
|
-
*/
|
|
67
|
-
icons?: boolean;
|
|
68
|
-
/**
|
|
69
|
-
This option can be used to inject additional content into
|
|
70
|
-
options. The `render` function will be called for each visible
|
|
71
|
-
completion, and should produce a DOM node to show. `position`
|
|
72
|
-
determines where in the DOM the result appears, relative to
|
|
73
|
-
other added widgets and the standard content. The default icons
|
|
74
|
-
have position 20, the label position 50, and the detail position
|
|
75
|
-
80.
|
|
76
|
-
*/
|
|
77
|
-
addToOptions?: {
|
|
78
|
-
render: (completion: Completion, state: EditorState) => Node | null;
|
|
79
|
-
position: number;
|
|
80
|
-
}[];
|
|
81
|
-
/**
|
|
82
|
-
By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
|
|
83
|
-
placed to the side of the selected. This option can be used to
|
|
84
|
-
override that. It will be given rectangles for the list of
|
|
85
|
-
completions, the selected option, the info element, and the
|
|
86
|
-
availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
|
|
87
|
-
and should return style and/or class strings for the info
|
|
88
|
-
element.
|
|
89
|
-
*/
|
|
90
|
-
positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
|
|
91
|
-
style?: string;
|
|
92
|
-
class?: string;
|
|
93
|
-
};
|
|
94
|
-
/**
|
|
95
|
-
The comparison function to use when sorting completions with the same
|
|
96
|
-
match score. Defaults to using
|
|
97
|
-
[`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
|
|
98
|
-
*/
|
|
99
|
-
compareCompletions?: (a: Completion, b: Completion) => number;
|
|
100
|
-
/**
|
|
101
|
-
By default, commands relating to an open completion only take
|
|
102
|
-
effect 75 milliseconds after the completion opened, so that key
|
|
103
|
-
presses made before the user is aware of the tooltip don't go to
|
|
104
|
-
the tooltip. This option can be used to configure that delay.
|
|
105
|
-
*/
|
|
106
|
-
interactionDelay?: number;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
6
|
/**
|
|
110
7
|
Objects type used to represent individual completions.
|
|
111
8
|
*/
|
|
@@ -351,6 +248,109 @@ selection range that has the same text in front of it.
|
|
|
351
248
|
*/
|
|
352
249
|
declare function insertCompletionText(state: EditorState, text: string, from: number, to: number): TransactionSpec;
|
|
353
250
|
|
|
251
|
+
interface CompletionConfig {
|
|
252
|
+
/**
|
|
253
|
+
When enabled (defaults to true), autocompletion will start
|
|
254
|
+
whenever the user types something that can be completed.
|
|
255
|
+
*/
|
|
256
|
+
activateOnTyping?: boolean;
|
|
257
|
+
/**
|
|
258
|
+
By default, when completion opens, the first option is selected
|
|
259
|
+
and can be confirmed with
|
|
260
|
+
[`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion). When this
|
|
261
|
+
is set to false, the completion widget starts with no completion
|
|
262
|
+
selected, and the user has to explicitly move to a completion
|
|
263
|
+
before you can confirm one.
|
|
264
|
+
*/
|
|
265
|
+
selectOnOpen?: boolean;
|
|
266
|
+
/**
|
|
267
|
+
Override the completion sources used. By default, they will be
|
|
268
|
+
taken from the `"autocomplete"` [language
|
|
269
|
+
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) (which should hold
|
|
270
|
+
[completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource) or arrays
|
|
271
|
+
of [completions](https://codemirror.net/6/docs/ref/#autocomplete.Completion)).
|
|
272
|
+
*/
|
|
273
|
+
override?: readonly CompletionSource[] | null;
|
|
274
|
+
/**
|
|
275
|
+
Determines whether the completion tooltip is closed when the
|
|
276
|
+
editor loses focus. Defaults to true.
|
|
277
|
+
*/
|
|
278
|
+
closeOnBlur?: boolean;
|
|
279
|
+
/**
|
|
280
|
+
The maximum number of options to render to the DOM.
|
|
281
|
+
*/
|
|
282
|
+
maxRenderedOptions?: number;
|
|
283
|
+
/**
|
|
284
|
+
Set this to false to disable the [default completion
|
|
285
|
+
keymap](https://codemirror.net/6/docs/ref/#autocomplete.completionKeymap). (This requires you to
|
|
286
|
+
add bindings to control completion yourself. The bindings should
|
|
287
|
+
probably have a higher precedence than other bindings for the
|
|
288
|
+
same keys.)
|
|
289
|
+
*/
|
|
290
|
+
defaultKeymap?: boolean;
|
|
291
|
+
/**
|
|
292
|
+
By default, completions are shown below the cursor when there is
|
|
293
|
+
space. Setting this to true will make the extension put the
|
|
294
|
+
completions above the cursor when possible.
|
|
295
|
+
*/
|
|
296
|
+
aboveCursor?: boolean;
|
|
297
|
+
/**
|
|
298
|
+
When given, this may return an additional CSS class to add to
|
|
299
|
+
the completion dialog element.
|
|
300
|
+
*/
|
|
301
|
+
tooltipClass?: (state: EditorState) => string;
|
|
302
|
+
/**
|
|
303
|
+
This can be used to add additional CSS classes to completion
|
|
304
|
+
options.
|
|
305
|
+
*/
|
|
306
|
+
optionClass?: (completion: Completion) => string;
|
|
307
|
+
/**
|
|
308
|
+
By default, the library will render icons based on the
|
|
309
|
+
completion's [type](https://codemirror.net/6/docs/ref/#autocomplete.Completion.type) in front of
|
|
310
|
+
each option. Set this to false to turn that off.
|
|
311
|
+
*/
|
|
312
|
+
icons?: boolean;
|
|
313
|
+
/**
|
|
314
|
+
This option can be used to inject additional content into
|
|
315
|
+
options. The `render` function will be called for each visible
|
|
316
|
+
completion, and should produce a DOM node to show. `position`
|
|
317
|
+
determines where in the DOM the result appears, relative to
|
|
318
|
+
other added widgets and the standard content. The default icons
|
|
319
|
+
have position 20, the label position 50, and the detail position
|
|
320
|
+
80.
|
|
321
|
+
*/
|
|
322
|
+
addToOptions?: {
|
|
323
|
+
render: (completion: Completion, state: EditorState) => Node | null;
|
|
324
|
+
position: number;
|
|
325
|
+
}[];
|
|
326
|
+
/**
|
|
327
|
+
By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
|
|
328
|
+
placed to the side of the selected. This option can be used to
|
|
329
|
+
override that. It will be given rectangles for the list of
|
|
330
|
+
completions, the selected option, the info element, and the
|
|
331
|
+
availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
|
|
332
|
+
and should return style and/or class strings for the info
|
|
333
|
+
element.
|
|
334
|
+
*/
|
|
335
|
+
positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
|
|
336
|
+
style?: string;
|
|
337
|
+
class?: string;
|
|
338
|
+
};
|
|
339
|
+
/**
|
|
340
|
+
The comparison function to use when sorting completions with the same
|
|
341
|
+
match score. Defaults to using
|
|
342
|
+
[`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
|
|
343
|
+
*/
|
|
344
|
+
compareCompletions?: (a: Completion, b: Completion) => number;
|
|
345
|
+
/**
|
|
346
|
+
By default, commands relating to an open completion only take
|
|
347
|
+
effect 75 milliseconds after the completion opened, so that key
|
|
348
|
+
presses made before the user is aware of the tooltip don't go to
|
|
349
|
+
the tooltip. This option can be used to configure that delay.
|
|
350
|
+
*/
|
|
351
|
+
interactionDelay?: number;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
354
|
/**
|
|
355
355
|
Convert a snippet template to a function that can
|
|
356
356
|
[apply](https://codemirror.net/6/docs/ref/#autocomplete.Completion.apply) it. Snippets are written
|
|
@@ -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,5 +1,5 @@
|
|
|
1
1
|
import { Annotation, StateEffect, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateField, Prec, Text, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
|
|
2
|
-
import { Direction,
|
|
2
|
+
import { Direction, ViewPlugin, logException, getTooltip, showTooltip, EditorView, Decoration, WidgetType, keymap } from '@codemirror/view';
|
|
3
3
|
import { syntaxTree, indentUnit } from '@codemirror/language';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -162,25 +162,17 @@ 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,
|
|
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 &&
|
|
168
|
-
state.sliceDoc(range.from
|
|
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
|
|
172
|
-
range: EditorSelection.cursor(range.from
|
|
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
|
}
|
|
176
|
-
function applyCompletion(view, option) {
|
|
177
|
-
const apply = option.completion.apply || option.completion.label;
|
|
178
|
-
let result = option.source;
|
|
179
|
-
if (typeof apply == "string")
|
|
180
|
-
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
|
|
181
|
-
else
|
|
182
|
-
apply(view, option.completion, result.from, result.to);
|
|
183
|
-
}
|
|
184
176
|
const SourceCache = /*@__PURE__*/new WeakMap();
|
|
185
177
|
function asSource(source) {
|
|
186
178
|
if (!Array.isArray(source))
|
|
@@ -384,6 +376,232 @@ function defaultPositionInfo(view, list, option, info, space) {
|
|
|
384
376
|
};
|
|
385
377
|
}
|
|
386
378
|
|
|
379
|
+
/**
|
|
380
|
+
Returns a command that moves the completion selection forward or
|
|
381
|
+
backward by the given amount.
|
|
382
|
+
*/
|
|
383
|
+
function moveCompletionSelection(forward, by = "option") {
|
|
384
|
+
return (view) => {
|
|
385
|
+
let cState = view.state.field(completionState, false);
|
|
386
|
+
if (!cState || !cState.open || cState.open.disabled ||
|
|
387
|
+
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
388
|
+
return false;
|
|
389
|
+
let step = 1, tooltip;
|
|
390
|
+
if (by == "page" && (tooltip = getTooltip(view, cState.open.tooltip)))
|
|
391
|
+
step = Math.max(2, Math.floor(tooltip.dom.offsetHeight /
|
|
392
|
+
tooltip.dom.querySelector("li").offsetHeight) - 1);
|
|
393
|
+
let { length } = cState.open.options;
|
|
394
|
+
let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
|
|
395
|
+
if (selected < 0)
|
|
396
|
+
selected = by == "page" ? 0 : length - 1;
|
|
397
|
+
else if (selected >= length)
|
|
398
|
+
selected = by == "page" ? length - 1 : 0;
|
|
399
|
+
view.dispatch({ effects: setSelectedEffect.of(selected) });
|
|
400
|
+
return true;
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
Accept the current completion.
|
|
405
|
+
*/
|
|
406
|
+
const acceptCompletion = (view) => {
|
|
407
|
+
let cState = view.state.field(completionState, false);
|
|
408
|
+
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
|
409
|
+
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
410
|
+
return false;
|
|
411
|
+
if (!cState.open.disabled)
|
|
412
|
+
return applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
413
|
+
return true;
|
|
414
|
+
};
|
|
415
|
+
/**
|
|
416
|
+
Explicitly start autocompletion.
|
|
417
|
+
*/
|
|
418
|
+
const startCompletion = (view) => {
|
|
419
|
+
let cState = view.state.field(completionState, false);
|
|
420
|
+
if (!cState)
|
|
421
|
+
return false;
|
|
422
|
+
view.dispatch({ effects: startCompletionEffect.of(true) });
|
|
423
|
+
return true;
|
|
424
|
+
};
|
|
425
|
+
/**
|
|
426
|
+
Close the currently active completion.
|
|
427
|
+
*/
|
|
428
|
+
const closeCompletion = (view) => {
|
|
429
|
+
let cState = view.state.field(completionState, false);
|
|
430
|
+
if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
|
|
431
|
+
return false;
|
|
432
|
+
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
433
|
+
return true;
|
|
434
|
+
};
|
|
435
|
+
class RunningQuery {
|
|
436
|
+
constructor(active, context) {
|
|
437
|
+
this.active = active;
|
|
438
|
+
this.context = context;
|
|
439
|
+
this.time = Date.now();
|
|
440
|
+
this.updates = [];
|
|
441
|
+
// Note that 'undefined' means 'not done yet', whereas 'null' means
|
|
442
|
+
// 'query returned null'.
|
|
443
|
+
this.done = undefined;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const DebounceTime = 50, MaxUpdateCount = 50, MinAbortTime = 1000;
|
|
447
|
+
const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
448
|
+
constructor(view) {
|
|
449
|
+
this.view = view;
|
|
450
|
+
this.debounceUpdate = -1;
|
|
451
|
+
this.running = [];
|
|
452
|
+
this.debounceAccept = -1;
|
|
453
|
+
this.composing = 0 /* CompositionState.None */;
|
|
454
|
+
for (let active of view.state.field(completionState).active)
|
|
455
|
+
if (active.state == 1 /* State.Pending */)
|
|
456
|
+
this.startQuery(active);
|
|
457
|
+
}
|
|
458
|
+
update(update) {
|
|
459
|
+
let cState = update.state.field(completionState);
|
|
460
|
+
if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState)
|
|
461
|
+
return;
|
|
462
|
+
let doesReset = update.transactions.some(tr => {
|
|
463
|
+
return (tr.selection || tr.docChanged) && !getUserEvent(tr);
|
|
464
|
+
});
|
|
465
|
+
for (let i = 0; i < this.running.length; i++) {
|
|
466
|
+
let query = this.running[i];
|
|
467
|
+
if (doesReset ||
|
|
468
|
+
query.updates.length + update.transactions.length > MaxUpdateCount && Date.now() - query.time > MinAbortTime) {
|
|
469
|
+
for (let handler of query.context.abortListeners) {
|
|
470
|
+
try {
|
|
471
|
+
handler();
|
|
472
|
+
}
|
|
473
|
+
catch (e) {
|
|
474
|
+
logException(this.view.state, e);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
query.context.abortListeners = null;
|
|
478
|
+
this.running.splice(i--, 1);
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
query.updates.push(...update.transactions);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (this.debounceUpdate > -1)
|
|
485
|
+
clearTimeout(this.debounceUpdate);
|
|
486
|
+
this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
|
|
487
|
+
? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
|
|
488
|
+
if (this.composing != 0 /* CompositionState.None */)
|
|
489
|
+
for (let tr of update.transactions) {
|
|
490
|
+
if (getUserEvent(tr) == "input")
|
|
491
|
+
this.composing = 2 /* CompositionState.Changed */;
|
|
492
|
+
else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
|
|
493
|
+
this.composing = 3 /* CompositionState.ChangedAndMoved */;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
startUpdate() {
|
|
497
|
+
this.debounceUpdate = -1;
|
|
498
|
+
let { state } = this.view, cState = state.field(completionState);
|
|
499
|
+
for (let active of cState.active) {
|
|
500
|
+
if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
|
|
501
|
+
this.startQuery(active);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
startQuery(active) {
|
|
505
|
+
let { state } = this.view, pos = cur(state);
|
|
506
|
+
let context = new CompletionContext(state, pos, active.explicitPos == pos);
|
|
507
|
+
let pending = new RunningQuery(active, context);
|
|
508
|
+
this.running.push(pending);
|
|
509
|
+
Promise.resolve(active.source(context)).then(result => {
|
|
510
|
+
if (!pending.context.aborted) {
|
|
511
|
+
pending.done = result || null;
|
|
512
|
+
this.scheduleAccept();
|
|
513
|
+
}
|
|
514
|
+
}, err => {
|
|
515
|
+
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
516
|
+
logException(this.view.state, err);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
scheduleAccept() {
|
|
520
|
+
if (this.running.every(q => q.done !== undefined))
|
|
521
|
+
this.accept();
|
|
522
|
+
else if (this.debounceAccept < 0)
|
|
523
|
+
this.debounceAccept = setTimeout(() => this.accept(), DebounceTime);
|
|
524
|
+
}
|
|
525
|
+
// For each finished query in this.running, try to create a result
|
|
526
|
+
// or, if appropriate, restart the query.
|
|
527
|
+
accept() {
|
|
528
|
+
var _a;
|
|
529
|
+
if (this.debounceAccept > -1)
|
|
530
|
+
clearTimeout(this.debounceAccept);
|
|
531
|
+
this.debounceAccept = -1;
|
|
532
|
+
let updated = [];
|
|
533
|
+
let conf = this.view.state.facet(completionConfig);
|
|
534
|
+
for (let i = 0; i < this.running.length; i++) {
|
|
535
|
+
let query = this.running[i];
|
|
536
|
+
if (query.done === undefined)
|
|
537
|
+
continue;
|
|
538
|
+
this.running.splice(i--, 1);
|
|
539
|
+
if (query.done) {
|
|
540
|
+
let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state));
|
|
541
|
+
// Replay the transactions that happened since the start of
|
|
542
|
+
// the request and see if that preserves the result
|
|
543
|
+
for (let tr of query.updates)
|
|
544
|
+
active = active.update(tr, conf);
|
|
545
|
+
if (active.hasResult()) {
|
|
546
|
+
updated.push(active);
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
|
|
551
|
+
if (current && current.state == 1 /* State.Pending */) {
|
|
552
|
+
if (query.done == null) {
|
|
553
|
+
// Explicitly failed. Should clear the pending status if it
|
|
554
|
+
// hasn't been re-set in the meantime.
|
|
555
|
+
let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
|
|
556
|
+
for (let tr of query.updates)
|
|
557
|
+
active = active.update(tr, conf);
|
|
558
|
+
if (active.state != 1 /* State.Pending */)
|
|
559
|
+
updated.push(active);
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
// Cleared by subsequent transactions. Restart.
|
|
563
|
+
this.startQuery(current);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
if (updated.length)
|
|
568
|
+
this.view.dispatch({ effects: setActiveEffect.of(updated) });
|
|
569
|
+
}
|
|
570
|
+
}, {
|
|
571
|
+
eventHandlers: {
|
|
572
|
+
blur(event) {
|
|
573
|
+
let state = this.view.state.field(completionState, false);
|
|
574
|
+
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
|
|
575
|
+
let dialog = state.open && getTooltip(this.view, state.open.tooltip);
|
|
576
|
+
if (!dialog || !dialog.dom.contains(event.relatedTarget))
|
|
577
|
+
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
compositionstart() {
|
|
581
|
+
this.composing = 1 /* CompositionState.Started */;
|
|
582
|
+
},
|
|
583
|
+
compositionend() {
|
|
584
|
+
if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
|
|
585
|
+
// Safari fires compositionend events synchronously, possibly
|
|
586
|
+
// from inside an update, so dispatch asynchronously to avoid reentrancy
|
|
587
|
+
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
|
|
588
|
+
}
|
|
589
|
+
this.composing = 0 /* CompositionState.None */;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
function applyCompletion(view, option) {
|
|
594
|
+
const apply = option.completion.apply || option.completion.label;
|
|
595
|
+
let result = view.state.field(completionState).active.find(a => a.source == option.source);
|
|
596
|
+
if (!(result instanceof ActiveResult))
|
|
597
|
+
return false;
|
|
598
|
+
if (typeof apply == "string")
|
|
599
|
+
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
|
|
600
|
+
else
|
|
601
|
+
apply(view, option.completion, result.from, result.to);
|
|
602
|
+
return true;
|
|
603
|
+
}
|
|
604
|
+
|
|
387
605
|
function optionContent(config) {
|
|
388
606
|
let content = config.addToOptions.slice();
|
|
389
607
|
if (config.icons)
|
|
@@ -689,14 +907,14 @@ function sortOptions(active, state) {
|
|
|
689
907
|
if (getMatch)
|
|
690
908
|
for (let n of getMatch(option))
|
|
691
909
|
match.push(n);
|
|
692
|
-
addOption(new Option(option, a, match, match[0]));
|
|
910
|
+
addOption(new Option(option, a.source, match, match[0]));
|
|
693
911
|
}
|
|
694
912
|
}
|
|
695
913
|
else {
|
|
696
914
|
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
|
|
697
915
|
for (let option of a.result.options)
|
|
698
916
|
if (match = matcher.match(option.label)) {
|
|
699
|
-
addOption(new Option(option, a, match, match[0] + (option.boost || 0)));
|
|
917
|
+
addOption(new Option(option, a.source, match, match[0] + (option.boost || 0)));
|
|
700
918
|
}
|
|
701
919
|
}
|
|
702
920
|
}
|
|
@@ -922,221 +1140,6 @@ const completionState = /*@__PURE__*/StateField.define({
|
|
|
922
1140
|
]
|
|
923
1141
|
});
|
|
924
1142
|
|
|
925
|
-
/**
|
|
926
|
-
Returns a command that moves the completion selection forward or
|
|
927
|
-
backward by the given amount.
|
|
928
|
-
*/
|
|
929
|
-
function moveCompletionSelection(forward, by = "option") {
|
|
930
|
-
return (view) => {
|
|
931
|
-
let cState = view.state.field(completionState, false);
|
|
932
|
-
if (!cState || !cState.open || cState.open.disabled ||
|
|
933
|
-
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
934
|
-
return false;
|
|
935
|
-
let step = 1, tooltip;
|
|
936
|
-
if (by == "page" && (tooltip = getTooltip(view, cState.open.tooltip)))
|
|
937
|
-
step = Math.max(2, Math.floor(tooltip.dom.offsetHeight /
|
|
938
|
-
tooltip.dom.querySelector("li").offsetHeight) - 1);
|
|
939
|
-
let { length } = cState.open.options;
|
|
940
|
-
let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
|
|
941
|
-
if (selected < 0)
|
|
942
|
-
selected = by == "page" ? 0 : length - 1;
|
|
943
|
-
else if (selected >= length)
|
|
944
|
-
selected = by == "page" ? length - 1 : 0;
|
|
945
|
-
view.dispatch({ effects: setSelectedEffect.of(selected) });
|
|
946
|
-
return true;
|
|
947
|
-
};
|
|
948
|
-
}
|
|
949
|
-
/**
|
|
950
|
-
Accept the current completion.
|
|
951
|
-
*/
|
|
952
|
-
const acceptCompletion = (view) => {
|
|
953
|
-
let cState = view.state.field(completionState, false);
|
|
954
|
-
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
|
955
|
-
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
956
|
-
return false;
|
|
957
|
-
if (!cState.open.disabled)
|
|
958
|
-
applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
959
|
-
return true;
|
|
960
|
-
};
|
|
961
|
-
/**
|
|
962
|
-
Explicitly start autocompletion.
|
|
963
|
-
*/
|
|
964
|
-
const startCompletion = (view) => {
|
|
965
|
-
let cState = view.state.field(completionState, false);
|
|
966
|
-
if (!cState)
|
|
967
|
-
return false;
|
|
968
|
-
view.dispatch({ effects: startCompletionEffect.of(true) });
|
|
969
|
-
return true;
|
|
970
|
-
};
|
|
971
|
-
/**
|
|
972
|
-
Close the currently active completion.
|
|
973
|
-
*/
|
|
974
|
-
const closeCompletion = (view) => {
|
|
975
|
-
let cState = view.state.field(completionState, false);
|
|
976
|
-
if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
|
|
977
|
-
return false;
|
|
978
|
-
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
979
|
-
return true;
|
|
980
|
-
};
|
|
981
|
-
class RunningQuery {
|
|
982
|
-
constructor(active, context) {
|
|
983
|
-
this.active = active;
|
|
984
|
-
this.context = context;
|
|
985
|
-
this.time = Date.now();
|
|
986
|
-
this.updates = [];
|
|
987
|
-
// Note that 'undefined' means 'not done yet', whereas 'null' means
|
|
988
|
-
// 'query returned null'.
|
|
989
|
-
this.done = undefined;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
const DebounceTime = 50, MaxUpdateCount = 50, MinAbortTime = 1000;
|
|
993
|
-
const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
994
|
-
constructor(view) {
|
|
995
|
-
this.view = view;
|
|
996
|
-
this.debounceUpdate = -1;
|
|
997
|
-
this.running = [];
|
|
998
|
-
this.debounceAccept = -1;
|
|
999
|
-
this.composing = 0 /* CompositionState.None */;
|
|
1000
|
-
for (let active of view.state.field(completionState).active)
|
|
1001
|
-
if (active.state == 1 /* State.Pending */)
|
|
1002
|
-
this.startQuery(active);
|
|
1003
|
-
}
|
|
1004
|
-
update(update) {
|
|
1005
|
-
let cState = update.state.field(completionState);
|
|
1006
|
-
if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState)
|
|
1007
|
-
return;
|
|
1008
|
-
let doesReset = update.transactions.some(tr => {
|
|
1009
|
-
return (tr.selection || tr.docChanged) && !getUserEvent(tr);
|
|
1010
|
-
});
|
|
1011
|
-
for (let i = 0; i < this.running.length; i++) {
|
|
1012
|
-
let query = this.running[i];
|
|
1013
|
-
if (doesReset ||
|
|
1014
|
-
query.updates.length + update.transactions.length > MaxUpdateCount && Date.now() - query.time > MinAbortTime) {
|
|
1015
|
-
for (let handler of query.context.abortListeners) {
|
|
1016
|
-
try {
|
|
1017
|
-
handler();
|
|
1018
|
-
}
|
|
1019
|
-
catch (e) {
|
|
1020
|
-
logException(this.view.state, e);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
query.context.abortListeners = null;
|
|
1024
|
-
this.running.splice(i--, 1);
|
|
1025
|
-
}
|
|
1026
|
-
else {
|
|
1027
|
-
query.updates.push(...update.transactions);
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
if (this.debounceUpdate > -1)
|
|
1031
|
-
clearTimeout(this.debounceUpdate);
|
|
1032
|
-
this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
|
|
1033
|
-
? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
|
|
1034
|
-
if (this.composing != 0 /* CompositionState.None */)
|
|
1035
|
-
for (let tr of update.transactions) {
|
|
1036
|
-
if (getUserEvent(tr) == "input")
|
|
1037
|
-
this.composing = 2 /* CompositionState.Changed */;
|
|
1038
|
-
else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
|
|
1039
|
-
this.composing = 3 /* CompositionState.ChangedAndMoved */;
|
|
1040
|
-
}
|
|
1041
|
-
}
|
|
1042
|
-
startUpdate() {
|
|
1043
|
-
this.debounceUpdate = -1;
|
|
1044
|
-
let { state } = this.view, cState = state.field(completionState);
|
|
1045
|
-
for (let active of cState.active) {
|
|
1046
|
-
if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
|
|
1047
|
-
this.startQuery(active);
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
startQuery(active) {
|
|
1051
|
-
let { state } = this.view, pos = cur(state);
|
|
1052
|
-
let context = new CompletionContext(state, pos, active.explicitPos == pos);
|
|
1053
|
-
let pending = new RunningQuery(active, context);
|
|
1054
|
-
this.running.push(pending);
|
|
1055
|
-
Promise.resolve(active.source(context)).then(result => {
|
|
1056
|
-
if (!pending.context.aborted) {
|
|
1057
|
-
pending.done = result || null;
|
|
1058
|
-
this.scheduleAccept();
|
|
1059
|
-
}
|
|
1060
|
-
}, err => {
|
|
1061
|
-
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
1062
|
-
logException(this.view.state, err);
|
|
1063
|
-
});
|
|
1064
|
-
}
|
|
1065
|
-
scheduleAccept() {
|
|
1066
|
-
if (this.running.every(q => q.done !== undefined))
|
|
1067
|
-
this.accept();
|
|
1068
|
-
else if (this.debounceAccept < 0)
|
|
1069
|
-
this.debounceAccept = setTimeout(() => this.accept(), DebounceTime);
|
|
1070
|
-
}
|
|
1071
|
-
// For each finished query in this.running, try to create a result
|
|
1072
|
-
// or, if appropriate, restart the query.
|
|
1073
|
-
accept() {
|
|
1074
|
-
var _a;
|
|
1075
|
-
if (this.debounceAccept > -1)
|
|
1076
|
-
clearTimeout(this.debounceAccept);
|
|
1077
|
-
this.debounceAccept = -1;
|
|
1078
|
-
let updated = [];
|
|
1079
|
-
let conf = this.view.state.facet(completionConfig);
|
|
1080
|
-
for (let i = 0; i < this.running.length; i++) {
|
|
1081
|
-
let query = this.running[i];
|
|
1082
|
-
if (query.done === undefined)
|
|
1083
|
-
continue;
|
|
1084
|
-
this.running.splice(i--, 1);
|
|
1085
|
-
if (query.done) {
|
|
1086
|
-
let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state));
|
|
1087
|
-
// Replay the transactions that happened since the start of
|
|
1088
|
-
// the request and see if that preserves the result
|
|
1089
|
-
for (let tr of query.updates)
|
|
1090
|
-
active = active.update(tr, conf);
|
|
1091
|
-
if (active.hasResult()) {
|
|
1092
|
-
updated.push(active);
|
|
1093
|
-
continue;
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
|
|
1097
|
-
if (current && current.state == 1 /* State.Pending */) {
|
|
1098
|
-
if (query.done == null) {
|
|
1099
|
-
// Explicitly failed. Should clear the pending status if it
|
|
1100
|
-
// hasn't been re-set in the meantime.
|
|
1101
|
-
let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
|
|
1102
|
-
for (let tr of query.updates)
|
|
1103
|
-
active = active.update(tr, conf);
|
|
1104
|
-
if (active.state != 1 /* State.Pending */)
|
|
1105
|
-
updated.push(active);
|
|
1106
|
-
}
|
|
1107
|
-
else {
|
|
1108
|
-
// Cleared by subsequent transactions. Restart.
|
|
1109
|
-
this.startQuery(current);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
if (updated.length)
|
|
1114
|
-
this.view.dispatch({ effects: setActiveEffect.of(updated) });
|
|
1115
|
-
}
|
|
1116
|
-
}, {
|
|
1117
|
-
eventHandlers: {
|
|
1118
|
-
blur(event) {
|
|
1119
|
-
let state = this.view.state.field(completionState, false);
|
|
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
|
-
}
|
|
1125
|
-
},
|
|
1126
|
-
compositionstart() {
|
|
1127
|
-
this.composing = 1 /* CompositionState.Started */;
|
|
1128
|
-
},
|
|
1129
|
-
compositionend() {
|
|
1130
|
-
if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
|
|
1131
|
-
// Safari fires compositionend events synchronously, possibly
|
|
1132
|
-
// from inside an update, so dispatch asynchronously to avoid reentrancy
|
|
1133
|
-
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
|
|
1134
|
-
}
|
|
1135
|
-
this.composing = 0 /* CompositionState.None */;
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
});
|
|
1139
|
-
|
|
1140
1143
|
const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
|
1141
1144
|
".cm-tooltip.cm-tooltip-autocomplete": {
|
|
1142
1145
|
"& > ul": {
|
|
@@ -1428,7 +1431,7 @@ function snippet(template) {
|
|
|
1428
1431
|
let spec = {
|
|
1429
1432
|
changes: { from, to, insert: Text.of(text) },
|
|
1430
1433
|
scrollIntoView: true,
|
|
1431
|
-
annotations: pickedCompletion.of(completion)
|
|
1434
|
+
annotations: completion ? pickedCompletion.of(completion) : undefined
|
|
1432
1435
|
};
|
|
1433
1436
|
if (ranges.length)
|
|
1434
1437
|
spec.selection = fieldSelection(ranges, 0);
|