2017-05-27 04:21:00 +00:00
|
|
|
#include "clang_complete.h"
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-05-20 06:35:14 +00:00
|
|
|
#include "clang_utils.h"
|
2017-05-10 04:00:05 +00:00
|
|
|
#include "platform.h"
|
2017-03-26 21:40:34 +00:00
|
|
|
#include "timer.h"
|
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
#include <loguru.hpp>
|
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
#include <algorithm>
|
2017-04-17 01:22:59 +00:00
|
|
|
#include <thread>
|
2017-03-26 21:40:34 +00:00
|
|
|
|
|
|
|
namespace {
|
2017-06-29 02:50:30 +00:00
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
unsigned Flags() {
|
2017-05-25 02:04:19 +00:00
|
|
|
// TODO: use clang_defaultEditingTranslationUnitOptions()?
|
2017-09-22 01:14:57 +00:00
|
|
|
return CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing |
|
|
|
|
CXTranslationUnit_CacheCompletionResults |
|
|
|
|
CXTranslationUnit_PrecompiledPreamble |
|
2017-09-27 06:03:43 +00:00
|
|
|
CXTranslationUnit_IncludeBriefCommentsInCodeCompletion |
|
|
|
|
CXTranslationUnit_DetailedPreprocessingRecord
|
2017-05-09 05:15:35 +00:00
|
|
|
#if !defined(_WIN32)
|
2017-09-22 01:14:57 +00:00
|
|
|
// For whatever reason, CreatePreambleOnFirstParse causes clang to
|
|
|
|
// become very crashy on windows.
|
|
|
|
// TODO: do more investigation, submit fixes to clang.
|
|
|
|
| CXTranslationUnit_CreatePreambleOnFirstParse
|
2017-05-09 05:15:35 +00:00
|
|
|
#endif
|
2017-09-22 01:14:57 +00:00
|
|
|
;
|
2017-05-10 04:52:15 +00:00
|
|
|
}
|
|
|
|
|
2017-12-06 17:46:24 +00:00
|
|
|
unsigned GetCompletionPriority(const CXCompletionString& str,
|
2017-12-07 19:53:48 +00:00
|
|
|
CXCursorKind result_kind,
|
2018-02-07 13:01:42 +00:00
|
|
|
const optional<std::string>& typedText) {
|
2017-12-06 17:46:24 +00:00
|
|
|
unsigned priority = clang_getCompletionPriority(str);
|
|
|
|
|
|
|
|
// XXX: What happens if priority overflows?
|
2017-05-10 04:52:15 +00:00
|
|
|
if (result_kind == CXCursor_Destructor) {
|
|
|
|
priority *= 100;
|
|
|
|
}
|
|
|
|
if (result_kind == CXCursor_ConversionFunction ||
|
2018-02-07 13:01:42 +00:00
|
|
|
(result_kind == CXCursor_CXXMethod && typedText &&
|
|
|
|
StartsWith(*typedText, "operator"))) {
|
2017-05-10 04:52:15 +00:00
|
|
|
priority *= 100;
|
|
|
|
}
|
|
|
|
if (clang_getCompletionAvailability(str) != CXAvailability_Available) {
|
|
|
|
priority *= 100;
|
|
|
|
}
|
|
|
|
return priority;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2017-06-29 17:30:22 +00:00
|
|
|
/*
|
2017-05-15 07:28:53 +00:00
|
|
|
bool IsCallKind(CXCursorKind kind) {
|
|
|
|
switch (kind) {
|
|
|
|
case CXCursor_ObjCInstanceMethodDecl:
|
|
|
|
case CXCursor_CXXMethod:
|
|
|
|
case CXCursor_FunctionTemplate:
|
|
|
|
case CXCursor_FunctionDecl:
|
|
|
|
case CXCursor_Constructor:
|
|
|
|
case CXCursor_Destructor:
|
|
|
|
case CXCursor_ConversionFunction:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2017-06-29 17:30:22 +00:00
|
|
|
*/
|
2017-05-15 07:28:53 +00:00
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
|
|
|
|
switch (cursor_kind) {
|
2017-09-22 02:45:17 +00:00
|
|
|
case CXCursor_UnexposedDecl:
|
|
|
|
return lsCompletionItemKind::Text;
|
|
|
|
|
2018-03-01 05:49:44 +00:00
|
|
|
case CXCursor_StructDecl:
|
|
|
|
case CXCursor_UnionDecl:
|
|
|
|
return lsCompletionItemKind::Struct;
|
|
|
|
case CXCursor_ClassDecl:
|
|
|
|
return lsCompletionItemKind::Class;
|
|
|
|
case CXCursor_EnumDecl:
|
|
|
|
return lsCompletionItemKind::Enum;
|
|
|
|
case CXCursor_FieldDecl:
|
|
|
|
return lsCompletionItemKind::Field;
|
|
|
|
case CXCursor_EnumConstantDecl:
|
|
|
|
return lsCompletionItemKind::EnumMember;
|
|
|
|
case CXCursor_FunctionDecl:
|
|
|
|
return lsCompletionItemKind::Function;
|
|
|
|
case CXCursor_VarDecl:
|
|
|
|
case CXCursor_ParmDecl:
|
|
|
|
return lsCompletionItemKind::Variable;
|
|
|
|
case CXCursor_ObjCInterfaceDecl:
|
|
|
|
return lsCompletionItemKind::Interface;
|
|
|
|
|
2017-05-15 07:28:53 +00:00
|
|
|
case CXCursor_ObjCInstanceMethodDecl:
|
|
|
|
case CXCursor_CXXMethod:
|
2017-12-15 05:18:43 +00:00
|
|
|
case CXCursor_ObjCClassMethodDecl:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Method;
|
|
|
|
|
|
|
|
case CXCursor_FunctionTemplate:
|
2018-03-20 02:51:42 +00:00
|
|
|
return lsCompletionItemKind::Function;
|
2017-05-15 07:28:53 +00:00
|
|
|
|
|
|
|
case CXCursor_Constructor:
|
|
|
|
case CXCursor_Destructor:
|
|
|
|
case CXCursor_ConversionFunction:
|
|
|
|
return lsCompletionItemKind::Constructor;
|
|
|
|
|
2017-12-15 05:18:43 +00:00
|
|
|
case CXCursor_ObjCIvarDecl:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Variable;
|
|
|
|
|
|
|
|
case CXCursor_ClassTemplate:
|
|
|
|
case CXCursor_ClassTemplatePartialSpecialization:
|
|
|
|
case CXCursor_UsingDeclaration:
|
|
|
|
case CXCursor_TypedefDecl:
|
|
|
|
case CXCursor_TypeAliasDecl:
|
|
|
|
case CXCursor_TypeAliasTemplateDecl:
|
2017-12-15 05:18:43 +00:00
|
|
|
case CXCursor_ObjCCategoryDecl:
|
|
|
|
case CXCursor_ObjCProtocolDecl:
|
|
|
|
case CXCursor_ObjCImplementationDecl:
|
|
|
|
case CXCursor_ObjCCategoryImplDecl:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Class;
|
|
|
|
|
2018-03-01 05:49:44 +00:00
|
|
|
case CXCursor_ObjCPropertyDecl:
|
|
|
|
return lsCompletionItemKind::Property;
|
2017-05-15 07:28:53 +00:00
|
|
|
|
|
|
|
case CXCursor_MacroInstantiation:
|
|
|
|
case CXCursor_MacroDefinition:
|
|
|
|
return lsCompletionItemKind::Interface;
|
|
|
|
|
|
|
|
case CXCursor_Namespace:
|
|
|
|
case CXCursor_NamespaceAlias:
|
|
|
|
case CXCursor_NamespaceRef:
|
|
|
|
return lsCompletionItemKind::Module;
|
|
|
|
|
|
|
|
case CXCursor_MemberRef:
|
|
|
|
case CXCursor_TypeRef:
|
2017-12-15 05:18:43 +00:00
|
|
|
case CXCursor_ObjCSuperClassRef:
|
|
|
|
case CXCursor_ObjCProtocolRef:
|
|
|
|
case CXCursor_ObjCClassRef:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Reference;
|
|
|
|
|
2018-01-11 02:43:01 +00:00
|
|
|
// return lsCompletionItemKind::Unit;
|
|
|
|
// return lsCompletionItemKind::Value;
|
|
|
|
// return lsCompletionItemKind::Keyword;
|
|
|
|
// return lsCompletionItemKind::Snippet;
|
|
|
|
// return lsCompletionItemKind::Color;
|
|
|
|
// return lsCompletionItemKind::File;
|
2017-05-15 07:28:53 +00:00
|
|
|
|
|
|
|
case CXCursor_NotImplemented:
|
2018-01-20 18:06:19 +00:00
|
|
|
case CXCursor_OverloadCandidate:
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Text;
|
|
|
|
|
2018-03-01 05:49:44 +00:00
|
|
|
case CXCursor_TemplateTypeParameter:
|
|
|
|
case CXCursor_TemplateTemplateParameter:
|
|
|
|
return lsCompletionItemKind::TypeParameter;
|
2017-12-30 00:32:43 +00:00
|
|
|
|
2017-05-15 07:28:53 +00:00
|
|
|
default:
|
2017-09-22 02:45:17 +00:00
|
|
|
LOG_S(WARNING) << "Unhandled completion kind " << cursor_kind;
|
2017-05-15 07:28:53 +00:00
|
|
|
return lsCompletionItemKind::Text;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
add detailedLabel completion style
Some completion UI, such as Emacs' completion-at-point and company-lsp,
display completion item label and detail side by side.
This does not look right, when you see things like:
"foo" "int foo()"
"bar" "void bar(int i = 0)"
When this option is enabled, the completion item label is very detailed,
it shows the full signature of the candidate.
The detail just contains the completion item parent context.
Also, in this mode, functions with default arguments,
generates one more item per default argument
so that the right function call can be selected.
That is, you get something like:
"int foo()" "Foo"
"void bar()" "Foo"
"void bar(int i = 0)" "Foo"
Be wary, this is quickly quite verbose,
items can end up truncated by the UIs.
2018-02-03 17:32:55 +00:00
|
|
|
void BuildCompletionItemTexts(std::vector<lsCompletionItem>& out,
|
|
|
|
CXCompletionString completion_string,
|
|
|
|
bool include_snippets) {
|
|
|
|
assert(!out.empty());
|
|
|
|
auto out_first = out.size() - 1;
|
|
|
|
|
|
|
|
std::string result_type;
|
|
|
|
|
|
|
|
int num_chunks = clang_getNumCompletionChunks(completion_string);
|
|
|
|
for (int i = 0; i < num_chunks; ++i) {
|
|
|
|
CXCompletionChunkKind kind =
|
2018-02-22 07:34:32 +00:00
|
|
|
clang_getCompletionChunkKind(completion_string, i);
|
add detailedLabel completion style
Some completion UI, such as Emacs' completion-at-point and company-lsp,
display completion item label and detail side by side.
This does not look right, when you see things like:
"foo" "int foo()"
"bar" "void bar(int i = 0)"
When this option is enabled, the completion item label is very detailed,
it shows the full signature of the candidate.
The detail just contains the completion item parent context.
Also, in this mode, functions with default arguments,
generates one more item per default argument
so that the right function call can be selected.
That is, you get something like:
"int foo()" "Foo"
"void bar()" "Foo"
"void bar(int i = 0)" "Foo"
Be wary, this is quickly quite verbose,
items can end up truncated by the UIs.
2018-02-03 17:32:55 +00:00
|
|
|
|
|
|
|
std::string text;
|
|
|
|
switch (kind) {
|
2018-03-01 05:49:44 +00:00
|
|
|
// clang-format off
|
|
|
|
case CXCompletionChunk_LeftParen: text = '('; break;
|
|
|
|
case CXCompletionChunk_RightParen: text = ')'; break;
|
|
|
|
case CXCompletionChunk_LeftBracket: text = '['; break;
|
|
|
|
case CXCompletionChunk_RightBracket: text = ']'; break;
|
|
|
|
case CXCompletionChunk_LeftBrace: text = '{'; break;
|
|
|
|
case CXCompletionChunk_RightBrace: text = '}'; break;
|
|
|
|
case CXCompletionChunk_LeftAngle: text = '<'; break;
|
|
|
|
case CXCompletionChunk_RightAngle: text = '>'; break;
|
|
|
|
case CXCompletionChunk_Comma: text = ", "; break;
|
|
|
|
case CXCompletionChunk_Colon: text = ':'; break;
|
|
|
|
case CXCompletionChunk_SemiColon: text = ';'; break;
|
|
|
|
case CXCompletionChunk_Equal: text = '='; break;
|
|
|
|
case CXCompletionChunk_HorizontalSpace: text = ' '; break;
|
|
|
|
case CXCompletionChunk_VerticalSpace: text = ' '; break;
|
2018-03-20 02:51:42 +00:00
|
|
|
// clang-format on
|
2018-02-22 07:34:32 +00:00
|
|
|
|
|
|
|
case CXCompletionChunk_ResultType:
|
|
|
|
result_type =
|
|
|
|
ToString(clang_getCompletionChunkText(completion_string, i));
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case CXCompletionChunk_TypedText:
|
|
|
|
case CXCompletionChunk_Placeholder:
|
|
|
|
case CXCompletionChunk_Text:
|
|
|
|
case CXCompletionChunk_Informative:
|
|
|
|
text = ToString(clang_getCompletionChunkText(completion_string, i));
|
|
|
|
|
|
|
|
for (auto i = out_first; i < out.size(); ++i) {
|
|
|
|
// first typed text is used for filtering
|
|
|
|
if (kind == CXCompletionChunk_TypedText && !out[i].filterText)
|
|
|
|
out[i].filterText = text;
|
|
|
|
|
|
|
|
if (kind == CXCompletionChunk_Placeholder)
|
|
|
|
out[i].parameters_.push_back(text);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CXCompletionChunk_CurrentParameter:
|
|
|
|
// We have our own parsing logic for active parameter. This doesn't seem
|
|
|
|
// to be very reliable.
|
|
|
|
continue;
|
|
|
|
|
|
|
|
case CXCompletionChunk_Optional: {
|
|
|
|
CXCompletionString nested =
|
|
|
|
clang_getCompletionChunkCompletionString(completion_string, i);
|
|
|
|
// duplicate last element, the recursive call will complete it
|
|
|
|
out.push_back(out.back());
|
|
|
|
BuildCompletionItemTexts(out, nested, include_snippets);
|
|
|
|
continue;
|
add detailedLabel completion style
Some completion UI, such as Emacs' completion-at-point and company-lsp,
display completion item label and detail side by side.
This does not look right, when you see things like:
"foo" "int foo()"
"bar" "void bar(int i = 0)"
When this option is enabled, the completion item label is very detailed,
it shows the full signature of the candidate.
The detail just contains the completion item parent context.
Also, in this mode, functions with default arguments,
generates one more item per default argument
so that the right function call can be selected.
That is, you get something like:
"int foo()" "Foo"
"void bar()" "Foo"
"void bar(int i = 0)" "Foo"
Be wary, this is quickly quite verbose,
items can end up truncated by the UIs.
2018-02-03 17:32:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto i = out_first; i < out.size(); ++i)
|
|
|
|
out[i].label += text;
|
|
|
|
|
|
|
|
if (kind == CXCompletionChunk_Informative)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (auto i = out_first; i < out.size(); ++i) {
|
|
|
|
if (!include_snippets && !out[i].parameters_.empty())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (kind == CXCompletionChunk_Placeholder) {
|
|
|
|
out[i].insertText +=
|
|
|
|
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
|
|
|
|
out[i].insertTextFormat = lsInsertTextFormat::Snippet;
|
|
|
|
} else {
|
|
|
|
out[i].insertText += text;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result_type.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (auto i = out_first; i < out.size(); ++i) {
|
|
|
|
// ' : ' for variables,
|
|
|
|
// ' -> ' (trailing return type-like) for functions
|
|
|
|
out[i].label += (out[i].label == out[i].filterText ? " : " : " -> ");
|
|
|
|
out[i].label += result_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-23 06:21:32 +00:00
|
|
|
// |do_insert|: if |!do_insert|, do not append strings to |insert| after
|
|
|
|
// a placeholder.
|
2017-09-22 01:14:57 +00:00
|
|
|
void BuildDetailString(CXCompletionString completion_string,
|
|
|
|
std::string& label,
|
|
|
|
std::string& detail,
|
|
|
|
std::string& insert,
|
2018-01-23 06:21:32 +00:00
|
|
|
bool& do_insert,
|
2017-12-05 17:21:54 +00:00
|
|
|
lsInsertTextFormat& format,
|
2017-11-19 12:57:16 +00:00
|
|
|
std::vector<std::string>* parameters,
|
|
|
|
bool include_snippets) {
|
2017-03-26 21:40:34 +00:00
|
|
|
int num_chunks = clang_getNumCompletionChunks(completion_string);
|
2018-02-01 20:21:58 +00:00
|
|
|
auto append = [&](const char* text) {
|
|
|
|
detail += text;
|
|
|
|
if (do_insert)
|
|
|
|
insert += text;
|
|
|
|
};
|
2017-04-16 08:55:14 +00:00
|
|
|
for (int i = 0; i < num_chunks; ++i) {
|
2017-09-22 01:14:57 +00:00
|
|
|
CXCompletionChunkKind kind =
|
|
|
|
clang_getCompletionChunkKind(completion_string, i);
|
2017-03-26 21:40:34 +00:00
|
|
|
|
|
|
|
switch (kind) {
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_Optional: {
|
|
|
|
CXCompletionString nested =
|
|
|
|
clang_getCompletionChunkCompletionString(completion_string, i);
|
2018-01-23 06:21:32 +00:00
|
|
|
BuildDetailString(nested, label, detail, insert, do_insert, format,
|
|
|
|
parameters, include_snippets);
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_Placeholder: {
|
|
|
|
std::string text =
|
2017-11-04 22:29:03 +00:00
|
|
|
ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-09-22 01:14:57 +00:00
|
|
|
parameters->push_back(text);
|
|
|
|
detail += text;
|
2017-11-19 12:57:16 +00:00
|
|
|
// Add parameter declarations as snippets if enabled
|
2017-11-19 22:11:54 +00:00
|
|
|
if (include_snippets) {
|
|
|
|
insert +=
|
|
|
|
"${" + std::to_string(parameters->size()) + ":" + text + "}";
|
2017-12-05 17:21:54 +00:00
|
|
|
format = lsInsertTextFormat::Snippet;
|
2018-01-23 06:21:32 +00:00
|
|
|
} else
|
|
|
|
do_insert = false;
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-05-15 07:28:53 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_CurrentParameter:
|
|
|
|
// We have our own parsing logic for active parameter. This doesn't seem
|
|
|
|
// to be very reliable.
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CXCompletionChunk_TypedText: {
|
|
|
|
std::string text =
|
2017-11-04 22:29:03 +00:00
|
|
|
ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-09-22 01:14:57 +00:00
|
|
|
label = text;
|
|
|
|
detail += text;
|
2018-01-23 06:21:32 +00:00
|
|
|
if (do_insert)
|
|
|
|
insert += text;
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_Text: {
|
|
|
|
std::string text =
|
2017-11-04 22:29:03 +00:00
|
|
|
ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-09-22 01:14:57 +00:00
|
|
|
detail += text;
|
2018-01-23 06:21:32 +00:00
|
|
|
if (do_insert)
|
|
|
|
insert += text;
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-05-15 07:58:29 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_Informative: {
|
2017-11-04 22:29:03 +00:00
|
|
|
detail += ToString(clang_getCompletionChunkText(completion_string, i));
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
|
|
|
}
|
2017-05-15 07:58:29 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_ResultType: {
|
|
|
|
CXString text = clang_getCompletionChunkText(completion_string, i);
|
2017-11-04 22:29:03 +00:00
|
|
|
std::string new_detail = ToString(text) + detail + " ";
|
2017-09-22 01:14:57 +00:00
|
|
|
detail = new_detail;
|
|
|
|
break;
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-02-01 20:21:58 +00:00
|
|
|
// clang-format off
|
|
|
|
case CXCompletionChunk_LeftParen: append("("); break;
|
|
|
|
case CXCompletionChunk_RightParen: append(")"); break;
|
|
|
|
case CXCompletionChunk_LeftBracket: append("["); break;
|
|
|
|
case CXCompletionChunk_RightBracket: append("]"); break;
|
|
|
|
case CXCompletionChunk_LeftBrace: append("{"); break;
|
|
|
|
case CXCompletionChunk_RightBrace: append("}"); break;
|
|
|
|
case CXCompletionChunk_LeftAngle: append("<"); break;
|
|
|
|
case CXCompletionChunk_RightAngle: append(">"); break;
|
|
|
|
case CXCompletionChunk_Comma: append(", "); break;
|
|
|
|
case CXCompletionChunk_Colon: append(":"); break;
|
|
|
|
case CXCompletionChunk_SemiColon: append(";"); break;
|
|
|
|
case CXCompletionChunk_Equal: append("="); break;
|
|
|
|
// clang-format on
|
2017-09-22 01:14:57 +00:00
|
|
|
case CXCompletionChunk_HorizontalSpace:
|
|
|
|
case CXCompletionChunk_VerticalSpace:
|
2018-02-01 20:21:58 +00:00
|
|
|
append(" ");
|
2017-09-22 01:14:57 +00:00
|
|
|
break;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-10-23 05:07:50 +00:00
|
|
|
void TryEnsureDocumentParsed(ClangCompleteManager* manager,
|
|
|
|
std::shared_ptr<CompletionSession> session,
|
2017-11-11 19:41:09 +00:00
|
|
|
std::unique_ptr<ClangTranslationUnit>* tu,
|
|
|
|
ClangIndex* index) {
|
2017-05-26 06:40:38 +00:00
|
|
|
// Nothing to do. We already have a translation unit.
|
|
|
|
if (*tu)
|
2017-05-11 06:25:41 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
std::vector<std::string> args = session->file.args;
|
2017-10-18 08:24:52 +00:00
|
|
|
|
|
|
|
// -fspell-checking enables FixIts for, ie, misspelled types.
|
|
|
|
if (!AnyStartsWith(args, "-fno-spell-checking") &&
|
|
|
|
!AnyStartsWith(args, "-fspell-checking")) {
|
|
|
|
args.push_back("-fspell-checking");
|
|
|
|
}
|
|
|
|
|
2018-01-30 00:27:43 +00:00
|
|
|
WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot(
|
|
|
|
{StripFileType(session->file.filename)});
|
2018-01-06 05:45:20 +00:00
|
|
|
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2017-09-22 02:45:17 +00:00
|
|
|
LOG_S(INFO) << "Creating completion session with arguments "
|
2018-02-21 07:52:49 +00:00
|
|
|
<< StringJoin(args, " ");
|
2017-11-11 19:41:09 +00:00
|
|
|
*tu = ClangTranslationUnit::Create(index, session->file.filename, args,
|
|
|
|
unsaved, Flags());
|
2017-06-10 04:13:16 +00:00
|
|
|
|
|
|
|
// Build diagnostics.
|
2018-03-06 01:18:33 +00:00
|
|
|
if (manager->config_->diagnostics.onParse && *tu) {
|
2017-10-23 05:07:50 +00:00
|
|
|
// If we're emitting diagnostics, do an immediate reparse, otherwise we will
|
2017-10-17 18:43:33 +00:00
|
|
|
// emit stale/bad diagnostics.
|
2017-11-11 19:41:09 +00:00
|
|
|
*tu = ClangTranslationUnit::Reparse(std::move(*tu), unsaved);
|
2017-10-23 15:38:01 +00:00
|
|
|
if (!*tu) {
|
|
|
|
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
|
|
|
<< session->file.filename;
|
|
|
|
return;
|
|
|
|
}
|
2017-10-17 18:43:33 +00:00
|
|
|
|
2017-12-12 05:20:29 +00:00
|
|
|
std::vector<lsDiagnostic> ls_diagnostics;
|
2017-06-10 04:13:16 +00:00
|
|
|
unsigned num_diagnostics = clang_getNumDiagnostics((*tu)->cx_tu);
|
|
|
|
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
2017-07-12 22:02:48 +00:00
|
|
|
optional<lsDiagnostic> diagnostic = BuildAndDisposeDiagnostic(
|
|
|
|
clang_getDiagnostic((*tu)->cx_tu, i), session->file.filename);
|
2017-12-23 23:20:13 +00:00
|
|
|
// Filter messages like "too many errors emitted, stopping now
|
|
|
|
// [-ferror-limit=]" which has line = 0 and got subtracted by 1 after
|
|
|
|
// conversion to lsDiagnostic
|
|
|
|
if (diagnostic && diagnostic->range.start.line >= 0)
|
2017-06-10 04:13:16 +00:00
|
|
|
ls_diagnostics.push_back(*diagnostic);
|
|
|
|
}
|
|
|
|
manager->on_diagnostic_(session->file.filename, ls_diagnostics);
|
|
|
|
}
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void CompletionParseMain(ClangCompleteManager* completion_manager) {
|
2017-05-11 06:25:41 +00:00
|
|
|
while (true) {
|
|
|
|
// Fetching the completion request blocks until we have a request.
|
2017-09-22 01:14:57 +00:00
|
|
|
ClangCompleteManager::ParseRequest request =
|
|
|
|
completion_manager->parse_requests_.Dequeue();
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
// If we don't get a session then that means we don't care about the file
|
|
|
|
// anymore - abandon the request.
|
2017-09-22 01:14:57 +00:00
|
|
|
std::shared_ptr<CompletionSession> session =
|
2017-10-23 07:28:21 +00:00
|
|
|
completion_manager->TryGetSession(request.path,
|
|
|
|
false /*mark_as_completion*/,
|
|
|
|
false /*create_if_needed*/);
|
2017-05-26 06:40:38 +00:00
|
|
|
if (!session)
|
|
|
|
continue;
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
// If we've parsed it more recently than the request time, don't bother
|
|
|
|
// reparsing.
|
|
|
|
if (session->tu_last_parsed_at &&
|
|
|
|
*session->tu_last_parsed_at > request.request_time) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2017-11-11 19:41:09 +00:00
|
|
|
std::unique_ptr<ClangTranslationUnit> parsing;
|
2017-10-23 05:07:50 +00:00
|
|
|
TryEnsureDocumentParsed(completion_manager, session, &parsing,
|
|
|
|
&session->index);
|
2017-05-26 06:40:38 +00:00
|
|
|
|
|
|
|
// Activate new translation unit.
|
2017-09-22 01:14:57 +00:00
|
|
|
// tu_last_parsed_at is only read by this thread, so it doesn't need to be
|
|
|
|
// under the mutex.
|
2017-05-26 06:40:38 +00:00
|
|
|
session->tu_last_parsed_at = std::chrono::high_resolution_clock::now();
|
|
|
|
std::lock_guard<std::mutex> lock(session->tu_lock);
|
|
|
|
session->tu = std::move(parsing);
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void CompletionQueryMain(ClangCompleteManager* completion_manager) {
|
2017-04-17 01:22:59 +00:00
|
|
|
while (true) {
|
2017-05-10 04:52:15 +00:00
|
|
|
// Fetching the completion request blocks until we have a request.
|
2017-09-22 01:14:57 +00:00
|
|
|
std::unique_ptr<ClangCompleteManager::CompletionRequest> request =
|
2018-02-18 17:15:39 +00:00
|
|
|
completion_manager->completion_request_.Dequeue();
|
2018-02-22 07:13:42 +00:00
|
|
|
|
|
|
|
// Drop older requests if we're not buffering.
|
|
|
|
while (completion_manager->config_->completion.dropOldRequests &&
|
|
|
|
!completion_manager->completion_request_.IsEmpty()) {
|
|
|
|
completion_manager->on_dropped_(request->id);
|
|
|
|
request = completion_manager->completion_request_.Dequeue();
|
|
|
|
}
|
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
std::string path = request->document.uri.GetPath();
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
std::shared_ptr<CompletionSession> session =
|
2017-10-23 07:28:21 +00:00
|
|
|
completion_manager->TryGetSession(path, true /*mark_as_completion*/,
|
|
|
|
true /*create_if_needed*/);
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-05-26 06:40:38 +00:00
|
|
|
std::lock_guard<std::mutex> lock(session->tu_lock);
|
2017-10-17 18:43:33 +00:00
|
|
|
Timer timer;
|
2017-10-23 05:07:50 +00:00
|
|
|
TryEnsureDocumentParsed(completion_manager, session, &session->tu,
|
|
|
|
&session->index);
|
|
|
|
timer.ResetAndPrint("[complete] TryEnsureDocumentParsed");
|
|
|
|
|
|
|
|
// It is possible we failed to create the document despite
|
|
|
|
// |TryEnsureDocumentParsed|.
|
|
|
|
if (!session->tu)
|
|
|
|
continue;
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2018-01-12 17:37:33 +00:00
|
|
|
timer.Reset();
|
|
|
|
WorkingFiles::Snapshot snapshot =
|
|
|
|
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
2018-01-06 05:45:20 +00:00
|
|
|
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
2018-01-12 17:37:33 +00:00
|
|
|
timer.ResetAndPrint("[complete] Creating WorkingFile snapshot");
|
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
// Emit code completion data.
|
|
|
|
if (request->position) {
|
|
|
|
// Language server is 0-based, clang is 1-based.
|
|
|
|
unsigned line = request->position->line + 1;
|
|
|
|
unsigned column = request->position->character + 1;
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
timer.Reset();
|
|
|
|
unsigned const kCompleteOptions =
|
|
|
|
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments;
|
|
|
|
CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
|
|
|
|
session->tu->cx_tu, session->file.filename.c_str(), line, column,
|
|
|
|
unsaved.data(), (unsigned)unsaved.size(), kCompleteOptions);
|
2017-09-22 02:45:17 +00:00
|
|
|
timer.ResetAndPrint("[complete] clangCodeCompleteAt");
|
2017-09-22 02:25:33 +00:00
|
|
|
if (!cx_results) {
|
|
|
|
if (request->on_complete)
|
|
|
|
request->on_complete({}, false /*is_cached_result*/);
|
|
|
|
continue;
|
2017-06-29 02:50:30 +00:00
|
|
|
}
|
2017-07-12 18:25:58 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
{
|
|
|
|
if (request->on_complete) {
|
2017-12-12 05:20:29 +00:00
|
|
|
std::vector<lsCompletionItem> ls_result;
|
add detailedLabel completion style
Some completion UI, such as Emacs' completion-at-point and company-lsp,
display completion item label and detail side by side.
This does not look right, when you see things like:
"foo" "int foo()"
"bar" "void bar(int i = 0)"
When this option is enabled, the completion item label is very detailed,
it shows the full signature of the candidate.
The detail just contains the completion item parent context.
Also, in this mode, functions with default arguments,
generates one more item per default argument
so that the right function call can be selected.
That is, you get something like:
"int foo()" "Foo"
"void bar()" "Foo"
"void bar(int i = 0)" "Foo"
Be wary, this is quickly quite verbose,
items can end up truncated by the UIs.
2018-02-03 17:32:55 +00:00
|
|
|
// this is a guess but can be larger in case of optional parameters,
|
|
|
|
// as they may be expanded into multiple items
|
2017-09-22 02:25:33 +00:00
|
|
|
ls_result.reserve(cx_results->NumResults);
|
|
|
|
|
|
|
|
timer.Reset();
|
|
|
|
for (unsigned i = 0; i < cx_results->NumResults; ++i) {
|
|
|
|
CXCompletionResult& result = cx_results->Results[i];
|
|
|
|
|
|
|
|
// TODO: Try to figure out how we can hide base method calls without
|
|
|
|
// also hiding method implementation assistance, ie,
|
|
|
|
//
|
|
|
|
// void Foo::* {
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
|
|
|
|
if (clang_getCompletionAvailability(result.CompletionString) ==
|
|
|
|
CXAvailability_NotAvailable)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// TODO: fill in more data
|
|
|
|
lsCompletionItem ls_completion_item;
|
|
|
|
|
|
|
|
ls_completion_item.kind = GetCompletionKind(result.CursorKind);
|
2017-11-04 22:29:03 +00:00
|
|
|
ls_completion_item.documentation = ToString(
|
2017-09-22 02:25:33 +00:00
|
|
|
clang_getCompletionBriefComment(result.CompletionString));
|
2017-12-06 17:46:24 +00:00
|
|
|
|
add detailedLabel completion style
Some completion UI, such as Emacs' completion-at-point and company-lsp,
display completion item label and detail side by side.
This does not look right, when you see things like:
"foo" "int foo()"
"bar" "void bar(int i = 0)"
When this option is enabled, the completion item label is very detailed,
it shows the full signature of the candidate.
The detail just contains the completion item parent context.
Also, in this mode, functions with default arguments,
generates one more item per default argument
so that the right function call can be selected.
That is, you get something like:
"int foo()" "Foo"
"void bar()" "Foo"
"void bar(int i = 0)" "Foo"
Be wary, this is quickly quite verbose,
items can end up truncated by the UIs.
2018-02-03 17:32:55 +00:00
|
|
|
// label/detail/filterText/insertText/priority
|
|
|
|
if (completion_manager->config_->completion.detailedLabel) {
|
|
|
|
ls_completion_item.detail = ToString(
|
|
|
|
clang_getCompletionParent(result.CompletionString, nullptr));
|
|
|
|
|
|
|
|
auto first_idx = ls_result.size();
|
|
|
|
ls_result.push_back(ls_completion_item);
|
|
|
|
|
|
|
|
// label/filterText/insertText
|
|
|
|
BuildCompletionItemTexts(
|
|
|
|
ls_result, result.CompletionString,
|
|
|
|
completion_manager->config_->client.snippetSupport);
|
|
|
|
|
|
|
|
for (auto i = first_idx; i < ls_result.size(); ++i) {
|
|
|
|
if (completion_manager->config_->client.snippetSupport &&
|
|
|
|
ls_result[i].insertTextFormat ==
|
|
|
|
lsInsertTextFormat::Snippet) {
|
|
|
|
ls_result[i].insertText += "$0";
|
|
|
|
}
|
|
|
|
|
|
|
|
ls_result[i].priority_ = GetCompletionPriority(
|
|
|
|
result.CompletionString, result.CursorKind,
|
|
|
|
ls_result[i].filterText);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
bool do_insert = true;
|
|
|
|
BuildDetailString(
|
|
|
|
result.CompletionString, ls_completion_item.label,
|
|
|
|
ls_completion_item.detail, ls_completion_item.insertText,
|
|
|
|
do_insert, ls_completion_item.insertTextFormat,
|
|
|
|
&ls_completion_item.parameters_,
|
|
|
|
completion_manager->config_->client.snippetSupport);
|
|
|
|
if (completion_manager->config_->client.snippetSupport &&
|
|
|
|
ls_completion_item.insertTextFormat ==
|
|
|
|
lsInsertTextFormat::Snippet) {
|
|
|
|
ls_completion_item.insertText += "$0";
|
|
|
|
}
|
|
|
|
ls_completion_item.priority_ = GetCompletionPriority(
|
|
|
|
result.CompletionString, result.CursorKind,
|
|
|
|
ls_completion_item.label);
|
|
|
|
ls_result.push_back(ls_completion_item);
|
|
|
|
}
|
2017-09-22 02:25:33 +00:00
|
|
|
}
|
2017-12-30 07:15:46 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
timer.ResetAndPrint("[complete] Building " +
|
|
|
|
std::to_string(ls_result.size()) +
|
|
|
|
" completion results");
|
|
|
|
|
|
|
|
request->on_complete(ls_result, false /*is_cached_result*/);
|
2017-07-16 00:25:52 +00:00
|
|
|
}
|
2017-07-12 18:25:58 +00:00
|
|
|
}
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
// Make sure |ls_results| is destroyed before clearing |cx_results|.
|
|
|
|
clang_disposeCodeCompleteResults(cx_results);
|
|
|
|
}
|
2017-05-20 06:35:14 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
// Emit diagnostics.
|
|
|
|
if (request->emit_diagnostics) {
|
2017-09-22 06:48:05 +00:00
|
|
|
// TODO: before emitting diagnostics check if we have another completion
|
|
|
|
// request and think about servicing that first, because it may be much
|
|
|
|
// faster than reparsing the document.
|
2018-01-12 18:01:35 +00:00
|
|
|
// TODO: have a separate thread for diagnostics?
|
2017-09-22 06:48:05 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
timer.Reset();
|
2017-10-23 05:07:50 +00:00
|
|
|
session->tu =
|
2017-11-11 19:41:09 +00:00
|
|
|
ClangTranslationUnit::Reparse(std::move(session->tu), unsaved);
|
2017-09-22 02:25:33 +00:00
|
|
|
timer.ResetAndPrint("[complete] clang_reparseTranslationUnit");
|
2017-10-23 05:07:50 +00:00
|
|
|
if (!session->tu) {
|
|
|
|
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
|
|
|
<< path;
|
|
|
|
continue;
|
|
|
|
}
|
2017-09-22 02:25:33 +00:00
|
|
|
|
|
|
|
size_t num_diagnostics = clang_getNumDiagnostics(session->tu->cx_tu);
|
2017-12-12 05:20:29 +00:00
|
|
|
std::vector<lsDiagnostic> ls_diagnostics;
|
2017-09-22 02:25:33 +00:00
|
|
|
ls_diagnostics.reserve(num_diagnostics);
|
|
|
|
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
|
|
|
CXDiagnostic cx_diag = clang_getDiagnostic(session->tu->cx_tu, i);
|
|
|
|
optional<lsDiagnostic> diagnostic =
|
|
|
|
BuildAndDisposeDiagnostic(cx_diag, path);
|
2017-12-23 23:20:13 +00:00
|
|
|
// Filter messages like "too many errors emitted, stopping now
|
|
|
|
// [-ferror-limit=]" which has line = 0 and got subtracted by 1 after
|
|
|
|
// conversion to lsDiagnostic
|
|
|
|
if (diagnostic && diagnostic->range.start.line >= 0)
|
2017-09-22 02:25:33 +00:00
|
|
|
ls_diagnostics.push_back(*diagnostic);
|
2017-07-16 00:08:07 +00:00
|
|
|
}
|
2017-09-22 02:25:33 +00:00
|
|
|
completion_manager->on_diagnostic_(session->file.filename,
|
|
|
|
ls_diagnostics);
|
2017-09-27 06:03:43 +00:00
|
|
|
|
2017-09-27 20:44:54 +00:00
|
|
|
/*
|
2017-09-27 06:03:43 +00:00
|
|
|
timer.Reset();
|
|
|
|
completion_manager->on_index_(session->tu.get(), unsaved,
|
|
|
|
session->file.filename, session->file.args);
|
|
|
|
timer.ResetAndPrint("[complete] Reindex file");
|
2017-09-27 20:44:54 +00:00
|
|
|
*/
|
2017-07-16 00:08:07 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 01:22:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2017-04-17 01:22:59 +00:00
|
|
|
} // namespace
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
CompletionSession::CompletionSession(const Project::Entry& file,
|
|
|
|
WorkingFiles* working_files)
|
|
|
|
: file(file),
|
|
|
|
working_files(working_files),
|
2017-09-22 02:45:17 +00:00
|
|
|
index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/) {}
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2017-09-22 02:45:17 +00:00
|
|
|
CompletionSession::~CompletionSession() {}
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
ClangCompleteManager::ParseRequest::ParseRequest(const std::string& path)
|
2017-09-22 01:14:57 +00:00
|
|
|
: request_time(std::chrono::high_resolution_clock::now()), path(path) {}
|
|
|
|
|
2018-02-22 07:13:42 +00:00
|
|
|
ClangCompleteManager::CompletionRequest::CompletionRequest(
|
|
|
|
const lsRequestId& id,
|
|
|
|
const lsTextDocumentIdentifier& document,
|
|
|
|
bool emit_diagnostics)
|
|
|
|
: id(id), document(document), emit_diagnostics(emit_diagnostics) {}
|
|
|
|
ClangCompleteManager::CompletionRequest::CompletionRequest(
|
|
|
|
const lsRequestId& id,
|
|
|
|
const lsTextDocumentIdentifier& document,
|
|
|
|
const lsPosition& position,
|
|
|
|
const OnComplete& on_complete,
|
|
|
|
bool emit_diagnostics)
|
|
|
|
: id(id),
|
|
|
|
document(document),
|
|
|
|
position(position),
|
|
|
|
on_complete(on_complete),
|
|
|
|
emit_diagnostics(emit_diagnostics) {}
|
2018-02-18 17:15:39 +00:00
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
ClangCompleteManager::ClangCompleteManager(Config* config,
|
|
|
|
Project* project,
|
|
|
|
WorkingFiles* working_files,
|
2017-09-27 06:03:43 +00:00
|
|
|
OnDiagnostic on_diagnostic,
|
2018-02-22 07:13:42 +00:00
|
|
|
OnIndex on_index,
|
|
|
|
OnDropped on_dropped)
|
2017-09-22 01:14:57 +00:00
|
|
|
: config_(config),
|
|
|
|
project_(project),
|
|
|
|
working_files_(working_files),
|
|
|
|
on_diagnostic_(on_diagnostic),
|
2017-09-27 06:03:43 +00:00
|
|
|
on_index_(on_index),
|
2018-02-22 07:13:42 +00:00
|
|
|
on_dropped_(on_dropped),
|
2017-10-23 07:28:21 +00:00
|
|
|
preloaded_sessions_(kMaxPreloadedSessions),
|
|
|
|
completion_sessions_(kMaxCompletionSessions) {
|
2017-04-17 01:22:59 +00:00
|
|
|
new std::thread([&]() {
|
2017-05-11 06:25:41 +00:00
|
|
|
SetCurrentThreadName("completequery");
|
|
|
|
CompletionQueryMain(this);
|
2017-07-16 00:08:07 +00:00
|
|
|
});
|
|
|
|
|
2017-05-11 06:25:41 +00:00
|
|
|
new std::thread([&]() {
|
|
|
|
SetCurrentThreadName("completeparse");
|
|
|
|
CompletionParseMain(this);
|
2017-04-17 01:22:59 +00:00
|
|
|
});
|
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-06-29 02:50:30 +00:00
|
|
|
ClangCompleteManager::~ClangCompleteManager() {}
|
|
|
|
|
2017-09-22 01:14:57 +00:00
|
|
|
void ClangCompleteManager::CodeComplete(
|
2018-02-22 07:13:42 +00:00
|
|
|
const lsRequestId& id,
|
2017-09-22 01:14:57 +00:00
|
|
|
const lsTextDocumentPositionParams& completion_location,
|
|
|
|
const OnComplete& on_complete) {
|
2018-03-10 23:40:27 +00:00
|
|
|
completion_request_.PushBack(std::make_unique<CompletionRequest>(
|
2018-02-22 07:13:42 +00:00
|
|
|
id, completion_location.textDocument, completion_location.position,
|
2018-02-18 18:07:13 +00:00
|
|
|
on_complete, false));
|
2017-09-22 02:25:33 +00:00
|
|
|
}
|
2017-05-26 06:40:38 +00:00
|
|
|
|
2017-09-22 02:25:33 +00:00
|
|
|
void ClangCompleteManager::DiagnosticsUpdate(
|
2018-02-22 07:13:42 +00:00
|
|
|
const lsRequestId& id,
|
2017-09-22 02:25:33 +00:00
|
|
|
const lsTextDocumentIdentifier& document) {
|
2018-02-22 07:13:42 +00:00
|
|
|
completion_request_.PushBack(
|
2018-03-10 23:40:27 +00:00
|
|
|
std::make_unique<CompletionRequest>(id, document, true));
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void ClangCompleteManager::NotifyView(const std::string& filename) {
|
2017-05-26 06:40:38 +00:00
|
|
|
//
|
|
|
|
// On view, we reparse only if the file has not been parsed. The existence of
|
|
|
|
// a CompletionSession instance implies the file is already parsed or will be
|
|
|
|
// parsed soon.
|
|
|
|
//
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
// Only reparse the file if we create a new CompletionSession.
|
|
|
|
if (EnsureCompletionOrCreatePreloadSession(filename))
|
2018-02-05 06:03:22 +00:00
|
|
|
parse_requests_.PushBack(ParseRequest(filename), true);
|
2017-03-28 01:47:12 +00:00
|
|
|
}
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void ClangCompleteManager::NotifyEdit(const std::string& filename) {
|
2017-05-26 06:40:38 +00:00
|
|
|
//
|
2017-10-23 07:28:21 +00:00
|
|
|
// We treat an edit like a view, because the completion logic will handle
|
|
|
|
// moving the CompletionSession instance from preloaded to completion
|
|
|
|
// storage.
|
2017-05-26 06:40:38 +00:00
|
|
|
//
|
2017-05-10 04:52:15 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
NotifyView(filename);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
|
|
|
|
2017-05-27 04:21:00 +00:00
|
|
|
void ClangCompleteManager::NotifySave(const std::string& filename) {
|
2017-05-26 06:40:38 +00:00
|
|
|
//
|
|
|
|
// On save, always reparse.
|
|
|
|
//
|
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
EnsureCompletionOrCreatePreloadSession(filename);
|
2018-02-05 06:03:22 +00:00
|
|
|
parse_requests_.PushBack(ParseRequest(filename), true);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
|
|
|
|
2017-10-17 18:43:33 +00:00
|
|
|
void ClangCompleteManager::NotifyClose(const std::string& filename) {
|
|
|
|
//
|
|
|
|
// On close, we clear any existing CompletionSession instance.
|
|
|
|
//
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
|
|
|
// Take and drop. It's okay if we don't actually drop the file, it'll
|
|
|
|
// eventually get pushed out of the caches as the user opens other files.
|
2017-12-04 02:23:14 +00:00
|
|
|
auto preloaded_ptr = preloaded_sessions_.TryTake(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
LOG_IF_S(INFO, !!preloaded_ptr)
|
|
|
|
<< "Dropped preloaded-based code completion session for " << filename;
|
2017-12-04 02:23:14 +00:00
|
|
|
auto completion_ptr = completion_sessions_.TryTake(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
LOG_IF_S(INFO, !!completion_ptr)
|
|
|
|
<< "Dropped completion-based code completion session for " << filename;
|
|
|
|
|
|
|
|
// We should never have both a preloaded and completion session.
|
|
|
|
assert((preloaded_ptr && completion_ptr) == false);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ClangCompleteManager::EnsureCompletionOrCreatePreloadSession(
|
|
|
|
const std::string& filename) {
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
2017-10-23 15:38:01 +00:00
|
|
|
|
|
|
|
// Check for an existing CompletionSession.
|
2017-12-04 02:23:14 +00:00
|
|
|
if (preloaded_sessions_.TryGet(filename) ||
|
|
|
|
completion_sessions_.TryGet(filename)) {
|
2017-10-23 15:38:01 +00:00
|
|
|
return false;
|
2017-10-23 15:39:33 +00:00
|
|
|
}
|
2017-10-23 15:38:01 +00:00
|
|
|
|
|
|
|
// No CompletionSession, create new one.
|
|
|
|
auto session = std::make_shared<CompletionSession>(
|
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_);
|
2017-12-04 02:23:14 +00:00
|
|
|
preloaded_sessions_.Insert(session->file.filename, session);
|
2017-10-23 15:38:01 +00:00
|
|
|
return true;
|
2017-10-17 18:43:33 +00:00
|
|
|
}
|
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
std::shared_ptr<CompletionSession> ClangCompleteManager::TryGetSession(
|
2017-09-22 01:14:57 +00:00
|
|
|
const std::string& filename,
|
2017-10-23 07:28:21 +00:00
|
|
|
bool mark_as_completion,
|
2017-09-22 01:14:57 +00:00
|
|
|
bool create_if_needed) {
|
2017-05-26 06:40:38 +00:00
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
// Try to find a preloaded session.
|
|
|
|
std::shared_ptr<CompletionSession> preloaded_session =
|
2017-12-04 02:23:14 +00:00
|
|
|
preloaded_sessions_.TryGet(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
|
|
|
|
if (preloaded_session) {
|
|
|
|
// If this request is for a completion, we should move it to
|
|
|
|
// |completion_sessions|.
|
|
|
|
if (mark_as_completion) {
|
2017-12-04 02:23:14 +00:00
|
|
|
assert(!completion_sessions_.TryGet(filename));
|
|
|
|
preloaded_sessions_.TryTake(filename);
|
|
|
|
completion_sessions_.Insert(filename, preloaded_session);
|
2017-10-23 07:28:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return preloaded_session;
|
2017-10-23 04:49:17 +00:00
|
|
|
}
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
// Try to find a completion session. If none create one.
|
|
|
|
std::shared_ptr<CompletionSession> completion_session =
|
2017-12-04 02:23:14 +00:00
|
|
|
completion_sessions_.TryGet(filename);
|
2017-10-23 07:28:21 +00:00
|
|
|
if (!completion_session && create_if_needed) {
|
|
|
|
completion_session = std::make_shared<CompletionSession>(
|
2017-10-23 04:49:17 +00:00
|
|
|
project_->FindCompilationEntryForFile(filename), working_files_);
|
2017-12-04 02:23:14 +00:00
|
|
|
completion_sessions_.Insert(filename, completion_session);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2017-10-23 07:28:21 +00:00
|
|
|
return completion_session;
|
2017-06-10 01:02:48 +00:00
|
|
|
}
|
2018-03-20 12:33:02 +00:00
|
|
|
|
|
|
|
void ClangCompleteManager::FlushSession(const std::string& filename) {
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
|
|
|
preloaded_sessions_.TryTake(filename);
|
|
|
|
completion_sessions_.TryTake(filename);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClangCompleteManager::FlushAllSessions() {
|
|
|
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
|
|
|
|
|
|
|
preloaded_sessions_.Clear();
|
|
|
|
completion_sessions_.Clear();
|
|
|
|
}
|