assert(ASTCtx);
Completion.Origin |= SymbolOrigin::AST;
Completion.Name = std::string(llvm::StringRef(SemaCCS->getTypedText()));
+ Completion.FilterText = SemaCCS->getAllTypedText();
if (Completion.Scope.empty()) {
if ((C.SemaResult->Kind == CodeCompletionResult::RK_Declaration) ||
(C.SemaResult->Kind == CodeCompletionResult::RK_Pattern))
Completion.Kind = toCompletionItemKind(C.IndexResult->SymInfo.Kind);
if (Completion.Name.empty())
Completion.Name = std::string(C.IndexResult->Name);
+ if (Completion.FilterText.empty())
+ Completion.FilterText = Completion.Name;
// If the completion was visible to Sema, no qualifier is needed. This
// avoids unneeded qualifiers in cases like with `using ns::X`.
if (Completion.RequiredQualifier.empty() && !C.SemaResult) {
Completion.Origin |= SymbolOrigin::Identifier;
Completion.Kind = CompletionItemKind::Text;
Completion.Name = std::string(C.IdentifierResult->Name);
+ Completion.FilterText = Completion.Name;
}
// Turn absolute path into a literal string that can be #included.
return Result.Pattern->getTypedText();
}
auto *CCS = codeCompletionString(Result);
- return CCS->getTypedText();
+ const CodeCompletionString::Chunk *OnlyText = nullptr;
+ for (auto &C : *CCS) {
+ if (C.Kind != CodeCompletionString::CK_TypedText)
+ continue;
+ if (OnlyText)
+ return CCAllocator->CopyString(CCS->getAllTypedText());
+ OnlyText = &C;
+ }
+ return OnlyText ? OnlyText->Text : llvm::StringRef();
}
// Build a CodeCompletion string for R, which must be from Results.
continue;
CodeCompletion Item;
Item.Name = Name.str() + "=";
+ Item.FilterText = Item.Name;
Item.Kind = CompletionItemKind::Text;
Result.Completions.push_back(Item);
}
Doc.append(*Documentation);
LSP.documentation = renderDoc(Doc, Opts.DocumentationFormat);
}
- LSP.sortText = sortText(Score.Total, Name);
- LSP.filterText = Name;
+ LSP.sortText = sortText(Score.Total, FilterText);
+ LSP.filterText = FilterText;
LSP.textEdit = {CompletionTokenRange, RequiredQualifier + Name};
// Merge continuous additionalTextEdits into main edit. The main motivation
// behind this is to help LSP clients, it seems most of them are confused when
struct CodeCompletion {
// The unqualified name of the symbol or other completion item.
std::string Name;
+ // The name of the symbol for filtering and sorting purposes. Typically the
+ // same as `Name`, but may be different e.g. for ObjC methods, `Name` is the
+ // first selector fragment but the `FilterText` is the entire selector.
+ std::string FilterText;
// The scope qualifier for the symbol name. e.g. "ns1::ns2::"
// Empty for non-symbol completions. Not inserted, but may be displayed.
std::string Scope;
MATCHER_P(nameStartsWith, Prefix, "") {
return llvm::StringRef(arg.Name).startswith(Prefix);
}
+MATCHER_P(filterText, F, "") { return arg.FilterText == F; }
MATCHER_P(scope, S, "") { return arg.Scope == S; }
MATCHER_P(qualifier, Q, "") { return arg.RequiredQualifier == Q; }
MATCHER_P(labeled, Label, "") {
TEST(CompletionTest, Render) {
CodeCompletion C;
C.Name = "x";
+ C.FilterText = "x";
C.Signature = "(bool) const";
C.SnippetSuffix = "(${0:bool})";
C.ReturnType = "int";
EXPECT_FALSE(R.deprecated);
EXPECT_EQ(R.score, .5f);
+ C.FilterText = "xtra";
+ R = C.render(Opts);
+ EXPECT_EQ(R.filterText, "xtra");
+ EXPECT_EQ(R.sortText, sortText(1.0, "xtra"));
+
Opts.EnableSnippets = true;
R = C.render(Opts);
EXPECT_EQ(R.insertText, "Foo::x(${0:bool})");
EXPECT_THAT(C, ElementsAre(snippetSuffix("${1:(unsigned int)}")));
}
+TEST(CompletionTest, ObjectiveCMethodFilterOnEntireSelector) {
+ auto Results = completions(R"objc(
+ @interface Foo
+ + (id)player:(id)player willRun:(id)run;
+ @end
+ id val = [Foo wi^]
+ )objc",
+ /*IndexSymbols=*/{},
+ /*Opts=*/{}, "Foo.m");
+
+ auto C = Results.Completions;
+ EXPECT_THAT(C, ElementsAre(named("player:")));
+ EXPECT_THAT(C, ElementsAre(filterText("player:willRun:")));
+ EXPECT_THAT(C, ElementsAre(kind(CompletionItemKind::Method)));
+ EXPECT_THAT(C, ElementsAre(returnType("id")));
+ EXPECT_THAT(C, ElementsAre(signature("(id) willRun:(id)")));
+ EXPECT_THAT(C, ElementsAre(snippetSuffix("${1:(id)} willRun:${2:(id)}")));
+}
+
TEST(CompletionTest, ObjectiveCSimpleMethodDeclaration) {
auto Results = completions(R"objc(
@interface Foo
return begin()[I];
}
- /// Returns the text in the TypedText chunk.
+ /// Returns the text in the first TypedText chunk.
const char *getTypedText() const;
+ /// Returns the combined text from all TypedText chunks.
+ std::string getAllTypedText() const;
+
/// Retrieve the priority of this code completion result.
unsigned getPriority() const { return Priority; }
return nullptr;
}
+std::string CodeCompletionString::getAllTypedText() const {
+ std::string Res;
+ for (const Chunk &C : *this)
+ if (C.Kind == CK_TypedText)
+ Res += C.Text;
+
+ return Res;
+}
+
const char *CodeCompletionAllocator::CopyString(const Twine &String) {
SmallString<128> Data;
StringRef Ref = String.toStringRef(Data);