From cc1d085b40a1fd19833604e3de701720da4445bb Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 29 Jan 2026 06:55:54 +0800 Subject: [PATCH] add install/list support by xlings - https://github.com/mcpp-community/OpenOrg/issues/1 Signed-off-by: sunrisepeak --- src/main.cpp | 16 ++++++++- src/platform.cppm | 1 + src/platform/linux.cppm | 11 ++++++ src/platform/windows.cppm | 11 ++++++ src/utils.cppm | 19 +++++++++++ src/xlings.cppm | 72 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/xlings.cppm diff --git a/src/main.cpp b/src/main.cpp index cf6c4d3..1ed0482 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ import d2x.log; import d2x.checker; import d2x.platform; import d2x.config; +import d2x.xlings; /* d2x command [options] --xxx-xxx @@ -22,8 +23,10 @@ void print_help() { std::println("Usage: $ d2x [command] [target] [options]\n"); std::println("Commands:"); std::println("\t new \t create new d2x project"); + std::println("\t install \t install d2x package via xlings"); + std::println("\t list \t list available d2x packages"); std::println("\t book \t open project's book"); - std::println("\t run \t run sourcecode file"); + //std::println("\t run \t run sourcecode file"); std::println("\t checker \t run checker for d2x project's exercises"); std::println("\t config \t configure d2x (.d2x.json)"); std::println("\t help \t help info\n"); @@ -121,6 +124,17 @@ int main(int argc, char* argv[]) { d2x::checker::run(); } else if (command == "config") { d2x::Config::run_interactive_config(); + } else if (command == "install") { + std::string package = argc >= 3 ? argv[2] : ""; + if (package.empty()) { + std::println("Usage: d2x install "); + std::println("Example: d2x install d2mcpp"); + return 1; + } + d2x::xlings::install(package); + } else if (command == "list") { + std::string query = argc >= 3 ? argv[2] : ""; + d2x::xlings::list(query); } else { std::println("Unknown command: {}", command); std::println("Use 'd2x help' for usage information"); diff --git a/src/platform.cppm b/src/platform.cppm index 78c0654..70a40a0 100644 --- a/src/platform.cppm +++ b/src/platform.cppm @@ -20,6 +20,7 @@ namespace platform { export using platform_impl::run_command_capture; export using platform_impl::clear_console; export using platform_impl::get_home_dir; + export using platform_impl::xlings_install; export [[nodiscard]] std::string get_rundir() { return gRundir; diff --git a/src/platform/linux.cppm b/src/platform/linux.cppm index 844ef8d..77d3637 100644 --- a/src/platform/linux.cppm +++ b/src/platform/linux.cppm @@ -35,6 +35,17 @@ namespace platform_impl { if (const char* home = std::getenv("HOME")) return home; return "."; } + + export bool xlings_install() { + std::println("正在安装 xlings..."); + int status = std::system("curl -fsSL https://d2learn.org/xlings-install.sh | bash"); + if (status == 0) { + std::println("xlings 安装成功!"); + return true; + } + std::println("xlings 安装失败"); + return false; + } } // namespace platform_impl } diff --git a/src/platform/windows.cppm b/src/platform/windows.cppm index a99bd32..c3ddc8b 100644 --- a/src/platform/windows.cppm +++ b/src/platform/windows.cppm @@ -34,6 +34,17 @@ namespace platform_impl { if (const char* appdata = std::getenv("APPDATA")) return appdata; return "."; } + + export bool xlings_install() { + std::println("正在安装 xlings..."); + int status = std::system("powershell -Command \"irm https://d2learn.org/xlings-install.ps1.txt | iex\""); + if (status == 0) { + std::println("xlings 安装成功!"); + return true; + } + std::println("xlings 安装失败"); + return false; + } } // namespace platform_impl } // namespace d2x diff --git a/src/utils.cppm b/src/utils.cppm index 02dc427..0e9ef6d 100644 --- a/src/utils.cppm +++ b/src/utils.cppm @@ -99,6 +99,25 @@ std::string read_file_to_string(const std::string& filepath) { return buffer.str(); } +[[nodiscard]] std::string strip_ansi(const std::string& str) { + std::string cleaned; + cleaned.reserve(str.size()); + + for (std::size_t i = 0; i < str.size(); ++i) { + if (str[i] == '\x1b' && i + 1 < str.size() && str[i + 1] == '[') { + // Skip ANSI escape sequence (ESC[...m) + i += 2; + while (i < str.size() && str[i] != 'm') { + ++i; + } + } else { + cleaned.push_back(str[i]); + } + } + + return cleaned; +} + [[nodiscard]] std::string get_env_or_default(std::string_view name, std::string_view default_value = "") { if (const char* value = std::getenv(name.data()); value != nullptr) { return value; diff --git a/src/xlings.cppm b/src/xlings.cppm new file mode 100644 index 0000000..aedd120 --- /dev/null +++ b/src/xlings.cppm @@ -0,0 +1,72 @@ +export module d2x.xlings; + +import std; + +import d2x.platform; +import d2x.utils; + +namespace d2x { +namespace xlings { + +export [[nodiscard]] bool has_xlings() { + auto [status, _] = d2x::platform::run_command_capture("xlings"); + return status == 0; +} + +export bool install(const std::string& pkgname) { + + std::println("开始安装 -> {}", pkgname); + + // Check if xlings is installed + if (!has_xlings()) { + std::print("xlings 未安装,是否现在安装? [Y/n]: "); + std::cout.flush(); + std::string input; + if (!std::getline(std::cin, input)) return false; + if (!input.empty() && input[0] != 'y' && input[0] != 'Y') { + std::println("已取消安装"); + return false; + } + + std::println("正在安装 xlings..."); + if (!d2x::platform::xlings_install()) { + std::println("xlings 安装失败"); + return false; + } + } + + // Install the package + std::string command = "xlings install d2x:" + pkgname; + std::println("正在执行: {}", command); + int status = std::system(command.c_str()); + + if (status == 0) { + return true; + } + + std::println("安装失败,命令返回状态码: {}", status); + return false; +} + +export void list(const std::string& query = "") { + std::string command = "xim -s d2x:" + query; + auto [status, output] = d2x::platform::run_command_capture(command); + + if (status != 0) { + std::println("查询失败: {}", output); + return; + } + + // Strip ANSI escape codes and print from first '{' + //auto clean_output = d2x::utils::strip_ansi(output); + //auto pos = clean_output.find('{'); + //if (pos != std::string::npos) { + // clean_output = clean_output.substr(pos); + //} + //std::println("{}", clean_output); + + std::println("{}", output); +} + +} // namespace xlings +} // namespace d2x