diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/debugger/debugger.cpp | 77 | ||||
| -rw-r--r-- | src/ui/source_view.cpp | 77 |
2 files changed, 141 insertions, 13 deletions
diff --git a/src/debugger/debugger.cpp b/src/debugger/debugger.cpp index cd1eac1..97af0e1 100644 --- a/src/debugger/debugger.cpp +++ b/src/debugger/debugger.cpp @@ -7,6 +7,8 @@ #include <algorithm> #include <array> +#include <cctype> +#include <cstdio> #include <set> #include <source_location> @@ -87,6 +89,79 @@ namespace Hsdbg return text != nullptr && text[0] != '\0' ? std::string(text) : std::string(fallback); } + // runs a command and hands back its stdout, trimmed; empty if it could + // not be spawned (used to ask the toolchain where it lives) + 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<char, 256> chunk{}; + + while (std::fgets(chunk.data(), static_cast<int>(chunk.size()), pipe) != nullptr) + output += chunk.data(); + +#ifdef HSDBG_WINDOWS + _pclose(pipe); +#else + pclose(pipe); +#endif + + while (!output.empty() && std::isspace(static_cast<unsigned char>(output.back())) != 0) + output.pop_back(); + + return output; + } + + // rust ships lldb data formatters (the same ones rust-lldb sources) that + // teach lldb how to print String, Vec, Option, enums and friends. without + // them those show as raw structs, so pull them in if a toolchain is around. + // harmless for c/c++ targets, and a no-op if lldb has no python scripting + 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()) @@ -463,6 +538,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 c7fb606..96a150b 100644 --- a/src/ui/source_view.cpp +++ b/src/ui/source_view.cpp @@ -10,6 +10,7 @@ #include <algorithm> #include <cctype> #include <fstream> +#include <optional> #include <string_view> #include <unordered_set> @@ -37,6 +38,12 @@ namespace Hsdbg return 0; } + enum class Language : uint8_t + { + Cpp, + Rust, + }; + auto is_word_start(char c) -> bool { return std::isalpha(static_cast<unsigned char>(c)) != 0 || c == '_'; @@ -47,9 +54,9 @@ namespace Hsdbg return std::isalnum(static_cast<unsigned char>(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<std::string_view> keywords = { + static const std::unordered_set<std::string_view> 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", @@ -64,13 +71,32 @@ namespace Hsdbg "xor", "xor_eq", }; - static const std::unordered_set<std::string_view> types = { + static const std::unordered_set<std::string_view> 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<std::string_view> 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<std::string_view> 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; @@ -94,7 +120,7 @@ namespace Hsdbg // splits every line into contiguous coloured spans. the whole file is // walked in order so a block comment opened on one line stays open on the // next, which a per-line pass could not know - auto highlight_lines(const std::vector<std::string>& lines) + auto highlight_lines(const std::vector<std::string>& lines, Language language) -> std::vector<std::vector<SourceSpan>> { std::vector<std::vector<SourceSpan>> out(lines.size()); @@ -170,6 +196,22 @@ namespace Hsdbg continue; } + // a rust lifetime ('a) looks like an unterminated char literal, + // so peel it off as an identifier before the string handling + 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); @@ -197,7 +239,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++; @@ -222,7 +264,7 @@ namespace Hsdbg const char d = line[at]; if (std::isalnum(static_cast<unsigned char>(d)) != 0 || - d == '.' || d == '\'') + d == '.' || d == '\'' || d == '_') ++at; else break; @@ -240,8 +282,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); @@ -261,9 +303,9 @@ namespace Hsdbg return out; } - auto is_highlightable(const std::filesystem::path& path) -> bool + auto language_of(const std::filesystem::path& path) -> std::optional<Language> { - static const std::unordered_set<std::string_view> extensions = { + static const std::unordered_set<std::string_view> cpp = { ".c", ".h", ".cc", ".cp", ".cpp", ".cxx", ".c++", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".ipp", ".m", ".mm", ".cu", ".cuh", }; @@ -274,7 +316,13 @@ namespace Hsdbg return static_cast<char>(std::tolower(ch)); }); - return extensions.contains(ext); + if (ext == ".rs") + return Language::Rust; + + if (cpp.contains(ext)) + return Language::Cpp; + + return std::nullopt; } // the dot sits in the gutter with this much room on either side, and the @@ -310,10 +358,13 @@ namespace Hsdbg lines.push_back(std::move(line)); } + const std::optional<Language> 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<std::vector<SourceSpan>>{}; + m_highlight = language.has_value(); + m_spans = language ? highlight_lines(m_lines, *language) + : std::vector<std::vector<SourceSpan>>{}; m_path_input = path.string(); m_error.clear(); m_highlighted_line = 0; |
