summaryrefslogtreecommitdiff
path: root/DevTools
diff options
context:
space:
mode:
authorItamar <itamar8910@gmail.com>2020-09-20 20:58:46 +0300
committerAndreas Kling <kling@serenityos.org>2020-09-21 20:16:03 +0200
commitb7bd2ed9d2e3343d806c2cd92ff52f2ed0087cf1 (patch)
tree70357d46fadd99be7d846596a7070d631102b9ff /DevTools
parent7d6e6eb268295f08ef2afac7de6659a29ed9f53d (diff)
downloadserenity-b7bd2ed9d2e3343d806c2cd92ff52f2ed0087cf1.zip
HackStudio: Add auto-complete capability to the Editor
Diffstat (limited to 'DevTools')
-rw-r--r--DevTools/HackStudio/Editor.cpp94
-rw-r--r--DevTools/HackStudio/Editor.h15
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 };
};
}