From 07619261272f449e2c5a5d8a30f9c39a94bb39a6 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Wed, 15 Mar 2023 11:33:18 +0000 Subject: [PATCH] HackStudio: Migrate git-diff indicators to TextEditor API As part of this, the CodeDocument now keeps track of the kind of difference for each line. Previously, we iterated every hunk every time the editor was painted, but now we do that once whenever the diff changes, and then save the type of difference for each line. --- Userland/DevTools/HackStudio/CodeDocument.cpp | 10 +++ Userland/DevTools/HackStudio/CodeDocument.h | 13 +++ Userland/DevTools/HackStudio/Editor.cpp | 85 ++++++++++++------- Userland/DevTools/HackStudio/Editor.h | 3 + .../DevTools/HackStudio/EditorWrapper.cpp | 4 +- 5 files changed, 84 insertions(+), 31 deletions(-) diff --git a/Userland/DevTools/HackStudio/CodeDocument.cpp b/Userland/DevTools/HackStudio/CodeDocument.cpp index f37b259e1e..c4caef0024 100644 --- a/Userland/DevTools/HackStudio/CodeDocument.cpp +++ b/Userland/DevTools/HackStudio/CodeDocument.cpp @@ -32,4 +32,14 @@ CodeDocument::CodeDocument(Client* client) { } +CodeDocument::DiffType CodeDocument::line_difference(size_t line) const +{ + return m_line_differences[line]; +} + +void CodeDocument::set_line_differences(Badge, Vector line_differences) +{ + m_line_differences = move(line_differences); +} + } diff --git a/Userland/DevTools/HackStudio/CodeDocument.h b/Userland/DevTools/HackStudio/CodeDocument.h index 4db89d1751..a0a2687091 100644 --- a/Userland/DevTools/HackStudio/CodeDocument.h +++ b/Userland/DevTools/HackStudio/CodeDocument.h @@ -13,6 +13,8 @@ namespace HackStudio { +class Editor; + class CodeDocument final : public GUI::TextDocument { public: virtual ~CodeDocument() override = default; @@ -29,6 +31,15 @@ public: virtual bool is_code_document() const override final { return true; } + enum class DiffType { + None, + AddedLine, + ModifiedLine, + DeletedLinesBefore, + }; + DiffType line_difference(size_t line) const; + void set_line_differences(Badge, Vector); + private: explicit CodeDocument(DeprecatedString const& file_path, Client* client = nullptr); explicit CodeDocument(Client* client = nullptr); @@ -37,6 +48,8 @@ private: Optional m_language; Vector m_breakpoint_lines; Optional m_execution_position; + + Vector m_line_differences; }; } diff --git a/Userland/DevTools/HackStudio/Editor.cpp b/Userland/DevTools/HackStudio/Editor.cpp index 1ca6c0ec28..e50bb353a5 100644 --- a/Userland/DevTools/HackStudio/Editor.cpp +++ b/Userland/DevTools/HackStudio/Editor.cpp @@ -86,6 +86,24 @@ Editor::Editor() add_breakpoint(line).release_value_but_fixme_should_propagate_errors(); }; + m_git_diff_indicator_id = register_gutter_indicator( + [&](auto& painter, Gfx::IntRect rect, size_t line) { + auto diff_type = code_document().line_difference(line); + switch (diff_type) { + case CodeDocument::DiffType::AddedLine: + painter.draw_text(rect, "+"sv, font(), Gfx::TextAlignment::Center); + break; + case CodeDocument::DiffType::ModifiedLine: + painter.draw_text(rect, "!"sv, font(), Gfx::TextAlignment::Center); + break; + case CodeDocument::DiffType::DeletedLinesBefore: + painter.draw_text(rect, "-"sv, font(), Gfx::TextAlignment::Center); + break; + case CodeDocument::DiffType::None: + VERIFY_NOT_REACHED(); + } + }).release_value_but_fixme_should_propagate_errors(); + m_breakpoint_indicator_id = register_gutter_indicator( [&](auto& painter, Gfx::IntRect rect, size_t) { auto const& icon = breakpoint_icon_bitmap(); @@ -159,36 +177,6 @@ void Editor::paint_event(GUI::PaintEvent& event) rect.set_height(rect.height() - horizontal_scrollbar().height()); painter.draw_rect(rect, palette().selection()); } - - if (gutter_visible()) { - size_t first_visible_line = text_position_at(event.rect().top_left()).line(); - size_t last_visible_line = text_position_at(event.rect().bottom_right()).line(); - - if (wrapper().git_repo()) { - for (auto& hunk : wrapper().hunks()) { - auto start_line = hunk.target_start_line; - auto finish_line = start_line + hunk.added_lines.size(); - - auto additions = hunk.added_lines.size(); - auto deletions = hunk.removed_lines.size(); - - for (size_t line_offset = 0; line_offset < additions; line_offset++) { - auto line = start_line + line_offset; - if (line < first_visible_line || line > last_visible_line) { - continue; - } - auto sign = (line_offset < deletions) ? "!"sv : "+"sv; - painter.draw_text(gutter_icon_rect(line), sign, font(), Gfx::TextAlignment::Center); - } - if (additions < deletions) { - auto deletions_line = min(finish_line, line_count() - 1); - if (deletions_line <= last_visible_line) { - painter.draw_text(gutter_icon_rect(deletions_line), "-"sv, font(), Gfx::TextAlignment::Center); - } - } - } - } - } } static HashMap& man_paths() @@ -827,4 +815,41 @@ void Editor::remove_breakpoint(size_t line_number) Debugger::the().on_breakpoint_change(wrapper().filename_title(), line_number, BreakpointChange::Removed); } +ErrorOr Editor::update_git_diff_indicators() +{ + clear_gutter_indicators(m_git_diff_indicator_id); + + if (!wrapper().git_repo()) + return {}; + + Vector line_differences; + TRY(line_differences.try_ensure_capacity(document().line_count())); + for (auto i = 0u; i < document().line_count(); ++i) + line_differences.unchecked_append(CodeDocument::DiffType::None); + + for (auto& hunk : wrapper().hunks()) { + auto start_line = hunk.target_start_line; + auto finish_line = start_line + hunk.added_lines.size(); + + auto additions = hunk.added_lines.size(); + auto deletions = hunk.removed_lines.size(); + + for (size_t line_offset = 0; line_offset < additions; line_offset++) { + auto line = start_line + line_offset; + auto difference = (line_offset < deletions) ? CodeDocument::DiffType::ModifiedLine : CodeDocument::DiffType::AddedLine; + line_differences[line] = difference; + add_gutter_indicator(m_git_diff_indicator_id, line); + } + if (additions < deletions) { + auto deletions_line = min(finish_line, line_count() - 1); + line_differences[deletions_line] = CodeDocument::DiffType::DeletedLinesBefore; + add_gutter_indicator(m_git_diff_indicator_id, deletions_line); + } + } + code_document().set_line_differences({}, move(line_differences)); + update(); + + return {}; +} + } diff --git a/Userland/DevTools/HackStudio/Editor.h b/Userland/DevTools/HackStudio/Editor.h index cffaa472e2..24f4180785 100644 --- a/Userland/DevTools/HackStudio/Editor.h +++ b/Userland/DevTools/HackStudio/Editor.h @@ -44,6 +44,8 @@ public: void clear_execution_position(); void set_debug_mode(bool); + ErrorOr update_git_diff_indicators(); + CodeDocument const& code_document() const; CodeDocument& code_document(); @@ -125,6 +127,7 @@ private: GutterIndicatorID m_breakpoint_indicator_id; GutterIndicatorID m_execution_indicator_id; + GutterIndicatorID m_git_diff_indicator_id; }; } diff --git a/Userland/DevTools/HackStudio/EditorWrapper.cpp b/Userland/DevTools/HackStudio/EditorWrapper.cpp index 6cb484f9be..ea104594db 100644 --- a/Userland/DevTools/HackStudio/EditorWrapper.cpp +++ b/Userland/DevTools/HackStudio/EditorWrapper.cpp @@ -87,8 +87,10 @@ void EditorWrapper::save() void EditorWrapper::update_diff() { - if (m_git_repo) + if (m_git_repo) { m_hunks = Diff::parse_hunks(m_git_repo->unstaged_diff(filename()).value()); + editor().update_git_diff_indicators().release_value_but_fixme_should_propagate_errors(); + } } void EditorWrapper::set_project_root(DeprecatedString const& project_root)