diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp index 682149b356..81d991a028 100644 --- a/DevTools/HackStudio/Editor.cpp +++ b/DevTools/HackStudio/Editor.cpp @@ -1,4 +1,5 @@ #include "Editor.h" +#include "CppLexer.h" #include "EditorWrapper.h" #include #include @@ -170,3 +171,71 @@ void Editor::mousemove_event(GMouseEvent& event) } GApplication::the().hide_tooltip(); } + +void Editor::cursor_did_change() +{ + if (m_has_brace_buddies) { + if (m_brace_buddies[0].index >= 0 && m_brace_buddies[0].index < document().spans().size()) + document().set_span_at_index(m_brace_buddies[0].index, m_brace_buddies[0].span_backup); + if (m_brace_buddies[1].index >= 0 && m_brace_buddies[1].index < document().spans().size()) + document().set_span_at_index(m_brace_buddies[1].index, m_brace_buddies[1].span_backup); + m_has_brace_buddies = false; + update(); + } + + enum class Direction { + Forward, + Backward, + }; + + auto find_span_of_type = [&](int i, CppToken::Type type, CppToken::Type not_type, Direction direction) { + int nesting_level = 0; + bool forward = direction == Direction::Forward; + for (forward ? ++i : --i; forward ? (i < document().spans().size()) : (i >= 0); forward ? ++i : --i) { + auto& span = document().spans().at(i); + auto span_token_type = (CppToken::Type)((uintptr_t)span.data); + if (span_token_type == not_type) { + ++nesting_level; + } else if (span_token_type == type) { + if (nesting_level-- <= 0) + return i; + } + } + return -1; + }; + + auto make_buddies = [&](int index0, int index1) { + auto& buddy0 = const_cast(document().spans()[index0]); + auto& buddy1 = const_cast(document().spans()[index1]); + m_has_brace_buddies = true; + m_brace_buddies[0].index = index0; + m_brace_buddies[1].index = index1; + m_brace_buddies[0].span_backup = buddy0; + m_brace_buddies[1].span_backup = buddy1; + buddy0.background_color = Color::DarkCyan; + buddy1.background_color = Color::DarkCyan; + buddy0.color = Color::White; + buddy1.color = Color::White; + update(); + }; + + for (int i = 0; i < document().spans().size(); ++i) { + auto& span = const_cast(document().spans().at(i)); + auto token_type = (CppToken::Type)((uintptr_t)span.data); + if (token_type == CppToken::Type::LeftCurly && span.range.start() == cursor()) { + auto buddy = find_span_of_type(i, CppToken::Type::RightCurly, CppToken::Type::LeftCurly, Direction::Forward); + if (buddy != -1) + make_buddies(i, buddy); + return; + } + + auto right_of_end = span.range.end(); + right_of_end.set_column(right_of_end.column() + 1); + if (token_type == CppToken::Type::RightCurly && right_of_end == cursor()) { + auto buddy = find_span_of_type(i, CppToken::Type::LeftCurly, CppToken::Type::RightCurly, Direction::Backward); + if (buddy != -1) + make_buddies(i, buddy); + return; + } + } +} diff --git a/DevTools/HackStudio/Editor.h b/DevTools/HackStudio/Editor.h index 87bd77692e..0b67cc788a 100644 --- a/DevTools/HackStudio/Editor.h +++ b/DevTools/HackStudio/Editor.h @@ -20,6 +20,7 @@ private: virtual void focusout_event(CEvent&) override; virtual void paint_event(GPaintEvent&) override; virtual void mousemove_event(GMouseEvent&) override; + virtual void cursor_did_change() override; void show_documentation_tooltip_if_available(const String&, const Point& screen_location); @@ -28,4 +29,12 @@ private: RefPtr m_documentation_tooltip_window; RefPtr m_documentation_html_view; String m_last_parsed_token; + + struct BuddySpan { + int index { -1 }; + GTextDocumentSpan span_backup; + }; + + bool m_has_brace_buddies { false }; + BuddySpan m_brace_buddies[2]; }; diff --git a/DevTools/HackStudio/main.cpp b/DevTools/HackStudio/main.cpp index 4c6ee230a7..3ca86d6c13 100644 --- a/DevTools/HackStudio/main.cpp +++ b/DevTools/HackStudio/main.cpp @@ -423,7 +423,7 @@ int main(int argc, char** argv) remove_current_editor_action->set_enabled(g_all_editor_wrappers.size() > 1); }; - open_file("test.frm"); + open_file("main.cpp"); update_actions(); return app.exec(); @@ -484,6 +484,7 @@ static void rehighlight() span.color = style.color; span.font = style.font; span.is_skippable = token.m_type == CppToken::Type::Whitespace; + span.data = (void*)token.m_type; spans.append(span); } current_editor().document().set_spans(spans); diff --git a/Libraries/LibGUI/GTextDocument.h b/Libraries/LibGUI/GTextDocument.h index 04cc9e32ba..4c31cd0bb7 100644 --- a/Libraries/LibGUI/GTextDocument.h +++ b/Libraries/LibGUI/GTextDocument.h @@ -15,8 +15,10 @@ class GTextDocumentLine; struct GTextDocumentSpan { GTextRange range; Color color; + Optional background_color; bool is_skippable { false }; const Font* font { nullptr }; + void* data { nullptr }; }; class GTextDocument : public RefCounted { @@ -54,6 +56,7 @@ public: bool has_spans() const { return !m_spans.is_empty(); } const Vector& spans() const { return m_spans; } + void set_span_at_index(int index, GTextDocumentSpan span) { m_spans[index] = move(span); } void append_line(NonnullOwnPtr); void remove_line(int line_index); diff --git a/Libraries/LibGUI/GTextEditor.cpp b/Libraries/LibGUI/GTextEditor.cpp index ae5b4fc2ed..19264eee70 100644 --- a/Libraries/LibGUI/GTextEditor.cpp +++ b/Libraries/LibGUI/GTextEditor.cpp @@ -389,6 +389,7 @@ void GTextEditor::paint_event(GPaintEvent& event) for (int i = 0; i < visual_line_text.length(); ++i) { const Font* font = &this->font(); Color color; + Optional background_color; GTextPosition physical_position(line_index, start_of_visual_line + i); // FIXME: This is *horribly* inefficient. for (auto& span : document().spans()) { @@ -397,8 +398,11 @@ void GTextEditor::paint_event(GPaintEvent& event) color = span.color; if (span.font) font = span.font; + background_color = span.background_color; break; } + if (background_color.has_value()) + painter.fill_rect(character_rect, background_color.value()); painter.draw_text(character_rect, visual_line_text.substring_view(i, 1), *font, m_text_alignment, color); character_rect.move_by(advance, 0); } @@ -1135,6 +1139,7 @@ void GTextEditor::set_cursor(const GTextPosition& a_position) update(old_cursor_line_rect); update_cursor(); } + cursor_did_change(); if (on_cursor_change) on_cursor_change(); } diff --git a/Libraries/LibGUI/GTextEditor.h b/Libraries/LibGUI/GTextEditor.h index a8bf2d6d09..1684348c5f 100644 --- a/Libraries/LibGUI/GTextEditor.h +++ b/Libraries/LibGUI/GTextEditor.h @@ -120,6 +120,7 @@ protected: virtual void leave_event(CEvent&) override; virtual void context_menu_event(GContextMenuEvent&) override; virtual void resize_event(GResizeEvent&) override; + virtual void cursor_did_change() {} GTextPosition text_position_at(const Point&) const;