Completion Provider¶
pegium::DefaultCompletionProvider is the generic content assist base in Pegium. It consumes parser completion traces, builds a small semantic context around the cursor, then lets you override narrowly-scoped hooks instead of rewriting the whole provider.
The default stays language-agnostic, so you only customize references, keywords, rule-level proposals, and snippets.
Default flow¶
- The parser computes the reachable completion features (
parser::ExpectPath) at the cursor. - The provider builds a
CompletionContextfor each feature. - One of the protected hooks emits
CompletionValueobjects. - The default provider turns them into LSP
CompletionItemvalues.
CompletionContext¶
CompletionContext gives each hook the document state around the cursor:
document: current workspace documentparams: original LSP completion requestoffset: absolute cursor offsettokenOffset/tokenEndOffset: token bounds used for replacementtokenText: token under the cursor, if anyprefix: text from token start to the cursornode: best AST node found near the completion anchorreference: concrete reference under the cursor when one already existsfeature: the active completion path (parser::ExpectPath), always present for the active completion alternative
The feature captures what the grammar expects next at the cursor — typically a keyword, a cross-reference, or a sub-rule.
CompletionValue¶
CompletionValue is the generic payload hooks produce before it becomes an LSP item:
label: visible label, and default inserted textnewText: replacement text when it differs fromlabeldetail: short right-side descriptionfilterText: alternate text used by fuzzy filteringsortText: explicit ordering keykind: explicit LSP item kinddocumentation: Markdown documentation payloadtextEdit: custom replacement rangeinsertTextFormat: set to::lsp::InsertTextFormat::Snippetfor snippetsdescription: optional indexed symbol used by the default reference path
Omit textEdit and the default provider computes it from tokenOffset..offset.
Reference completion¶
Reference completion is driven by ReferenceInfo, and the default provider uses the ScopeProvider. Good scoping gives you good completion for free.
Hooks¶
completionFor¶
Top-level dispatch hook. Override it only to replace the routing between Reference, Rule, and Keyword features.
completionForReference¶
Called for reference features. The default iterates over getReferenceCandidates(...), then delegates item creation to createReferenceCompletionItem(...).
getReferenceCandidates¶
The best low-risk extension point for filtering reference proposals.
createReferenceCompletionItem¶
Transforms one AstNodeDescription into a CompletionValue.
completionForRule¶
Called for parser rule features. The natural place to add snippets tied to a specific rule.
completionForKeyword¶
Called for keyword features. The default emits the literal as a keyword item after filterKeyword(...).
filterKeyword¶
Cheap boolean filter run before keyword item creation.
fillCompletionItem¶
Final hook before returning the LSP item.
continueCompletion¶
Controls whether later parser features are still processed.
Common override patterns¶
Filter reference candidates¶
class MyCompletionProvider final : public pegium::DefaultCompletionProvider {
public:
using DefaultCompletionProvider::DefaultCompletionProvider;
protected:
std::vector<const pegium::workspace::AstNodeDescription *>
getReferenceCandidates(
const pegium::CompletionContext &context,
const pegium::ReferenceInfo &reference) const override {
auto candidates =
DefaultCompletionProvider::getReferenceCandidates(context, reference);
std::erase_if(candidates, [](const auto *candidate) {
return candidate->name.starts_with("_");
});
return candidates;
}
};
Add a keyword hook¶
void completionForKeyword(const pegium::CompletionContext &context,
const pegium::grammar::Literal &keyword,
const pegium::CompletionAcceptor &acceptor) const override {
if (keyword.getValue() == "entity" && context.prefix.empty()) {
acceptor(pegium::CompletionValue{
.label = "entity",
.detail = "Top-level declaration",
});
return;
}
DefaultCompletionProvider::completionForKeyword(context, keyword, acceptor);
}
Add a rule snippet¶
void completionForRule(const pegium::CompletionContext &context,
const pegium::grammar::AbstractRule &rule,
const pegium::CompletionAcceptor &acceptor) const override {
if (rule.getName() != "Entity") {
return;
}
acceptor(pegium::CompletionValue{
.label = "entity",
.newText = "entity ${1:Name} {\\n\\t$0\\n}",
.detail = "Snippet",
.insertTextFormat = ::lsp::InsertTextFormat::Snippet,
});
}
Provider options¶
CompletionProviderOptions currently supports:
triggerCharactersallCommitCharacters
Practical advice¶
Start from the narrowest hook that solves the problem:
getReferenceCandidates(...)to filter scope resultscreateReferenceCompletionItem(...)to reshape reference itemscompletionForKeyword(...)orfilterKeyword(...)for keyword logiccompletionForRule(...)for snippets and templatescompletionFor(...)only when you need to replace the dispatch strategy