Enable DocumentLink (#168)

Co-authored-by: star9029 <hengxings783@gmail.com>
This commit is contained in:
ykiko
2025-08-06 10:58:04 +08:00
committed by GitHub
parent 072ddb3a59
commit 64e505d699
15 changed files with 191 additions and 75 deletions

View File

@@ -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;

View File

@@ -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()) {

View File

@@ -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);
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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;

View File

@@ -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");
}