From 633db416e60f040efbea1fa685a4ed8b5fb925cc Mon Sep 17 00:00:00 2001 From: ykiko Date: Sun, 24 Nov 2024 17:15:02 +0800 Subject: [PATCH] Some update for `Server`. --- include/Server/Async.h | 287 +++++++++++++++---------- include/Server/Logger.h | 1 + include/Server/Server.h | 211 +++++++++--------- src/Compiler/Compiler.cpp | 2 +- src/Server/Server.cpp | 442 +++++++++++++++++++++++--------------- 5 files changed, 540 insertions(+), 403 deletions(-) diff --git a/include/Server/Async.h b/include/Server/Async.h index 2910ad3e..bc7f065c 100644 --- a/include/Server/Async.h +++ b/include/Server/Async.h @@ -18,20 +18,16 @@ #include #include -namespace clice { +namespace clice::async { template struct promise; -} - -namespace clice::async { - extern uv_loop_t* loop; template T& uv_cast(U* u) { - assert(u->data && "uv_cast: invalid uv handle"); + assert(u && u->data && "uv_cast: invalid uv handle"); return *static_cast(u->data); } @@ -62,8 +58,8 @@ struct result { } template - void return_value(T&& value) noexcept { - new (&value) Value(std::forward(value)); + void return_value(T&& val) noexcept { + new (&value) Value(std::forward(val)); } }; @@ -96,138 +92,131 @@ void schedule(promise promise) { schedule(promise.handle()); } +template > +struct task_awaiter : result { + std::remove_cvref_t task; + std::coroutine_handle<> caller; + + void await_suspend(std::coroutine_handle<> caller) noexcept { + static_assert(!std::is_reference_v, "return type must not be a reference"); + this->caller = caller; + uv_work_t* work = new uv_work_t{.data = this}; + uv_queue_work( + loop, + work, + [](uv_work_t* work) { + auto& awaiter = uv_cast(work); + if constexpr(!std::is_void_v) { + new (&awaiter.value) Ret(awaiter.task()); + } else { + awaiter.task(); + } + }, + [](uv_work_t* work, int status) { + auto& awaiter = uv_cast(work); + awaiter.caller.resume(); + delete work; + }); + } +}; + /// Schedule a task to run in the thread pool. template -auto schedule_task(Task&& task) { - using Func = std::remove_cvref_t; - using Ret = std::invoke_result_t; - - static_assert(!std::is_reference_v, "return type must not be a reference"); - - struct Awaiter : result { - Func func; - std::coroutine_handle<> caller; - - void await_suspend(std::coroutine_handle<> caller) noexcept { - this->caller = caller; - uv_work_t* work = new uv_work_t{.data = this}; - uv_queue_work( - loop, - work, - [](uv_work_t* work) { - auto& awaiter = uv_cast(work); - if constexpr(!std::is_void_v) { - new (&awaiter.value) Ret(awaiter.func()); - } else { - awaiter.func(); - } - }, - [](uv_work_t* work, int status) { - auto& awaiter = uv_cast(work); - awaiter.caller.resume(); - delete work; - }); - } - }; - - return Awaiter{{}, std::forward(task)}; +task_awaiter schedule_task(Task&& task) { + return {{}, std::forward(task)}; } -inline auto sleep(std::chrono::milliseconds ms) { - struct Awaiter { - std::chrono::milliseconds ms; - std::coroutine_handle<> caller; +struct sleep_awaiter { + std::chrono::milliseconds ms; + std::coroutine_handle<> caller; - bool await_ready() noexcept { - return ms.count() == 0; - } + bool await_ready() noexcept { + return ms.count() == 0; + } - void await_suspend(std::coroutine_handle<> caller) noexcept { - this->caller = caller; - uv_timer_t* timer = new uv_timer_t{.data = this}; - uv_timer_init(loop, timer); - uv_timer_start( - timer, - [](uv_timer_t* timer) { - auto& awaiter = uv_cast(timer); - awaiter.caller.resume(); - uv_close((uv_handle_t*)timer, [](uv_handle_t* handle) { delete handle; }); - }, - ms.count(), - 0); - } + void await_suspend(std::coroutine_handle<> caller) noexcept { + this->caller = caller; + uv_timer_t* timer = new uv_timer_t{.data = this}; + uv_timer_init(loop, timer); + uv_timer_start( + timer, + [](uv_timer_t* timer) { + auto& awaiter = uv_cast(timer); + awaiter.caller.resume(); + uv_close((uv_handle_t*)timer, [](uv_handle_t* handle) { delete handle; }); + }, + ms.count(), + 0); + } - void await_resume() noexcept {} - }; + void await_resume() noexcept {} +}; - return Awaiter{ms}; +inline sleep_awaiter sleep(std::chrono::milliseconds ms) { + return {ms}; +} + +struct fs_awaiter { + std::string path; + std::coroutine_handle<> caller; + std::chrono::system_clock::time_point modified; + + bool await_ready() noexcept { + return false; + } + + void await_suspend(std::coroutine_handle<> caller) noexcept { + this->caller = caller; + uv_fs_t* req = new uv_fs_t{.data = this}; + uv_fs_open(loop, req, path.c_str(), UV_FS_O_RDONLY, 0, [](uv_fs_t* req) { + auto& awaiter = uv_cast(req); + if(req->result < 0) { + llvm::errs() << "Error: " << uv_strerror(req->result) << "\n"; + awaiter.caller.resume(); + delete req; + return; + } + + uv_fs_close(loop, req, req->result, [](uv_fs_t* req) { + auto& awaiter = uv_cast(req); + uv_timespec_t& mtime = req->statbuf.st_mtim; + using namespace std::chrono; + awaiter.modified = + system_clock::time_point(seconds(mtime.tv_sec) + nanoseconds(mtime.tv_nsec)); + awaiter.caller.resume(); + delete req; + }); + }); + } + + decltype(auto) await_resume() noexcept { + return modified; + } +}; + +inline fs_awaiter modified_time(llvm::StringRef path) { + return {path.str()}; } template int run(Ps&&... ps) { (schedule(std::forward(ps)), ...); - return uv_run(uv_default_loop(), UV_RUN_DEFAULT); + return uv_run(loop, UV_RUN_DEFAULT); } -} // namespace clice::async - -namespace clice { - template -class promise { -public: - struct promise_type : async::result { - std::coroutine_handle<> caller; +struct awaiter { + using coroutine_handle = typename promise::coroutine_handle; - auto get_return_object() { - return promise{std::coroutine_handle::from_promise(*this)}; - } - - void unhandled_exception() { - std::abort(); - } - - std::suspend_always initial_suspend() { - return {}; - } - - auto final_suspend() noexcept { - struct FinalAwaiter { - std::coroutine_handle<> caller; - - bool await_ready() noexcept { - return false; - } - - void await_suspend(std::coroutine_handle<> self) noexcept { - self.destroy(); - /// If this coroutine is a top-level coroutine, its caller is empty. - if(!caller) { - return; - } - - /// Schedule the caller to run in the event loop. - async::schedule(caller); - } - - void await_resume() noexcept {} - }; - - return FinalAwaiter{.caller = caller}; - } - }; - - using coroutine_handle = std::coroutine_handle; - - promise(coroutine_handle handle) : h(handle) {} + coroutine_handle h; bool await_ready() noexcept { return false; } void await_suspend(std::coroutine_handle<> handle) noexcept { - this->h.promise().caller = handle; - async::schedule(*this); + h.promise().caller = handle; + async::schedule(h); } decltype(auto) await_resume() noexcept @@ -239,6 +228,68 @@ public: void await_resume() noexcept requires (std::is_void_v) {} +}; + +template +struct promise_type : result { + std::coroutine_handle<> caller; + + auto get_return_object() { + return promise{std::coroutine_handle::from_promise(*this)}; + } + + void unhandled_exception() { + std::abort(); + } + + std::suspend_always initial_suspend() { + return {}; + } + + auto final_suspend() noexcept { + struct FinalAwaiter { + std::coroutine_handle<> caller; + + bool await_ready() noexcept { + return false; + } + + void await_suspend(std::coroutine_handle<> self) noexcept { + self.destroy(); + /// If this coroutine is a top-level coroutine, its caller is empty. + if(!caller) { + return; + } + + /// Schedule the caller to run in the event loop. + async::schedule(caller); + } + + void await_resume() noexcept {} + }; + + return FinalAwaiter{.caller = caller}; + } +}; + +template +class promise { +public: + using promise_type = async::promise_type; + + using coroutine_handle = std::coroutine_handle; + + promise(coroutine_handle handle) : h(handle) {} + + promise(const promise&) = delete; + + promise(promise&& other) : h(other.h) { + other.h = nullptr; + } + + awaiter operator co_await() const noexcept { + return {h}; + } coroutine_handle handle() const noexcept { return h; @@ -248,5 +299,5 @@ private: coroutine_handle h; }; -} // namespace clice +} // namespace clice::async diff --git a/include/Server/Logger.h b/include/Server/Logger.h index 8cbef85f..fe86a1ca 100644 --- a/include/Server/Logger.h +++ b/include/Server/Logger.h @@ -4,6 +4,7 @@ #include #include +#include #include #include diff --git a/include/Server/Server.h b/include/Server/Server.h index 130b5dcb..abbc6f01 100644 --- a/include/Server/Server.h +++ b/include/Server/Server.h @@ -2,6 +2,7 @@ #include "Async.h" #include "Config.h" +#include "Logger.h" #include "Basic/Document.h" #include "Compiler/Compiler.h" #include "Support/JSON.h" @@ -125,154 +126,123 @@ namespace clice { class Server { public: - Server() { - addMethod("initialize", &Server::onInitialize); - addMethod("initialized", &Server::onInitialized); - addMethod("shutdown", &Server::onShutdown); - addMethod("exit", &Server::onExit); + Server(); - addMethod("textDocument/didOpen", &Server::onDidOpen); - addMethod("textDocument/didChange", &Server::onDidChange); - addMethod("textDocument/didSave", &Server::onDidSave); - addMethod("textDocument/didClose", &Server::onDidClose); - - addMethod("textDocument/declaration", &Server::onGotoDeclaration); - addMethod("textDocument/definition", &Server::onGotoDefinition); - addMethod("textDocument/typeDefinition", &Server::onGotoTypeDefinition); - addMethod("textDocument/implementation", &Server::onGotoImplementation); - addMethod("textDocument/references", &Server::onFindReferences); - addMethod("textDocument/callHierarchy/prepare", &Server::onPrepareCallHierarchy); - addMethod("textDocument/callHierarchy/incomingCalls", &Server::onIncomingCall); - addMethod("textDocument/callHierarchy/outgoingCalls", &Server::onOutgoingCall); - addMethod("textDocument/typeHierarchy/prepare", &Server::onPrepareTypeHierarchy); - addMethod("textDocument/typeHierarchy/supertypes", &Server::onSupertypes); - addMethod("textDocument/typeHierarchy/subtypes", &Server::onSubtypes); - addMethod("textDocument/documentHighlight", &Server::onDocumentHighlight); - addMethod("textDocument/documentLink", &Server::onDocumentLink); - addMethod("textDocument/hover", &Server::onHover); - addMethod("textDocument/codeLens", &Server::onCodeLens); - addMethod("textDocument/foldingRange", &Server::onFoldingRange); - addMethod("textDocument/documentSymbol", &Server::onDocumentSymbol); - addMethod("textDocument/semanticTokens", &Server::onSemanticTokens); - addMethod("textDocument/inlayHint", &Server::onInlayHint); - addMethod("textDocument/completion", &Server::onCodeCompletion); - addMethod("textDocument/signatureHelp", &Server::onSignatureHelp); - addMethod("textDocument/codeAction", &Server::onCodeAction); - addMethod("textDocument/formatting", &Server::onFormatting); - addMethod("textDocument/rangeFormatting", &Server::onRangeFormatting); - } - - promise dispatch(json::Value value); - - void run(int argc, const char** argv) { - auto loop = [this](json::Value value) -> promise { - co_await dispatch(std::move(value)); - }; - async::start_server(loop, "127.0.0.1", 50051); - } + void run(int argc, const char** argv); private: - using onRequest = llvm::unique_function(json::Value, json::Value)>; - using onNotification = llvm::unique_function(json::Value)>; + using onRequest = llvm::unique_function(json::Value, json::Value)>; + using onNotification = llvm::unique_function(json::Value)>; template void addMethod(llvm::StringRef name, - promise (Server::*method)(json::Value, const Param&)) { - requests.try_emplace(name, - [this, method](json::Value id, json::Value value) -> promise { - co_await (this->*method)(std::move(id), - json::deserialize(value)); - }); + async::promise (Server::*method)(json::Value, const Param&)) { + requests.try_emplace( + name, + [this, method](json::Value id, json::Value value) -> async::promise { + co_await (this->*method)(std::move(id), json::deserialize(value)); + }); } template - void addMethod(llvm::StringRef name, promise (Server::*method)(const Param&)) { - notifications.try_emplace(name, [this, method](json::Value value) -> promise { + void addMethod(llvm::StringRef name, async::promise (Server::*method)(const Param&)) { + notifications.try_emplace(name, [this, method](json::Value value) -> async::promise { co_await (this->*method)(json::deserialize(value)); }); } + llvm::StringMap requests; + llvm::StringMap notifications; + private: /// ============================================================================ /// Lifestyle Message /// ============================================================================ - promise onInitialize(json::Value id, const proto::InitializeParams& params); + async::promise onInitialize(json::Value id, const proto::InitializeParams& params); - promise onInitialized(const proto::InitializedParams& params); + async::promise onInitialized(const proto::InitializedParams& params); - promise onShutdown(json::Value id, const proto::None&); + async::promise onShutdown(json::Value id, const proto::None&); - promise onExit(const proto::None&); + async::promise onExit(const proto::None&); /// ============================================================================ /// Document Synchronization /// ============================================================================ - promise onDidOpen(const proto::DidOpenTextDocumentParams& document); + async::promise onDidOpen(const proto::DidOpenTextDocumentParams& document); - promise onDidChange(const proto::DidChangeTextDocumentParams& document); + async::promise onDidChange(const proto::DidChangeTextDocumentParams& document); - promise onDidSave(const proto::DidSaveTextDocumentParams& document); + async::promise onDidSave(const proto::DidSaveTextDocumentParams& document); - promise onDidClose(const proto::DidCloseTextDocumentParams& document); + async::promise onDidClose(const proto::DidCloseTextDocumentParams& document); /// ============================================================================ /// Language Features /// ============================================================================ - promise onGotoDeclaration(json::Value id, const proto::DeclarationParams& params); + async::promise onGotoDeclaration(json::Value id, const proto::DeclarationParams& params); - promise onGotoDefinition(json::Value id, const proto::DefinitionParams& params); + async::promise onGotoDefinition(json::Value id, const proto::DefinitionParams& params); - promise onGotoTypeDefinition(json::Value id, const proto::TypeDefinitionParams& params); + async::promise onGotoTypeDefinition(json::Value id, + const proto::TypeDefinitionParams& params); - promise onGotoImplementation(json::Value id, const proto::ImplementationParams& params); + async::promise onGotoImplementation(json::Value id, + const proto::ImplementationParams& params); - promise onFindReferences(json::Value id, const proto::ReferenceParams& params); + async::promise onFindReferences(json::Value id, const proto::ReferenceParams& params); - promise onPrepareCallHierarchy(json::Value id, - const proto::CallHierarchyPrepareParams& params); + async::promise onPrepareCallHierarchy(json::Value id, + const proto::CallHierarchyPrepareParams& params); - promise onIncomingCall(json::Value id, - const proto::CallHierarchyIncomingCallsParams& params); + async::promise onIncomingCall(json::Value id, + const proto::CallHierarchyIncomingCallsParams& params); - promise onOutgoingCall(json::Value id, - const proto::CallHierarchyOutgoingCallsParams& params); + async::promise onOutgoingCall(json::Value id, + const proto::CallHierarchyOutgoingCallsParams& params); - promise onPrepareTypeHierarchy(json::Value id, - const proto::TypeHierarchyPrepareParams& params); + async::promise onPrepareTypeHierarchy(json::Value id, + const proto::TypeHierarchyPrepareParams& params); - promise onSupertypes(json::Value id, const proto::TypeHierarchySupertypesParams& params); + async::promise onSupertypes(json::Value id, + const proto::TypeHierarchySupertypesParams& params); - promise onSubtypes(json::Value id, const proto::TypeHierarchySubtypesParams& params); + async::promise onSubtypes(json::Value id, + const proto::TypeHierarchySubtypesParams& params); - promise onDocumentHighlight(json::Value id, const proto::DocumentHighlightParams& params); + async::promise onDocumentHighlight(json::Value id, + const proto::DocumentHighlightParams& params); - promise onDocumentLink(json::Value id, const proto::DocumentLinkParams& params); + async::promise onDocumentLink(json::Value id, const proto::DocumentLinkParams& params); - promise onHover(json::Value id, const proto::HoverParams& params); + async::promise onHover(json::Value id, const proto::HoverParams& params); - promise onCodeLens(json::Value id, const proto::CodeLensParams& params); + async::promise onCodeLens(json::Value id, const proto::CodeLensParams& params); - promise onFoldingRange(json::Value id, const proto::FoldingRangeParams& params); + async::promise onFoldingRange(json::Value id, const proto::FoldingRangeParams& params); - promise onDocumentSymbol(json::Value id, const proto::DocumentSymbolParams& params); + async::promise onDocumentSymbol(json::Value id, + const proto::DocumentSymbolParams& params); - promise onSemanticTokens(json::Value id, const proto::SemanticTokensParams& params); + async::promise onSemanticTokens(json::Value id, + const proto::SemanticTokensParams& params); - promise onInlayHint(json::Value id, const proto::InlayHintParams& params); + async::promise onInlayHint(json::Value id, const proto::InlayHintParams& params); - promise onCodeCompletion(json::Value id, const proto::CompletionParams& params); + async::promise onCodeCompletion(json::Value id, const proto::CompletionParams& params); - promise onSignatureHelp(json::Value id, const proto::SignatureHelpParams& params); + async::promise onSignatureHelp(json::Value id, const proto::SignatureHelpParams& params); - promise onCodeAction(json::Value id, const proto::CodeActionParams& params); + async::promise onCodeAction(json::Value id, const proto::CodeActionParams& params); - promise onFormatting(json::Value id, const proto::DocumentFormattingParams& params); + async::promise onFormatting(json::Value id, + const proto::DocumentFormattingParams& params); - promise onRangeFormatting(json::Value id, - const proto::DocumentRangeFormattingParams& params); + async::promise onRangeFormatting(json::Value id, + const proto::DocumentRangeFormattingParams& params); private: /// Information of building precompiled header. @@ -310,40 +280,51 @@ private: /// Information of building precompiled module. struct PCM {}; - promise updatePCH() { + async::promise updatePCH(llvm::StringRef filepath, + llvm::StringRef content, + llvm::ArrayRef args); + + async::promise updatePCM() { co_return; } - promise updatePCM() { - co_return; - } + async::promise buildAST(llvm::StringRef filepath, llvm::StringRef content); - promise buildAST(llvm::StringRef filepath, llvm::StringRef content) { - llvm::SmallString<128> path = filepath; - - /// FIXME: lookup from CDB file and adjust and remove unnecessary arguments. - llvm::SmallVector args = { - "clang++", - "-std=c++20", - path.c_str(), - "-resource-dir", - "/home/ykiko/C++/clice2/build/lib/clang/20", + struct TranslationUnit { + enum class State { + Building, + Ready, }; - /// through arguments to judge is it a module. - bool isModule = false; - co_await (isModule ? updatePCM() : updatePCH()); + enum class TaskKind { + Build, + Consume, + }; - auto compiler = co_await async::schedule_task([=]() { - std::unique_ptr compiler = std::make_unique(path, content, args); - compiler->buildAST(); - return compiler; - }); + struct Task { + TaskKind kind; + async::promise request; + }; + + State state; + std::unique_ptr compiler; + std::vector tasks; + }; + + async::promise schedule(llvm::StringRef path, async::promise request) { + auto& unit = units[path]; + if(unit.state == TranslationUnit::State::Building) { + // unit.requests.push_back(std::move(request)); + } else { + co_await request; + } + + co_return; } private: - llvm::StringMap requests; - llvm::StringMap notifications; + llvm::StringMap pchs; + llvm::StringMap units; }; } // namespace clice diff --git a/src/Compiler/Compiler.cpp b/src/Compiler/Compiler.cpp index d789bc5e..c23ef8c1 100644 --- a/src/Compiler/Compiler.cpp +++ b/src/Compiler/Compiler.cpp @@ -46,7 +46,7 @@ bool Compiler::applyPCH(llvm::StringRef filepath, std::uint32_t bound, bool endA auto& preproc = instance->getPreprocessorOpts(); preproc.UsePredefines = false; preproc.ImplicitPCHInclude = filepath; - preproc.PrecompiledPreambleBytes.first = bound; + preproc.PrecompiledPreambleBytes.first = {}; preproc.PrecompiledPreambleBytes.second = endAtStart; preproc.DisablePCHOrModuleValidation = clang::DisableValidationForModuleKind::PCH; return true; diff --git a/src/Server/Server.cpp b/src/Server/Server.cpp index e39568f9..690b0053 100644 --- a/src/Server/Server.cpp +++ b/src/Server/Server.cpp @@ -3,199 +3,303 @@ namespace clice { -promise Server::onInitialize(json::Value id, const proto::InitializeParams& params) { - llvm::outs() << "onInitialize\n"; +Server::Server() { + addMethod("initialize", &Server::onInitialize); + addMethod("initialized", &Server::onInitialized); + addMethod("shutdown", &Server::onShutdown); + addMethod("exit", &Server::onExit); + + addMethod("textDocument/didOpen", &Server::onDidOpen); + addMethod("textDocument/didChange", &Server::onDidChange); + addMethod("textDocument/didSave", &Server::onDidSave); + addMethod("textDocument/didClose", &Server::onDidClose); + + addMethod("textDocument/declaration", &Server::onGotoDeclaration); + addMethod("textDocument/definition", &Server::onGotoDefinition); + addMethod("textDocument/typeDefinition", &Server::onGotoTypeDefinition); + addMethod("textDocument/implementation", &Server::onGotoImplementation); + addMethod("textDocument/references", &Server::onFindReferences); + addMethod("textDocument/callHierarchy/prepare", &Server::onPrepareCallHierarchy); + addMethod("textDocument/callHierarchy/incomingCalls", &Server::onIncomingCall); + addMethod("textDocument/callHierarchy/outgoingCalls", &Server::onOutgoingCall); + addMethod("textDocument/typeHierarchy/prepare", &Server::onPrepareTypeHierarchy); + addMethod("textDocument/typeHierarchy/supertypes", &Server::onSupertypes); + addMethod("textDocument/typeHierarchy/subtypes", &Server::onSubtypes); + addMethod("textDocument/documentHighlight", &Server::onDocumentHighlight); + addMethod("textDocument/documentLink", &Server::onDocumentLink); + addMethod("textDocument/hover", &Server::onHover); + addMethod("textDocument/codeLens", &Server::onCodeLens); + addMethod("textDocument/foldingRange", &Server::onFoldingRange); + addMethod("textDocument/documentSymbol", &Server::onDocumentSymbol); + addMethod("textDocument/semanticTokens", &Server::onSemanticTokens); + addMethod("textDocument/inlayHint", &Server::onInlayHint); + addMethod("textDocument/completion", &Server::onCodeCompletion); + addMethod("textDocument/signatureHelp", &Server::onSignatureHelp); + addMethod("textDocument/codeAction", &Server::onCodeAction); + addMethod("textDocument/formatting", &Server::onFormatting); + addMethod("textDocument/rangeFormatting", &Server::onRangeFormatting); +} + +void Server::run(int argc, const char** argv) { + logger::init("console", argv[0]); + + auto dispatch = [this](json::Value value) -> async::promise { + assert(value.kind() == json::Value::Object); + auto object = value.getAsObject(); + assert(object && "value is not an object"); + if(auto method = object->get("method")) { + auto name = *method->getAsString(); + auto params = object->get("params"); + if(auto id = object->get("id")) { + if(auto iter = requests.find(name); iter != requests.end()) { + logger::info("Request: {0}", name.str()); + co_await iter->second(std::move(*id), + params ? std::move(*params) : json::Value(nullptr)); + } else { + logger::error("Unknown request: {0}", name.str()); + } + } else { + if(auto iter = notifications.find(name); iter != notifications.end()) { + logger::info("Notification: {0}", name.str()); + co_await iter->second(params ? std::move(*params) : json::Value(nullptr)); + } else { + logger::error("Unknown notification: {0}", name.str()); + } + } + } + + co_return; + }; + + async::start_server(dispatch, "127.0.0.1", 50051); +} + +async::promise Server::onInitialize(json::Value id, const proto::InitializeParams& params) { async::write(std::move(id), json::serialize(proto::InitializeResult())); - async::sleep(std::chrono::seconds(10)); co_return; } -promise Server::onInitialized(const proto::InitializedParams& params) { - llvm::outs() << "onInitialized\n"; +async::promise Server::onInitialized(const proto::InitializedParams& params) { co_return; } -promise Server::onExit(const proto::None&) { - llvm::outs() << "onExit\n"; +async::promise Server::onExit(const proto::None&) { co_return; } -promise Server::onShutdown(json::Value id, const proto::None&) { - llvm::outs() << "onShutdown\n"; +async::promise Server::onShutdown(json::Value id, const proto::None&) { co_return; } -promise Server::onDidOpen(const proto::DidOpenTextDocumentParams& params) { - llvm::outs() << "onDidOpen: " << params.textDocument.uri << "\n"; +async::promise Server::onDidOpen(const proto::DidOpenTextDocumentParams& params) { auto path = URI::resolve(params.textDocument.uri); llvm::StringRef content = params.textDocument.text; - co_await async::schedule_task([&]() { - // TODO: lookup - std::vector args = { - "clang++", - "-std=c++20", - path.c_str(), - "-resource-dir", - "/home/ykiko/C++/clice2/build/lib/clang/20", - }; - Compiler compiler(path, content, args); + co_await buildAST(path, content); - auto bounds = clang::Lexer::ComputePreamble(content, {}, false); + co_return; +} - llvm::outs() << "Generating PCH\n"; +async::promise Server::onDidChange(const proto::DidChangeTextDocumentParams& document) { + auto path = URI::resolve(document.textDocument.uri); + llvm::StringRef content = document.contentChanges[0].text; - compiler.generatePCH("/home/ykiko/C++/clice2/build/cache/xxx.pch", - bounds.Size, - bounds.PreambleEndsAtStartOfLine); + co_await buildAST(path, content); + co_return; +} + +async::promise Server::onDidSave(const proto::DidSaveTextDocumentParams& document) { + co_return; +} + +async::promise Server::onDidClose(const proto::DidCloseTextDocumentParams& document) { + co_return; +} + +async::promise Server::onGotoDeclaration(json::Value id, + const proto::DeclarationParams& params) { + co_return; +} + +async::promise Server::onGotoDefinition(json::Value id, + const proto::DefinitionParams& params) { + co_return; +} + +async::promise Server::onGotoTypeDefinition(json::Value id, + const proto::TypeDefinitionParams& params) { + co_return; +} + +async::promise Server::onGotoImplementation(json::Value id, + const proto::ImplementationParams& params) { + co_return; +} + +async::promise Server::onFindReferences(json::Value id, + const proto::ReferenceParams& params) { + co_return; +} + +async::promise + Server::onPrepareCallHierarchy(json::Value id, + const proto::CallHierarchyPrepareParams& params) { + co_return; +} + +async::promise Server::onIncomingCall(json::Value id, + const proto::CallHierarchyIncomingCallsParams& params) { + co_return; +} + +async::promise Server::onOutgoingCall(json::Value id, + const proto::CallHierarchyOutgoingCallsParams& params) { + co_return; +} + +async::promise + Server::onPrepareTypeHierarchy(json::Value id, + const proto::TypeHierarchyPrepareParams& params) { + co_return; +} + +async::promise Server::onSupertypes(json::Value id, + const proto::TypeHierarchySupertypesParams& params) { + co_return; +} + +async::promise Server::onSubtypes(json::Value id, + const proto::TypeHierarchySubtypesParams& params) { + co_return; +} + +async::promise Server::onDocumentHighlight(json::Value id, + const proto::DocumentHighlightParams& params) { + co_return; +} + +async::promise Server::onDocumentLink(json::Value id, + const proto::DocumentLinkParams& params) { + co_return; +} + +async::promise Server::onHover(json::Value id, const proto::HoverParams& params) { + co_return; +} + +async::promise Server::onCodeLens(json::Value id, const proto::CodeLensParams& params) { + co_return; +} + +async::promise Server::onFoldingRange(json::Value id, + const proto::FoldingRangeParams& params) { + co_return; +} + +async::promise Server::onDocumentSymbol(json::Value id, + const proto::DocumentSymbolParams& params) { + co_return; +} + +async::promise Server::onSemanticTokens(json::Value id, + const proto::SemanticTokensParams& params) { + + co_return; +} + +async::promise Server::onInlayHint(json::Value id, const proto::InlayHintParams& params) { + co_return; +} + +async::promise Server::onCodeCompletion(json::Value id, + const proto::CompletionParams& params) { + co_return; +} + +async::promise Server::onSignatureHelp(json::Value id, + const proto::SignatureHelpParams& params) { + co_return; +} + +async::promise Server::onCodeAction(json::Value id, const proto::CodeActionParams& params) { + co_return; +} + +async::promise Server::onFormatting(json::Value id, + const proto::DocumentFormattingParams& params) { + co_return; +} + +async::promise Server::onRangeFormatting(json::Value id, + const proto::DocumentRangeFormattingParams& params) { + co_return; +} + +async::promise Server::updatePCH(llvm::StringRef filepath, + llvm::StringRef content, + llvm::ArrayRef args) { + logger::info("Start building PCH for {0}", filepath.str()); + clang::PreambleBounds bounds = {0, 0}; + std::string outpath = "/home/ykiko/C++/clice2/build/cache/xxx.pch"; + co_await async::schedule_task([&] { + Compiler compiler(filepath, content, args); + bounds = clang::Lexer::ComputePreamble(content, {}, false); + compiler.generatePCH(outpath, bounds.Size, bounds.PreambleEndsAtStartOfLine); }); + logger::info("Build PCH success"); - llvm::outs() << "build PCH success\n"; - - co_return; -} - -promise Server::onDidChange(const proto::DidChangeTextDocumentParams& document) { - co_return; -} - -promise Server::onDidSave(const proto::DidSaveTextDocumentParams& document) { - co_return; -} - -promise Server::onDidClose(const proto::DidCloseTextDocumentParams& document) { - co_return; -} - -promise Server::onGotoDeclaration(json::Value id, const proto::DeclarationParams& params) { - co_return; -} - -promise Server::onGotoDefinition(json::Value id, const proto::DefinitionParams& params) { - co_return; -} - -promise Server::onGotoTypeDefinition(json::Value id, - const proto::TypeDefinitionParams& params) { - co_return; -} - -promise Server::onGotoImplementation(json::Value id, - const proto::ImplementationParams& params) { - co_return; -} - -promise Server::onFindReferences(json::Value id, const proto::ReferenceParams& params) { - co_return; -} - -promise Server::onPrepareCallHierarchy(json::Value id, - const proto::CallHierarchyPrepareParams& params) { - co_return; -} - -promise Server::onIncomingCall(json::Value id, - const proto::CallHierarchyIncomingCallsParams& params) { - co_return; -} - -promise Server::onOutgoingCall(json::Value id, - const proto::CallHierarchyOutgoingCallsParams& params) { - co_return; -} - -promise Server::onPrepareTypeHierarchy(json::Value id, - const proto::TypeHierarchyPrepareParams& params) { - co_return; -} - -promise Server::onSupertypes(json::Value id, - const proto::TypeHierarchySupertypesParams& params) { - co_return; -} - -promise Server::onSubtypes(json::Value id, const proto::TypeHierarchySubtypesParams& params) { - co_return; -} - -promise Server::onDocumentHighlight(json::Value id, - const proto::DocumentHighlightParams& params) { - co_return; -} - -promise Server::onDocumentLink(json::Value id, const proto::DocumentLinkParams& params) { - co_return; -} - -promise Server::onHover(json::Value id, const proto::HoverParams& params) { - co_return; -} - -promise Server::onCodeLens(json::Value id, const proto::CodeLensParams& params) { - co_return; -} - -promise Server::onFoldingRange(json::Value id, const proto::FoldingRangeParams& params) { - co_return; -} - -promise Server::onDocumentSymbol(json::Value id, const proto::DocumentSymbolParams& params) { - co_return; -} - -promise Server::onSemanticTokens(json::Value id, const proto::SemanticTokensParams& params) { - co_return; -} - -promise Server::onInlayHint(json::Value id, const proto::InlayHintParams& params) { - co_return; -} - -promise Server::onCodeCompletion(json::Value id, const proto::CompletionParams& params) { - co_return; -} - -promise Server::onSignatureHelp(json::Value id, const proto::SignatureHelpParams& params) { - co_return; -} - -promise Server::onCodeAction(json::Value id, const proto::CodeActionParams& params) { - co_return; -} - -promise Server::onFormatting(json::Value id, const proto::DocumentFormattingParams& params) { - co_return; -} - -promise Server::onRangeFormatting(json::Value id, - const proto::DocumentRangeFormattingParams& params) { - co_return; -} - -promise Server::dispatch(json::Value value) { - assert(value.kind() == json::Value::Object); - auto object = value.getAsObject(); - assert(object && "value is not an object"); - if(auto method = object->get("method")) { - auto name = *method->getAsString(); - auto params = object->get("params"); - if(auto id = object->get("id")) { - if(auto iter = requests.find(name); iter != requests.end()) { - co_await iter->second(std::move(*id), - params ? std::move(*params) : json::Value(nullptr)); - } else { - llvm::errs() << "Unknown request: " << name << "\n"; - } - } else { - if(auto iter = notifications.find(name); iter != notifications.end()) { - co_await iter->second(params ? std::move(*params) : json::Value(nullptr)); - } else { - llvm::errs() << "Unknown notification: " << name << "\n"; - } - } + auto preamble2 = content.substr(0, bounds.Size).str(); + if(bounds.PreambleEndsAtStartOfLine) { + preamble2.append("@"); } + pchs.try_emplace(filepath, PCH{.path = outpath, .preamble = std::move(preamble2)}); co_return; } +async::promise Server::buildAST(llvm::StringRef filepath, llvm::StringRef content) { + llvm::SmallString<128> path = filepath; + + /// FIXME: lookup from CDB file and adjust and remove unnecessary arguments. + llvm::SmallVector args = { + "clang++", + "-std=c++20", + path.c_str(), + "-resource-dir", + "/home/ykiko/C++/clice2/build/lib/clang/20", + }; + + /// through arguments to judge is it a module. + bool isModule = false; + co_await (isModule ? updatePCM() : updatePCH(filepath, content, args)); + + auto& pch = pchs.at(filepath); + + logger::info("Start building AST for {0}", filepath.str()); + auto compiler = co_await async::schedule_task([&] { + std::uint32_t boundSize = pch.preamble.size(); + bool endAtStart = false; + if(pch.preamble.back() == '@') { + boundSize -= 1; + endAtStart = true; + } + + std::unique_ptr compiler = std::make_unique(path, content, args); + compiler->applyPCH(pch.path, boundSize, endAtStart); + compiler->buildAST(); + return compiler; + }); + logger::info("Build AST success"); + + auto& unit = units[filepath]; + unit.state = TranslationUnit::State::Ready; + unit.compiler = std::move(compiler); + + for(auto& task: unit.tasks) { + co_await task.request; + if(task.kind == TranslationUnit::TaskKind::Build) { + break; + } + } +} } // namespace clice