Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import d2x.log;
import d2x.checker;
import d2x.platform;
import d2x.config;
import d2x.xlings;

/*
d2x command [options] --xxx-xxx
Expand All @@ -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");
Expand Down Expand Up @@ -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 <package-name>");
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");
Expand Down
1 change: 1 addition & 0 deletions src/platform.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions src/platform/linux.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down
11 changes: 11 additions & 0 deletions src/platform/windows.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 19 additions & 0 deletions src/utils.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
72 changes: 72 additions & 0 deletions src/xlings.cppm
Original file line number Diff line number Diff line change
@@ -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