mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-23 16:15:07 +00:00
Revamp completion and signatureHelp, set completion.detailedLabel: true and add completion.duplicateOptional
This commit is contained in:
parent
7149851ea2
commit
11890fc3b1
@ -11,6 +11,7 @@
|
|||||||
#include <clang/Frontend/CompilerInstance.h>
|
#include <clang/Frontend/CompilerInstance.h>
|
||||||
#include <clang/Lex/PreprocessorOptions.h>
|
#include <clang/Lex/PreprocessorOptions.h>
|
||||||
#include <clang/Sema/CodeCompleteConsumer.h>
|
#include <clang/Sema/CodeCompleteConsumer.h>
|
||||||
|
#include <clang/Sema/Sema.h>
|
||||||
#include <llvm/ADT/Twine.h>
|
#include <llvm/ADT/Twine.h>
|
||||||
#include <llvm/Config/llvm-config.h>
|
#include <llvm/Config/llvm-config.h>
|
||||||
#include <llvm/Support/CrashRecoveryContext.h>
|
#include <llvm/Support/CrashRecoveryContext.h>
|
||||||
@ -30,268 +31,6 @@ std::string StripFileType(const std::string &path) {
|
|||||||
return Ret.str();
|
return Ret.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned GetCompletionPriority(const CodeCompletionString &CCS,
|
|
||||||
CXCursorKind result_kind,
|
|
||||||
const std::optional<std::string> &typedText) {
|
|
||||||
unsigned priority = CCS.getPriority();
|
|
||||||
if (CCS.getAvailability() != CXAvailability_Available ||
|
|
||||||
result_kind == CXCursor_Destructor ||
|
|
||||||
result_kind == CXCursor_ConversionFunction ||
|
|
||||||
(result_kind == CXCursor_CXXMethod && typedText &&
|
|
||||||
StartsWith(*typedText, "operator")))
|
|
||||||
priority *= 100;
|
|
||||||
return priority;
|
|
||||||
}
|
|
||||||
|
|
||||||
lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
|
|
||||||
switch (cursor_kind) {
|
|
||||||
case CXCursor_UnexposedDecl:
|
|
||||||
return lsCompletionItemKind::Text;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
case CXCursor_ObjCInstanceMethodDecl:
|
|
||||||
case CXCursor_CXXMethod:
|
|
||||||
case CXCursor_ObjCClassMethodDecl:
|
|
||||||
return lsCompletionItemKind::Method;
|
|
||||||
|
|
||||||
case CXCursor_FunctionTemplate:
|
|
||||||
return lsCompletionItemKind::Function;
|
|
||||||
|
|
||||||
case CXCursor_Constructor:
|
|
||||||
case CXCursor_Destructor:
|
|
||||||
case CXCursor_ConversionFunction:
|
|
||||||
return lsCompletionItemKind::Constructor;
|
|
||||||
|
|
||||||
case CXCursor_ObjCIvarDecl:
|
|
||||||
return lsCompletionItemKind::Variable;
|
|
||||||
|
|
||||||
case CXCursor_ClassTemplate:
|
|
||||||
case CXCursor_ClassTemplatePartialSpecialization:
|
|
||||||
case CXCursor_UsingDeclaration:
|
|
||||||
case CXCursor_TypedefDecl:
|
|
||||||
case CXCursor_TypeAliasDecl:
|
|
||||||
case CXCursor_TypeAliasTemplateDecl:
|
|
||||||
case CXCursor_ObjCCategoryDecl:
|
|
||||||
case CXCursor_ObjCProtocolDecl:
|
|
||||||
case CXCursor_ObjCImplementationDecl:
|
|
||||||
case CXCursor_ObjCCategoryImplDecl:
|
|
||||||
return lsCompletionItemKind::Class;
|
|
||||||
|
|
||||||
case CXCursor_ObjCPropertyDecl:
|
|
||||||
return lsCompletionItemKind::Property;
|
|
||||||
|
|
||||||
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:
|
|
||||||
case CXCursor_ObjCSuperClassRef:
|
|
||||||
case CXCursor_ObjCProtocolRef:
|
|
||||||
case CXCursor_ObjCClassRef:
|
|
||||||
return lsCompletionItemKind::Reference;
|
|
||||||
|
|
||||||
// return lsCompletionItemKind::Unit;
|
|
||||||
// return lsCompletionItemKind::Value;
|
|
||||||
// return lsCompletionItemKind::Keyword;
|
|
||||||
// return lsCompletionItemKind::Snippet;
|
|
||||||
// return lsCompletionItemKind::Color;
|
|
||||||
// return lsCompletionItemKind::File;
|
|
||||||
|
|
||||||
case CXCursor_NotImplemented:
|
|
||||||
case CXCursor_OverloadCandidate:
|
|
||||||
return lsCompletionItemKind::Text;
|
|
||||||
|
|
||||||
case CXCursor_TemplateTypeParameter:
|
|
||||||
case CXCursor_TemplateTemplateParameter:
|
|
||||||
return lsCompletionItemKind::TypeParameter;
|
|
||||||
|
|
||||||
default:
|
|
||||||
LOG_S(WARNING) << "Unhandled completion kind " << cursor_kind;
|
|
||||||
return lsCompletionItemKind::Text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BuildCompletionItemTexts(std::vector<lsCompletionItem> &out,
|
|
||||||
CodeCompletionString &CCS,
|
|
||||||
bool include_snippets) {
|
|
||||||
assert(!out.empty());
|
|
||||||
auto out_first = out.size() - 1;
|
|
||||||
|
|
||||||
std::string result_type;
|
|
||||||
|
|
||||||
for (unsigned i = 0, num_chunks = CCS.size(); i < num_chunks; ++i) {
|
|
||||||
const CodeCompletionString::Chunk &Chunk = CCS[i];
|
|
||||||
CodeCompletionString::ChunkKind Kind = Chunk.Kind;
|
|
||||||
std::string text;
|
|
||||||
switch (Kind) {
|
|
||||||
case CodeCompletionString::CK_TypedText:
|
|
||||||
case CodeCompletionString::CK_Text:
|
|
||||||
case CodeCompletionString::CK_Placeholder:
|
|
||||||
case CodeCompletionString::CK_Informative:
|
|
||||||
if (Chunk.Text)
|
|
||||||
text = Chunk.Text;
|
|
||||||
for (auto i = out_first; i < out.size(); i++) {
|
|
||||||
// first TypedText is used for filtering
|
|
||||||
if (Kind == CodeCompletionString::CK_TypedText && !out[i].filterText)
|
|
||||||
out[i].filterText = text;
|
|
||||||
if (Kind == CodeCompletionString::CK_Placeholder)
|
|
||||||
out[i].parameters_.push_back(text);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CodeCompletionString::CK_ResultType:
|
|
||||||
if (Chunk.Text)
|
|
||||||
result_type = Chunk.Text;
|
|
||||||
continue;
|
|
||||||
case CodeCompletionString::CK_CurrentParameter:
|
|
||||||
// We have our own parsing logic for active parameter. This doesn't seem
|
|
||||||
// to be very reliable.
|
|
||||||
continue;
|
|
||||||
case CodeCompletionString::CK_Optional: {
|
|
||||||
// duplicate last element, the recursive call will complete it
|
|
||||||
out.push_back(out.back());
|
|
||||||
BuildCompletionItemTexts(out, *Chunk.Optional, include_snippets);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// clang-format off
|
|
||||||
case CodeCompletionString::CK_LeftParen: text = '('; break;
|
|
||||||
case CodeCompletionString::CK_RightParen: text = ')'; break;
|
|
||||||
case CodeCompletionString::CK_LeftBracket: text = '['; break;
|
|
||||||
case CodeCompletionString::CK_RightBracket: text = ']'; break;
|
|
||||||
case CodeCompletionString::CK_LeftBrace: text = '{'; break;
|
|
||||||
case CodeCompletionString::CK_RightBrace: text = '}'; break;
|
|
||||||
case CodeCompletionString::CK_LeftAngle: text = '<'; break;
|
|
||||||
case CodeCompletionString::CK_RightAngle: text = '>'; break;
|
|
||||||
case CodeCompletionString::CK_Comma: text = ", "; break;
|
|
||||||
case CodeCompletionString::CK_Colon: text = ':'; break;
|
|
||||||
case CodeCompletionString::CK_SemiColon: text = ';'; break;
|
|
||||||
case CodeCompletionString::CK_Equal: text = '='; break;
|
|
||||||
case CodeCompletionString::CK_HorizontalSpace: text = ' '; break;
|
|
||||||
case CodeCompletionString::CK_VerticalSpace: text = ' '; break;
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Kind != CodeCompletionString::CK_Informative)
|
|
||||||
for (auto i = out_first; i < out.size(); ++i) {
|
|
||||||
out[i].label += text;
|
|
||||||
if (!include_snippets && !out[i].parameters_.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (Kind == CodeCompletionString::CK_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.size())
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// |do_insert|: if |!do_insert|, do not append strings to |insert| after
|
|
||||||
// a placeholder.
|
|
||||||
void BuildDetailString(const CodeCompletionString &CCS, lsCompletionItem &item,
|
|
||||||
bool &do_insert, std::vector<std::string> *parameters,
|
|
||||||
bool include_snippets, int &angle_stack) {
|
|
||||||
for (unsigned i = 0, num_chunks = CCS.size(); i < num_chunks; ++i) {
|
|
||||||
const CodeCompletionString::Chunk &Chunk = CCS[i];
|
|
||||||
CodeCompletionString::ChunkKind Kind = Chunk.Kind;
|
|
||||||
const char *text = nullptr;
|
|
||||||
switch (Kind) {
|
|
||||||
case CodeCompletionString::CK_TypedText:
|
|
||||||
item.label = Chunk.Text;
|
|
||||||
[[fallthrough]];
|
|
||||||
case CodeCompletionString::CK_Text:
|
|
||||||
item.detail += Chunk.Text;
|
|
||||||
if (do_insert)
|
|
||||||
item.insertText += Chunk.Text;
|
|
||||||
break;
|
|
||||||
case CodeCompletionString::CK_Placeholder: {
|
|
||||||
parameters->push_back(Chunk.Text);
|
|
||||||
item.detail += Chunk.Text;
|
|
||||||
// Add parameter declarations as snippets if enabled
|
|
||||||
if (include_snippets) {
|
|
||||||
item.insertText +=
|
|
||||||
"${" + std::to_string(parameters->size()) + ":" + Chunk.Text + "}";
|
|
||||||
item.insertTextFormat = lsInsertTextFormat::Snippet;
|
|
||||||
} else
|
|
||||||
do_insert = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CodeCompletionString::CK_Informative:
|
|
||||||
item.detail += Chunk.Text;
|
|
||||||
break;
|
|
||||||
case CodeCompletionString::CK_Optional: {
|
|
||||||
// Do not add text to insert string if we're in angle brackets.
|
|
||||||
bool should_insert = do_insert && angle_stack == 0;
|
|
||||||
BuildDetailString(*Chunk.Optional, item, should_insert, parameters,
|
|
||||||
include_snippets, angle_stack);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CodeCompletionString::CK_ResultType:
|
|
||||||
item.detail = Chunk.Text + item.detail + " ";
|
|
||||||
break;
|
|
||||||
case CodeCompletionString::CK_CurrentParameter:
|
|
||||||
// We have our own parsing logic for active parameter. This doesn't seem
|
|
||||||
// to be very reliable.
|
|
||||||
break;
|
|
||||||
// clang-format off
|
|
||||||
case CodeCompletionString::CK_LeftParen: text = "("; break;
|
|
||||||
case CodeCompletionString::CK_RightParen: text = ")"; break;
|
|
||||||
case CodeCompletionString::CK_LeftBracket: text = "["; break;
|
|
||||||
case CodeCompletionString::CK_RightBracket: text = "]"; break;
|
|
||||||
case CodeCompletionString::CK_LeftBrace: text = "{"; break;
|
|
||||||
case CodeCompletionString::CK_RightBrace: text = "}"; break;
|
|
||||||
case CodeCompletionString::CK_LeftAngle: text = "<"; angle_stack++; break;
|
|
||||||
case CodeCompletionString::CK_RightAngle: text = ">"; angle_stack--; break;
|
|
||||||
case CodeCompletionString::CK_Comma: text = ", "; break;
|
|
||||||
case CodeCompletionString::CK_Colon: text = ":"; break;
|
|
||||||
case CodeCompletionString::CK_SemiColon: text = ";"; break;
|
|
||||||
case CodeCompletionString::CK_Equal: text = "="; break;
|
|
||||||
case CodeCompletionString::CK_HorizontalSpace:
|
|
||||||
case CodeCompletionString::CK_VerticalSpace: text = " "; break;
|
|
||||||
// clang-format on
|
|
||||||
}
|
|
||||||
if (text) {
|
|
||||||
item.detail += text;
|
|
||||||
if (do_insert && include_snippets)
|
|
||||||
item.insertText += text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LocationInRange(SourceLocation L, CharSourceRange R,
|
bool LocationInRange(SourceLocation L, CharSourceRange R,
|
||||||
const SourceManager &M) {
|
const SourceManager &M) {
|
||||||
assert(R.isCharRange());
|
assert(R.isCharRange());
|
||||||
@ -324,70 +63,6 @@ CharSourceRange DiagnosticRange(const clang::Diagnostic &D, const LangOptions &L
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class CaptureCompletionResults : public CodeCompleteConsumer {
|
|
||||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
|
|
||||||
CodeCompletionTUInfo CCTUInfo;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::vector<lsCompletionItem> ls_items;
|
|
||||||
|
|
||||||
CaptureCompletionResults(const CodeCompleteOptions &Opts)
|
|
||||||
: CodeCompleteConsumer(Opts, false),
|
|
||||||
Alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
|
||||||
CCTUInfo(Alloc) {}
|
|
||||||
|
|
||||||
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
|
||||||
CodeCompletionResult *Results,
|
|
||||||
unsigned NumResults) override {
|
|
||||||
ls_items.reserve(NumResults);
|
|
||||||
for (unsigned i = 0; i != NumResults; i++) {
|
|
||||||
auto &R = Results[i];
|
|
||||||
if (R.Availability == CXAvailability_NotAccessible ||
|
|
||||||
R.Availability == CXAvailability_NotAvailable)
|
|
||||||
continue;
|
|
||||||
CodeCompletionString *CCS = R.CreateCodeCompletionString(
|
|
||||||
S, Context, getAllocator(), getCodeCompletionTUInfo(),
|
|
||||||
includeBriefComments());
|
|
||||||
lsCompletionItem ls_item;
|
|
||||||
ls_item.kind = GetCompletionKind(R.CursorKind);
|
|
||||||
if (const char *brief = CCS->getBriefComment())
|
|
||||||
ls_item.documentation = brief;
|
|
||||||
|
|
||||||
// label/detail/filterText/insertText/priority
|
|
||||||
if (g_config->completion.detailedLabel) {
|
|
||||||
ls_item.detail = CCS->getParentContextName().str();
|
|
||||||
|
|
||||||
size_t first_idx = ls_items.size();
|
|
||||||
ls_items.push_back(ls_item);
|
|
||||||
BuildCompletionItemTexts(ls_items, *CCS,
|
|
||||||
g_config->client.snippetSupport);
|
|
||||||
|
|
||||||
for (size_t j = first_idx; j < ls_items.size(); j++) {
|
|
||||||
if (g_config->client.snippetSupport &&
|
|
||||||
ls_items[j].insertTextFormat == lsInsertTextFormat::Snippet)
|
|
||||||
ls_items[j].insertText += "$0";
|
|
||||||
ls_items[j].priority_ = GetCompletionPriority(
|
|
||||||
*CCS, Results[i].CursorKind, ls_items[j].filterText);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bool do_insert = true;
|
|
||||||
int angle_stack = 0;
|
|
||||||
BuildDetailString(*CCS, ls_item, do_insert, &ls_item.parameters_,
|
|
||||||
g_config->client.snippetSupport, angle_stack);
|
|
||||||
if (g_config->client.snippetSupport &&
|
|
||||||
ls_item.insertTextFormat == lsInsertTextFormat::Snippet)
|
|
||||||
ls_item.insertText += "$0";
|
|
||||||
ls_item.priority_ =
|
|
||||||
GetCompletionPriority(*CCS, Results[i].CursorKind, ls_item.label);
|
|
||||||
ls_items.push_back(ls_item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
|
||||||
|
|
||||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class StoreDiags : public DiagnosticConsumer {
|
class StoreDiags : public DiagnosticConsumer {
|
||||||
const LangOptions *LangOpts;
|
const LangOptions *LangOpts;
|
||||||
@ -548,6 +223,8 @@ void CompletionMain(CompletionManager *completion_manager) {
|
|||||||
while (g_config->completion.dropOldRequests &&
|
while (g_config->completion.dropOldRequests &&
|
||||||
!completion_manager->completion_request_.IsEmpty()) {
|
!completion_manager->completion_request_.IsEmpty()) {
|
||||||
completion_manager->on_dropped_(request->id);
|
completion_manager->on_dropped_(request->id);
|
||||||
|
request->Consumer.reset();
|
||||||
|
request->on_complete(nullptr);
|
||||||
request = completion_manager->completion_request_.Dequeue();
|
request = completion_manager->completion_request_.Dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,15 +238,9 @@ void CompletionMain(CompletionManager *completion_manager) {
|
|||||||
BuildCompilerInvocation(session->file.args, session->FS);
|
BuildCompilerInvocation(session->file.args, session->FS);
|
||||||
if (!CI)
|
if (!CI)
|
||||||
continue;
|
continue;
|
||||||
clang::CodeCompleteOptions CCOpts;
|
|
||||||
CCOpts.IncludeBriefComments = true;
|
|
||||||
#if LLVM_VERSION_MAJOR >= 7
|
|
||||||
CCOpts.IncludeFixIts = true;
|
|
||||||
#endif
|
|
||||||
CCOpts.IncludeCodePatterns = true;
|
|
||||||
auto &FOpts = CI->getFrontendOpts();
|
auto &FOpts = CI->getFrontendOpts();
|
||||||
FOpts.CodeCompleteOpts = CCOpts;
|
FOpts.CodeCompleteOpts = request->CCOpts;
|
||||||
FOpts.CodeCompletionAt.FileName = session->file.filename;
|
FOpts.CodeCompletionAt.FileName = path;
|
||||||
FOpts.CodeCompletionAt.Line = request->position.line + 1;
|
FOpts.CodeCompletionAt.Line = request->position.line + 1;
|
||||||
FOpts.CodeCompletionAt.Column = request->position.character + 1;
|
FOpts.CodeCompletionAt.Column = request->position.character + 1;
|
||||||
FOpts.SkipFunctionBodies = true;
|
FOpts.SkipFunctionBodies = true;
|
||||||
@ -583,14 +254,13 @@ void CompletionMain(CompletionManager *completion_manager) {
|
|||||||
if (!Clang)
|
if (!Clang)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto Consumer = new CaptureCompletionResults(CCOpts);
|
Clang->setCodeCompletionConsumer(request->Consumer.release());
|
||||||
Clang->setCodeCompletionConsumer(Consumer);
|
|
||||||
if (!Parse(*Clang))
|
if (!Parse(*Clang))
|
||||||
continue;
|
continue;
|
||||||
for (auto &Buf : Bufs)
|
for (auto &Buf : Bufs)
|
||||||
Buf.release();
|
Buf.release();
|
||||||
|
|
||||||
request->on_complete(Consumer->ls_items, false /*is_cached_result*/);
|
request->on_complete(&Clang->getCodeCompletionConsumer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -718,9 +388,6 @@ void CompletionManager::CodeComplete(
|
|||||||
const lsRequestId &id,
|
const lsRequestId &id,
|
||||||
const lsTextDocumentPositionParams &completion_location,
|
const lsTextDocumentPositionParams &completion_location,
|
||||||
const OnComplete &on_complete) {
|
const OnComplete &on_complete) {
|
||||||
completion_request_.PushBack(std::make_unique<CompletionRequest>(
|
|
||||||
id, completion_location.textDocument, completion_location.position,
|
|
||||||
on_complete));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionManager::DiagnosticsUpdate(
|
void CompletionManager::DiagnosticsUpdate(
|
||||||
@ -833,14 +500,3 @@ void CompletionManager::FlushAllSessions() {
|
|||||||
preloaded_sessions_.Clear();
|
preloaded_sessions_.Clear();
|
||||||
completion_sessions_.Clear();
|
completion_sessions_.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CodeCompleteCache::WithLock(std::function<void()> action) {
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CodeCompleteCache::IsCacheValid(lsTextDocumentPositionParams position) {
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
return cached_path_ == position.textDocument.uri.GetPath() &&
|
|
||||||
cached_completion_position_ == position.position;
|
|
||||||
}
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include <clang/Frontend/CompilerInstance.h>
|
#include <clang/Frontend/CompilerInstance.h>
|
||||||
#include <clang/Frontend/FrontendActions.h>
|
#include <clang/Frontend/FrontendActions.h>
|
||||||
|
#include <clang/Sema/CodeCompleteOptions.h>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -67,8 +68,9 @@ struct CompletionSession
|
|||||||
struct CompletionManager {
|
struct CompletionManager {
|
||||||
using OnDiagnostic = std::function<void(
|
using OnDiagnostic = std::function<void(
|
||||||
std::string path, std::vector<lsDiagnostic> diagnostics)>;
|
std::string path, std::vector<lsDiagnostic> diagnostics)>;
|
||||||
using OnComplete = std::function<void(
|
// If OptConsumer is nullptr, the request has been cancelled.
|
||||||
const std::vector<lsCompletionItem> &results, bool is_cached_result)>;
|
using OnComplete =
|
||||||
|
std::function<void(clang::CodeCompleteConsumer *OptConsumer)>;
|
||||||
using OnDropped = std::function<void(lsRequestId request_id)>;
|
using OnDropped = std::function<void(lsRequestId request_id)>;
|
||||||
|
|
||||||
struct PreloadRequest {
|
struct PreloadRequest {
|
||||||
@ -81,13 +83,19 @@ struct CompletionManager {
|
|||||||
struct CompletionRequest {
|
struct CompletionRequest {
|
||||||
CompletionRequest(const lsRequestId &id,
|
CompletionRequest(const lsRequestId &id,
|
||||||
const lsTextDocumentIdentifier &document,
|
const lsTextDocumentIdentifier &document,
|
||||||
const lsPosition &position, const OnComplete &on_complete)
|
const lsPosition &position,
|
||||||
|
std::unique_ptr<clang::CodeCompleteConsumer> Consumer,
|
||||||
|
clang::CodeCompleteOptions CCOpts,
|
||||||
|
const OnComplete &on_complete)
|
||||||
: id(id), document(document), position(position),
|
: id(id), document(document), position(position),
|
||||||
|
Consumer(std::move(Consumer)), CCOpts(CCOpts),
|
||||||
on_complete(on_complete) {}
|
on_complete(on_complete) {}
|
||||||
|
|
||||||
lsRequestId id;
|
lsRequestId id;
|
||||||
lsTextDocumentIdentifier document;
|
lsTextDocumentIdentifier document;
|
||||||
lsPosition position;
|
lsPosition position;
|
||||||
|
std::unique_ptr<clang::CodeCompleteConsumer> Consumer;
|
||||||
|
clang::CodeCompleteOptions CCOpts;
|
||||||
OnComplete on_complete;
|
OnComplete on_complete;
|
||||||
};
|
};
|
||||||
struct DiagnosticRequest {
|
struct DiagnosticRequest {
|
||||||
@ -164,14 +172,22 @@ struct CompletionManager {
|
|||||||
// Cached completion information, so we can give fast completion results when
|
// Cached completion information, so we can give fast completion results when
|
||||||
// the user erases a character. vscode will resend the completion request if
|
// the user erases a character. vscode will resend the completion request if
|
||||||
// that happens.
|
// that happens.
|
||||||
struct CodeCompleteCache {
|
template <typename T>
|
||||||
|
struct CompleteConsumerCache {
|
||||||
// NOTE: Make sure to access these variables under |WithLock|.
|
// NOTE: Make sure to access these variables under |WithLock|.
|
||||||
std::optional<std::string> cached_path_;
|
std::optional<std::string> path;
|
||||||
std::optional<lsPosition> cached_completion_position_;
|
std::optional<lsPosition> position;
|
||||||
std::vector<lsCompletionItem> cached_results_;
|
T result;
|
||||||
|
|
||||||
std::mutex mutex_;
|
std::mutex mutex;
|
||||||
|
|
||||||
void WithLock(std::function<void()> action);
|
void WithLock(std::function<void()> action) {
|
||||||
bool IsCacheValid(lsTextDocumentPositionParams position);
|
std::lock_guard lock(mutex);
|
||||||
|
action();
|
||||||
|
}
|
||||||
|
bool IsCacheValid(const lsTextDocumentPositionParams ¶ms) {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
return path == params.textDocument.uri.GetPath() &&
|
||||||
|
position == params.position;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
28
src/config.h
28
src/config.h
@ -80,16 +80,7 @@ struct Config {
|
|||||||
// When this option is enabled, the completion item label is very detailed,
|
// When this option is enabled, the completion item label is very detailed,
|
||||||
// it shows the full signature of the candidate.
|
// it shows the full signature of the candidate.
|
||||||
// The detail just contains the completion item parent context.
|
// The detail just contains the completion item parent context.
|
||||||
// Also, in this mode, functions with default arguments,
|
bool detailedLabel = true;
|
||||||
// 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.
|
|
||||||
bool detailedLabel = false;
|
|
||||||
|
|
||||||
// On large projects, completion can take a long time. By default if ccls
|
// On large projects, completion can take a long time. By default if ccls
|
||||||
// receives multiple completion requests while completion is still running
|
// receives multiple completion requests while completion is still running
|
||||||
@ -97,6 +88,15 @@ struct Config {
|
|||||||
// completion requests will be serviced.
|
// completion requests will be serviced.
|
||||||
bool dropOldRequests = true;
|
bool dropOldRequests = true;
|
||||||
|
|
||||||
|
// Functions with default arguments, generate one more item per default
|
||||||
|
// argument. 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.
|
||||||
|
bool duplicateOptional = true;
|
||||||
|
|
||||||
// If true, filter and sort completion response. ccls filters and sorts
|
// If true, filter and sort completion response. ccls filters and sorts
|
||||||
// completions to try to be nicer to clients that can't handle big numbers
|
// completions to try to be nicer to clients that can't handle big numbers
|
||||||
// of completion candidates. This behaviour can be disabled by specifying
|
// of completion candidates. This behaviour can be disabled by specifying
|
||||||
@ -226,10 +226,10 @@ struct Config {
|
|||||||
MAKE_REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, resourceDir);
|
MAKE_REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, resourceDir);
|
||||||
MAKE_REFLECT_STRUCT(Config::ClientCapability, snippetSupport);
|
MAKE_REFLECT_STRUCT(Config::ClientCapability, snippetSupport);
|
||||||
MAKE_REFLECT_STRUCT(Config::CodeLens, localVariables);
|
MAKE_REFLECT_STRUCT(Config::CodeLens, localVariables);
|
||||||
MAKE_REFLECT_STRUCT(Config::Completion, caseSensitivity, dropOldRequests,
|
MAKE_REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel,
|
||||||
detailedLabel, filterAndSort, includeBlacklist,
|
dropOldRequests, duplicateOptional, filterAndSort,
|
||||||
includeMaxPathSize, includeSuffixWhitelist,
|
includeBlacklist, includeMaxPathSize,
|
||||||
includeWhitelist);
|
includeSuffixWhitelist, includeWhitelist);
|
||||||
MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange,
|
MAKE_REFLECT_STRUCT(Config::Diagnostics, blacklist, frequencyMs, onChange,
|
||||||
onOpen, onSave, spellChecking, whitelist)
|
onOpen, onSave, spellChecking, whitelist)
|
||||||
MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist)
|
MAKE_REFLECT_STRUCT(Config::Highlight, lsRanges, blacklist, whitelist)
|
||||||
|
@ -15,11 +15,9 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
struct CompletionManager;
|
struct CompletionManager;
|
||||||
struct CodeCompleteCache;
|
|
||||||
struct Config;
|
struct Config;
|
||||||
class DiagnosticsPublisher;
|
class DiagnosticsPublisher;
|
||||||
struct VFS;
|
struct VFS;
|
||||||
struct ImportManager;
|
|
||||||
struct IncludeComplete;
|
struct IncludeComplete;
|
||||||
struct MultiQueueWaiter;
|
struct MultiQueueWaiter;
|
||||||
struct Project;
|
struct Project;
|
||||||
@ -104,14 +102,10 @@ struct MessageHandler {
|
|||||||
Project *project = nullptr;
|
Project *project = nullptr;
|
||||||
DiagnosticsPublisher *diag_pub = nullptr;
|
DiagnosticsPublisher *diag_pub = nullptr;
|
||||||
VFS *vfs = nullptr;
|
VFS *vfs = nullptr;
|
||||||
ImportManager *import_manager = nullptr;
|
|
||||||
SemanticHighlightSymbolCache *semantic_cache = nullptr;
|
SemanticHighlightSymbolCache *semantic_cache = nullptr;
|
||||||
WorkingFiles *working_files = nullptr;
|
WorkingFiles *working_files = nullptr;
|
||||||
CompletionManager *clang_complete = nullptr;
|
CompletionManager *clang_complete = nullptr;
|
||||||
IncludeComplete *include_complete = nullptr;
|
IncludeComplete *include_complete = nullptr;
|
||||||
CodeCompleteCache *global_code_complete_cache = nullptr;
|
|
||||||
CodeCompleteCache *non_global_code_complete_cache = nullptr;
|
|
||||||
CodeCompleteCache *signature_cache = nullptr;
|
|
||||||
|
|
||||||
virtual MethodType GetMethodType() const = 0;
|
virtual MethodType GetMethodType() const = 0;
|
||||||
virtual void Run(std::unique_ptr<InMessage> message) = 0;
|
virtual void Run(std::unique_ptr<InMessage> message) = 0;
|
||||||
|
@ -4,12 +4,16 @@
|
|||||||
#include "clang_complete.hh"
|
#include "clang_complete.hh"
|
||||||
#include "fuzzy_match.h"
|
#include "fuzzy_match.h"
|
||||||
#include "include_complete.h"
|
#include "include_complete.h"
|
||||||
|
#include "log.hh"
|
||||||
#include "message_handler.h"
|
#include "message_handler.h"
|
||||||
#include "pipeline.hh"
|
#include "pipeline.hh"
|
||||||
#include "working_files.h"
|
#include "working_files.h"
|
||||||
using namespace ccls;
|
using namespace ccls;
|
||||||
|
|
||||||
|
#include <clang/Sema/CodeCompleteConsumer.h>
|
||||||
|
#include <clang/Sema/Sema.h>
|
||||||
#include <llvm/Support/Timer.h>
|
#include <llvm/Support/Timer.h>
|
||||||
|
using namespace clang;
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
#include <regex>
|
#include <regex>
|
||||||
@ -249,24 +253,245 @@ bool IsOpenParenOrAngle(const std::vector<std::string> &lines,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Handler_TextDocumentCompletion : MessageHandler {
|
unsigned GetCompletionPriority(const CodeCompletionString &CCS,
|
||||||
|
CXCursorKind result_kind,
|
||||||
|
const std::optional<std::string> &typedText) {
|
||||||
|
unsigned priority = CCS.getPriority();
|
||||||
|
if (CCS.getAvailability() != CXAvailability_Available ||
|
||||||
|
result_kind == CXCursor_Destructor ||
|
||||||
|
result_kind == CXCursor_ConversionFunction ||
|
||||||
|
(result_kind == CXCursor_CXXMethod && typedText &&
|
||||||
|
StartsWith(*typedText, "operator")))
|
||||||
|
priority *= 100;
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
lsCompletionItemKind GetCompletionKind(CXCursorKind cursor_kind) {
|
||||||
|
switch (cursor_kind) {
|
||||||
|
case CXCursor_UnexposedDecl:
|
||||||
|
return lsCompletionItemKind::Text;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
case CXCursor_ObjCInstanceMethodDecl:
|
||||||
|
case CXCursor_CXXMethod:
|
||||||
|
case CXCursor_ObjCClassMethodDecl:
|
||||||
|
return lsCompletionItemKind::Method;
|
||||||
|
|
||||||
|
case CXCursor_FunctionTemplate:
|
||||||
|
return lsCompletionItemKind::Function;
|
||||||
|
|
||||||
|
case CXCursor_Constructor:
|
||||||
|
case CXCursor_Destructor:
|
||||||
|
case CXCursor_ConversionFunction:
|
||||||
|
return lsCompletionItemKind::Constructor;
|
||||||
|
|
||||||
|
case CXCursor_ObjCIvarDecl:
|
||||||
|
return lsCompletionItemKind::Variable;
|
||||||
|
|
||||||
|
case CXCursor_ClassTemplate:
|
||||||
|
case CXCursor_ClassTemplatePartialSpecialization:
|
||||||
|
case CXCursor_UsingDeclaration:
|
||||||
|
case CXCursor_TypedefDecl:
|
||||||
|
case CXCursor_TypeAliasDecl:
|
||||||
|
case CXCursor_TypeAliasTemplateDecl:
|
||||||
|
case CXCursor_ObjCCategoryDecl:
|
||||||
|
case CXCursor_ObjCProtocolDecl:
|
||||||
|
case CXCursor_ObjCImplementationDecl:
|
||||||
|
case CXCursor_ObjCCategoryImplDecl:
|
||||||
|
return lsCompletionItemKind::Class;
|
||||||
|
|
||||||
|
case CXCursor_ObjCPropertyDecl:
|
||||||
|
return lsCompletionItemKind::Property;
|
||||||
|
|
||||||
|
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:
|
||||||
|
case CXCursor_ObjCSuperClassRef:
|
||||||
|
case CXCursor_ObjCProtocolRef:
|
||||||
|
case CXCursor_ObjCClassRef:
|
||||||
|
return lsCompletionItemKind::Reference;
|
||||||
|
|
||||||
|
// return lsCompletionItemKind::Unit;
|
||||||
|
// return lsCompletionItemKind::Value;
|
||||||
|
// return lsCompletionItemKind::Keyword;
|
||||||
|
// return lsCompletionItemKind::Snippet;
|
||||||
|
// return lsCompletionItemKind::Color;
|
||||||
|
// return lsCompletionItemKind::File;
|
||||||
|
|
||||||
|
case CXCursor_NotImplemented:
|
||||||
|
case CXCursor_OverloadCandidate:
|
||||||
|
return lsCompletionItemKind::Text;
|
||||||
|
|
||||||
|
case CXCursor_TemplateTypeParameter:
|
||||||
|
case CXCursor_TemplateTemplateParameter:
|
||||||
|
return lsCompletionItemKind::TypeParameter;
|
||||||
|
|
||||||
|
default:
|
||||||
|
LOG_S(WARNING) << "Unhandled completion kind " << cursor_kind;
|
||||||
|
return lsCompletionItemKind::Text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildItem(std::vector<lsCompletionItem> &out,
|
||||||
|
const CodeCompletionString &CCS) {
|
||||||
|
assert(!out.empty());
|
||||||
|
auto first = out.size() - 1;
|
||||||
|
|
||||||
|
std::string result_type;
|
||||||
|
|
||||||
|
for (const auto &Chunk : CCS) {
|
||||||
|
CodeCompletionString::ChunkKind Kind = Chunk.Kind;
|
||||||
|
std::string text;
|
||||||
|
switch (Kind) {
|
||||||
|
case CodeCompletionString::CK_TypedText:
|
||||||
|
text = Chunk.Text;
|
||||||
|
for (auto i = first; i < out.size(); i++)
|
||||||
|
if (Kind == CodeCompletionString::CK_TypedText && !out[i].filterText)
|
||||||
|
out[i].filterText = text;
|
||||||
|
break;
|
||||||
|
case CodeCompletionString::CK_Placeholder:
|
||||||
|
text = Chunk.Text;
|
||||||
|
for (auto i = first; i < out.size(); i++)
|
||||||
|
out[i].parameters_.push_back(text);
|
||||||
|
break;
|
||||||
|
case CodeCompletionString::CK_ResultType:
|
||||||
|
result_type = Chunk.Text;
|
||||||
|
continue;
|
||||||
|
case CodeCompletionString::CK_CurrentParameter:
|
||||||
|
// This should never be present while collecting completion items.
|
||||||
|
llvm_unreachable("unexpected CK_CurrentParameter");
|
||||||
|
continue;
|
||||||
|
case CodeCompletionString::CK_Optional: {
|
||||||
|
// Duplicate last element, the recursive call will complete it.
|
||||||
|
if (g_config->completion.duplicateOptional) {
|
||||||
|
out.push_back(out.back());
|
||||||
|
BuildItem(out, *Chunk.Optional);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
text = Chunk.Text;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = first; i < out.size(); ++i) {
|
||||||
|
out[i].label += text;
|
||||||
|
if (!g_config->client.snippetSupport && !out[i].parameters_.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Kind == CodeCompletionString::CK_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.size())
|
||||||
|
for (auto i = 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CompletionConsumer : public CodeCompleteConsumer {
|
||||||
|
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
|
||||||
|
CodeCompletionTUInfo CCTUInfo;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool from_cache;
|
||||||
|
std::vector<lsCompletionItem> ls_items;
|
||||||
|
|
||||||
|
CompletionConsumer(const CodeCompleteOptions &Opts, bool from_cache)
|
||||||
|
: CodeCompleteConsumer(Opts, false),
|
||||||
|
Alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
||||||
|
CCTUInfo(Alloc), from_cache(from_cache) {}
|
||||||
|
|
||||||
|
void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context,
|
||||||
|
CodeCompletionResult *Results,
|
||||||
|
unsigned NumResults) override {
|
||||||
|
ls_items.reserve(NumResults);
|
||||||
|
for (unsigned i = 0; i != NumResults; i++) {
|
||||||
|
auto &R = Results[i];
|
||||||
|
if (R.Availability == CXAvailability_NotAccessible ||
|
||||||
|
R.Availability == CXAvailability_NotAvailable)
|
||||||
|
continue;
|
||||||
|
CodeCompletionString *CCS = R.CreateCodeCompletionString(
|
||||||
|
S, Context, getAllocator(), getCodeCompletionTUInfo(),
|
||||||
|
includeBriefComments());
|
||||||
|
lsCompletionItem ls_item;
|
||||||
|
ls_item.kind = GetCompletionKind(R.CursorKind);
|
||||||
|
if (const char *brief = CCS->getBriefComment())
|
||||||
|
ls_item.documentation = brief;
|
||||||
|
ls_item.detail = CCS->getParentContextName().str();
|
||||||
|
|
||||||
|
size_t first_idx = ls_items.size();
|
||||||
|
ls_items.push_back(ls_item);
|
||||||
|
BuildItem(ls_items, *CCS);
|
||||||
|
|
||||||
|
for (size_t j = first_idx; j < ls_items.size(); j++) {
|
||||||
|
if (g_config->client.snippetSupport &&
|
||||||
|
ls_items[j].insertTextFormat == lsInsertTextFormat::Snippet)
|
||||||
|
ls_items[j].insertText += "$0";
|
||||||
|
ls_items[j].priority_ = GetCompletionPriority(
|
||||||
|
*CCS, Results[i].CursorKind, ls_items[j].filterText);
|
||||||
|
if (!g_config->completion.detailedLabel) {
|
||||||
|
ls_items[j].detail = ls_items[j].label;
|
||||||
|
ls_items[j].label = ls_items[j].filterText.value_or("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
||||||
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Handler_TextDocumentCompletion
|
||||||
|
: BaseMessageHandler<In_TextDocumentComplete> {
|
||||||
MethodType GetMethodType() const override { return kMethodType; }
|
MethodType GetMethodType() const override { return kMethodType; }
|
||||||
|
|
||||||
void Run(std::unique_ptr<InMessage> message) override {
|
void Run(In_TextDocumentComplete *request) override {
|
||||||
auto request = std::shared_ptr<In_TextDocumentComplete>(
|
static CompleteConsumerCache<std::vector<lsCompletionItem>> cache;
|
||||||
static_cast<In_TextDocumentComplete *>(message.release()));
|
|
||||||
auto ¶ms = request->params;
|
|
||||||
|
|
||||||
auto write_empty_result = [request]() {
|
auto ¶ms = request->params;
|
||||||
Out_TextDocumentComplete out;
|
Out_TextDocumentComplete out;
|
||||||
out.id = request->id;
|
out.id = request->id;
|
||||||
pipeline::WriteStdout(kMethodType, out);
|
|
||||||
};
|
|
||||||
|
|
||||||
std::string path = params.textDocument.uri.GetPath();
|
std::string path = params.textDocument.uri.GetPath();
|
||||||
WorkingFile *file = working_files->GetFileByFilename(path);
|
WorkingFile *file = working_files->GetFileByFilename(path);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
write_empty_result();
|
pipeline::WriteStdout(kMethodType, out);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,19 +536,15 @@ struct Handler_TextDocumentCompletion : MessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (did_fail_check) {
|
if (did_fail_check) {
|
||||||
write_empty_result();
|
pipeline::WriteStdout(kMethodType, out);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_global_completion = false;
|
std::string completion_text;
|
||||||
std::string existing_completion;
|
|
||||||
lsPosition end_pos = params.position;
|
lsPosition end_pos = params.position;
|
||||||
if (file) {
|
params.position = file->FindStableCompletionSource(
|
||||||
params.position = file->FindStableCompletionSource(
|
request->params.position, &completion_text, &end_pos);
|
||||||
request->params.position, &is_global_completion, &existing_completion,
|
|
||||||
&end_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseIncludeLineResult result = ParseIncludeLine(buffer_line);
|
ParseIncludeLineResult result = ParseIncludeLine(buffer_line);
|
||||||
bool has_open_paren = IsOpenParenOrAngle(file->buffer_lines, end_pos);
|
bool has_open_paren = IsOpenParenOrAngle(file->buffer_lines, end_pos);
|
||||||
@ -368,69 +589,44 @@ struct Handler_TextDocumentCompletion : MessageHandler {
|
|||||||
|
|
||||||
pipeline::WriteStdout(kMethodType, out);
|
pipeline::WriteStdout(kMethodType, out);
|
||||||
} else {
|
} else {
|
||||||
CompletionManager::OnComplete callback = std::bind(
|
CompletionManager::OnComplete callback =
|
||||||
[this, request, params, is_global_completion, existing_completion,
|
[completion_text, has_open_paren, id = request->id,
|
||||||
has_open_paren](const std::vector<lsCompletionItem> &results,
|
params = request->params](CodeCompleteConsumer *OptConsumer) {
|
||||||
bool is_cached_result) {
|
if (!OptConsumer)
|
||||||
|
return;
|
||||||
|
auto *Consumer = static_cast<CompletionConsumer *>(OptConsumer);
|
||||||
Out_TextDocumentComplete out;
|
Out_TextDocumentComplete out;
|
||||||
out.id = request->id;
|
out.id = id;
|
||||||
out.result.items = results;
|
out.result.items = Consumer->ls_items;
|
||||||
|
|
||||||
// Emit completion results.
|
FilterAndSortCompletionResponse(&out, completion_text,
|
||||||
FilterAndSortCompletionResponse(&out, existing_completion,
|
|
||||||
has_open_paren);
|
has_open_paren);
|
||||||
pipeline::WriteStdout(kMethodType, out);
|
pipeline::WriteStdout(kMethodType, out);
|
||||||
|
if (!Consumer->from_cache) {
|
||||||
// Cache completion results.
|
|
||||||
if (!is_cached_result) {
|
|
||||||
std::string path = params.textDocument.uri.GetPath();
|
std::string path = params.textDocument.uri.GetPath();
|
||||||
if (is_global_completion) {
|
cache.WithLock([&]() {
|
||||||
global_code_complete_cache->WithLock([&]() {
|
cache.path = path;
|
||||||
global_code_complete_cache->cached_path_ = path;
|
cache.position = params.position;
|
||||||
global_code_complete_cache->cached_results_ = results;
|
cache.result = Consumer->ls_items;
|
||||||
});
|
|
||||||
} else {
|
|
||||||
non_global_code_complete_cache->WithLock([&]() {
|
|
||||||
non_global_code_complete_cache->cached_path_ = path;
|
|
||||||
non_global_code_complete_cache->cached_completion_position_ =
|
|
||||||
params.position;
|
|
||||||
non_global_code_complete_cache->cached_results_ = results;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
std::placeholders::_1, std::placeholders::_2);
|
|
||||||
|
|
||||||
bool is_cache_match = false;
|
|
||||||
global_code_complete_cache->WithLock([&]() {
|
|
||||||
is_cache_match = is_global_completion &&
|
|
||||||
global_code_complete_cache->cached_path_ == path &&
|
|
||||||
!global_code_complete_cache->cached_results_.empty();
|
|
||||||
});
|
|
||||||
if (is_cache_match) {
|
|
||||||
CompletionManager::OnComplete freshen_global =
|
|
||||||
[this](std::vector<lsCompletionItem> results,
|
|
||||||
bool is_cached_result) {
|
|
||||||
assert(!is_cached_result);
|
|
||||||
|
|
||||||
// note: path is updated in the normal completion handler.
|
|
||||||
global_code_complete_cache->WithLock([&]() {
|
|
||||||
global_code_complete_cache->cached_results_ = results;
|
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
};
|
||||||
|
|
||||||
global_code_complete_cache->WithLock([&]() {
|
clang::CodeCompleteOptions CCOpts;
|
||||||
callback(global_code_complete_cache->cached_results_,
|
CCOpts.IncludeBriefComments = true;
|
||||||
true /*is_cached_result*/);
|
#if LLVM_VERSION_MAJOR >= 7
|
||||||
});
|
CCOpts.IncludeFixIts = true;
|
||||||
clang_complete->CodeComplete(request->id, params, freshen_global);
|
#endif
|
||||||
} else if (non_global_code_complete_cache->IsCacheValid(params)) {
|
if (cache.IsCacheValid(params)) {
|
||||||
non_global_code_complete_cache->WithLock([&]() {
|
CompletionConsumer Consumer(CCOpts, true);
|
||||||
callback(non_global_code_complete_cache->cached_results_,
|
cache.WithLock([&]() { Consumer.ls_items = cache.result; });
|
||||||
true /*is_cached_result*/);
|
callback(&Consumer);
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
clang_complete->CodeComplete(request->id, params, callback);
|
clang_complete->completion_request_.PushBack(
|
||||||
|
std::make_unique<CompletionManager::CompletionRequest>(
|
||||||
|
request->id, params.textDocument, params.position,
|
||||||
|
std::make_unique<CompletionConsumer>(CCOpts, false), CCOpts,
|
||||||
|
callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,44 +6,29 @@
|
|||||||
#include "pipeline.hh"
|
#include "pipeline.hh"
|
||||||
using namespace ccls;
|
using namespace ccls;
|
||||||
|
|
||||||
|
#include <clang/Sema/Sema.h>
|
||||||
|
using namespace clang;
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
MethodType kMethodType = "textDocument/signatureHelp";
|
MethodType kMethodType = "textDocument/signatureHelp";
|
||||||
|
|
||||||
struct In_TextDocumentSignatureHelp : public RequestInMessage {
|
|
||||||
MethodType GetMethodType() const override { return kMethodType; }
|
|
||||||
lsTextDocumentPositionParams params;
|
|
||||||
};
|
|
||||||
MAKE_REFLECT_STRUCT(In_TextDocumentSignatureHelp, id, params);
|
|
||||||
REGISTER_IN_MESSAGE(In_TextDocumentSignatureHelp);
|
|
||||||
|
|
||||||
// Represents a parameter of a callable-signature. A parameter can
|
// Represents a parameter of a callable-signature. A parameter can
|
||||||
// have a label and a doc-comment.
|
// have a label and a doc-comment.
|
||||||
struct lsParameterInformation {
|
struct lsParameterInformation {
|
||||||
// The label of this parameter. Will be shown in
|
|
||||||
// the UI.
|
|
||||||
std::string label;
|
std::string label;
|
||||||
|
// Not available in clang
|
||||||
// The human-readable doc-comment of this parameter. Will be shown
|
// std::optional<std::string> documentation;
|
||||||
// in the UI but can be omitted.
|
|
||||||
std::optional<std::string> documentation;
|
|
||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(lsParameterInformation, label, documentation);
|
MAKE_REFLECT_STRUCT(lsParameterInformation, label);
|
||||||
|
|
||||||
// Represents the signature of something callable. A signature
|
// Represents the signature of something callable. A signature
|
||||||
// can have a label, like a function-name, a doc-comment, and
|
// can have a label, like a function-name, a doc-comment, and
|
||||||
// a set of parameters.
|
// a set of parameters.
|
||||||
struct lsSignatureInformation {
|
struct lsSignatureInformation {
|
||||||
// The label of this signature. Will be shown in
|
|
||||||
// the UI.
|
|
||||||
std::string label;
|
std::string label;
|
||||||
|
|
||||||
// The human-readable doc-comment of this signature. Will be shown
|
|
||||||
// in the UI but can be omitted.
|
|
||||||
std::optional<std::string> documentation;
|
std::optional<std::string> documentation;
|
||||||
|
|
||||||
// The parameters of this signature.
|
|
||||||
std::vector<lsParameterInformation> parameters;
|
std::vector<lsParameterInformation> parameters;
|
||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(lsSignatureInformation, label, documentation, parameters);
|
MAKE_REFLECT_STRUCT(lsSignatureInformation, label, documentation, parameters);
|
||||||
@ -52,30 +37,20 @@ MAKE_REFLECT_STRUCT(lsSignatureInformation, label, documentation, parameters);
|
|||||||
// callable. There can be multiple signature but only one
|
// callable. There can be multiple signature but only one
|
||||||
// active and only one active parameter.
|
// active and only one active parameter.
|
||||||
struct lsSignatureHelp {
|
struct lsSignatureHelp {
|
||||||
// One or more signatures.
|
|
||||||
std::vector<lsSignatureInformation> signatures;
|
std::vector<lsSignatureInformation> signatures;
|
||||||
|
int activeSignature = 0;
|
||||||
// The active signature. If omitted or the value lies outside the
|
int activeParameter;
|
||||||
// range of `signatures` the value defaults to zero or is ignored if
|
|
||||||
// `signatures.length === 0`. Whenever possible implementors should
|
|
||||||
// make an active decision about the active signature and shouldn't
|
|
||||||
// rely on a default value.
|
|
||||||
// In future version of the protocol this property might become
|
|
||||||
// mandantory to better express this.
|
|
||||||
std::optional<int> activeSignature;
|
|
||||||
|
|
||||||
// The active parameter of the active signature. If omitted or the value
|
|
||||||
// lies outside the range of `signatures[activeSignature].parameters`
|
|
||||||
// defaults to 0 if the active signature has parameters. If
|
|
||||||
// the active signature has no parameters it is ignored.
|
|
||||||
// In future version of the protocol this property might become
|
|
||||||
// mandantory to better express the active parameter if the
|
|
||||||
// active signature does have any.
|
|
||||||
std::optional<int> activeParameter;
|
|
||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature,
|
MAKE_REFLECT_STRUCT(lsSignatureHelp, signatures, activeSignature,
|
||||||
activeParameter);
|
activeParameter);
|
||||||
|
|
||||||
|
struct In_TextDocumentSignatureHelp : public RequestInMessage {
|
||||||
|
MethodType GetMethodType() const override { return kMethodType; }
|
||||||
|
lsTextDocumentPositionParams params;
|
||||||
|
};
|
||||||
|
MAKE_REFLECT_STRUCT(In_TextDocumentSignatureHelp, id, params);
|
||||||
|
REGISTER_IN_MESSAGE(In_TextDocumentSignatureHelp);
|
||||||
|
|
||||||
struct Out_TextDocumentSignatureHelp
|
struct Out_TextDocumentSignatureHelp
|
||||||
: public lsOutMessage<Out_TextDocumentSignatureHelp> {
|
: public lsOutMessage<Out_TextDocumentSignatureHelp> {
|
||||||
lsRequestId id;
|
lsRequestId id;
|
||||||
@ -83,88 +58,164 @@ struct Out_TextDocumentSignatureHelp
|
|||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(Out_TextDocumentSignatureHelp, jsonrpc, id, result);
|
MAKE_REFLECT_STRUCT(Out_TextDocumentSignatureHelp, jsonrpc, id, result);
|
||||||
|
|
||||||
struct Handler_TextDocumentSignatureHelp : MessageHandler {
|
std::string BuildOptional(const CodeCompletionString &CCS,
|
||||||
|
std::vector<lsParameterInformation> &ls_params) {
|
||||||
|
std::string ret;
|
||||||
|
for (const auto &Chunk : CCS) {
|
||||||
|
switch (Chunk.Kind) {
|
||||||
|
case CodeCompletionString::CK_Optional:
|
||||||
|
ret += BuildOptional(*Chunk.Optional, ls_params);
|
||||||
|
break;
|
||||||
|
case CodeCompletionString::CK_Placeholder:
|
||||||
|
// A string that acts as a placeholder for, e.g., a function call
|
||||||
|
// argument.
|
||||||
|
// Intentional fallthrough here.
|
||||||
|
case CodeCompletionString::CK_CurrentParameter: {
|
||||||
|
// A piece of text that describes the parameter that corresponds to
|
||||||
|
// the code-completion location within a function call, message send,
|
||||||
|
// macro invocation, etc.
|
||||||
|
ret += Chunk.Text;
|
||||||
|
ls_params.push_back(lsParameterInformation{Chunk.Text});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CodeCompletionString::CK_VerticalSpace:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret += Chunk.Text;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SignatureHelpConsumer : public CodeCompleteConsumer {
|
||||||
|
std::shared_ptr<GlobalCodeCompletionAllocator> Alloc;
|
||||||
|
CodeCompletionTUInfo CCTUInfo;
|
||||||
|
public:
|
||||||
|
bool from_cache;
|
||||||
|
lsSignatureHelp ls_sighelp;
|
||||||
|
SignatureHelpConsumer(const clang::CodeCompleteOptions &CCOpts,
|
||||||
|
bool from_cache)
|
||||||
|
: CodeCompleteConsumer(CCOpts, false),
|
||||||
|
Alloc(std::make_shared<GlobalCodeCompletionAllocator>()),
|
||||||
|
CCTUInfo(Alloc), from_cache(from_cache) {}
|
||||||
|
void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg,
|
||||||
|
OverloadCandidate *Candidates,
|
||||||
|
unsigned NumCandidates
|
||||||
|
#if LLVM_VERSION_MAJOR >= 8
|
||||||
|
,
|
||||||
|
SourceLocation OpenParLoc
|
||||||
|
#endif
|
||||||
|
) override {
|
||||||
|
ls_sighelp.activeParameter = (int)CurrentArg;
|
||||||
|
for (unsigned i = 0; i < NumCandidates; i++) {
|
||||||
|
OverloadCandidate Cand = Candidates[i];
|
||||||
|
// We want to avoid showing instantiated signatures, because they may be
|
||||||
|
// long in some cases (e.g. when 'T' is substituted with 'std::string', we
|
||||||
|
// would get 'std::basic_string<char>').
|
||||||
|
if (auto *Func = Cand.getFunction())
|
||||||
|
if (auto *Pattern = Func->getTemplateInstantiationPattern())
|
||||||
|
Cand = OverloadCandidate(Pattern);
|
||||||
|
|
||||||
|
const auto *CCS =
|
||||||
|
Cand.CreateSignatureString(CurrentArg, S, *Alloc, CCTUInfo, true);
|
||||||
|
|
||||||
|
const char *ret_type = nullptr;
|
||||||
|
lsSignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
|
||||||
|
#if LLVM_VERSION_MAJOR >= 8
|
||||||
|
const RawComment *RC = getCompletionComment(S.getASTContext(), Cand.getFunction());
|
||||||
|
ls_sig.documentation = RC ? RC->getBriefText(S.getASTContext()) : "";
|
||||||
|
#endif
|
||||||
|
for (const auto &Chunk : *CCS)
|
||||||
|
switch (Chunk.Kind) {
|
||||||
|
case CodeCompletionString::CK_ResultType:
|
||||||
|
ret_type = Chunk.Text;
|
||||||
|
break;
|
||||||
|
case CodeCompletionString::CK_Placeholder:
|
||||||
|
case CodeCompletionString::CK_CurrentParameter: {
|
||||||
|
ls_sig.label += Chunk.Text;
|
||||||
|
ls_sig.parameters.push_back(lsParameterInformation{Chunk.Text});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case CodeCompletionString::CK_Optional:
|
||||||
|
ls_sig.label += BuildOptional(*Chunk.Optional, ls_sig.parameters);
|
||||||
|
break;
|
||||||
|
case CodeCompletionString::CK_VerticalSpace:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ls_sig.label += Chunk.Text;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ret_type) {
|
||||||
|
ls_sig.label += " -> ";
|
||||||
|
ls_sig.label += ret_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort(
|
||||||
|
ls_sighelp.signatures.begin(), ls_sighelp.signatures.end(),
|
||||||
|
[](const lsSignatureInformation &l, const lsSignatureInformation &r) {
|
||||||
|
if (l.parameters.size() != r.parameters.size())
|
||||||
|
return l.parameters.size() < r.parameters.size();
|
||||||
|
if (l.label.size() != r.label.size())
|
||||||
|
return l.label.size() < r.label.size();
|
||||||
|
return l.label < r.label;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
||||||
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Handler_TextDocumentSignatureHelp
|
||||||
|
: BaseMessageHandler<In_TextDocumentSignatureHelp> {
|
||||||
MethodType GetMethodType() const override { return kMethodType; }
|
MethodType GetMethodType() const override { return kMethodType; }
|
||||||
|
|
||||||
void Run(std::unique_ptr<InMessage> message) override {
|
void Run(In_TextDocumentSignatureHelp *request) override {
|
||||||
auto request = static_cast<In_TextDocumentSignatureHelp *>(message.get());
|
static CompleteConsumerCache<lsSignatureHelp> cache;
|
||||||
lsTextDocumentPositionParams ¶ms = request->params;
|
|
||||||
WorkingFile *file =
|
auto ¶ms = request->params;
|
||||||
working_files->GetFileByFilename(params.textDocument.uri.GetPath());
|
std::string path = params.textDocument.uri.GetPath();
|
||||||
std::string search;
|
if (WorkingFile *file = working_files->GetFileByFilename(path)) {
|
||||||
int active_param = 0;
|
std::string completion_text;
|
||||||
if (file) {
|
lsPosition end_pos = params.position;
|
||||||
lsPosition completion_position;
|
params.position = file->FindStableCompletionSource(
|
||||||
search = file->FindClosestCallNameInBuffer(params.position, &active_param,
|
request->params.position, &completion_text, &end_pos);
|
||||||
&completion_position);
|
|
||||||
params.position = completion_position;
|
|
||||||
}
|
}
|
||||||
if (search.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
CompletionManager::OnComplete callback = std::bind(
|
|
||||||
[this](InMessage *message, std::string search, int active_param,
|
|
||||||
const std::vector<lsCompletionItem> &results,
|
|
||||||
bool is_cached_result) {
|
|
||||||
auto msg = static_cast<In_TextDocumentSignatureHelp *>(message);
|
|
||||||
|
|
||||||
|
CompletionManager::OnComplete callback =
|
||||||
|
[id = request->id,
|
||||||
|
params = request->params](CodeCompleteConsumer *OptConsumer) {
|
||||||
|
if (!OptConsumer)
|
||||||
|
return;
|
||||||
|
auto *Consumer = static_cast<SignatureHelpConsumer *>(OptConsumer);
|
||||||
Out_TextDocumentSignatureHelp out;
|
Out_TextDocumentSignatureHelp out;
|
||||||
out.id = msg->id;
|
out.id = id;
|
||||||
|
out.result = Consumer->ls_sighelp;
|
||||||
for (auto &result : results) {
|
|
||||||
if (result.label != search)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
lsSignatureInformation signature;
|
|
||||||
signature.label = result.detail;
|
|
||||||
for (auto ¶meter : result.parameters_) {
|
|
||||||
lsParameterInformation ls_param;
|
|
||||||
ls_param.label = parameter;
|
|
||||||
signature.parameters.push_back(ls_param);
|
|
||||||
}
|
|
||||||
out.result.signatures.push_back(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prefer the signature with least parameter count but still larger
|
|
||||||
// than active_param.
|
|
||||||
out.result.activeSignature = 0;
|
|
||||||
if (out.result.signatures.size()) {
|
|
||||||
size_t num_parameters = SIZE_MAX;
|
|
||||||
for (size_t i = 0; i < out.result.signatures.size(); ++i) {
|
|
||||||
size_t t = out.result.signatures[i].parameters.size();
|
|
||||||
if (active_param < t && t < num_parameters) {
|
|
||||||
out.result.activeSignature = int(i);
|
|
||||||
num_parameters = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set signature to what we parsed from the working file.
|
|
||||||
out.result.activeParameter = active_param;
|
|
||||||
|
|
||||||
pipeline::WriteStdout(kMethodType, out);
|
pipeline::WriteStdout(kMethodType, out);
|
||||||
|
if (!Consumer->from_cache) {
|
||||||
if (!is_cached_result) {
|
std::string path = params.textDocument.uri.GetPath();
|
||||||
signature_cache->WithLock([&]() {
|
cache.WithLock([&]() {
|
||||||
signature_cache->cached_path_ =
|
cache.path = path;
|
||||||
msg->params.textDocument.uri.GetPath();
|
cache.position = params.position;
|
||||||
signature_cache->cached_completion_position_ =
|
cache.result = Consumer->ls_sighelp;
|
||||||
msg->params.position;
|
|
||||||
signature_cache->cached_results_ = results;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
delete message;
|
CodeCompleteOptions CCOpts;
|
||||||
},
|
CCOpts.IncludeGlobals = false;
|
||||||
message.release(), search, active_param, std::placeholders::_1,
|
CCOpts.IncludeMacros = false;
|
||||||
std::placeholders::_2);
|
CCOpts.IncludeBriefComments = false;
|
||||||
|
if (cache.IsCacheValid(params)) {
|
||||||
if (signature_cache->IsCacheValid(params)) {
|
SignatureHelpConsumer Consumer(CCOpts, true);
|
||||||
signature_cache->WithLock([&]() {
|
cache.WithLock([&]() { Consumer.ls_sighelp = cache.result; });
|
||||||
callback(signature_cache->cached_results_, true /*is_cached_result*/);
|
callback(&Consumer);
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
clang_complete->CodeComplete(request->id, params, std::move(callback));
|
clang_complete->completion_request_.PushBack(
|
||||||
|
std::make_unique<CompletionManager::CompletionRequest>(
|
||||||
|
request->id, params.textDocument, params.position,
|
||||||
|
std::make_unique<SignatureHelpConsumer>(CCOpts, false), CCOpts,
|
||||||
|
callback));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -474,9 +474,6 @@ void MainLoop() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
IncludeComplete include_complete(&project);
|
IncludeComplete include_complete(&project);
|
||||||
auto global_code_complete_cache = std::make_unique<CodeCompleteCache>();
|
|
||||||
auto non_global_code_complete_cache = std::make_unique<CodeCompleteCache>();
|
|
||||||
auto signature_cache = std::make_unique<CodeCompleteCache>();
|
|
||||||
DB db;
|
DB db;
|
||||||
|
|
||||||
// Setup shared references.
|
// Setup shared references.
|
||||||
@ -490,10 +487,6 @@ void MainLoop() {
|
|||||||
handler->working_files = &working_files;
|
handler->working_files = &working_files;
|
||||||
handler->clang_complete = &clang_complete;
|
handler->clang_complete = &clang_complete;
|
||||||
handler->include_complete = &include_complete;
|
handler->include_complete = &include_complete;
|
||||||
handler->global_code_complete_cache = global_code_complete_cache.get();
|
|
||||||
handler->non_global_code_complete_cache =
|
|
||||||
non_global_code_complete_cache.get();
|
|
||||||
handler->signature_cache = signature_cache.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -375,31 +375,17 @@ std::string WorkingFile::FindClosestCallNameInBuffer(
|
|||||||
return buffer_content.substr(offset, start_offset - offset + 1);
|
return buffer_content.substr(offset, start_offset - offset + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lsPosition WorkingFile::FindStableCompletionSource(
|
lsPosition
|
||||||
lsPosition position, bool *is_global_completion,
|
WorkingFile::FindStableCompletionSource(lsPosition position,
|
||||||
std::string *existing_completion, lsPosition *replace_end_pos) const {
|
std::string *existing_completion,
|
||||||
*is_global_completion = true;
|
lsPosition *replace_end_pos) const {
|
||||||
|
|
||||||
int start_offset = GetOffsetForPosition(position, buffer_content);
|
int start_offset = GetOffsetForPosition(position, buffer_content);
|
||||||
int offset = start_offset;
|
int offset = start_offset;
|
||||||
|
|
||||||
while (offset > 0) {
|
while (offset > 0) {
|
||||||
char c = buffer_content[offset - 1];
|
char c = buffer_content[offset - 1];
|
||||||
if (!isalnum(c) && c != '_') {
|
if (!isalnum(c) && c != '_')
|
||||||
// Global completion is everything except for dot (.), arrow (->), and
|
|
||||||
// double colon (::)
|
|
||||||
if (c == '.')
|
|
||||||
*is_global_completion = false;
|
|
||||||
if (offset > 2) {
|
|
||||||
char pc = buffer_content[offset - 2];
|
|
||||||
if (pc == ':' && c == ':')
|
|
||||||
*is_global_completion = false;
|
|
||||||
else if (pc == '-' && c == '>')
|
|
||||||
*is_global_completion = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
--offset;
|
--offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,6 @@ struct WorkingFile {
|
|||||||
// The out param |existing_completion| is set to any existing completion
|
// The out param |existing_completion| is set to any existing completion
|
||||||
// content the user has entered.
|
// content the user has entered.
|
||||||
lsPosition FindStableCompletionSource(lsPosition position,
|
lsPosition FindStableCompletionSource(lsPosition position,
|
||||||
bool *is_global_completion,
|
|
||||||
std::string *existing_completion,
|
std::string *existing_completion,
|
||||||
lsPosition *replace_end_pos) const;
|
lsPosition *replace_end_pos) const;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user