diff options
author | Itamar <itamar8910@gmail.com> | 2020-09-20 20:58:46 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-21 20:16:03 +0200 |
commit | b7bd2ed9d2e3343d806c2cd92ff52f2ed0087cf1 (patch) | |
tree | 70357d46fadd99be7d846596a7070d631102b9ff /DevTools | |
parent | 7d6e6eb268295f08ef2afac7de6659a29ed9f53d (diff) | |
download | serenity-b7bd2ed9d2e3343d806c2cd92ff52f2ed0087cf1.zip |
HackStudio: Add auto-complete capability to the Editor
Diffstat (limited to 'DevTools')
-rw-r--r-- | DevTools/HackStudio/Editor.cpp | 94 | ||||
-rw-r--r-- | DevTools/HackStudio/Editor.h | 15 |
2 files changed, 109 insertions, 0 deletions
diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp index 275fe3cdf5..2e1a0fa188 100644 --- a/DevTools/HackStudio/Editor.cpp +++ b/DevTools/HackStudio/Editor.cpp @@ -25,6 +25,7 @@ */ #include "Editor.h" +#include "CppAutoComplete.h" #include "EditorWrapper.h" #include <AK/ByteBuffer.h> #include <AK/LexicalPath.h> @@ -53,6 +54,8 @@ Editor::Editor() m_documentation_tooltip_window->set_rect(0, 0, 500, 400); m_documentation_tooltip_window->set_window_type(GUI::WindowType::Tooltip); m_documentation_page_view = m_documentation_tooltip_window->set_main_widget<Web::InProcessWebView>(); + + m_autocomplete_box = make<AutoCompleteBox>(make_weak_ptr()); } Editor::~Editor() @@ -307,9 +310,49 @@ void Editor::mousedown_event(GUI::MouseEvent& event) void Editor::keydown_event(GUI::KeyEvent& event) { + if (m_autocomplete_in_focus) { + if (event.key() == Key_Escape) { + m_autocomplete_in_focus = false; + m_autocomplete_box->close(); + return; + } + if (event.key() == Key_Down) { + m_autocomplete_box->next_suggestion(); + return; + } + if (event.key() == Key_Up) { + m_autocomplete_box->previous_suggestion(); + return; + } + if (event.key() == Key_Return || event.key() == Key_Tab) { + m_autocomplete_box->apply_suggestion(); + close_autocomplete(); + return; + } + } + + auto autocomplete_action = [this]() { + auto data = get_autocomplete_request_data(); + if (data.has_value()) { + update_autocomplete(data.value()); + if (m_autocomplete_in_focus) + show_autocomplete(data.value()); + } else { + close_autocomplete(); + } + }; + if (event.key() == Key_Control) m_holding_ctrl = true; + + if (m_holding_ctrl && event.key() == Key_Space) { + autocomplete_action(); + } GUI::TextEditor::keydown_event(event); + + if (m_autocomplete_in_focus) { + autocomplete_action(); + } } void Editor::keyup_event(GUI::KeyEvent& event) @@ -421,4 +464,55 @@ void Editor::set_document(GUI::TextDocument& doc) GUI::TextEditor::set_document(doc); } +Optional<Editor::AutoCompleteRequestData> Editor::get_autocomplete_request_data() +{ + auto highlighter = wrapper().editor().syntax_highlighter(); + if (!highlighter) + return {}; + auto& spans = document().spans(); + for (size_t span_index = 2; span_index < spans.size(); ++span_index) { + auto& span = spans[span_index]; + if (!span.range.contains(cursor())) { + continue; + } + + if (highlighter->is_identifier(spans[span_index - 1].data)) { + auto completion_span = spans[span_index - 1]; + + auto adjusted_range = completion_span.range; + auto end_line_length = document().line(completion_span.range.end().line()).length(); + adjusted_range.end().set_column(min(end_line_length, adjusted_range.end().column() + 1)); + auto text_in_span = document().text_in_range(adjusted_range); + + return AutoCompleteRequestData { completion_span.range.end(), text_in_span }; + } + } + return {}; +} + +void Editor::update_autocomplete(const AutoCompleteRequestData& data) +{ + // TODO: Move this part to a language server component :) + auto suggestions = CppAutoComplete::get_suggestions(text(), data.position); + if (suggestions.is_empty()) { + close_autocomplete(); + return; + } + + m_autocomplete_box->update_suggestions(data.partial_input, move(suggestions)); + m_autocomplete_in_focus = true; +} + +void Editor::show_autocomplete(const AutoCompleteRequestData& data) +{ + auto suggestion_box_location = content_rect_for_position(data.position).bottom_right().translated(screen_relative_rect().top_left().translated(ruler_width(), 0).translated(10, 5)); + m_autocomplete_box->show(suggestion_box_location); +} + +void Editor::close_autocomplete() +{ + m_autocomplete_box->close(); + m_autocomplete_in_focus = false; +} + } diff --git a/DevTools/HackStudio/Editor.h b/DevTools/HackStudio/Editor.h index e5f1cf335d..e034772c8d 100644 --- a/DevTools/HackStudio/Editor.h +++ b/DevTools/HackStudio/Editor.h @@ -26,9 +26,11 @@ #pragma once +#include "AutoCompleteBox.h" #include "CodeDocument.h" #include "Debugger/BreakpointCallback.h" #include <AK/Optional.h> +#include <AK/OwnPtr.h> #include <LibGUI/TextEditor.h> #include <LibWeb/Forward.h> @@ -78,15 +80,28 @@ private: static const Gfx::Bitmap& breakpoint_icon_bitmap(); static const Gfx::Bitmap& current_position_icon_bitmap(); + struct AutoCompleteRequestData { + GUI::TextPosition position; + String partial_input; + }; + + Optional<AutoCompleteRequestData> get_autocomplete_request_data(); + + void update_autocomplete(const AutoCompleteRequestData&); + void show_autocomplete(const AutoCompleteRequestData&); + void close_autocomplete(); + explicit Editor(); RefPtr<GUI::Window> m_documentation_tooltip_window; + OwnPtr<AutoCompleteBox> m_autocomplete_box; RefPtr<Web::InProcessWebView> m_documentation_page_view; String m_last_parsed_token; GUI::TextPosition m_previous_text_position { 0, 0 }; bool m_hovering_editor { false }; bool m_hovering_link { false }; bool m_holding_ctrl { false }; + bool m_autocomplete_in_focus { false }; }; } |