Function ide_completion::completions
source · pub fn completions(
db: &RootDatabase,
config: &CompletionConfig,
position: FilePosition,
trigger_character: Option<char>,
) -> Option<Vec<CompletionItem>>
Expand description
Main entry point for completion. We run completion as a two-phase process.
First, we look at the position and collect a so-called CompletionContext
.
This is a somewhat messy process, because, during completion, syntax tree is
incomplete and can look really weird.
Once the context is collected, we run a series of completion routines which look at the context and produce completion items. One subtlety about this phase is that completion engine should not filter by the substring which is already present, it should give all possible variants for the identifier at the caret. In other words, for
fn f() {
let foo = 92;
let _ = bar$0
}
foo
should be present among the completion variants. Filtering by
identifier prefix/fuzzy match should be done higher in the stack, together
with ordering of completions (currently this is done by the client).
§Speculative Completion Problem
There’s a curious unsolved problem in the current implementation. Often, you want to compute completions on a slightly different text document.
In the simplest case, when the code looks like let x =
, you want to
insert a fake identifier to get a better syntax tree: let x = complete_me
.
We do this in CompletionContext
, and it works OK-enough for syntax
analysis. However, we might want to, eg, ask for the type of complete_me
variable, and that’s where our current infrastructure breaks down. salsa
doesn’t allow such “phantom” inputs.
Another case where this would be instrumental is macro expansion. We want to
insert a fake ident and re-expand code. There’s expand_speculative
as a
workaround for this.
A different use-case is completion of injection (examples and links in doc comments). When computing completion for a path in a doc-comment, you want to inject a fake path expression into the item being documented and complete that.
IntelliJ has CodeFragment/Context infrastructure for that. You can create a
temporary PSI node, and say that the context (“parent”) of this node is some
existing node. Asking for, eg, type of this CodeFragment
node works
correctly, as the underlying infrastructure makes use of contexts to do
analysis.