@@ -29,15 +29,16 @@ unsigned Flags() {
2929
3030unsigned GetCompletionPriority (const CXCompletionString& str,
3131 CXCursorKind result_kind,
32- const std::string& label ) {
32+ const std::string& typedText ) {
3333 unsigned priority = clang_getCompletionPriority (str);
3434
3535 // XXX: What happens if priority overflows?
3636 if (result_kind == CXCursor_Destructor) {
3737 priority *= 100 ;
3838 }
3939 if (result_kind == CXCursor_ConversionFunction ||
40- (result_kind == CXCursor_CXXMethod && StartsWith (label, " operator" ))) {
40+ (result_kind == CXCursor_CXXMethod &&
41+ StartsWith (typedText, " operator" ))) {
4142 priority *= 100 ;
4243 }
4344 if (clang_getCompletionAvailability (str) != CXAvailability_Available) {
@@ -149,6 +150,103 @@ lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
149150 }
150151}
151152
153+ void BuildCompletionItemTexts (std::vector<lsCompletionItem>& out,
154+ CXCompletionString completion_string,
155+ bool include_snippets) {
156+ assert (!out.empty ());
157+ auto out_first = out.size () - 1 ;
158+
159+ std::string result_type;
160+
161+ int num_chunks = clang_getNumCompletionChunks (completion_string);
162+ for (int i = 0 ; i < num_chunks; ++i) {
163+ CXCompletionChunkKind kind =
164+ clang_getCompletionChunkKind (completion_string, i);
165+
166+ std::string text;
167+ switch (kind) {
168+ case CXCompletionChunk_LeftParen: text = ' (' ; break ;
169+ case CXCompletionChunk_RightParen: text = ' )' ; break ;
170+ case CXCompletionChunk_LeftBracket: text = ' [' ; break ;
171+ case CXCompletionChunk_RightBracket: text = ' ]' ; break ;
172+ case CXCompletionChunk_LeftBrace: text = ' {' ; break ;
173+ case CXCompletionChunk_RightBrace: text = ' }' ; break ;
174+ case CXCompletionChunk_LeftAngle: text = ' <' ; break ;
175+ case CXCompletionChunk_RightAngle: text = ' >' ; break ;
176+ case CXCompletionChunk_Comma: text = " , " ; break ;
177+ case CXCompletionChunk_Colon: text = ' :' ; break ;
178+ case CXCompletionChunk_SemiColon: text = ' ;' ; break ;
179+ case CXCompletionChunk_Equal: text = ' =' ; break ;
180+ case CXCompletionChunk_HorizontalSpace: text = ' ' ; break ;
181+ case CXCompletionChunk_VerticalSpace: text = ' ' ; break ;
182+
183+ case CXCompletionChunk_ResultType:
184+ result_type =
185+ ToString (clang_getCompletionChunkText (completion_string, i));
186+ continue ;
187+
188+ case CXCompletionChunk_TypedText:
189+ case CXCompletionChunk_Placeholder:
190+ case CXCompletionChunk_Text:
191+ case CXCompletionChunk_Informative:
192+ text = ToString (clang_getCompletionChunkText (completion_string, i));
193+
194+ for (auto i = out_first; i < out.size (); ++i) {
195+ // first typed text is used for filtering
196+ if (kind == CXCompletionChunk_TypedText && out[i].filterText .empty ())
197+ out[i].filterText = text;
198+
199+ if (kind == CXCompletionChunk_Placeholder)
200+ out[i].parameters_ .push_back (text);
201+ }
202+ break ;
203+
204+ case CXCompletionChunk_CurrentParameter:
205+ // We have our own parsing logic for active parameter. This doesn't seem
206+ // to be very reliable.
207+ continue ;
208+
209+ case CXCompletionChunk_Optional: {
210+ CXCompletionString nested =
211+ clang_getCompletionChunkCompletionString (completion_string, i);
212+ // duplicate last element, the recursive call will complete it
213+ out.push_back (out.back ());
214+ BuildCompletionItemTexts (out, nested, include_snippets);
215+ continue ;
216+ }
217+ }
218+
219+ for (auto i = out_first; i < out.size (); ++i)
220+ out[i].label += text;
221+
222+ if (kind == CXCompletionChunk_Informative)
223+ continue ;
224+
225+ for (auto i = out_first; i < out.size (); ++i) {
226+ if (!include_snippets && !out[i].parameters_ .empty ())
227+ continue ;
228+
229+ if (kind == CXCompletionChunk_Placeholder) {
230+ out[i].insertText +=
231+ " ${" + std::to_string (out[i].parameters_ .size ()) + " :" + text + " }" ;
232+ out[i].insertTextFormat = lsInsertTextFormat::Snippet;
233+ } else {
234+ out[i].insertText += text;
235+ }
236+ }
237+ }
238+
239+ if (result_type.empty ())
240+ return ;
241+
242+ for (auto i = out_first; i < out.size (); ++i) {
243+ // ' : ' for variables,
244+ // ' -> ' (trailing return type-like) for functions
245+ out[i].label += (out[i].label == out[i].filterText ? " : " : " -> " );
246+ out[i].label += result_type;
247+ }
248+ }
249+
152250// |do_insert|: if |!do_insert|, do not append strings to |insert| after
153251// a placeholder.
154252void BuildDetailString (CXCompletionString completion_string,
@@ -387,6 +485,8 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
387485 {
388486 if (request->on_complete ) {
389487 std::vector<lsCompletionItem> ls_result;
488+ // this is a guess but can be larger in case of optional parameters,
489+ // as they may be expanded into multiple items
390490 ls_result.reserve (cx_results->NumResults );
391491
392492 timer.Reset ();
@@ -407,29 +507,52 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
407507 // TODO: fill in more data
408508 lsCompletionItem ls_completion_item;
409509
410- bool do_insert = true ;
411- // kind/label/detail/docs/sortText
412510 ls_completion_item.kind = GetCompletionKind (result.CursorKind );
413- BuildDetailString (
414- result.CompletionString , ls_completion_item.label ,
415- ls_completion_item.detail , ls_completion_item.insertText ,
416- do_insert, ls_completion_item.insertTextFormat ,
417- &ls_completion_item.parameters_ ,
418- completion_manager->config_ ->client .snippetSupport );
419- if (completion_manager->config_ ->client .snippetSupport &&
420- ls_completion_item.insertTextFormat ==
421- lsInsertTextFormat::Snippet) {
422- ls_completion_item.insertText += " $0" ;
423- }
424-
425511 ls_completion_item.documentation = ToString (
426512 clang_getCompletionBriefComment (result.CompletionString ));
427513
428- ls_completion_item.priority_ = GetCompletionPriority (
429- result.CompletionString , result.CursorKind ,
430- ls_completion_item.label );
431-
432- ls_result.push_back (ls_completion_item);
514+ // label/detail/filterText/insertText/priority
515+ if (completion_manager->config_ ->completion .detailedLabel ) {
516+ ls_completion_item.detail = ToString (
517+ clang_getCompletionParent (result.CompletionString , nullptr ));
518+
519+ auto first_idx = ls_result.size ();
520+ ls_result.push_back (ls_completion_item);
521+
522+ // label/filterText/insertText
523+ BuildCompletionItemTexts (
524+ ls_result, result.CompletionString ,
525+ completion_manager->config_ ->client .snippetSupport );
526+
527+ for (auto i = first_idx; i < ls_result.size (); ++i) {
528+ if (completion_manager->config_ ->client .snippetSupport &&
529+ ls_result[i].insertTextFormat ==
530+ lsInsertTextFormat::Snippet) {
531+ ls_result[i].insertText += " $0" ;
532+ }
533+
534+ ls_result[i].priority_ = GetCompletionPriority (
535+ result.CompletionString , result.CursorKind ,
536+ ls_result[i].filterText );
537+ }
538+ } else {
539+ bool do_insert = true ;
540+ BuildDetailString (
541+ result.CompletionString , ls_completion_item.label ,
542+ ls_completion_item.detail , ls_completion_item.insertText ,
543+ do_insert, ls_completion_item.insertTextFormat ,
544+ &ls_completion_item.parameters_ ,
545+ completion_manager->config_ ->client .snippetSupport );
546+ if (completion_manager->config_ ->client .snippetSupport &&
547+ ls_completion_item.insertTextFormat ==
548+ lsInsertTextFormat::Snippet) {
549+ ls_completion_item.insertText += " $0" ;
550+ }
551+ ls_completion_item.priority_ = GetCompletionPriority (
552+ result.CompletionString , result.CursorKind ,
553+ ls_completion_item.label );
554+ ls_result.push_back (ls_completion_item);
555+ }
433556 }
434557
435558 timer.ResetAndPrint (" [complete] Building " +
0 commit comments