#include "Feature/Formatting.h" #include "Server/Server.h" #include "Server/Convert.h" #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" #include "Feature/SemanticToken.h" #include "Feature/InlayHint.h" namespace clice { auto Server::on_completion(proto::CompletionParams params) -> Result { 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 items = feature::code_complete(params, {}); return proto::to_json(kind, content, items); }); } } auto Server::on_hover(proto::HoverParams params) -> Result { auto path = mapping.to_path(params.textDocument.uri); auto opening_file = opening_files.get_or_add(path); auto guard = co_await opening_file->ast_built_lock.try_lock(); auto offset = to_offset(kind, opening_file->content, params.position); 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 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); }); } } auto Server::on_document_symbol(proto::DocumentSymbolParams params) -> Result { auto path = mapping.to_path(params.textDocument.uri); auto opening_file = opening_files.get_or_add(path); auto guard = co_await opening_file->ast_built_lock.try_lock(); auto ast = opening_file->ast; if(!ast) { co_return json::Value(nullptr); } llvm::StringRef content = ast->interested_content(); auto to_range = [&](LocalSourceRange range) { auto c = PositionConverter(content, kind); auto begin = c.toPosition(range.begin); auto end = c.toPosition(range.end); return proto::Range{begin, end}; }; auto transform = [&to_range](this auto& self, feature::DocumentSymbol& symbol) -> proto::DocumentSymbol { proto::DocumentSymbol result; result.name = std::move(symbol.name); result.detail = std::move(symbol.detail); /// FIXME: Add kind map. result.kind = static_cast(symbol.kind.value()); result.range = to_range(symbol.range); result.selectionRange = to_range(symbol.selectionRange); for(auto& child: symbol.children) { result.children.emplace_back(self(child)); } return result; }; co_return co_await async::submit([&ast, &transform] { auto symbols = feature::document_symbols(*ast); std::vector result; for(auto& symbol: symbols) { result.emplace_back(transform(symbol)); } return json::serialize(result); }); } auto Server::on_document_link(proto::DocumentLinkParams params) -> Result { auto path = mapping.to_path(params.textDocument.uri); auto opening_file = opening_files.get_or_add(path); auto guard = co_await opening_file->ast_built_lock.try_lock(); 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()); llvm::StringRef content = ast->interested_content(); PositionConverter converter(content, kind); converter.to_positions(links, [](feature::DocumentLink& link) { return link.range; }); std::vector result; for(auto& link: links) { result.emplace_back(converter.lookup(link.range), mapping.to_uri(link.file)); } return json::serialize(result); }); } auto Server::on_document_format(proto::DocumentFormattingParams params) -> Result { auto path = mapping.to_path(params.textDocument.uri); auto opening_file = opening_files.get_or_add(path); auto content = opening_file->content; co_return co_await async::submit([&, kind = this->kind] { auto edits = feature::document_format(path, content, std::nullopt); /// FIXME: adjust position encoding... return json::serialize(edits); }); } auto Server::on_document_range_format(proto::DocumentRangeFormattingParams params) -> Result { auto path = mapping.to_path(params.textDocument.uri); auto opening_file = opening_files.get_or_add(path); auto content = opening_file->content; co_return co_await async::submit([&, kind = this->kind] { auto begin = to_offset(kind, content, params.range.start); auto end = to_offset(kind, content, params.range.end); auto edits = feature::document_format(path, content, LocalSourceRange(begin, end)); /// FIXME: adjust position encoding... return json::serialize(edits); }); } async::Task Server::on_folding_range(proto::FoldingRangeParams params) { auto path = mapping.to_path(params.textDocument.uri); auto opening_file = opening_files.get_or_add(path); auto guard = co_await opening_file->ast_built_lock.try_lock(); auto ast = opening_file->ast; if(!ast) { co_return json::Value(nullptr); } co_return co_await async::submit([&, kind = this->kind] { llvm::StringRef content = ast->interested_content(); auto foldings = feature::folding_ranges(*ast); PositionConverter converter(content, kind); converter.to_positions(foldings, [](feature::FoldingRange& folding) { return folding.range; }); std::vector result; for(auto&& folding: foldings) { auto [begin_offset, end_offset] = folding.range; auto [begin_line, begin_char] = converter.lookup(begin_offset); auto [end_line, end_char] = converter.lookup(end_offset); proto::FoldingRange range; range.startLine = begin_line; range.startCharacter = begin_char; range.endLine = end_line; range.endCharacter = end_char; range.kind = "region"; range.collapsedText = folding.text; result.emplace_back(std::move(range)); } return json::serialize(result); }); } auto Server::on_semantic_token(proto::SemanticTokensParams params) -> Result { auto path = mapping.to_path(params.textDocument.uri); auto opening_file = opening_files.get_or_add(path); auto guard = co_await opening_file->ast_built_lock.try_lock(); 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); }); } auto Server::on_inlay_hint(proto::InlayHintParams params) -> Result { auto path = mapping.to_path(params.textDocument.uri); auto opening_file = opening_files.get_or_add(path); auto guard = co_await opening_file->ast_built_lock.try_lock(); auto ast = opening_file->ast; if(!ast) { co_return json::Value(nullptr); } co_return co_await async::submit([kind = this->kind, ¶ms, &ast] { auto content = ast->interested_content(); LocalSourceRange range{ to_offset(kind, content, params.range.start), to_offset(kind, content, params.range.end), }; auto hints = feature::inlay_hint(*ast, range, {}); PositionConverter converter(content, kind); std::vector result; for(auto& hint: hints) { auto& back = result.emplace_back(converter.toPosition(hint.offset)); back.label.emplace_back(std::move(hint.parts[0].name)); /// FIXME: Determine the set of possible kinds; for now, we'll use Type. back.kind = proto::InlayHintKind::Type; } return json::serialize(result); }); } } // namespace clice