Enable DocumentLink (#168)
Co-authored-by: star9029 <hengxings783@gmail.com>
This commit is contained in:
@@ -152,8 +152,8 @@ template <typename Action>
|
||||
CompilationResult run_clang(CompilationParams& params,
|
||||
const auto& before_execute,
|
||||
const auto& after_execute) {
|
||||
auto diagnostics = params.diagnostics ? std::move(params.diagnostics)
|
||||
: std::make_shared<std::vector<Diagnostic>>();
|
||||
auto diagnostics =
|
||||
params.diagnostics ? params.diagnostics : std::make_shared<std::vector<Diagnostic>>();
|
||||
auto diagnostic_engine =
|
||||
clang::CompilerInstance::createDiagnostics(*params.vfs,
|
||||
new clang::DiagnosticOptions(),
|
||||
@@ -188,10 +188,10 @@ CompilationResult run_clang(CompilationParams& params,
|
||||
}
|
||||
|
||||
auto& pp = instance->getPreprocessor();
|
||||
// FIXME: clang-tidy, include-fixer, etc?
|
||||
/// FIXME: clang-tidy, include-fixer, etc?
|
||||
|
||||
// `BeginSourceFile` may create new preprocessor, so all operations related to preprocessor
|
||||
// should be done after `BeginSourceFile`.
|
||||
/// `BeginSourceFile` may create new preprocessor, so all operations related to preprocessor
|
||||
/// should be done after `BeginSourceFile`.
|
||||
|
||||
/// Collect directives.
|
||||
llvm::DenseMap<clang::FileID, Directive> directives;
|
||||
|
||||
@@ -7,9 +7,38 @@ namespace clice::feature {
|
||||
|
||||
namespace {}
|
||||
|
||||
DocumentLinks documentLinks(CompilationUnit& unit);
|
||||
DocumentLinks document_links(CompilationUnit& unit) {
|
||||
DocumentLinks links;
|
||||
|
||||
index::Shared<DocumentLinks> indexDocumentLink(CompilationUnit& unit) {
|
||||
auto& interested_diretive = unit.directives()[unit.interested_file()];
|
||||
for(auto include: interested_diretive.includes) {
|
||||
auto [_, range] = unit.decompose_range(include.filename_range);
|
||||
links.emplace_back(range, unit.file_path(include.fid).str());
|
||||
}
|
||||
|
||||
auto content = unit.interested_content();
|
||||
for(auto& has_include: interested_diretive.has_includes) {
|
||||
/// If the include path is empty, skip it.
|
||||
if(has_include.fid.isInvalid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto location = has_include.location;
|
||||
auto [_, offset] = unit.decompose_location(location);
|
||||
|
||||
/// FIXME: handle incomplete code, the <> or "" may not be in pair.
|
||||
auto sub_content = content.substr(offset);
|
||||
char c = sub_content[0] == '<' ? '>' : '"';
|
||||
std::uint32_t end_offset = offset + sub_content.find_first_of(c, 1) + 1;
|
||||
|
||||
links.emplace_back(LocalSourceRange{offset, end_offset},
|
||||
unit.file_path(has_include.fid).str());
|
||||
}
|
||||
|
||||
return links;
|
||||
}
|
||||
|
||||
index::Shared<DocumentLinks> index_document_link(CompilationUnit& unit) {
|
||||
index::Shared<DocumentLinks> result;
|
||||
|
||||
for(auto& [fid, diretives]: unit.directives()) {
|
||||
|
||||
@@ -69,7 +69,7 @@ Shared<std::vector<char>> FeatureIndex::build(CompilationUnit& unit) {
|
||||
indices[fid].foldings = std::move(result);
|
||||
}
|
||||
|
||||
for(auto&& [fid, result]: feature::indexDocumentLink(unit)) {
|
||||
for(auto&& [fid, result]: feature::index_document_link(unit)) {
|
||||
indices[fid].links = std::move(result);
|
||||
}
|
||||
|
||||
|
||||
@@ -73,6 +73,8 @@ async::Task<> Server::build_pch(std::string path, std::string content) {
|
||||
|
||||
log::info("Start building PCH for {}, command: [{}]", path, command);
|
||||
|
||||
std::vector<feature::DocumentLink> links;
|
||||
|
||||
/// PCH file is written until destructing, Add a single block
|
||||
/// for it.
|
||||
bool cond = co_await async::submit([&] {
|
||||
@@ -86,6 +88,8 @@ async::Task<> Server::build_pch(std::string path, std::string content) {
|
||||
return false;
|
||||
}
|
||||
|
||||
links = feature::document_links(*result);
|
||||
|
||||
/// TODO: index PCH.
|
||||
|
||||
return true;
|
||||
@@ -98,6 +102,8 @@ async::Task<> Server::build_pch(std::string path, std::string content) {
|
||||
auto& openFile = server.opening_files[path];
|
||||
/// Update the built PCH info.
|
||||
openFile.pch = std::move(info);
|
||||
openFile.pch_includes = std::move(links);
|
||||
|
||||
/// Resume waiters on this event.
|
||||
openFile.pch_built_event.set();
|
||||
openFile.pch_built_event.clear();
|
||||
|
||||
@@ -1,55 +1,13 @@
|
||||
#include "Server/Server.h"
|
||||
#include "Server/Convert.h"
|
||||
#include "Compiler/Compilation.h"
|
||||
#include "Feature/CodeCompletion.h"
|
||||
#include "Feature/Hover.h"
|
||||
#include "Feature/DocumentLink.h"
|
||||
#include "Feature/SemanticToken.h"
|
||||
|
||||
namespace clice {
|
||||
|
||||
async::Task<json::Value> Server::on_hover(proto::HoverParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
|
||||
auto opening_file = &opening_files[path];
|
||||
auto guard = co_await opening_file->ast_built_lock.try_lock();
|
||||
|
||||
auto offset = to_offset(kind, opening_file->content, params.position);
|
||||
|
||||
opening_file = &opening_files[path];
|
||||
auto content = opening_file->content;
|
||||
auto ast = opening_file->ast;
|
||||
if(!ast) {
|
||||
co_return json::Value(nullptr);
|
||||
}
|
||||
|
||||
co_return co_await async::submit([kind = this->kind, offset, &ast] {
|
||||
auto hover = feature::hover(*ast, offset);
|
||||
|
||||
proto::Hover result;
|
||||
result.contents.kind = "markdown";
|
||||
result.contents.value = std::format("{}: {}", hover.kind.name(), hover.name);
|
||||
|
||||
return json::serialize(result);
|
||||
});
|
||||
}
|
||||
|
||||
async::Task<json::Value> Server::on_semantic_token(proto::SemanticTokensParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
|
||||
auto opening_file = &opening_files[path];
|
||||
auto guard = co_await opening_file->ast_built_lock.try_lock();
|
||||
|
||||
opening_file = &opening_files[path];
|
||||
auto content = opening_file->content;
|
||||
auto ast = opening_file->ast;
|
||||
if(!ast) {
|
||||
co_return json::Value(nullptr);
|
||||
}
|
||||
|
||||
co_return co_await async::submit([kind = this->kind, &ast] {
|
||||
auto tokens = feature::semantic_tokens(*ast);
|
||||
return proto::to_json(kind, ast->interested_content(), tokens);
|
||||
});
|
||||
}
|
||||
|
||||
async::Task<json::Value> Server::on_completion(proto::CompletionParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
auto opening_file = &opening_files[path];
|
||||
@@ -78,4 +36,80 @@ async::Task<json::Value> Server::on_completion(proto::CompletionParams params) {
|
||||
}
|
||||
}
|
||||
|
||||
async::Task<json::Value> Server::on_hover(proto::HoverParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
|
||||
auto opening_file = &opening_files[path];
|
||||
auto guard = co_await opening_file->ast_built_lock.try_lock();
|
||||
|
||||
auto offset = to_offset(kind, opening_file->content, params.position);
|
||||
|
||||
opening_file = &opening_files[path];
|
||||
auto content = opening_file->content;
|
||||
auto ast = opening_file->ast;
|
||||
if(!ast) {
|
||||
co_return json::Value(nullptr);
|
||||
}
|
||||
|
||||
co_return co_await async::submit([kind = this->kind, offset, &ast] {
|
||||
auto hover = feature::hover(*ast, offset);
|
||||
|
||||
proto::Hover result;
|
||||
result.contents.kind = "markdown";
|
||||
result.contents.value = std::format("{}: {}", hover.kind.name(), hover.name);
|
||||
|
||||
return json::serialize(result);
|
||||
});
|
||||
}
|
||||
|
||||
async::Task<json::Value> Server::on_document_link(proto::DocumentLinkParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
|
||||
auto opening_file = &opening_files[path];
|
||||
auto guard = co_await opening_file->ast_built_lock.try_lock();
|
||||
|
||||
opening_file = &opening_files[path];
|
||||
auto content = opening_file->content;
|
||||
auto ast = opening_file->ast;
|
||||
if(!ast) {
|
||||
co_return json::Value(nullptr);
|
||||
}
|
||||
|
||||
auto pch_links = opening_file->pch_includes;
|
||||
auto mapping = this->mapping;
|
||||
|
||||
co_return co_await async::submit([&, kind = this->kind] {
|
||||
auto links = feature::document_links(*ast);
|
||||
links.insert(links.begin(), pch_links.begin(), pch_links.end());
|
||||
|
||||
PositionConverter converter(content, kind);
|
||||
converter.to_positions(links, [](feature::DocumentLink& link) { return link.range; });
|
||||
|
||||
std::vector<proto::DocumentLink> result;
|
||||
for(auto& link: links) {
|
||||
result.emplace_back(converter.lookup(link.range), mapping.to_uri(link.file));
|
||||
}
|
||||
return json::serialize(result);
|
||||
});
|
||||
}
|
||||
|
||||
async::Task<json::Value> Server::on_semantic_token(proto::SemanticTokensParams params) {
|
||||
auto path = mapping.to_path(params.textDocument.uri);
|
||||
|
||||
auto opening_file = &opening_files[path];
|
||||
auto guard = co_await opening_file->ast_built_lock.try_lock();
|
||||
|
||||
opening_file = &opening_files[path];
|
||||
auto content = opening_file->content;
|
||||
auto ast = opening_file->ast;
|
||||
if(!ast) {
|
||||
co_return json::Value(nullptr);
|
||||
}
|
||||
|
||||
co_return co_await async::submit([kind = this->kind, &ast] {
|
||||
auto tokens = feature::semantic_tokens(*ast);
|
||||
return proto::to_json(kind, ast->interested_content(), tokens);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace clice
|
||||
|
||||
@@ -39,14 +39,17 @@ async::Task<json::Value> Server::on_initialize(proto::InitializeParams params) {
|
||||
capabilities.textDocumentSync.change = proto::TextDocumentSyncKind::Full;
|
||||
capabilities.textDocumentSync.save = true;
|
||||
|
||||
/// Hover
|
||||
capabilities.hoverProvider = true;
|
||||
|
||||
/// Completion
|
||||
capabilities.completionProvider.triggerCharacters = {".", "<", ">", ":", "\"", "/", "*"};
|
||||
capabilities.completionProvider.resolveProvider = false;
|
||||
capabilities.completionProvider.completionItem.labelDetailsSupport = true;
|
||||
|
||||
/// Hover
|
||||
capabilities.hoverProvider = true;
|
||||
|
||||
/// DocumentLink
|
||||
capabilities.documentLinkProvider.resolveProvider = false;
|
||||
|
||||
/// Semantic tokens.
|
||||
capabilities.semanticTokensProvider.range = false;
|
||||
capabilities.semanticTokensProvider.full = true;
|
||||
|
||||
@@ -63,8 +63,9 @@ Server::Server() {
|
||||
register_callback<&Server::on_did_save>("textDocument/didSave");
|
||||
register_callback<&Server::on_did_close>("textDocument/didClose");
|
||||
|
||||
register_callback<&Server::on_hover>("textDocument/hover");
|
||||
register_callback<&Server::on_completion>("textDocument/completion");
|
||||
register_callback<&Server::on_hover>("textDocument/hover");
|
||||
register_callback<&Server::on_document_link>("textDocument/documentLink");
|
||||
register_callback<&Server::on_semantic_token>("textDocument/semanticTokens/full");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user