Enable SignatureHelp (#187)
This commit is contained in:
@@ -281,6 +281,25 @@ const clang::NamedDecl* decl_of_impl(const void* T) {
|
||||
}
|
||||
|
||||
auto decl_of(clang::QualType type) -> const clang::NamedDecl* {
|
||||
if(auto TST = type->getAs<clang::TemplateSpecializationType>()) {
|
||||
auto decl = TST->getTemplateName().getAsTemplateDecl();
|
||||
if(type->isDependentType()) {
|
||||
return decl;
|
||||
}
|
||||
|
||||
/// For a template specialization type, the template name is possibly a `ClassTemplateDecl`
|
||||
/// `TypeAliasTemplateDecl` or `TemplateTemplateParmDecl` and `BuiltinTemplateDecl`.
|
||||
if(llvm::isa<clang::TypeAliasTemplateDecl>(decl)) {
|
||||
return decl->getTemplatedDecl();
|
||||
}
|
||||
|
||||
if(llvm::isa<clang::TemplateTemplateParmDecl, clang::BuiltinTemplateDecl>(decl)) {
|
||||
return decl;
|
||||
}
|
||||
|
||||
return instantiated_from(TST->getAsCXXRecordDecl());
|
||||
}
|
||||
|
||||
switch(type->getTypeClass()) {
|
||||
#define ABSTRACT_TYPE(TY, BASE)
|
||||
#define TYPE(TY, BASE) \
|
||||
@@ -288,27 +307,6 @@ auto decl_of(clang::QualType type) -> const clang::NamedDecl* {
|
||||
#include "clang/AST/TypeNodes.inc"
|
||||
}
|
||||
|
||||
/// FIXME: Handle Template Specialization type in the future
|
||||
/// if(auto TST = type->getAs<clang::TemplateSpecializationType>()) {
|
||||
/// auto decl = TST->getTemplateName().getAsTemplateDecl();
|
||||
/// if(type->isDependentType()) {
|
||||
/// return decl;
|
||||
/// }
|
||||
///
|
||||
/// /// For a template specialization type, the template name is possibly a
|
||||
/// `ClassTemplateDecl`
|
||||
/// /// `TypeAliasTemplateDecl` or `TemplateTemplateParmDecl` and `BuiltinTemplateDecl`.
|
||||
/// if(llvm::isa<clang::TypeAliasTemplateDecl>(decl)) {
|
||||
/// return decl->getTemplatedDecl();
|
||||
/// }
|
||||
///
|
||||
/// if(llvm::isa<clang::TemplateTemplateParmDecl, clang::BuiltinTemplateDecl>(decl)) {
|
||||
/// return decl;
|
||||
/// }
|
||||
///
|
||||
/// return instantiated_from(TST->getAsCXXRecordDecl());
|
||||
///}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,57 +1,179 @@
|
||||
#include "Compiler/Compilation.h"
|
||||
#include "Feature/SignatureHelp.h"
|
||||
#include "clang/Sema/Sema.h"
|
||||
#include "clang/Sema/CodeCompleteConsumer.h"
|
||||
|
||||
namespace clice::feature {
|
||||
|
||||
namespace {
|
||||
|
||||
class SignatureHelpCollector final : public clang::CodeCompleteConsumer {
|
||||
class Collector final : public clang::CodeCompleteConsumer {
|
||||
public:
|
||||
SignatureHelpCollector(clang::CodeCompleteOptions options) :
|
||||
clang::CodeCompleteConsumer(options), allocator(new clang::GlobalCodeCompletionAllocator()),
|
||||
info(allocator) {}
|
||||
Collector(proto::SignatureHelp& help, clang::CodeCompleteOptions complete_options) :
|
||||
clang::CodeCompleteConsumer(complete_options), help(help),
|
||||
info(std::make_shared<clang::GlobalCodeCompletionAllocator>()) {}
|
||||
|
||||
void ProcessOverloadCandidates(clang::Sema& sema,
|
||||
unsigned CurrentArg,
|
||||
std::uint32_t current_arg,
|
||||
OverloadCandidate* candidates,
|
||||
unsigned count,
|
||||
clang::SourceLocation openParLoc,
|
||||
std::uint32_t candidate_count,
|
||||
clang::SourceLocation open_paren_loc,
|
||||
bool braced) final {
|
||||
llvm::outs() << "ProcessOverloadCandidates\n";
|
||||
auto range = llvm::make_range(candidates, candidates + count);
|
||||
help.signatures.reserve(candidate_count);
|
||||
|
||||
// FIXME: How can we determine the "active overload candidate"?
|
||||
// Right now the overloaded candidates seem to be provided in a "best fit"
|
||||
// order, so I'm not too worried about this.
|
||||
help.activeSignature = 0;
|
||||
|
||||
auto range = llvm::make_range(candidates, candidates + candidate_count);
|
||||
|
||||
auto policy = sema.getPrintingPolicy();
|
||||
policy.AnonymousTagLocations = false;
|
||||
policy.SuppressStrongLifetime = true;
|
||||
policy.SuppressUnwrittenScope = true;
|
||||
policy.SuppressScope = true;
|
||||
policy.CleanUglifiedParameters = true;
|
||||
// Show signatures of constructors as they are declared:
|
||||
// vector(int n) rather than vector<string>(int n)
|
||||
// This is less noisy without being less clear, and avoids tricky cases.
|
||||
policy.SuppressTemplateArgsInCXXConstructors = true;
|
||||
|
||||
for(auto& candidate: range) {
|
||||
/// 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>').
|
||||
/// FIXME: In fact, in such case, we may resugar the template arguments.
|
||||
if(auto func = candidate.getFunction()) {
|
||||
if(auto pattern = func->getTemplateInstantiationPattern()) {
|
||||
candidate = OverloadCandidate(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
llvm::SmallString<128> buffer;
|
||||
llvm::raw_svector_ostream os(buffer);
|
||||
|
||||
auto& signature = help.signatures.emplace_back();
|
||||
|
||||
/// FIXME: Handle explicit this and variadic params...
|
||||
signature.activeParameter = current_arg;
|
||||
|
||||
auto add_param = [&](auto&& param) {
|
||||
if(signature.parameters.size() > 0) {
|
||||
os << ", ";
|
||||
}
|
||||
|
||||
/// FIXME: Handle param comments in the future.
|
||||
auto& label = signature.parameters.emplace_back().label;
|
||||
label[0] = buffer.size();
|
||||
param.print(os, policy);
|
||||
label[1] = buffer.size();
|
||||
};
|
||||
|
||||
switch(candidate.getKind()) {
|
||||
case clang::CodeCompleteConsumer::OverloadCandidate::CK_Function: {
|
||||
candidate.getFunction()->dump();
|
||||
break;
|
||||
}
|
||||
case clang::CodeCompleteConsumer::OverloadCandidate::CK_Function:
|
||||
case clang::CodeCompleteConsumer::OverloadCandidate::CK_FunctionTemplate: {
|
||||
candidate.getFunctionTemplate()->dump();
|
||||
auto func = candidate.getFunction();
|
||||
func->getDeclName().print(os, policy);
|
||||
os << "(";
|
||||
/// FIXME: Handle C++23 explicit object params.
|
||||
for(auto param: func->parameters()) {
|
||||
add_param(*param);
|
||||
}
|
||||
os << ")";
|
||||
|
||||
if(!llvm::isa<clang::CXXConstructorDecl, clang::CXXDestructorDecl>(func)) {
|
||||
os << " -> ";
|
||||
func->getReturnType().print(os, policy);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case clang::CodeCompleteConsumer::OverloadCandidate::CK_FunctionType: {
|
||||
candidate.getFunctionType()->dump();
|
||||
auto type = candidate.getFunctionType();
|
||||
os << "(";
|
||||
if(auto proto = llvm::dyn_cast<clang::FunctionProtoType>(type)) {
|
||||
for(auto type: proto->param_types()) {
|
||||
add_param(type);
|
||||
}
|
||||
}
|
||||
os << ") -> ";
|
||||
type->getReturnType().print(os, policy);
|
||||
break;
|
||||
}
|
||||
|
||||
case clang::CodeCompleteConsumer::OverloadCandidate::CK_FunctionProtoTypeLoc: {
|
||||
candidate.getFunctionProtoTypeLoc().dump();
|
||||
auto loc = candidate.getFunctionProtoTypeLoc();
|
||||
os << "(";
|
||||
for(auto type: loc.getParams()) {
|
||||
add_param(*type);
|
||||
}
|
||||
os << ") -> ";
|
||||
loc.getTypePtr()->getReturnType().print(os, policy);
|
||||
break;
|
||||
}
|
||||
|
||||
case clang::CodeCompleteConsumer::OverloadCandidate::CK_Template: {
|
||||
candidate.getTemplate()->dump();
|
||||
auto decl = candidate.getTemplate();
|
||||
/// Add template name first.
|
||||
decl->getDeclName().print(os, policy);
|
||||
os << "<";
|
||||
for(auto param: *decl->getTemplateParameters()) {
|
||||
add_param(*param);
|
||||
}
|
||||
os << "> ";
|
||||
|
||||
if(auto cls = llvm::dyn_cast<clang::ClassTemplateDecl>(decl)) {
|
||||
os << "-> ";
|
||||
os << cls->getTemplatedDecl()->getKindName();
|
||||
} else if(auto func = llvm::dyn_cast<clang::FunctionTemplateDecl>(decl)) {
|
||||
os << "() -> ";
|
||||
func->getTemplatedDecl()->getReturnType().print(os, policy);
|
||||
} else if(auto type = llvm::dyn_cast<clang::TypeAliasTemplateDecl>(decl)) {
|
||||
os << "-> ";
|
||||
type->getTemplatedDecl()->getUnderlyingType().print(os, policy);
|
||||
} else if(auto var = llvm::dyn_cast<clang::VarTemplateDecl>(decl)) {
|
||||
os << "-> ";
|
||||
var->getTemplatedDecl()->getType().print(os, policy);
|
||||
} else if(auto tmp = llvm::dyn_cast<clang::TemplateTemplateParmDecl>(decl)) {
|
||||
os << "-> type";
|
||||
} else if(auto con = llvm::dyn_cast<clang::ConceptDecl>(decl)) {
|
||||
os << "-> concept";
|
||||
} else {
|
||||
std::unreachable();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case clang::CodeCompleteConsumer::OverloadCandidate::CK_Aggregate: {
|
||||
candidate.getAggregate()->dump();
|
||||
auto cls = candidate.getAggregate();
|
||||
cls->getDeclName().print(os, policy);
|
||||
os << "{";
|
||||
|
||||
if(auto type = llvm::dyn_cast<clang::CXXRecordDecl>(cls)) {
|
||||
for(auto& base: type->bases()) {
|
||||
add_param(base.getType());
|
||||
}
|
||||
}
|
||||
|
||||
for(auto field: cls->fields()) {
|
||||
add_param(*field);
|
||||
}
|
||||
os << "}";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
signature.label = buffer.str();
|
||||
}
|
||||
|
||||
/// FIXME: Sort the result according the params num and kind ...
|
||||
}
|
||||
|
||||
clang::CodeCompletionAllocator& getAllocator() final {
|
||||
return *allocator;
|
||||
return info.getAllocator();
|
||||
}
|
||||
|
||||
clang::CodeCompletionTUInfo& getCodeCompletionTUInfo() final {
|
||||
@@ -59,18 +181,30 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> allocator;
|
||||
proto::SignatureHelp& help;
|
||||
clang::CodeCompletionTUInfo info;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::vector<SignatureHelpItem> signatureHelp(CompilationParams& params,
|
||||
const config::SignatureHelpOption& option) {
|
||||
std::vector<SignatureHelpItem> items;
|
||||
auto consumer = new SignatureHelpCollector({});
|
||||
if(auto info = complete(params, consumer)) {}
|
||||
return items;
|
||||
proto::SignatureHelp signature_help(CompilationParams& params,
|
||||
const config::SignatureHelpOption& options) {
|
||||
proto::SignatureHelp help;
|
||||
|
||||
clang::CodeCompleteOptions complete_options;
|
||||
complete_options.IncludeMacros = false;
|
||||
complete_options.IncludeCodePatterns = false;
|
||||
complete_options.IncludeGlobals = false;
|
||||
complete_options.IncludeNamespaceLevelDecls = false;
|
||||
complete_options.IncludeBriefComments = false;
|
||||
complete_options.LoadExternal = true;
|
||||
complete_options.IncludeFixIts = false;
|
||||
|
||||
auto consumer = new Collector(help, complete_options);
|
||||
if(auto info = complete(params, consumer)) {
|
||||
/// FIXME: do something.
|
||||
}
|
||||
return help;
|
||||
}
|
||||
|
||||
} // namespace clice::feature
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Compiler/Compilation.h"
|
||||
#include "Feature/CodeCompletion.h"
|
||||
#include "Feature/Hover.h"
|
||||
#include "Feature/SignatureHelp.h"
|
||||
#include "Feature/DocumentLink.h"
|
||||
#include "Feature/DocumentSymbol.h"
|
||||
#include "Feature/FoldingRange.h"
|
||||
@@ -59,6 +60,32 @@ async::Task<json::Value> Server::on_hover(proto::HoverParams params) {
|
||||
});
|
||||
}
|
||||
|
||||
async::Task<json::Value> Server::on_signature_help(proto::SignatureHelpParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
auto opening_file = opening_files.get_or_add(path);
|
||||
|
||||
if(!opening_file->pch_build_task.empty()) {
|
||||
co_await opening_file->pch_built_event;
|
||||
}
|
||||
|
||||
auto& content = opening_file->content;
|
||||
auto offset = to_offset(kind, content, params.position);
|
||||
auto& pch = opening_file->pch;
|
||||
{
|
||||
/// Set compilation params ... .
|
||||
CompilationParams params;
|
||||
params.arguments = database.get_command(path, true).arguments;
|
||||
params.add_remapped_file(path, content);
|
||||
params.pch = {pch->path, pch->preamble.size()};
|
||||
params.completion = {path, offset};
|
||||
|
||||
co_return co_await async::submit([kind = this->kind, &content, ¶ms] {
|
||||
auto help = feature::signature_help(params, {});
|
||||
return json::serialize(help);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async::Task<json::Value> Server::on_document_symbol(proto::DocumentSymbolParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
auto opening_file = opening_files.get_or_add(path);
|
||||
|
||||
@@ -53,6 +53,9 @@ async::Task<json::Value> Server::on_initialize(proto::InitializeParams params) {
|
||||
/// Hover
|
||||
capabilities.hoverProvider = true;
|
||||
|
||||
/// SignatureHelp
|
||||
capabilities.signatureHelpProvider.triggerCharacters = {"(", ")", "{", "}", "<", ">", ","};
|
||||
|
||||
/// DocumentSymbol
|
||||
capabilities.documentSymbolProvider = {};
|
||||
|
||||
|
||||
@@ -108,6 +108,7 @@ Server::Server() {
|
||||
|
||||
register_callback<&Server::on_completion>("textDocument/completion");
|
||||
register_callback<&Server::on_hover>("textDocument/hover");
|
||||
register_callback<&Server::on_signature_help>("textDocument/signatureHelp");
|
||||
register_callback<&Server::on_document_symbol>("textDocument/documentSymbol");
|
||||
register_callback<&Server::on_document_link>("textDocument/documentLink");
|
||||
register_callback<&Server::on_folding_range>("textDocument/foldingRange");
|
||||
|
||||
Reference in New Issue
Block a user