diff options
-rw-r--r-- | DevTools/HackStudio/AutoCompleteBox.cpp | 33 | ||||
-rw-r--r-- | DevTools/HackStudio/AutoCompleteBox.h | 5 | ||||
-rw-r--r-- | DevTools/HackStudio/AutoCompleteResponse.h | 76 | ||||
-rw-r--r-- | DevTools/HackStudio/Editor.cpp | 24 | ||||
-rw-r--r-- | DevTools/HackStudio/Editor.h | 1 | ||||
-rw-r--r-- | DevTools/HackStudio/LanguageClient.cpp | 2 | ||||
-rw-r--r-- | DevTools/HackStudio/LanguageClient.h | 8 | ||||
-rw-r--r-- | DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.cpp | 10 | ||||
-rw-r--r-- | DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.h | 6 | ||||
-rw-r--r-- | DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.cpp | 2 | ||||
-rw-r--r-- | DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.h | 6 | ||||
-rw-r--r-- | DevTools/HackStudio/LanguageServers/LanguageClient.ipc | 2 |
12 files changed, 125 insertions, 50 deletions
diff --git a/DevTools/HackStudio/AutoCompleteBox.cpp b/DevTools/HackStudio/AutoCompleteBox.cpp index 444a83038e..d5950c125c 100644 --- a/DevTools/HackStudio/AutoCompleteBox.cpp +++ b/DevTools/HackStudio/AutoCompleteBox.cpp @@ -37,7 +37,7 @@ static RefPtr<Gfx::Bitmap> s_cplusplus_icon; class AutoCompleteSuggestionModel final : public GUI::Model { public: - explicit AutoCompleteSuggestionModel(Vector<String>&& suggestions) + explicit AutoCompleteSuggestionModel(Vector<AutoCompleteResponse>&& suggestions) : m_suggestions(move(suggestions)) { } @@ -48,6 +48,12 @@ public: __Column_Count, }; + enum InternalRole { + __ModelRoleCustom = (int)GUI::ModelRole::Custom, + PartialInputLength, + Kind, + }; + virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_suggestions.size(); } virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return Column::__Column_Count; } virtual GUI::Variant data(const GUI::ModelIndex& index, GUI::ModelRole role) const override @@ -55,21 +61,29 @@ public: auto& suggestion = m_suggestions.at(index.row()); if (role == GUI::ModelRole::Display) { if (index.column() == Column::Name) { - return suggestion; + return suggestion.completion; } if (index.column() == Column::Icon) { // TODO: Have separate icons for fields, functions, methods etc - if (suggestion.ends_with(".cpp")) + // FIXME: Probably should have different icons for the different kinds, rather than for "c++". + if (suggestion.kind == CompletionKind::Identifier) return *s_cplusplus_icon; return *s_cplusplus_icon; } } + + if ((int)role == InternalRole::Kind) + return (u32)suggestion.kind; + + if ((int)role == InternalRole::PartialInputLength) + return (i64)suggestion.partial_input_length; + return {}; } virtual void update() override {}; private: - Vector<String> m_suggestions; + Vector<AutoCompleteResponse> m_suggestions; }; AutoCompleteBox::~AutoCompleteBox() { } @@ -89,9 +103,8 @@ AutoCompleteBox::AutoCompleteBox(WeakPtr<Editor> editor) m_suggestion_view->set_column_headers_visible(false); } -void AutoCompleteBox::update_suggestions(String partial_input, Vector<String>&& suggestions) +void AutoCompleteBox::update_suggestions(Vector<AutoCompleteResponse>&& suggestions) { - m_partial_input = partial_input; if (suggestions.is_empty()) return; @@ -112,7 +125,6 @@ void AutoCompleteBox::show(Gfx::IntPoint suggstion_box_location) void AutoCompleteBox::close() { - m_partial_input.empty(); m_popup_window->hide(); } @@ -155,11 +167,10 @@ void AutoCompleteBox::apply_suggestion() auto suggestion_index = m_suggestion_view->model()->index(selected_index.row(), AutoCompleteSuggestionModel::Column::Name); auto suggestion = suggestion_index.data().to_string(); + size_t partial_length = suggestion_index.data((GUI::ModelRole)AutoCompleteSuggestionModel::InternalRole::PartialInputLength).to_i64(); - ASSERT(!m_partial_input.is_null()); - ASSERT(suggestion.starts_with(m_partial_input)); - - auto completion = suggestion.substring(m_partial_input.length(), suggestion.length() - m_partial_input.length()); + ASSERT(suggestion.length() >= partial_length); + auto completion = suggestion.substring_view(partial_length, suggestion.length() - partial_length); m_editor->insert_at_cursor_or_replace_selection(completion); } diff --git a/DevTools/HackStudio/AutoCompleteBox.h b/DevTools/HackStudio/AutoCompleteBox.h index 0fe715aa12..ef4bdcf4a5 100644 --- a/DevTools/HackStudio/AutoCompleteBox.h +++ b/DevTools/HackStudio/AutoCompleteBox.h @@ -26,18 +26,20 @@ #pragma once +#include "AutoCompleteResponse.h" #include <AK/WeakPtr.h> #include <LibGUI/Widget.h> namespace HackStudio { class Editor; + class AutoCompleteBox final { public: explicit AutoCompleteBox(WeakPtr<Editor>); ~AutoCompleteBox(); - void update_suggestions(const String partial_input, Vector<String>&& suggestions); + void update_suggestions(Vector<AutoCompleteResponse>&& suggestions); void show(Gfx::IntPoint suggstion_box_location); void close(); @@ -51,6 +53,5 @@ private: WeakPtr<Editor> m_editor; RefPtr<GUI::Window> m_popup_window; RefPtr<GUI::TableView> m_suggestion_view; - String m_partial_input; }; } diff --git a/DevTools/HackStudio/AutoCompleteResponse.h b/DevTools/HackStudio/AutoCompleteResponse.h new file mode 100644 index 0000000000..e1fd298dec --- /dev/null +++ b/DevTools/HackStudio/AutoCompleteResponse.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, the SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include <AK/String.h> +#include <AK/Types.h> +#include <LibIPC/Decoder.h> +#include <LibIPC/Encoder.h> + +namespace HackStudio { + +enum class CompletionKind { + Identifier, +}; + +struct AutoCompleteResponse { + String completion; + size_t partial_input_length { 0 }; + CompletionKind kind { CompletionKind::Identifier }; +}; + +} + +namespace IPC { + +template<> +inline bool encode(IPC::Encoder& encoder, const HackStudio::AutoCompleteResponse& response) +{ + encoder << response.completion; + encoder << (u64)response.partial_input_length; + encoder << (u32)response.kind; + return true; +} + +template<> +inline bool decode(IPC::Decoder& decoder, HackStudio::AutoCompleteResponse& response) +{ + u32 kind = 0; + u64 partial_input_length = 0; + bool ok = decoder.decode(response.completion) + && decoder.decode(partial_input_length) + && decoder.decode(kind); + + if (ok) { + response.kind = static_cast<HackStudio::CompletionKind>(kind); + response.partial_input_length = partial_input_length; + } + + return ok; +} + +} diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp index c52a8d1a3b..45398c1b35 100644 --- a/DevTools/HackStudio/Editor.cpp +++ b/DevTools/HackStudio/Editor.cpp @@ -494,28 +494,10 @@ void Editor::set_document(GUI::TextDocument& doc) Optional<Editor::AutoCompleteRequestData> Editor::get_autocomplete_request_data() { - auto highlighter = wrapper().editor().syntax_highlighter(); - if (!highlighter) + if (!wrapper().editor().m_language_client) 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 {}; + return Editor::AutoCompleteRequestData { { cursor().line(), cursor().column() > 0 ? cursor().column() - 1 : 0 } }; } void Editor::update_autocomplete(const AutoCompleteRequestData& data) @@ -531,7 +513,7 @@ void Editor::update_autocomplete(const AutoCompleteRequestData& data) show_autocomplete(data); - m_autocomplete_box->update_suggestions(data.partial_input, move(suggestions)); + m_autocomplete_box->update_suggestions(move(suggestions)); m_autocomplete_in_focus = true; }; diff --git a/DevTools/HackStudio/Editor.h b/DevTools/HackStudio/Editor.h index fb208a2fa0..90273ff27f 100644 --- a/DevTools/HackStudio/Editor.h +++ b/DevTools/HackStudio/Editor.h @@ -86,7 +86,6 @@ private: struct AutoCompleteRequestData { GUI::TextPosition position; - String partial_input; }; Optional<AutoCompleteRequestData> get_autocomplete_request_data(); diff --git a/DevTools/HackStudio/LanguageClient.cpp b/DevTools/HackStudio/LanguageClient.cpp index 36539802d3..f394dc674d 100644 --- a/DevTools/HackStudio/LanguageClient.cpp +++ b/DevTools/HackStudio/LanguageClient.cpp @@ -61,7 +61,7 @@ void LanguageClient::request_autocomplete(const String& path, size_t cursor_line m_connection.post_message(Messages::LanguageServer::AutoCompleteSuggestions(path, cursor_line, cursor_column)); } -void LanguageClient::provide_autocomplete_suggestions(const Vector<String>& suggestions) +void LanguageClient::provide_autocomplete_suggestions(const Vector<AutoCompleteResponse>& suggestions) { if (on_autocomplete_suggestions) on_autocomplete_suggestions(suggestions); diff --git a/DevTools/HackStudio/LanguageClient.h b/DevTools/HackStudio/LanguageClient.h index 905c09c951..7d0f56d228 100644 --- a/DevTools/HackStudio/LanguageClient.h +++ b/DevTools/HackStudio/LanguageClient.h @@ -26,12 +26,14 @@ #pragma once +#include "AutoCompleteResponse.h" #include <AK/Forward.h> #include <AK/LexicalPath.h> #include <AK/Types.h> +#include <LibIPC/ServerConnection.h> + #include <DevTools/HackStudio/LanguageServers/LanguageClientEndpoint.h> #include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h> -#include <LibIPC/ServerConnection.h> namespace HackStudio { @@ -104,9 +106,9 @@ public: virtual void remove_text(const String& path, size_t from_line, size_t from_column, size_t to_line, size_t to_column); virtual void request_autocomplete(const String& path, size_t cursor_line, size_t cursor_column); - void provide_autocomplete_suggestions(const Vector<String>&); + void provide_autocomplete_suggestions(const Vector<AutoCompleteResponse>&); - Function<void(Vector<String>)> on_autocomplete_suggestions; + Function<void(Vector<AutoCompleteResponse>)> on_autocomplete_suggestions; private: ServerConnection& m_connection; diff --git a/DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.cpp b/DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.cpp index 1ebdd9a8d6..6fda4cff65 100644 --- a/DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.cpp +++ b/DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.cpp @@ -32,7 +32,7 @@ namespace LanguageServers::Cpp { -Vector<String> AutoComplete::get_suggestions(const String& code, GUI::TextPosition autocomplete_position) +Vector<AutoCompleteResponse> AutoComplete::get_suggestions(const String& code, GUI::TextPosition autocomplete_position) { auto lines = code.split('\n', true); Lexer lexer(code); @@ -46,7 +46,7 @@ Vector<String> AutoComplete::get_suggestions(const String& code, GUI::TextPositi #ifdef DEBUG_AUTOCOMPLETE for (auto& suggestion : suggestions) { - dbg() << "suggestion: " << suggestion; + dbg() << "suggestion: " << suggestion.completion; } #endif @@ -71,10 +71,10 @@ Optional<size_t> AutoComplete::token_in_position(const Vector<Cpp::Token>& token return {}; } -Vector<String> AutoComplete::identifier_prefixes(const Vector<String> lines, const Vector<Cpp::Token>& tokens, size_t target_token_index) +Vector<AutoCompleteResponse> AutoComplete::identifier_prefixes(const Vector<String> lines, const Vector<Cpp::Token>& tokens, size_t target_token_index) { auto partial_input = text_of_token(lines, tokens[target_token_index]); - Vector<String> suggestions; + Vector<AutoCompleteResponse> suggestions; HashTable<String> suggestions_lookup; // To avoid duplicate results @@ -85,7 +85,7 @@ Vector<String> AutoComplete::identifier_prefixes(const Vector<String> lines, con auto text = text_of_token(lines, token); if (text.starts_with(partial_input) && !suggestions_lookup.contains(text)) { suggestions_lookup.set(text); - suggestions.append(text); + suggestions.append({ text, partial_input.length(), HackStudio::CompletionKind::Identifier }); } } return suggestions; diff --git a/DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.h b/DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.h index ad0104f5e1..d0b772b0ad 100644 --- a/DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.h +++ b/DevTools/HackStudio/LanguageServers/Cpp/AutoComplete.h @@ -28,23 +28,25 @@ #include <AK/String.h> #include <AK/Vector.h> +#include <DevTools/HackStudio/AutoCompleteResponse.h> #include <LibCpp/Lexer.h> #include <LibGUI/TextPosition.h> namespace LanguageServers::Cpp { using namespace ::Cpp; +using ::HackStudio::AutoCompleteResponse; class AutoComplete { public: AutoComplete() = delete; - static Vector<String> get_suggestions(const String& code, GUI::TextPosition autocomplete_position); + static Vector<AutoCompleteResponse> get_suggestions(const String& code, GUI::TextPosition autocomplete_position); private: static Optional<size_t> token_in_position(const Vector<Cpp::Token>&, GUI::TextPosition); static String text_of_token(const Vector<String> lines, const Cpp::Token&); - static Vector<String> identifier_prefixes(const Vector<String> lines, const Vector<Cpp::Token>&, size_t target_token_index); + static Vector<AutoCompleteResponse> identifier_prefixes(const Vector<String> lines, const Vector<Cpp::Token>&, size_t target_token_index); }; } diff --git a/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.cpp b/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.cpp index 5ffc750b8b..f57719f8cd 100644 --- a/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.cpp +++ b/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.cpp @@ -159,7 +159,7 @@ void ClientConnection::handle(const Messages::LanguageServer::AutoCompleteSugges return; } - Vector<String> suggestions = AutoComplete::get_suggestions(document->text(), { (size_t)message.cursor_line(), (size_t)message.cursor_column() }); + auto suggestions = AutoComplete::get_suggestions(document->text(), { (size_t)message.cursor_line(), (size_t)message.cursor_column() }); post_message(Messages::LanguageClient::AutoCompleteSuggestions(move(suggestions))); } diff --git a/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.h b/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.h index 006688a524..2df3519d92 100644 --- a/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.h +++ b/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.h @@ -28,11 +28,13 @@ #include <AK/HashMap.h> #include <AK/LexicalPath.h> -#include <DevTools/HackStudio/LanguageServers/LanguageClientEndpoint.h> -#include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h> +#include <DevTools/HackStudio/AutoCompleteResponse.h> #include <LibGUI/TextDocument.h> #include <LibIPC/ClientConnection.h> +#include <DevTools/HackStudio/LanguageServers/LanguageClientEndpoint.h> +#include <DevTools/HackStudio/LanguageServers/LanguageServerEndpoint.h> + namespace LanguageServers::Cpp { class ClientConnection final diff --git a/DevTools/HackStudio/LanguageServers/LanguageClient.ipc b/DevTools/HackStudio/LanguageServers/LanguageClient.ipc index dbd0d33d46..66a63d53fb 100644 --- a/DevTools/HackStudio/LanguageServers/LanguageClient.ipc +++ b/DevTools/HackStudio/LanguageServers/LanguageClient.ipc @@ -1,4 +1,4 @@ endpoint LanguageClient = 8002 { - AutoCompleteSuggestions(Vector<String> suggestions) =| + AutoCompleteSuggestions(Vector<HackStudio::AutoCompleteResponse> suggestions) =| } |