@@ -53,8 +53,8 @@ auto completion_kind(const clang::NamedDecl* decl) -> protocol::CompletionItemKi
5353 return protocol::CompletionItemKind::Module;
5454 }
5555
56- if (llvm::isa<clang::FunctionDecl, clang::FunctionTemplateDecl >(decl)) {
57- return protocol::CompletionItemKind::Function ;
56+ if (llvm::isa<clang::CXXConstructorDecl >(decl)) {
57+ return protocol::CompletionItemKind::Constructor ;
5858 }
5959
6060 if (llvm::isa<clang::CXXMethodDecl,
@@ -64,8 +64,8 @@ auto completion_kind(const clang::NamedDecl* decl) -> protocol::CompletionItemKi
6464 return protocol::CompletionItemKind::Method;
6565 }
6666
67- if (llvm::isa<clang::CXXConstructorDecl >(decl)) {
68- return protocol::CompletionItemKind::Constructor ;
67+ if (llvm::isa<clang::FunctionDecl, clang::FunctionTemplateDecl >(decl)) {
68+ return protocol::CompletionItemKind::Function ;
6969 }
7070
7171 if (llvm::isa<clang::FieldDecl, clang::IndirectFieldDecl>(decl)) {
@@ -109,6 +109,65 @@ auto completion_kind(const clang::NamedDecl* decl) -> protocol::CompletionItemKi
109109 return protocol::CompletionItemKind::Text;
110110}
111111
112+ // / Extract the function signature (parameter list) from a CodeCompletionString.
113+ // / Returns something like "(int x, float y)" for display in labelDetails.detail.
114+ auto extract_signature (const clang::CodeCompletionString& ccs) -> std::string {
115+ std::string signature;
116+ bool in_parens = false ;
117+
118+ for (const auto & chunk: ccs) {
119+ using CK = clang::CodeCompletionString::ChunkKind;
120+ switch (chunk.Kind ) {
121+ case CK::CK_LeftParen:
122+ in_parens = true ;
123+ signature += ' (' ;
124+ break ;
125+ case CK::CK_RightParen:
126+ signature += ' )' ;
127+ in_parens = false ;
128+ break ;
129+ case CK::CK_Placeholder:
130+ case CK::CK_CurrentParameter:
131+ if (in_parens && chunk.Text ) {
132+ signature += chunk.Text ;
133+ }
134+ break ;
135+ case CK::CK_Text:
136+ case CK::CK_Informative:
137+ if (in_parens && chunk.Text ) {
138+ signature += chunk.Text ;
139+ }
140+ break ;
141+ case CK::CK_LeftAngle:
142+ signature += ' <' ;
143+ in_parens = true ;
144+ break ;
145+ case CK::CK_RightAngle:
146+ signature += ' >' ;
147+ in_parens = false ;
148+ break ;
149+ case CK::CK_Comma:
150+ if (in_parens) {
151+ signature += " , " ;
152+ }
153+ break ;
154+ default : break ;
155+ }
156+ }
157+
158+ return signature;
159+ }
160+
161+ // / Extract the return type from a CodeCompletionString.
162+ auto extract_return_type (const clang::CodeCompletionString& ccs) -> std::string {
163+ for (const auto & chunk: ccs) {
164+ if (chunk.Kind == clang::CodeCompletionString::CK_ResultType && chunk.Text ) {
165+ return chunk.Text ;
166+ }
167+ }
168+ return {};
169+ }
170+
112171struct OverloadItem {
113172 protocol::CompletionItem item;
114173 float score = 0 .0F ;
@@ -159,6 +218,8 @@ class CodeCompletionCollector final : public clang::CodeCompleteConsumer {
159218 overloads.reserve (candidate_count);
160219 std::unordered_map<std::string, std::size_t > overload_index;
161220
221+ bool prefix_starts_with_underscore = prefix.spelling .starts_with (" _" );
222+
162223 auto build_item =
163224 [&](llvm::StringRef label, protocol::CompletionItemKind kind, llvm::StringRef insert) {
164225 protocol::CompletionItem item{
@@ -177,11 +238,18 @@ class CodeCompletionCollector final : public clang::CodeCompleteConsumer {
177238 auto try_add = [&](llvm::StringRef label,
178239 protocol::CompletionItemKind kind,
179240 llvm::StringRef insert_text,
180- llvm::StringRef overload_key) {
241+ llvm::StringRef overload_key,
242+ llvm::StringRef signature = {},
243+ llvm::StringRef return_type = {}) {
181244 if (label.empty ()) {
182245 return ;
183246 }
184247
248+ // Filter out _/__ prefixed internal symbols unless user typed _.
249+ if (!prefix_starts_with_underscore && label.starts_with (" _" )) {
250+ return ;
251+ }
252+
185253 auto score = matcher.match (label);
186254 if (!score.has_value ()) {
187255 return ;
@@ -193,6 +261,16 @@ class CodeCompletionCollector final : public clang::CodeCompleteConsumer {
193261 if (inserted) {
194262 auto item = build_item (label, kind, insert_text);
195263 item.sort_text = std::format (" {}" , *score);
264+ if (!signature.empty () || !return_type.empty ()) {
265+ protocol::CompletionItemLabelDetails details;
266+ if (!signature.empty ()) {
267+ details.detail = signature.str ();
268+ }
269+ if (!return_type.empty ()) {
270+ details.description = return_type.str ();
271+ }
272+ item.label_details = std::move (details);
273+ }
196274 overloads.push_back ({
197275 .item = std::move (item),
198276 .score = *score,
@@ -211,6 +289,16 @@ class CodeCompletionCollector final : public clang::CodeCompleteConsumer {
211289
212290 auto item = build_item (label, kind, insert_text);
213291 item.sort_text = std::format (" {}" , *score);
292+ if (!signature.empty () || !return_type.empty ()) {
293+ protocol::CompletionItemLabelDetails details;
294+ if (!signature.empty ()) {
295+ details.detail = signature.str ();
296+ }
297+ if (!return_type.empty ()) {
298+ details.description = return_type.str ();
299+ }
300+ item.label_details = std::move (details);
301+ }
214302 collected.push_back (std::move (item));
215303 };
216304
@@ -246,24 +334,77 @@ class CodeCompletionCollector final : public clang::CodeCompleteConsumer {
246334 auto kind = completion_kind (declaration);
247335
248336 llvm::SmallString<256 > qualified_name;
249- if (options.bundle_overloads && kind == protocol::CompletionItemKind::Function) {
337+ bool is_callable = kind == protocol::CompletionItemKind::Function ||
338+ kind == protocol::CompletionItemKind::Method ||
339+ kind == protocol::CompletionItemKind::Constructor;
340+ if (options.bundle_overloads && is_callable) {
250341 llvm::raw_svector_ostream stream (qualified_name);
251342 declaration->printQualifiedName (stream);
252343 }
253344
254- try_add (label, kind, label, qualified_name.str ());
345+ std::string signature;
346+ std::string return_type;
347+ auto * ccs =
348+ candidate.CreateCodeCompletionString (sema,
349+ context,
350+ getAllocator (),
351+ getCodeCompletionTUInfo (),
352+ /* IncludeBriefComments=*/ false );
353+ if (ccs) {
354+ signature = extract_signature (*ccs);
355+ return_type = extract_return_type (*ccs);
356+ }
357+
358+ try_add (label, kind, label, qualified_name.str (), signature, return_type);
255359 break ;
256360 }
257361 }
258362 }
259363
260364 for (auto & entry: overloads) {
261365 if (entry.count > 1 ) {
262- entry.item .detail = " (...)" ;
366+ protocol::CompletionItemLabelDetails details;
367+ details.detail = std::format (" (…) +{} overloads" , entry.count );
368+ entry.item .label_details = std::move (details);
263369 }
264370 collected.push_back (std::move (entry.item ));
265371 }
266372
373+ // In bundle mode, deduplicate by label: when the same name appears as
374+ // both a class and its constructors/deduction guides, keep only the
375+ // highest-priority kind (Class > Function/Method > others).
376+ if (options.bundle_overloads ) {
377+ auto kind_priority = [](protocol::CompletionItemKind k) -> int {
378+ switch (k) {
379+ case protocol::CompletionItemKind::Class:
380+ case protocol::CompletionItemKind::Struct: return 3 ;
381+ case protocol::CompletionItemKind::Function:
382+ case protocol::CompletionItemKind::Method: return 2 ;
383+ case protocol::CompletionItemKind::Constructor: return 1 ;
384+ default : return 0 ;
385+ }
386+ };
387+
388+ std::unordered_map<std::string, std::size_t > label_index;
389+ std::vector<protocol::CompletionItem> deduped;
390+ deduped.reserve (collected.size ());
391+
392+ for (auto & item: collected) {
393+ auto [it, inserted] = label_index.try_emplace (item.label , deduped.size ());
394+ if (inserted) {
395+ deduped.push_back (std::move (item));
396+ } else {
397+ auto & existing = deduped[it->second ];
398+ int old_prio = existing.kind .has_value () ? kind_priority (*existing.kind ) : 0 ;
399+ int new_prio = item.kind .has_value () ? kind_priority (*item.kind ) : 0 ;
400+ if (new_prio > old_prio) {
401+ existing = std::move (item);
402+ }
403+ }
404+ }
405+ collected.swap (deduped);
406+ }
407+
267408 output.clear ();
268409 output.swap (collected);
269410 }
0 commit comments