From 0e8a995bc2cb5416228c12add93d10a631b7f952 Mon Sep 17 00:00:00 2001 From: hachem Date: Sun, 14 Dec 2025 16:44:22 +0100 Subject: feat: macro expansion --- src/ui/source_view.cpp | 132 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 127 insertions(+), 5 deletions(-) (limited to 'src/ui/source_view.cpp') diff --git a/src/ui/source_view.cpp b/src/ui/source_view.cpp index e0c26b4..5ced658 100644 --- a/src/ui/source_view.cpp +++ b/src/ui/source_view.cpp @@ -100,7 +100,8 @@ namespace Hsdbg return true; } - auto highlight_lines(const std::vector& lines, Language language) + auto highlight_lines(const std::vector& lines, Language language, + const MacroTable& macros) -> std::vector> { std::vector> out(lines.size()); @@ -260,9 +261,15 @@ namespace Hsdbg while (at < size && is_word(line[at])) ++at; - if (const SyntaxKind kind = classify_word( - std::string_view(line).substr(start, at - start), language); - kind != SyntaxKind::Default) + const std::string_view word = + std::string_view(line).substr(start, at - start); + SyntaxKind kind = classify_word(word, language); + + if (kind == SyntaxKind::Default && language == Language::Cpp && + macros.find(word) != nullptr) + kind = SyntaxKind::Macro; + + if (kind != SyntaxKind::Default) { flush_default(start); emit(start, at - start, kind); @@ -314,6 +321,42 @@ namespace Hsdbg { return ImGui::CalcTextSize(std::to_string(line_count).c_str()).x; } + + auto capture_invocation(const std::string& line, uint32_t start) -> std::string + { + const size_t size = line.size(); + size_t at = start; + + while (at < size && is_word(line[at])) + ++at; + + size_t paren = at; + + while (paren < size && std::isspace(static_cast(line[paren])) != 0) + ++paren; + + if (paren < size && line[paren] == '(') + { + int depth = 0; + + for (size_t i = paren; i < size; ++i) + { + if (line[i] == '(') + { + ++depth; + } + else if (line[i] == ')') + { + --depth; + + if (depth == 0) + return line.substr(start, i + 1 - start); + } + } + } + + return line.substr(start, at - start); + } } auto SourceView::open(const std::filesystem::path& path) -> Result @@ -338,7 +381,13 @@ namespace Hsdbg m_path = path; m_lines = std::move(lines); m_highlight = language.has_value(); - m_spans = language ? highlight_lines(m_lines, *language) + + m_macros.clear(); + + if (language == Language::Cpp) + m_macros.build(m_lines, path.parent_path()); + + m_spans = language ? highlight_lines(m_lines, *language, m_macros) : std::vector>{}; m_path_input = path.string(); m_error.clear(); @@ -354,11 +403,20 @@ namespace Hsdbg m_path.clear(); m_lines.clear(); m_spans.clear(); + m_macros.clear(); + m_macro_request.reset(); m_highlight = false; m_error.clear(); m_highlighted_line = 0; } + auto SourceView::take_macro_request() -> std::optional + { + std::optional request = std::move(m_macro_request); + m_macro_request.reset(); + return request; + } + auto SourceView::set_highlighted_line(uint32_t line) -> void { m_highlighted_line = line; @@ -533,6 +591,10 @@ namespace Hsdbg { ImGui::TextUnformatted(begin, end); } + else if (span.kind == SyntaxKind::Macro) + { + draw_macro_span(draw_list, text, span); + } else { ImGui::PushStyleColor(ImGuiCol_Text, @@ -549,4 +611,64 @@ namespace Hsdbg ImGui::EndChild(); } + + auto SourceView::draw_macro_span(ImDrawList* draw_list, const std::string& line, + const SourceSpan& span) -> void + { + const char* const begin = line.c_str() + span.start; + const char* const end = begin + span.length; + const ImU32 color = m_syntax_colors[static_cast(SyntaxKind::Macro)]; + + ImGui::PushStyleColor(ImGuiCol_Text, color); + ImGui::TextUnformatted(begin, end); + ImGui::PopStyleColor(); + + if (!ImGui::IsItemHovered()) + return; + + const ImVec2 rect_min = ImGui::GetItemRectMin(); + const ImVec2 rect_max = ImGui::GetItemRectMax(); + draw_list->AddLine(ImVec2(rect_min.x, rect_max.y - 1.0f), + ImVec2(rect_max.x, rect_max.y - 1.0f), color); + + const std::string invocation = capture_invocation(line, span.start); + const MacroExpansion expansion = expand_stages(m_macros, invocation); + + ImGui::BeginTooltip(); + + ImGui::PushStyleColor(ImGuiCol_Text, color); + ImGui::TextUnformatted(invocation.c_str()); + ImGui::PopStyleColor(); + + ImGui::Separator(); + + std::string preview = expansion.levels.back(); + + if (constexpr size_t limit = 240; preview.size() > limit) + { + preview.resize(limit); + preview += " ..."; + } + + ImGui::PushTextWrapPos(ImGui::GetFontSize() * 30.0f); + ImGui::TextUnformatted(preview.c_str()); + ImGui::PopTextWrapPos(); + + ImGui::Spacing(); + + const size_t steps = expansion.levels.size() - 1; + + if (steps == 0) + ImGui::TextDisabled("no expansion"); + else if (!expansion.fully_expanded()) + ImGui::TextDisabled("%zu+ levels — click to step through", steps); + else + ImGui::TextDisabled("%zu level%s — click to step through", steps, + steps == 1 ? "" : "s"); + + ImGui::EndTooltip(); + + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) + m_macro_request = invocation; + } } -- cgit v1.3