SupportedSymbolKinds |= *Params.capabilities.WorkspaceSymbolKinds;
if (Params.capabilities.CompletionItemKinds)
SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
+ SupportsCompletionLabelDetails = Params.capabilities.CompletionLabelDetail;
SupportsCodeAction = Params.capabilities.CodeActionStructure;
SupportsHierarchicalDocumentSymbol =
Params.capabilities.HierarchicalDocumentSymbol;
CompletionItem C = R.render(Opts);
C.kind = adjustKindToCapability(
C.kind, SupportedCompletionItemKinds);
+ if (!SupportsCompletionLabelDetails)
+ removeCompletionLabelDetails(C);
LSPList.items.push_back(std::move(C));
}
return Reply(std::move(LSPList));
SymbolKindBitset SupportedSymbolKinds;
/// The supported completion item kinds of the client.
CompletionItemKindBitset SupportedCompletionItemKinds;
+ // Whether the client supports CompletionItem.labelDetails.
+ bool SupportsCompletionLabelDetails = false;
/// Whether the client supports CodeAction response objects.
bool SupportsCodeAction = false;
/// From capabilities of textDocument/documentSymbol.
CompletionItem CodeCompletion::render(const CodeCompleteOptions &Opts) const {
CompletionItem LSP;
const auto *InsertInclude = Includes.empty() ? nullptr : &Includes[0];
+ // We could move our indicators from label into labelDetails->description.
+ // In VSCode there are rendering issues that prevent these being aligned.
LSP.label = ((InsertInclude && InsertInclude->Insertion)
? Opts.IncludeIndicator.Insert
: Opts.IncludeIndicator.NoInsert) +
(Opts.ShowOrigins ? "[" + llvm::to_string(Origin) + "]" : "") +
- RequiredQualifier + Name + Signature;
+ RequiredQualifier + Name;
+ LSP.labelDetails.emplace();
+ LSP.labelDetails->detail = Signature;
LSP.kind = Kind;
LSP.detail = BundleSize > 1
if (auto *Item = Completion->getObject("completionItem")) {
if (auto SnippetSupport = Item->getBoolean("snippetSupport"))
R.CompletionSnippets = *SnippetSupport;
+ if (auto LabelDetailsSupport = Item->getBoolean("labelDetailsSupport"))
+ R.CompletionLabelDetail = *LabelDetailsSupport;
if (const auto *DocumentationFormat =
Item->getArray("documentationFormat")) {
for (const auto &Format : *DocumentationFormat) {
return false;
}
+llvm::json::Value toJSON(const CompletionItemLabelDetails &CD) {
+ llvm::json::Object Result;
+ if (!CD.detail.empty())
+ Result["detail"] = CD.detail;
+ if (!CD.description.empty())
+ Result["description"] = CD.description;
+ return Result;
+}
+
+void removeCompletionLabelDetails(CompletionItem &C) {
+ if (!C.labelDetails)
+ return;
+ if (!C.labelDetails->detail.empty())
+ C.label += C.labelDetails->detail;
+ if (!C.labelDetails->description.empty())
+ C.label = C.labelDetails->description + C.label;
+ C.labelDetails.reset();
+}
+
llvm::json::Value toJSON(const CompletionItem &CI) {
assert(!CI.label.empty() && "completion item label is required");
llvm::json::Object Result{{"label", CI.label}};
Result["kind"] = static_cast<int>(CI.kind);
if (!CI.detail.empty())
Result["detail"] = CI.detail;
+ if (CI.labelDetails)
+ Result["labelDetails"] = *CI.labelDetails;
if (CI.documentation)
Result["documentation"] = CI.documentation;
if (!CI.sortText.empty())
/// textDocument.completion.completionItem.documentationFormat
MarkupKind CompletionDocumentationFormat = MarkupKind::PlainText;
+ /// The client has support for completion item label details.
+ /// textDocument.completion.completionItem.labelDetailsSupport.
+ bool CompletionLabelDetail = false;
+
/// Client supports CodeAction return value for textDocument/codeAction.
/// textDocument.codeAction.codeActionLiteralSupport.
bool CodeActionStructure = false;
Snippet = 2,
};
+/// Additional details for a completion item label.
+struct CompletionItemLabelDetails {
+ /// An optional string which is rendered less prominently directly after label
+ /// without any spacing. Should be used for function signatures or type
+ /// annotations.
+ std::string detail;
+
+ /// An optional string which is rendered less prominently after
+ /// CompletionItemLabelDetails.detail. Should be used for fully qualified
+ /// names or file path.
+ std::string description;
+};
+llvm::json::Value toJSON(const CompletionItemLabelDetails &);
+
struct CompletionItem {
/// The label of this completion item. By default also the text that is
/// inserted when selecting this completion.
std::string label;
+ /// Additional details for the label.
+ std::optional<CompletionItemLabelDetails> labelDetails;
+
/// The kind of this completion item. Based of the kind an icon is chosen by
/// the editor.
CompletionItemKind kind = CompletionItemKind::Missing;
llvm::json::Value toJSON(const CompletionItem &);
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const CompletionItem &);
+/// Remove the labelDetails field (for clients that don't support it).
+/// Places the information into other fields of the completion item.
+void removeCompletionLabelDetails(CompletionItem &);
+
bool operator<(const CompletionItem &, const CompletionItem &);
/// Represents a collection of completion items to be presented in the editor.
Opts.EnableSnippets = false;
auto R = C.render(Opts);
- EXPECT_EQ(R.label, "Foo::x(bool) const");
+ EXPECT_EQ(R.label, "Foo::x");
+ EXPECT_EQ(R.labelDetails->detail, "(bool) const");
EXPECT_EQ(R.insertText, "Foo::x");
EXPECT_EQ(R.insertTextFormat, InsertTextFormat::PlainText);
EXPECT_EQ(R.filterText, "x");
Include.Insertion.emplace();
R = C.render(Opts);
- EXPECT_EQ(R.label, "^Foo::x(bool) const");
+ EXPECT_EQ(R.label, "^Foo::x");
+ EXPECT_EQ(R.labelDetails->detail, "(bool) const");
EXPECT_THAT(R.additionalTextEdits, Not(IsEmpty()));
Opts.ShowOrigins = true;
R = C.render(Opts);
- EXPECT_EQ(R.label, "^[AS]Foo::x(bool) const");
+ EXPECT_EQ(R.label, "^[AS]Foo::x");
+ EXPECT_EQ(R.labelDetails->detail, "(bool) const");
C.BundleSize = 2;
R = C.render(Opts);