System for creating custom completion sources and handling completion results with filtering, validation, and dynamic updates.
Functions that generate completion options based on editor context, supporting both synchronous and asynchronous completion generation.
/**
* Function signature for a completion source
*/
type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
/**
* Create a completion source from a fixed list of options
*/
function completeFromList(list: readonly (string | Completion)[]): CompletionSource;
/**
* Wrap completion source to only fire within specific syntax nodes
*/
function ifIn(nodes: readonly string[], source: CompletionSource): CompletionSource;
/**
* Wrap completion source to avoid firing in specific syntax nodes
*/
function ifNotIn(nodes: readonly string[], source: CompletionSource): CompletionSource;
/**
* Word-based completion source that scans the document
*/
const completeAnyWord: CompletionSource;Usage Examples:
import { completeFromList, ifIn, CompletionContext } from "@codemirror/autocomplete";
// Simple word list completion
const keywordSource = completeFromList([
"function", "const", "let", "var", "return", "if", "else"
]);
// Context-aware completion
const stringCompletion = ifIn(["String"], completeFromList([
"charAt", "indexOf", "substring", "toLowerCase", "toUpperCase"
]));
// Custom async completion source
const apiCompletion = async (context: CompletionContext) => {
const token = context.matchBefore(/\w+$/);
if (!token) return null;
const response = await fetch(`/api/completions?q=${token.text}`);
const suggestions = await response.json();
return {
from: token.from,
options: suggestions.map(s => ({
label: s.name,
detail: s.signature,
type: s.kind
}))
};
};Context object passed to completion sources providing editor state and utility methods.
class CompletionContext {
/**
* The editor state that the completion happens in
*/
readonly state: EditorState;
/**
* The position at which the completion is happening
*/
readonly pos: number;
/**
* Whether completion was activated explicitly or implicitly by typing
*/
readonly explicit: boolean;
/**
* The editor view (may be undefined in some contexts)
*/
readonly view?: EditorView;
/**
* Get token before current position matching any of the given types
*/
tokenBefore(types: readonly string[]): {from: number, to: number, text: string, type: any} | null;
/**
* Get match of the given expression directly before the cursor
*/
matchBefore(expr: RegExp): {from: number, to: number, text: string} | null;
/**
* True when the query has been aborted
*/
get aborted(): boolean;
/**
* Register abort handlers for cleanup when query is cancelled
*/
addEventListener(type: "abort", listener: () => void, options?: {onDocChange: boolean}): void;
}Results returned by completion sources, defining the completion options and behavior.
interface CompletionResult {
/**
* The start of the range that is being completed
*/
from: number;
/**
* The end of the range that is being completed (defaults to cursor position)
*/
to?: number;
/**
* The completion options to show
*/
options: readonly Completion[];
/**
* Pattern for validating continued typing without re-querying
*/
validFor?: RegExp | ((text: string, from: number, to: number, state: EditorState) => boolean);
/**
* Whether to filter and score completions (defaults to true)
*/
filter?: boolean;
/**
* Custom function to compute match highlighting ranges
*/
getMatch?: (completion: Completion, matched?: readonly number[]) => readonly number[];
/**
* Synchronously update result after typing or deletion
*/
update?: (current: CompletionResult, from: number, to: number, context: CompletionContext) => CompletionResult | null;
/**
* Update result for position changes due to other transactions
*/
map?: (current: CompletionResult, changes: ChangeDesc) => CompletionResult | null;
/**
* Default commit characters for all options in this result
*/
commitCharacters?: readonly string[];
}Individual completion options with metadata for display and application.
interface Completion {
/**
* The label to show in completion picker (used for matching)
*/
label: string;
/**
* Optional override for visible label (when different from match text)
*/
displayLabel?: string;
/**
* Short additional information shown after the label
*/
detail?: string;
/**
* Additional info shown when completion is selected
*/
info?: string | ((completion: Completion) => CompletionInfo | Promise<CompletionInfo>);
/**
* How to apply the completion (defaults to replacing with label)
*/
apply?: string | ((view: EditorView, completion: Completion, from: number, to: number) => void);
/**
* Type for icon styling (class suffix for cm-completionIcon-)
*/
type?: string;
/**
* Characters that trigger immediate completion when typed
*/
commitCharacters?: readonly string[];
/**
* Ranking adjustment from -99 to 99
*/
boost?: number;
/**
* Section grouping for organization
*/
section?: string | CompletionSection;
}
type CompletionInfo = Node | null | {dom: Node, destroy?(): void};
interface CompletionSection {
name: string;
header?: (section: CompletionSection) => HTMLElement;
rank?: number;
}Usage Examples:
// Rich completion objects
const functionCompletions = [
{
label: "setTimeout",
detail: "(callback: () => void, delay: number) => number",
info: "Executes a function after a specified delay",
type: "function",
apply: "setTimeout(${}, ${})",
commitCharacters: ["("]
},
{
label: "console.log",
detail: "(...data: any[]) => void",
info: "Outputs a message to the web console",
type: "method",
section: "Console"
}
];
// Custom apply function
const customCompletion = {
label: "for",
type: "keyword",
apply: (view, completion, from, to) => {
view.dispatch({
changes: {from, to, insert: "for (let i = 0; i < ; i++) {\n\t\n}"},
selection: {anchor: from + 17}
});
}
};Helper functions for working with completions and insertion.
/**
* Helper function that returns a transaction spec for inserting completion text
*/
function insertCompletionText(state: EditorState, text: string, from: number, to: number): TransactionSpec;
/**
* Annotation added to transactions produced by picking a completion
*/
const pickedCompletion: Annotation<Completion>;Usage Example:
import { insertCompletionText, pickedCompletion } from "@codemirror/autocomplete";
// Custom completion application
const customApply = (view: EditorView, completion: Completion, from: number, to: number) => {
const spec = insertCompletionText(view.state, completion.label, from, to);
spec.annotations = [pickedCompletion.of(completion)];
view.dispatch(view.state.update(spec));
};