Programmatic control over completion state, selection, and lifecycle for building custom completion interfaces and integrating with editor interactions.
Commands for starting, stopping, and managing the completion interface.
/**
* Explicitly start autocompletion
*/
const startCompletion: Command;
/**
* Close the currently active completion
*/
const closeCompletion: Command;
/**
* Accept the current completion selection
*/
const acceptCompletion: Command;Usage Examples:
import { startCompletion, closeCompletion, acceptCompletion } from "@codemirror/autocomplete";
import { EditorView } from "@codemirror/view";
// Manual completion trigger
const triggerCompletion = (view: EditorView) => {
return startCompletion(view);
};
// Close completion programmatically
const hideCompletion = (view: EditorView) => {
return closeCompletion(view);
};
// Accept current selection
const confirmCompletion = (view: EditorView) => {
return acceptCompletion(view);
};
// Custom completion controls
const customCompletionKeys = [
{ key: "Ctrl-Space", run: startCompletion },
{ key: "Escape", run: closeCompletion },
{ key: "Tab", run: acceptCompletion }
];Commands for navigating through completion options programmatically.
/**
* Move completion selection forward or backward
*/
function moveCompletionSelection(forward: boolean, by?: "option" | "page"): Command;Usage Examples:
import { moveCompletionSelection } from "@codemirror/autocomplete";
// Navigation commands
const nextOption = moveCompletionSelection(true);
const prevOption = moveCompletionSelection(false);
const nextPage = moveCompletionSelection(true, "page");
const prevPage = moveCompletionSelection(false, "page");
// Custom navigation keymap
const navigationKeys = [
{ key: "ArrowDown", run: nextOption },
{ key: "ArrowUp", run: prevOption },
{ key: "PageDown", run: nextPage },
{ key: "PageUp", run: prevPage }
];
// Smart navigation with fallback
const smartNavigation = (view: EditorView, forward: boolean) => {
// Try completion navigation first
if (moveCompletionSelection(forward)(view)) {
return true;
}
// Fall back to normal cursor movement
return false;
};Functions to inspect the current completion state for conditional logic.
Usage Examples:
import {
completionStatus,
currentCompletions,
selectedCompletion,
selectedCompletionIndex
} from "@codemirror/autocomplete";
// Check completion state
const handleKeyPress = (view: EditorView, event: KeyboardEvent) => {
const status = completionStatus(view.state);
if (status === "active") {
const selected = selectedCompletion(view.state);
const index = selectedCompletionIndex(view.state);
const all = currentCompletions(view.state);
console.log(`Selected: ${selected?.label} (${index}/${all.length})`);
// Custom handling for active completion
if (event.key === "Tab" && selected) {
return acceptCompletion(view);
}
} else if (status === "pending") {
// Completion is loading
console.log("Completion in progress...");
} else {
// No active completion
if (event.key === " " && event.ctrlKey) {
return startCompletion(view);
}
}
return false;
};Building custom completion UIs using the programmatic API.
Usage Examples:
import {
startCompletion,
currentCompletions,
selectedCompletionIndex,
setSelectedCompletion,
acceptCompletion
} from "@codemirror/autocomplete";
// Custom completion widget
class CustomCompletionWidget {
private view: EditorView;
private element: HTMLElement;
constructor(view: EditorView) {
this.view = view;
this.element = this.createWidget();
}
createWidget(): HTMLElement {
const widget = document.createElement("div");
widget.className = "custom-completion-widget";
// Update widget when state changes
this.view.dom.addEventListener("update", () => {
this.updateWidget();
});
return widget;
}
updateWidget() {
const completions = currentCompletions(this.view.state);
const selectedIndex = selectedCompletionIndex(this.view.state);
this.element.innerHTML = "";
completions.forEach((completion, index) => {
const item = document.createElement("div");
item.className = "completion-item";
item.textContent = completion.label;
if (index === selectedIndex) {
item.classList.add("selected");
}
item.addEventListener("click", () => {
// Set selection and accept
this.view.dispatch({
effects: setSelectedCompletion(index)
});
acceptCompletion(this.view);
});
this.element.appendChild(item);
});
}
show() {
document.body.appendChild(this.element);
startCompletion(this.view);
}
hide() {
if (this.element.parentNode) {
this.element.parentNode.removeChild(this.element);
}
closeCompletion(this.view);
}
}
// Usage
const customWidget = new CustomCompletionWidget(view);
customWidget.show();Handling completion in response to editor events and user interactions.
Usage Examples:
import { ViewPlugin, ViewUpdate } from "@codemirror/view";
import { completionStatus, startCompletion } from "@codemirror/autocomplete";
// Auto-completion on specific patterns
const autoCompletionPlugin = ViewPlugin.fromClass(class {
constructor(private view: EditorView) {}
update(update: ViewUpdate) {
if (!update.docChanged) return;
// Check for completion triggers
const changes = update.changes;
changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
const text = inserted.toString();
// Trigger completion on dot notation
if (text === "." && completionStatus(this.view.state) !== "active") {
setTimeout(() => startCompletion(this.view), 0);
}
// Trigger completion after import statements
if (text.includes("import") && text.includes("from")) {
setTimeout(() => startCompletion(this.view), 100);
}
});
}
});
// Completion with custom timing
const timedCompletionPlugin = ViewPlugin.fromClass(class {
private timeout: number | null = null;
constructor(private view: EditorView) {}
update(update: ViewUpdate) {
if (update.docChanged) {
// Clear existing timeout
if (this.timeout) {
clearTimeout(this.timeout);
}
// Set new timeout for completion
this.timeout = setTimeout(() => {
const status = completionStatus(this.view.state);
if (status !== "active" && status !== "pending") {
startCompletion(this.view);
}
this.timeout = null;
}, 500);
}
}
});Combining completion commands with other editor operations.
Usage Examples:
// Smart Enter key handling
const smartEnter = (view: EditorView) => {
const status = completionStatus(view.state);
if (status === "active") {
const selected = selectedCompletion(view.state);
if (selected) {
// Accept completion and add newline
acceptCompletion(view);
setTimeout(() => {
view.dispatch({
changes: { from: view.state.selection.main.head, insert: "\n" },
selection: { anchor: view.state.selection.main.head + 1 }
});
}, 0);
return true;
}
}
// Normal Enter behavior
return false;
};
// Completion with auto-formatting
const formatAfterCompletion = (view: EditorView) => {
const selected = selectedCompletion(view.state);
if (selected && acceptCompletion(view)) {
// Apply formatting after completion
setTimeout(() => {
// Custom formatting logic
if (selected.type === "function") {
// Format function calls
formatFunctionCall(view);
} else if (selected.type === "class") {
// Format class instantiation
formatClassInstantiation(view);
}
}, 10);
return true;
}
return false;
};
// Multi-step completion workflow
const workflowCompletion = async (view: EditorView) => {
// Step 1: Start completion
if (!startCompletion(view)) return false;
// Step 2: Wait for completion to be active
await new Promise(resolve => {
const checkActive = () => {
if (completionStatus(view.state) === "active") {
resolve(undefined);
} else {
setTimeout(checkActive, 10);
}
};
checkActive();
});
// Step 3: Auto-select first function option
const completions = currentCompletions(view.state);
const functionIndex = completions.findIndex(c => c.type === "function");
if (functionIndex >= 0) {
view.dispatch({ effects: setSelectedCompletion(functionIndex) });
}
return true;
};