diff --git a/clangd.md b/clangd.md index c95bb708..c9ba7d45 100644 --- a/clangd.md +++ b/clangd.md @@ -22,7 +22,8 @@ TODO: FIXME: - 为每个 Token 都提供语义高亮: https://github.com/clangd/clangd/issues/1115 - no-self-contained: https://github.com/clangd/clangd/issues/45 - +- 提供更好的模板代码补全(需要在索引文件里面记录模板实例化),https://github.com/clangd/clangd/issues/443,然后补全的时候获取`.`号前面的表达式类型,之后再这里查找 +- 支持模块:https://github.com/clangd/clangd/issues/1293 ## 性能优化 TODO: 寻找核心优化点 @@ -43,6 +44,7 @@ TODO: - [支持离线索引](https://github.com/clangd/clangd/issues/587) - 另外请见 https://discourse.llvm.org/t/using-background-and-static-indexes-simultaneously-for-large-codebases/3706/7 + ## 详细的支持功能列表 需要具体到哪些功能要做 diff --git a/clice.md b/clice.md index 4700feeb..9b0c5281 100644 --- a/clice.md +++ b/clice.md @@ -27,52 +27,52 @@ ### Language Features -首先对 LSP 支持的功能进行概览:LSP 3.17 currently: +首先对 LSP 支持的功能进行概览: LSP 3.17 currently: -- Goto Declaration:跳转到声明 -- Goto Definition:跳转到定义 -- Goto Type Definition:跳转到类型定义 -- Goto Implementation:跳转到实现 -- Find References:查找所有引用 -- Prepare Call Hierarchy:没搞懂 -- Call Hierarchy Incoming Calls:没搞懂 -- Call Hierarchy Outgoing Calls:没搞懂 -- Prepare Type Hierarchy:没搞懂 -- Type Hierarchy Supertypes:没搞懂 -- Type Hierarchy Subtypes:没搞懂 -- Document Highlights:没搞懂 -- Document Link:没搞懂 -- Document Link Resolve:没搞懂 -- Hover:悬停提示 -- Code Lens:没搞懂 -- Code Lens Refresh Request:没搞懂 -- Folding Range:把某段代码折叠起来 -- Selection Range:没搞懂 -- Document Symbols:没搞懂 -- Semantic Tokens:用于语义高亮 -- Inline Value:没搞懂 -- Inline Value Refresh:没搞懂 -- Inlay Hint:用于内嵌提示,比如函数参数或者`auto`的类型 -- Inlay Hint Resolve:没搞懂 -- Inlay Hint Refresh:刷新内嵌提示 -- Monikers:没搞懂 -- Completion:代码补全 -- Completion Item Resolve:解决重载函数的代码补全 -- PublishDiagnostics Notification:发出诊断信息 -- Pull Diagnostics:没搞懂 -- Signature Help Request:请求函数签名信息 -- Code Action:重构等操作(还有那个 quick fix) -- Code Action Resolve:没搞懂 -- Document Color:没搞懂 -- Color Presentation:没搞懂 -- Document Formatting:格式化 -- Document Range Formatting:只格式化某个部分 -- Document on Type Formatting:没搞懂 -- Rename:重命名 -- Prepare Rename:解决重命名 -- Linked Editing Range:没搞懂 +- Goto Declaration: 跳转到声明 +- Goto Definition: 跳转到定义 +- Goto Type Definition: 跳转到类型定义 +- Goto Implementation: 跳转到实现 +- Find References: 查找所有引用 +- Prepare Call Hierarchy: 没搞懂 +- Call Hierarchy Incoming Calls: 没搞懂 +- Call Hierarchy Outgoing Calls: 没搞懂 +- Prepare Type Hierarchy: 没搞懂 +- Type Hierarchy Supertypes: 没搞懂 +- Type Hierarchy Subtypes: 没搞懂 +- Document Highlights: 没搞懂 +- Document Link: 用来处理文件链接跳转,例如点击一个文件名跳转到文件 +- Document Link Resolve: 没搞懂 +- Hover: 悬停提示 +- Code Lens: 没搞懂 +- Code Lens Refresh Request: 没搞懂 +- Folding Range: 把某段代码折叠起来 +- Selection Range: 没搞懂 +- Document Symbols: 列出文档中的所有符号,可以用于 vscode 左边那个 Outline +- Semantic Tokens: 用于语义高亮 +- Inline Value: 没搞懂 +- Inline Value Refresh: 没搞懂 +- Inlay Hint: 用于内嵌提示,比如函数参数或者`auto`的类型 +- Inlay Hint Resolve: 没搞懂 +- Inlay Hint Refresh: 刷新内嵌提示 +- Monikers: 没搞懂 +- Completion: 代码补全 +- Completion Item Resolve: 解决重载函数的代码补全 +- PublishDiagnostics Notification: 发出诊断信息 +- Pull Diagnostics: 没搞懂 +- Signature Help Request: 请求函数签名信息 +- Code Action: 重构等操作(还有那个 quick fix) +- Code Action Resolve: 没搞懂 +- Document Color: 没搞懂 +- Color Presentation: 没搞懂 +- Document Formatting: 格式化 +- Document Range Formatting: 只格式化某个部分 +- Document on Type Formatting: 没搞懂 +- Rename: 重命名 +- Prepare Rename: 解决重命名 +- Linked Editing Range: 没搞懂 -这些任务从最终实现的角度来说可以主要分成三种: +这些任务从最终实现的角度来说可以主要分成三种: 1. CodeCompletion 这个需要利用 CodeCompletionConsumer 调用 Clang 提供的接口来实现,然而我们实际上可以做一些更加复杂的分析(clangd 目前没有做)。比如判断当前的是不是在 Template 语境下从而决定补全`sizeof`的时候要不要补全`...`。在补全成员的时候,似乎我们也可以获取`expr.f`中的父对象的类型,从而根据它的类型来做一些补全。有待进一步研究。 2. Semantic Tokens 等基于当前 AST 的操作,则是遍历 AST 渲染 Token 即可。 3. 剩下很多的,例如 Find References 等等等查询功能,都是在已经索引好的文件中进行查询,不需要对语法树进行什么改动。 diff --git a/include/Clang/Clang.h b/include/Clang/Clang.h index 19600e8d..16ee52fd 100644 --- a/include/Clang/Clang.h +++ b/include/Clang/Clang.h @@ -1,3 +1,5 @@ +#pragma once + #include "clang/AST/RecursiveASTVisitor.h" #include #include @@ -5,4 +7,25 @@ #include #include #include -#include \ No newline at end of file +#include + +namespace clice { + +using clang::CompilerInstance; +using clang::CompilerInvocation; + +template +union uninitialized { + T value; + + uninitialized() {} + + ~uninitialized() { value.~T(); } + + template + auto& construct(Args&&... args) { + return *new (&value) T{std::forward(args)...}; + } +}; + +} // namespace clice diff --git a/include/Clang/CompileDatabase.h b/include/Clang/CompileDatabase.h new file mode 100644 index 00000000..e8e73b34 --- /dev/null +++ b/include/Clang/CompileDatabase.h @@ -0,0 +1,35 @@ +#include + +namespace clice { + +class CompileDatabase { +private: + std::unique_ptr database; + +public: + static auto& instance() { + static CompileDatabase instance; + return instance; + } + + void load(std::string_view path) { + std::string error; + database = clang::tooling::CompilationDatabase::loadFromDirectory(path, error); + if(!database) { + llvm::errs() << "Failed to load compilation database. " << error << "\n"; + std::terminate(); + } + } + + auto lookup(std::string_view path) { + auto commands = database->getCompileCommands(path); + llvm::ArrayRef command = commands[0].CommandLine; + std::vector args = {command.front().c_str(), "-Xclang", "-no-round-trip-args"}; + for(auto& arg: command.drop_front()) { + args.push_back(arg.c_str()); + } + return args; + } +}; + +} // namespace clice diff --git a/include/Clang/Compiler.h b/include/Clang/Compiler.h index e69de29b..2aad1fcb 100644 --- a/include/Clang/Compiler.h +++ b/include/Clang/Compiler.h @@ -0,0 +1,7 @@ +#include "Clang.h" + +namespace clice { + +inline auto createCompilerInvocation() {} + +} // namespace clice diff --git a/include/Clang/Diagnostics.h b/include/Clang/Diagnostics.h new file mode 100644 index 00000000..1a665fe8 --- /dev/null +++ b/include/Clang/Diagnostics.h @@ -0,0 +1,9 @@ +#include "Clang.h" + +namespace clice { + +class Diagnostic {}; + +class DiagnosticConsumer : clang::DiagnosticConsumer {}; + +} // namespace clice diff --git a/include/Clang/ParsedAST.h b/include/Clang/ParsedAST.h index e4e9ed8d..a030f382 100644 --- a/include/Clang/ParsedAST.h +++ b/include/Clang/ParsedAST.h @@ -1,7 +1,49 @@ +#include "Diagnostics.h" +#include "Preamble.h" +#include "CompileDatabase.h" + namespace clice { class ParsedAST { private: + using Decl = clang::Decl*; + using PathRef = llvm::StringRef; + using TokenBuffer = clang::syntax::TokenBuffer; + using ASTConsumer = std::unique_ptr; + + struct FrontendAction : public clang::ASTFrontendAction { + ASTConsumer CreateASTConsumer(CompilerInstance& instance, PathRef file) override; + }; + +private: + /// path of translation unit + PathRef path; + /// llvm version + std::string version; + /// headers part of the tu, when a file is loaded, we will build the preamble, the reuse it. + Preamble preamble; + /// some extra info + std::vector topLevelDecls; + std::vector diagnostics; + /// core members for clang frontend + uninitialized tokens; + clang::SyntaxOnlyAction action; + CompilerInstance instance; + + ParsedAST() = default; + +public: + std::unique_ptr build(std::string_view path, + const std::shared_ptr& invocation, + const std::shared_ptr& preamble); + + auto& Tokens() { return tokens.value; } + + auto& Diagnostics() { return diagnostics; } + + auto& ASTContext() { return instance.getASTContext(); } + + auto& TranslationUnit() { return *instance.getASTContext().getTranslationUnitDecl(); } }; } // namespace clice diff --git a/include/Clang/Preamble.h b/include/Clang/Preamble.h index 70b763e7..fc7e9bd5 100644 --- a/include/Clang/Preamble.h +++ b/include/Clang/Preamble.h @@ -1,5 +1,10 @@ -namespace { +#include "Clang.h" -class Preamble {}; +namespace clice{ + +class Preamble { + public: + +}; } // namespace diff --git a/include/LSP/Scheduler.h b/include/LSP/Scheduler.h new file mode 100644 index 00000000..193ef82f --- /dev/null +++ b/include/LSP/Scheduler.h @@ -0,0 +1,10 @@ +#include + +namespace clice { + +class Scheduler { + std::mutex mutex; + llvm::StringMap> parsedASTs; +}; + +} // namespace clice diff --git a/include/LSP/Server.h b/include/LSP/Server.h index 321ea609..f2d75463 100644 --- a/include/LSP/Server.h +++ b/include/LSP/Server.h @@ -1,15 +1,22 @@ #include +#include namespace clice { +// global server instance +extern class Server server; + /// core class responsible for starting the server class Server { - static uv_loop_t* loop; - static uv_pipe_t stdin_pipe; + uv_loop_t* loop; + uv_pipe_t stdin_pipe; public: - static int Initialize(); - static int Exit(); + int initialize(); + int exit(); + +public: + void handle_message(std::string_view message); }; } // namespace clice diff --git a/src/Clang/ParsedAST.cpp b/src/Clang/ParsedAST.cpp new file mode 100644 index 00000000..35f12bcc --- /dev/null +++ b/src/Clang/ParsedAST.cpp @@ -0,0 +1,53 @@ +#include + +namespace clice { + +std::unique_ptr ParsedAST::build(std::string_view path, + const std::shared_ptr& invocation, + const std::shared_ptr& preamble) { + auto AST = new ParsedAST(); + AST->path = path; + + // some settings for CompilerInstance + auto& instance = AST->instance; + instance.setInvocation(invocation); + instance.createDiagnostics(); + + if(!instance.createTarget()) { + llvm::errs() << "Failed to create target\n"; + std::terminate(); + } + + if(auto manager = instance.createFileManager()) { + instance.createSourceManager(*manager); + } else { + llvm::errs() << "Failed to create file manager\n"; + std::terminate(); + } + + instance.createPreprocessor(clang::TranslationUnitKind::TU_Complete); + + instance.createASTContext(); + + // start FrontendAction + const auto& input = instance.getFrontendOpts().Inputs[0]; + auto& action = AST->action; + + if(!action.BeginSourceFile(instance, input)) { + llvm::errs() << "Failed to begin source file\n"; + std::terminate(); + } + + clang::syntax::TokenCollector collector = {instance.getPreprocessor()}; + + if(llvm::Error error = action.Execute()) { + llvm::errs() << "Failed to execute action: " << error << "\n"; + std::terminate(); + } + + AST->tokens.construct(std::move(collector).consume()); + + return std::unique_ptr(AST); +} + +} // namespace clice diff --git a/src/Clang/SemanticTokens.cpp b/src/Clang/SemanticTokens.cpp index bbd12fa4..5bb452d7 100644 --- a/src/Clang/SemanticTokens.cpp +++ b/src/Clang/SemanticTokens.cpp @@ -1,176 +1,170 @@ #include namespace tooling = clang::tooling; + namespace { std::unique_ptr datebase; -auto GetCommands(std::string_view path, std::string_view compile_commands_path) - -> std::vector { - if (!datebase) { - std::string error; - datebase = tooling::CompilationDatabase::loadFromDirectory( - compile_commands_path, error); +auto GetCommands(std::string_view path, + std::string_view compile_commands_path) -> std::vector { + if(!datebase) { + std::string error; + datebase = tooling::CompilationDatabase::loadFromDirectory(compile_commands_path, error); - if (!datebase) { - llvm::errs() << "Failed to load compilation database. " << error << "\n"; - std::terminate(); + if(!datebase) { + llvm::errs() << "Failed to load compilation database. " << error << "\n"; + std::terminate(); + } } - } - return datebase->getCompileCommands(path); + return datebase->getCompileCommands(path); } auto createDiagnostic() { - clang::DiagnosticOptions DiagOpts; + clang::DiagnosticOptions DiagOpts; - clang::TextDiagnosticPrinter *DiagClient = - new clang::TextDiagnosticPrinter(llvm::errs(), &DiagOpts); + clang::TextDiagnosticPrinter* DiagClient = + new clang::TextDiagnosticPrinter(llvm::errs(), &DiagOpts); - llvm::IntrusiveRefCntPtr DiagID = - new clang::DiagnosticIDs(); + llvm::IntrusiveRefCntPtr DiagID = new clang::DiagnosticIDs(); - return clang::DiagnosticsEngine(DiagID, &DiagOpts, DiagClient); + return clang::DiagnosticsEngine(DiagID, &DiagOpts, DiagClient); } -auto createInvocation(std::string_view path, - std::string_view compile_commands) { - auto commands = GetCommands(path, compile_commands); - llvm::ArrayRef command = commands[0].CommandLine; +auto createInvocation(std::string_view path, std::string_view compile_commands) { + auto commands = GetCommands(path, compile_commands); + llvm::ArrayRef command = commands[0].CommandLine; - std::vector args = {command.front().c_str(), "-Xclang", - "-no-round-trip-args"}; + std::vector args = {command.front().c_str(), "-Xclang", "-no-round-trip-args"}; - for (auto &arg : command.drop_front()) { - args.push_back(arg.c_str()); - } + for(auto& arg: command.drop_front()) { + args.push_back(arg.c_str()); + } - static auto engine = createDiagnostic(); - auto invocation = clang::createInvocation(args); + static auto engine = createDiagnostic(); + auto invocation = clang::createInvocation(args); - // set input file - auto &inputs = invocation->getFrontendOpts().Inputs; - inputs.push_back( - clang::FrontendInputFile(path, clang::InputKind{clang::Language::CXX})); + // set input file + auto& inputs = invocation->getFrontendOpts().Inputs; + inputs.push_back(clang::FrontendInputFile(path, clang::InputKind{clang::Language::CXX})); - return invocation; + return invocation; } struct DiagnosticConsumer : clang::DiagnosticConsumer { - void BeginSourceFile(const clang::LangOptions &LangOpts, - const clang::Preprocessor *PP) override {} + void BeginSourceFile(const clang::LangOptions& LangOpts, + const clang::Preprocessor* PP) override {} - void EndSourceFile() override {} + void EndSourceFile() override {} - void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, - const clang::Diagnostic &Info) override { - if (DiagLevel == clang::DiagnosticsEngine::Level::Note) { - return; + void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel, + const clang::Diagnostic& Info) override { + if(DiagLevel == clang::DiagnosticsEngine::Level::Note) { + return; + } + + llvm::errs() << "Diagnostic: "; + llvm::SmallVector buf; + Info.FormatDiagnostic(buf); + llvm::errs().write(buf.data(), buf.size()); + llvm::errs() << "\n"; } - - llvm::errs() << "Diagnostic: "; - llvm::SmallVector buf; - Info.FormatDiagnostic(buf); - llvm::errs().write(buf.data(), buf.size()); - llvm::errs() << "\n"; - } }; auto createInstance(std::string_view path, std::string_view compile_commands) { - std::unique_ptr instance = - std::make_unique(); + std::unique_ptr instance = std::make_unique(); - auto invocation = createInvocation(path, compile_commands); - instance->setInvocation( - std::make_shared(*invocation)); + auto invocation = createInvocation(path, compile_commands); + instance->setInvocation(std::make_shared(*invocation)); - instance->createDiagnostics(new DiagnosticConsumer(), true); + instance->createDiagnostics(new DiagnosticConsumer(), true); - if (!instance->createTarget()) { - llvm::errs() << "Failed to create target\n"; - std::terminate(); - } + if(!instance->createTarget()) { + llvm::errs() << "Failed to create target\n"; + std::terminate(); + } - if (auto manager = instance->createFileManager()) { - instance->createSourceManager(*manager); - } else { - llvm::errs() << "Failed to create file manager\n"; - std::terminate(); - } + if(auto manager = instance->createFileManager()) { + instance->createSourceManager(*manager); + } else { + llvm::errs() << "Failed to create file manager\n"; + std::terminate(); + } - instance->createPreprocessor(clang::TranslationUnitKind::TU_Complete); + instance->createPreprocessor(clang::TranslationUnitKind::TU_Complete); - instance->createASTContext(); + instance->createASTContext(); - return instance; + return instance; } class AST { - clang::FrontendAction *action; - clang::CompilerInstance *instance; - clang::syntax::TokenBuffer *tokens; + clang::FrontendAction* action; + clang::CompilerInstance* instance; + clang::syntax::TokenBuffer* tokens; private: - AST() = default; + AST() = default; public: - AST(const AST &) = delete; + AST(const AST&) = delete; - AST(AST &&other) noexcept - : action(other.action), instance(other.instance), tokens(other.tokens) { - other.action = nullptr; - other.instance = nullptr; - other.tokens = nullptr; - } - - ~AST() { - if (action) { - action->EndSourceFile(); - delete action; - delete instance; - delete tokens; - } - } - - static AST create(std::string_view path, std::string_view compile_commands) { - AST ast; - - ast.instance = createInstance(path, compile_commands).release(); - ast.action = new clang::SyntaxOnlyAction(); - - const auto &input = ast.instance->getFrontendOpts().Inputs[0]; - - if (!ast.action->BeginSourceFile(*ast.instance, input)) { - llvm::errs() << "Failed to begin source file\n"; - std::terminate(); + AST(AST&& other) noexcept : + action(other.action), instance(other.instance), tokens(other.tokens) { + other.action = nullptr; + other.instance = nullptr; + other.tokens = nullptr; } - clang::syntax::TokenCollector collector = {ast.instance->getPreprocessor()}; - - if (llvm::Error error = ast.action->Execute()) { - llvm::errs() << "Failed to execute action: " << error << "\n"; - std::terminate(); + ~AST() { + if(action) { + action->EndSourceFile(); + delete action; + delete instance; + delete tokens; + } } - ast.tokens = new auto(std::move(collector).consume()); + static AST create(std::string_view path, std::string_view compile_commands) { + AST ast; - return ast; - } + ast.instance = createInstance(path, compile_commands).release(); + ast.action = new clang::SyntaxOnlyAction(); - auto &getASTContext() { return instance->getASTContext(); } + const auto& input = ast.instance->getFrontendOpts().Inputs[0]; - auto &getSourceManager() { return instance->getSourceManager(); } + if(!ast.action->BeginSourceFile(*ast.instance, input)) { + llvm::errs() << "Failed to begin source file\n"; + std::terminate(); + } - auto getTokens() { return tokens->expandedTokens(); } + clang::syntax::TokenCollector collector = {ast.instance->getPreprocessor()}; + + if(llvm::Error error = ast.action->Execute()) { + llvm::errs() << "Failed to execute action: " << error << "\n"; + std::terminate(); + } + + ast.tokens = new auto(std::move(collector).consume()); + + return ast; + } + + auto& getASTContext() { return instance->getASTContext(); } + + auto& getSourceManager() { return instance->getSourceManager(); } + + auto getTokens() { return tokens->expandedTokens(); } }; struct Visitor : clang::RecursiveASTVisitor { - bool VisitTranslationUnitDecl(clang::TranslationUnitDecl *tu) { - tu->dump(); - return true; - } + bool VisitTranslationUnitDecl(clang::TranslationUnitDecl* tu) { + tu->dump(); + return true; + } - bool VisitCXXMethodDecl(clang::CXXMethodDecl *decl) { return true; } + bool VisitCXXMethodDecl(clang::CXXMethodDecl* decl) { return true; } }; -} // namespace \ No newline at end of file +} // namespace diff --git a/src/LSP/Server.cpp b/src/LSP/Server.cpp index f4ef593a..1561b603 100644 --- a/src/LSP/Server.cpp +++ b/src/LSP/Server.cpp @@ -1,44 +1,55 @@ +#include #include namespace clice { -uv_loop_t* Server::loop; -uv_pipe_t Server::stdin_pipe; +Server server; -static void on_read(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { - if(nread > 0) { - printf("Read: %s\n", buf->base); - } else if(nread < 0) { - if(nread != UV_EOF) { - fprintf(stderr, "Read error: %s\n", uv_err_name(nread)); - } - - if(!uv_is_closing((uv_handle_t*)&pipe)) { - uv_close((uv_handle_t*)&pipe, NULL); - } - } - - if(buf->base) { - free(buf->base); - } -} - -static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - buf->base = (char*)malloc(suggested_size); - buf->len = suggested_size; -} - -int Server::Initialize() { +int Server::initialize() { loop = uv_default_loop(); + + // initialize pipe and bind to stdin uv_pipe_init(loop, &stdin_pipe, 0); uv_pipe_open(&stdin_pipe, 0); + + auto alloc_buffer = [](uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { + buf->base = (char*)std::malloc(suggested_size); + buf->len = suggested_size; + }; + + auto on_read = [](uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf) { + if(nread > 0) { + server.handle_message(std::string_view(buf->base, nread)); + } else if(nread < 0) { + // TODO: error handling + } + std::free(buf->base); + }; + uv_read_start((uv_stream_t*)&stdin_pipe, alloc_buffer, on_read); + + // start the event loop return uv_run(loop, UV_RUN_DEFAULT); } -int Server::Exit() { - uv_close((uv_handle_t*)&stdin_pipe, NULL); - return uv_run(loop, UV_RUN_DEFAULT); +int Server::exit() { + // close the pipe + // uv_close((uv_handle_t*)&stdin_pipe, NULL); + + // stop the event loop + uv_stop(loop); + return 0; } +void Server::handle_message(std::string_view message) { + const char* json = R"({ + "name": "John", + "age": 30, + "city": "New York" + })"; + + simdjson::dom::parser parser; + +} // namespace clice + } // namespace clice diff --git a/src/main.cpp b/src/main.cpp index d444f66c..95032324 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,8 +1,82 @@ #include +#include +#include +#include + +uv_loop_t* loop = nullptr; + +struct Task { + struct promise_type { + Task get_return_object() { return Task{Handle::from_promise(*this)}; } + + std::suspend_never initial_suspend() { return {}; } + + std::suspend_always final_suspend() noexcept { return {}; } + + void return_void() {} + + void unhandled_exception() { std::terminate(); } + }; + + struct Handle { + std::coroutine_handle coro; + + static Handle from_promise(promise_type& p) { + return Handle{std::coroutine_handle::from_promise(p)}; + } + + ~Handle() { + if(coro) + coro.destroy(); + } + }; + + Handle h; +}; + +struct uv_awaitable { + uv_work_t req; + std::function work_fn; + std::coroutine_handle<> handle; + + bool await_ready() { return false; } + + void await_suspend(std::coroutine_handle<> handle) { + this->handle = handle; + req.data = this; + std::cout << "Scheduling work..." << std::endl; + uv_queue_work( + loop, + &req, + [](uv_work_t* req) { + auto* self = static_cast(req->data); + self->work_fn(); + }, + [](uv_work_t* req, int status) { + auto& handle = static_cast(req->data)->handle; + if(handle.done()) { + std::cout << "Work done" << std::endl; + } else { + handle.resume(); + } + }); + } + + auto await_resume() { return 1; } +}; + +Task async_factorial(int n) { + long result = 1; + auto s = co_await uv_awaitable{.work_fn = [&result, n] { + for(int i = 1; i <= n; ++i) { + result *= i; + } + }}; + std::cout << "Factorial of " << n << " is " << result << std::endl; +} int main() { - using namespace clice; - Server::Initialize(); - Server::Exit(); + auto& server = clice::server; + server.initialize(); return 0; }