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.
This commit is contained in:
Sam Atkins 2023-03-15 11:33:18 +00:00 committed by Andreas Kling
parent 620bf45f43
commit 0761926127
Notes: sideshowbarker 2024-07-17 06:54:15 +09:00
5 changed files with 84 additions and 31 deletions

View file

@ -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<HackStudio::Editor>, Vector<HackStudio::CodeDocument::DiffType> line_differences)
{
m_line_differences = move(line_differences);
}
}

View file

@ -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<Editor>, Vector<DiffType>);
private:
explicit CodeDocument(DeprecatedString const& file_path, Client* client = nullptr);
explicit CodeDocument(Client* client = nullptr);
@ -37,6 +48,8 @@ private:
Optional<Syntax::Language> m_language;
Vector<size_t> m_breakpoint_lines;
Optional<size_t> m_execution_position;
Vector<DiffType> m_line_differences;
};
}

View file

@ -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<DeprecatedString, DeprecatedString>& 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<void> Editor::update_git_diff_indicators()
{
clear_gutter_indicators(m_git_diff_indicator_id);
if (!wrapper().git_repo())
return {};
Vector<CodeDocument::DiffType> 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 {};
}
}

View file

@ -44,6 +44,8 @@ public:
void clear_execution_position();
void set_debug_mode(bool);
ErrorOr<void> 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;
};
}

View file

@ -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)