From 6f02694e2b994a4d4ba0308b384a37eb6fbafe89 Mon Sep 17 00:00:00 2001 From: hachem Date: Sat, 31 May 2025 22:42:38 +0200 Subject: feat: add rust support lmao --- src/debugger/debugger.cpp | 71 ++++++++++++++++++++++++++++++++++++++++++++ src/ui/source_view.cpp | 75 +++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 133 insertions(+), 13 deletions(-) diff --git a/src/debugger/debugger.cpp b/src/debugger/debugger.cpp index e642851..e26bd46 100644 --- a/src/debugger/debugger.cpp +++ b/src/debugger/debugger.cpp @@ -7,6 +7,8 @@ #include #include +#include +#include #include #include @@ -83,6 +85,73 @@ namespace Hsdbg return text != nullptr && text[0] != '\0' ? std::string(text) : std::string(fallback); } + auto capture_command(const char* command) -> std::string + { +#ifdef HSDBG_WINDOWS + FILE* pipe = _popen(command, "r"); +#else + FILE* pipe = popen(command, "r"); +#endif + if (pipe == nullptr) + return {}; + + std::string output; + std::array chunk{}; + + while (std::fgets(chunk.data(), static_cast(chunk.size()), pipe) != nullptr) + output += chunk.data(); + +#ifdef HSDBG_WINDOWS + _pclose(pipe); +#else + pclose(pipe); +#endif + + while (!output.empty() && std::isspace(static_cast(output.back())) != 0) + output.pop_back(); + + return output; + } + + auto load_rust_formatters(lldb::SBDebugger& debugger) -> void + { + const std::string sysroot = capture_command("rustc --print sysroot 2>/dev/null"); + + if (sysroot.empty()) + return; + + const std::filesystem::path etc = + std::filesystem::path(sysroot) / "lib" / "rustlib" / "etc"; + + std::error_code error; + if (!std::filesystem::exists(etc / "lldb_commands", error)) + return; + + lldb::SBCommandInterpreter interpreter = debugger.GetCommandInterpreter(); + lldb::SBCommandReturnObject result; + + const std::string import = + std::format("command script import \"{}\"", (etc / "lldb_lookup.py").string()); + interpreter.HandleCommand(import.c_str(), result); + + if (!result.Succeeded()) + { + Log::warn("debugger: rust formatters unavailable ({})", + text_or(result.GetError(), "lldb has no python scripting")); + return; + } + + const std::string source = + std::format("command source \"{}\"", (etc / "lldb_commands").string()); + interpreter.HandleCommand(source.c_str(), result); + + if (result.Succeeded()) + Log::info("debugger: loaded rust type formatters from {}", etc.string()); + else + Log::warn("debugger: could not load rust formatters ({})", + text_or(result.GetError(), "unknown error")); + } + auto path_of(lldb::SBFileSpec spec) -> std::filesystem::path { if (!spec.IsValid()) @@ -447,6 +516,8 @@ namespace Hsdbg m_session->listener = lldb::SBListener("hsdbg"); Log::info("debugger: {}", lldb::SBDebugger::GetVersionString()); + + load_rust_formatters(m_session->debugger); } Debugger::~Debugger() diff --git a/src/ui/source_view.cpp b/src/ui/source_view.cpp index 06cc983..79dd02e 100644 --- a/src/ui/source_view.cpp +++ b/src/ui/source_view.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,12 @@ namespace Hsdbg return 0; } + enum class Language : uint8_t + { + Cpp, + Rust, + }; + auto is_word_start(char c) -> bool { return std::isalpha(static_cast(c)) != 0 || c == '_'; @@ -45,9 +52,9 @@ namespace Hsdbg return std::isalnum(static_cast(c)) != 0 || c == '_'; } - auto classify_word(std::string_view word) -> SyntaxKind + auto classify_word(std::string_view word, Language language) -> SyntaxKind { - static const std::unordered_set keywords = { + static const std::unordered_set cpp_keywords = { "alignas", "alignof", "and", "and_eq", "asm", "bitand", "bitor", "break", "case", "catch", "class", "co_await", "co_return", "co_yield", "compl", "concept", "const", "consteval", "constexpr", "constinit", "const_cast", @@ -62,13 +69,32 @@ namespace Hsdbg "xor", "xor_eq", }; - static const std::unordered_set types = { + static const std::unordered_set cpp_types = { "auto", "bool", "char", "char8_t", "char16_t", "char32_t", "double", "float", "int", "int8_t", "int16_t", "int32_t", "int64_t", "intptr_t", "long", "ptrdiff_t", "short", "signed", "size_t", "ssize_t", "uint8_t", "uint16_t", "uint32_t", "uint64_t", "uintptr_t", "unsigned", "void", "wchar_t", }; + static const std::unordered_set rust_keywords = { + "as", "async", "await", "break", "const", "continue", "crate", "dyn", "else", + "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", + "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", + "static", "struct", "super", "trait", "true", "type", "union", "unsafe", + "use", "where", "while", "box", "dyn", "macro_rules", "yield", + }; + + static const std::unordered_set rust_types = { + "bool", "char", "str", "String", "i8", "i16", "i32", "i64", "i128", "isize", + "u8", "u16", "u32", "u64", "u128", "usize", "f32", "f64", "Vec", "Option", + "Result", "Box", "Rc", "Arc", "Cell", "RefCell", "HashMap", "HashSet", + "BTreeMap", "BTreeSet", "VecDeque", "Cow", + }; + + const bool rust = language == Language::Rust; + const auto& keywords = rust ? rust_keywords : cpp_keywords; + const auto& types = rust ? rust_types : cpp_types; + if (keywords.contains(word)) return SyntaxKind::Keyword; @@ -89,7 +115,7 @@ namespace Hsdbg return true; } - auto highlight_lines(const std::vector& lines) + auto highlight_lines(const std::vector& lines, Language language) -> std::vector> { std::vector> out(lines.size()); @@ -165,6 +191,20 @@ namespace Hsdbg continue; } + if (c == '\'' && language == Language::Rust && at + 1 < size && + is_word_start(line[at + 1]) && !(at + 2 < size && line[at + 2] == '\'')) + { + flush_default(at); + const uint32_t start = at++; + + while (at < size && is_word(line[at])) + ++at; + + emit(start, at - start, SyntaxKind::Default); + run = at; + continue; + } + if (c == '"' || c == '\'') { flush_default(at); @@ -192,7 +232,7 @@ namespace Hsdbg continue; } - if (c == '#' && only_space_before(line, at)) + if (c == '#' && language == Language::Cpp && only_space_before(line, at)) { flush_default(at); const uint32_t start = at++; @@ -217,7 +257,7 @@ namespace Hsdbg const char d = line[at]; if (std::isalnum(static_cast(d)) != 0 || - d == '.' || d == '\'') + d == '.' || d == '\'' || d == '_') ++at; else break; @@ -235,8 +275,8 @@ namespace Hsdbg while (at < size && is_word(line[at])) ++at; - if (const SyntaxKind kind = - classify_word(std::string_view(line).substr(start, at - start)); + if (const SyntaxKind kind = classify_word( + std::string_view(line).substr(start, at - start), language); kind != SyntaxKind::Default) { flush_default(start); @@ -256,9 +296,9 @@ namespace Hsdbg return out; } - auto is_highlightable(const std::filesystem::path& path) -> bool + auto language_of(const std::filesystem::path& path) -> std::optional { - static const std::unordered_set extensions = { + static const std::unordered_set cpp = { ".c", ".h", ".cc", ".cp", ".cpp", ".cxx", ".c++", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".ipp", ".m", ".mm", ".cu", ".cuh", }; @@ -269,7 +309,13 @@ namespace Hsdbg return static_cast(std::tolower(ch)); }); - return extensions.contains(ext); + if (ext == ".rs") + return Language::Rust; + + if (cpp.contains(ext)) + return Language::Cpp; + + return std::nullopt; } constexpr float GUTTER_MARGIN = 6.0f; @@ -303,10 +349,13 @@ namespace Hsdbg lines.push_back(std::move(line)); } + const std::optional language = language_of(path); + m_path = path; m_lines = std::move(lines); - m_highlight = is_highlightable(path); - m_spans = m_highlight ? highlight_lines(m_lines) : std::vector>{}; + m_highlight = language.has_value(); + m_spans = language ? highlight_lines(m_lines, *language) + : std::vector>{}; m_path_input = path.string(); m_error.clear(); m_highlighted_line = 0; -- cgit v1.3