Autocompletion system for the CodeMirror code editor with completion sources, snippets, and bracket closing
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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;
};