@codemirror/autocomplete
Advanced tools
Comparing version 0.18.7 to 0.18.8
@@ -0,1 +1,9 @@ | ||
## 0.18.8 (2021-06-30) | ||
### New features | ||
Add an `ifIn` helper function that constrains a completion source to only fire when in a given syntax node. Add support for unfiltered completions | ||
A completion result can now set a `filter: false` property to disable filtering and sorting of completions, when it already did so itself. | ||
## 0.18.7 (2021-06-14) | ||
@@ -2,0 +10,0 @@ |
import { EditorState, Transaction, StateCommand, Facet, Extension } from '@codemirror/state'; | ||
import { EditorView, KeyBinding, Command } from '@codemirror/view'; | ||
import * as lezer_tree from 'lezer-tree'; | ||
@@ -126,6 +125,6 @@ interface CompletionConfig { | ||
tokenBefore(types: readonly string[]): { | ||
from: number; | ||
from: any; | ||
to: number; | ||
text: string; | ||
type: lezer_tree.NodeType; | ||
type: any; | ||
} | null; | ||
@@ -159,2 +158,7 @@ /** | ||
/** | ||
Wrap the given completion source so that it will only fire when the | ||
cursor is in a syntax node with one of the given names. | ||
*/ | ||
declare function ifIn(nodes: readonly string[], source: CompletionSource): CompletionSource; | ||
/** | ||
Wrap the given completion source so that it will not fire when the | ||
@@ -200,2 +204,11 @@ cursor is in a syntax node with one of the given names. | ||
span?: RegExp; | ||
/** | ||
By default, the library filters and scores completions. Set | ||
`filter` to `false` to disable this, and cause your completions | ||
to all be included, in the order they were given. When there are | ||
other sources, unfiltered completions appear at the top of the | ||
list of completions. `span` must not be given `filter` is | ||
`false`, because it only works when filtering. | ||
*/ | ||
filter?: boolean; | ||
} | ||
@@ -312,2 +325,2 @@ | ||
export { Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion }; | ||
export { Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion }; |
@@ -108,2 +108,14 @@ import { Facet, combineConfig, StateEffect, StateField, Transaction, Text, EditorSelection, Prec, CharCategory } from '@codemirror/state'; | ||
/** | ||
Wrap the given completion source so that it will only fire when the | ||
cursor is in a syntax node with one of the given names. | ||
*/ | ||
function ifIn(nodes, source) { | ||
return (context) => { | ||
for (let pos = syntaxTree(context.state).resolve(context.pos, -1); pos; pos = pos.parent) | ||
if (nodes.indexOf(pos.name) > -1) | ||
return source(context); | ||
return null; | ||
}; | ||
} | ||
/** | ||
Wrap the given completion source so that it will not fire when the | ||
@@ -588,12 +600,18 @@ cursor is in a syntax node with one of the given names. | ||
function sortOptions(active, state) { | ||
let options = []; | ||
let options = [], i = 0; | ||
for (let a of active) | ||
if (a.hasResult()) { | ||
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match; | ||
for (let option of a.result.options) | ||
if (match = matcher.match(option.label)) { | ||
if (option.boost != null) | ||
match[0] += option.boost; | ||
options.push(new Option(option, a, match)); | ||
} | ||
if (a.result.filter === false) { | ||
for (let option of a.result.options) | ||
options.push(new Option(option, a, [1e9 - i++])); | ||
} | ||
else { | ||
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match; | ||
for (let option of a.result.options) | ||
if (match = matcher.match(option.label)) { | ||
if (option.boost != null) | ||
match[0] += option.boost; | ||
options.push(new Option(option, a, match)); | ||
} | ||
} | ||
} | ||
@@ -661,3 +679,3 @@ options.sort(cmpOption); | ||
let value = this.active.find(s => s.source == source) || | ||
new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */, false); | ||
new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */); | ||
return value.update(tr, conf); | ||
@@ -671,3 +689,3 @@ }); | ||
if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult())) | ||
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */, false) : a); | ||
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a); | ||
for (let effect of tr.effects) | ||
@@ -712,9 +730,8 @@ if (effect.is(setSelectedEffect)) | ||
class ActiveSource { | ||
constructor(source, state, explicit) { | ||
constructor(source, state, explicitPos = -1) { | ||
this.source = source; | ||
this.state = state; | ||
this.explicit = explicit; | ||
this.explicitPos = explicitPos; | ||
} | ||
hasResult() { return false; } | ||
explicitAt(_pos) { return this.explicit; } | ||
update(tr, conf) { | ||
@@ -727,8 +744,8 @@ let event = tr.annotation(Transaction.userEvent), value = this; | ||
else if (tr.selection && value.state != 0 /* Inactive */) | ||
value = new ActiveSource(value.source, 0 /* Inactive */, false); | ||
value = new ActiveSource(value.source, 0 /* Inactive */); | ||
for (let effect of tr.effects) { | ||
if (effect.is(startCompletionEffect)) | ||
value = new ActiveSource(value.source, 1 /* Pending */, effect.value); | ||
value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1); | ||
else if (effect.is(closeCompletionEffect)) | ||
value = new ActiveSource(value.source, 0 /* Inactive */, false); | ||
value = new ActiveSource(value.source, 0 /* Inactive */); | ||
else if (effect.is(setActiveEffect)) | ||
@@ -741,13 +758,15 @@ for (let active of effect.value) | ||
} | ||
handleUserEvent(_tr, type, conf) { | ||
return type == "delete" || !conf.activateOnTyping ? this : new ActiveSource(this.source, 1 /* Pending */, false); | ||
handleUserEvent(tr, type, conf) { | ||
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */); | ||
} | ||
handleChange(tr) { | ||
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */, false) : this; | ||
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes); | ||
} | ||
map(changes) { | ||
return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos)); | ||
} | ||
} | ||
class ActiveResult extends ActiveSource { | ||
constructor(source, explicitPos, result, from, to, span) { | ||
super(source, 2 /* Result */, explicitPos > -1); | ||
this.explicitPos = explicitPos; | ||
super(source, 2 /* Result */, explicitPos); | ||
this.result = result; | ||
@@ -759,20 +778,18 @@ this.from = from; | ||
hasResult() { return true; } | ||
explicitAt(pos) { return this.explicitPos == pos; } | ||
mapExplicit(mapping) { | ||
return this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos); | ||
} | ||
handleUserEvent(tr, type, conf) { | ||
let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1); | ||
let pos = cur(tr.state); | ||
if ((this.explicit ? pos < from : pos <= from) || pos > to) | ||
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */, false); | ||
if ((this.explicitPos > -1 ? pos < from : pos <= from) || pos > to) | ||
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */); | ||
let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos); | ||
if (this.span && (from == to || this.span.test(tr.state.sliceDoc(from, to)))) | ||
return new ActiveResult(this.source, this.mapExplicit(tr.changes), this.result, from, to, this.span); | ||
return new ActiveSource(this.source, 1 /* Pending */, false); | ||
return new ActiveResult(this.source, explicitPos, this.result, from, to, this.span); | ||
return new ActiveSource(this.source, 1 /* Pending */, explicitPos); | ||
} | ||
handleChange(tr) { | ||
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */, false) : this.map(tr.changes); | ||
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes); | ||
} | ||
map(mapping) { | ||
return new ActiveResult(this.source, this.mapExplicit(mapping), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1), this.span); | ||
return mapping.empty ? this : | ||
new ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1), this.span); | ||
} | ||
@@ -783,3 +800,3 @@ } | ||
const setActiveEffect = /*@__PURE__*/StateEffect.define({ | ||
map(sources, mapping) { return sources.map(s => s.hasResult() && !mapping.empty ? s.map(mapping) : s); } | ||
map(sources, mapping) { return sources.map(s => s.map(mapping)); } | ||
}); | ||
@@ -849,4 +866,4 @@ const setSelectedEffect = /*@__PURE__*/StateEffect.define(); | ||
class RunningQuery { | ||
constructor(source, context) { | ||
this.source = source; | ||
constructor(active, context) { | ||
this.active = active; | ||
this.context = context; | ||
@@ -901,3 +918,3 @@ this.time = Date.now(); | ||
clearTimeout(this.debounceUpdate); | ||
this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.source == a.source)) | ||
this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source)) | ||
? setTimeout(() => this.startUpdate(), DebounceTime) : -1; | ||
@@ -916,3 +933,3 @@ if (this.composing != 0 /* None */) | ||
for (let active of cState.active) { | ||
if (active.state == 1 /* Pending */ && !this.running.some(r => r.source == active.source)) | ||
if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source)) | ||
this.startQuery(active); | ||
@@ -923,4 +940,4 @@ } | ||
let { state } = this.view, pos = cur(state); | ||
let context = new CompletionContext(state, pos, active.explicitAt(pos)); | ||
let pending = new RunningQuery(active.source, context); | ||
let context = new CompletionContext(state, pos, active.explicitPos == pos); | ||
let pending = new RunningQuery(active, context); | ||
this.running.push(pending); | ||
@@ -958,3 +975,3 @@ Promise.resolve(active.source(context)).then(result => { | ||
if (query.done) { | ||
let active = new ActiveResult(query.source, query.context.explicit ? query.context.pos : -1, 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), query.done.span ? ensureAnchor(query.done.span, true) : null); | ||
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), query.done.span && query.done.filter !== false ? ensureAnchor(query.done.span, true) : null); | ||
// Replay the transactions that happened since the start of | ||
@@ -969,3 +986,3 @@ // the request and see if that preserves the result | ||
} | ||
let current = this.view.state.field(completionState).active.find(a => a.source == query.source); | ||
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source); | ||
if (current && current.state == 1 /* Pending */) { | ||
@@ -975,3 +992,3 @@ if (query.done == null) { | ||
// hasn't been re-set in the meantime. | ||
let active = new ActiveSource(query.source, 0 /* Inactive */, false); | ||
let active = new ActiveSource(query.active.source, 0 /* Inactive */); | ||
for (let tr of query.updates) | ||
@@ -1324,2 +1341,2 @@ active = active.update(tr, conf); | ||
export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion }; | ||
export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion }; |
{ | ||
"name": "@codemirror/autocomplete", | ||
"version": "0.18.7", | ||
"version": "0.18.8", | ||
"description": "Autocompletion for the CodeMirror code editor", | ||
@@ -5,0 +5,0 @@ "scripts": { |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
127330
2948