mirror of
https://github.com/MaskRay/ccls.git
synced 2025-04-17 22:32:13 +00:00
202 lines
6.9 KiB
C++
202 lines
6.9 KiB
C++
// Copyright 2017-2018 ccls Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#include "message_handler.hh"
|
|
#include "pipeline.hh"
|
|
#include "sema_manager.hh"
|
|
|
|
#include <clang/Sema/Sema.h>
|
|
|
|
namespace ccls {
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
struct ParameterInformation {
|
|
std::vector<int> label;
|
|
};
|
|
struct SignatureInformation {
|
|
std::string label;
|
|
std::optional<std::string> documentation;
|
|
std::vector<ParameterInformation> parameters;
|
|
};
|
|
struct SignatureHelp {
|
|
std::vector<SignatureInformation> signatures;
|
|
int activeSignature = 0;
|
|
int activeParameter = 0;
|
|
};
|
|
REFLECT_STRUCT(ParameterInformation, label);
|
|
REFLECT_STRUCT(SignatureInformation, label, documentation, parameters);
|
|
REFLECT_STRUCT(SignatureHelp, signatures, activeSignature, activeParameter);
|
|
|
|
void buildOptional(const CodeCompletionString &ccs, std::string &label,
|
|
std::vector<ParameterInformation> &ls_params) {
|
|
for (const auto &chunk : ccs) {
|
|
switch (chunk.Kind) {
|
|
case CodeCompletionString::CK_Optional:
|
|
buildOptional(*chunk.Optional, label, 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.
|
|
int off = (int)label.size();
|
|
label += chunk.Text;
|
|
ls_params.push_back({{off, (int)label.size()}});
|
|
break;
|
|
}
|
|
case CodeCompletionString::CK_VerticalSpace:
|
|
break;
|
|
default:
|
|
label += chunk.Text;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
class SignatureHelpConsumer : public CodeCompleteConsumer {
|
|
std::shared_ptr<GlobalCodeCompletionAllocator> alloc;
|
|
CodeCompletionTUInfo cCTUInfo;
|
|
|
|
public:
|
|
bool from_cache;
|
|
SignatureHelp ls_sighelp;
|
|
SignatureHelpConsumer(const clang::CodeCompleteOptions &opts, bool from_cache)
|
|
:
|
|
#if LLVM_VERSION_MAJOR >= 9 // rC358696
|
|
CodeCompleteConsumer(opts),
|
|
#else
|
|
CodeCompleteConsumer(opts, false),
|
|
#endif
|
|
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
|
|
#if LLVM_VERSION_MAJOR >= 14
|
|
,
|
|
bool braced
|
|
#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 =
|
|
#if LLVM_VERSION_MAJOR >= 14
|
|
cand.CreateSignatureString(currentArg, s, *alloc, cCTUInfo, true, braced);
|
|
#else
|
|
cand.CreateSignatureString(currentArg, s, *alloc, cCTUInfo, true);
|
|
#endif
|
|
|
|
const char *ret_type = nullptr;
|
|
SignatureInformation &ls_sig = ls_sighelp.signatures.emplace_back();
|
|
const RawComment *rc =
|
|
getCompletionComment(s.getASTContext(), cand.getFunction());
|
|
ls_sig.documentation = rc ? rc->getBriefText(s.getASTContext()) : "";
|
|
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: {
|
|
int off = (int)ls_sig.label.size();
|
|
ls_sig.label += chunk.Text;
|
|
ls_sig.parameters.push_back({{off, (int)ls_sig.label.size()}});
|
|
break;
|
|
}
|
|
case CodeCompletionString::CK_Optional:
|
|
buildOptional(*chunk.Optional, ls_sig.label, 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 SignatureInformation &l, const SignatureInformation &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; }
|
|
};
|
|
} // namespace
|
|
|
|
void MessageHandler::textDocument_signatureHelp(
|
|
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
|
static CompleteConsumerCache<SignatureHelp> cache;
|
|
Position begin_pos = param.position;
|
|
std::string path = param.textDocument.uri.getPath();
|
|
WorkingFile *wf = wfiles->getFile(path);
|
|
if (!wf) {
|
|
reply.notOpened(path);
|
|
return;
|
|
}
|
|
std::string buffer_line;
|
|
if (param.position.line >= 0 && param.position.line < wf->buffer_lines.size())
|
|
buffer_line = wf->buffer_lines[param.position.line];
|
|
{
|
|
std::string filter;
|
|
begin_pos = wf->getCompletionPosition(param.position, &filter);
|
|
}
|
|
|
|
SemaManager::OnComplete callback =
|
|
[reply, path, begin_pos, buffer_line](CodeCompleteConsumer *optConsumer) {
|
|
if (!optConsumer)
|
|
return;
|
|
auto *consumer = static_cast<SignatureHelpConsumer *>(optConsumer);
|
|
reply(consumer->ls_sighelp);
|
|
if (!consumer->from_cache) {
|
|
cache.withLock([&]() {
|
|
cache.path = path;
|
|
cache.line = buffer_line;
|
|
cache.position = begin_pos;
|
|
cache.result = consumer->ls_sighelp;
|
|
});
|
|
}
|
|
};
|
|
|
|
CodeCompleteOptions ccOpts;
|
|
ccOpts.IncludeGlobals = false;
|
|
ccOpts.IncludeMacros = false;
|
|
ccOpts.IncludeBriefComments = true;
|
|
if (cache.isCacheValid(path, buffer_line, begin_pos)) {
|
|
SignatureHelpConsumer consumer(ccOpts, true);
|
|
cache.withLock([&]() { consumer.ls_sighelp = cache.result; });
|
|
callback(&consumer);
|
|
} else {
|
|
manager->comp_tasks.pushBack(std::make_unique<SemaManager::CompTask>(
|
|
reply.id, param.textDocument.uri.getPath(), param.position,
|
|
std::make_unique<SignatureHelpConsumer>(ccOpts, false), ccOpts,
|
|
callback));
|
|
}
|
|
}
|
|
} // namespace ccls
|