diff options
author | Itamar <itamar8910@gmail.com> | 2020-09-27 00:11:15 +0300 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-09-30 21:46:59 +0200 |
commit | a39c4cc3405fc37a9621fccbeaed712a9480c96c (patch) | |
tree | bfb39be3ba367c5afcab90b1beda3a5aadb735fd | |
parent | 863f14788fa74591b58a276b194c4ceb5ae3d9e6 (diff) | |
download | serenity-a39c4cc3405fc37a9621fccbeaed712a9480c96c.zip |
HackStudio: Integrate with C++ Language Server
Editors now communicate with the c++ language server when openning and
editing c++ source files, and go through the language server to get
autocomplete suggestions.
-rw-r--r-- | DevTools/HackStudio/CodeDocument.cpp | 19 | ||||
-rw-r--r-- | DevTools/HackStudio/CodeDocument.h | 10 | ||||
-rw-r--r-- | DevTools/HackStudio/Editor.cpp | 96 | ||||
-rw-r--r-- | DevTools/HackStudio/Editor.h | 9 | ||||
-rw-r--r-- | DevTools/HackStudio/EditorWrapper.cpp | 4 | ||||
-rw-r--r-- | DevTools/HackStudio/EditorWrapper.h | 2 | ||||
-rw-r--r-- | DevTools/HackStudio/HackStudio.h | 2 | ||||
-rw-r--r-- | DevTools/HackStudio/HackStudioWidget.cpp | 17 | ||||
-rw-r--r-- | DevTools/HackStudio/HackStudioWidget.h | 1 | ||||
-rw-r--r-- | DevTools/HackStudio/Language.h | 37 | ||||
-rw-r--r-- | DevTools/HackStudio/ProjectFile.cpp | 2 | ||||
-rw-r--r-- | DevTools/HackStudio/main.cpp | 25 | ||||
-rw-r--r-- | Libraries/LibGUI/TextEditor.h | 7 |
13 files changed, 197 insertions, 34 deletions
diff --git a/DevTools/HackStudio/CodeDocument.cpp b/DevTools/HackStudio/CodeDocument.cpp index dbe8a22dac..cddbcb4ae3 100644 --- a/DevTools/HackStudio/CodeDocument.cpp +++ b/DevTools/HackStudio/CodeDocument.cpp @@ -28,11 +28,30 @@ namespace HackStudio { +NonnullRefPtr<CodeDocument> CodeDocument::create(const LexicalPath& file_path, Client* client) +{ + return adopt(*new CodeDocument(file_path, client)); +} + NonnullRefPtr<CodeDocument> CodeDocument::create(Client* client) { return adopt(*new CodeDocument(client)); } +CodeDocument::CodeDocument(const LexicalPath& file_path, Client* client) + : TextDocument(client) + , m_file_path(file_path) +{ + if (file_path.basename().ends_with(".cpp") || file_path.basename().ends_with(".h")) + m_language = Language::Cpp; + else if (file_path.basename().ends_with(".js")) + m_language = Language::JavaScript; + else if (file_path.basename().ends_with(".ini")) + m_language = Language::Ini; + else if (file_path.basename().ends_with(".sh")) + m_language = Language::Shell; +} + CodeDocument::CodeDocument(Client* client) : TextDocument(client) { diff --git a/DevTools/HackStudio/CodeDocument.h b/DevTools/HackStudio/CodeDocument.h index 307f08718e..a044d3592c 100644 --- a/DevTools/HackStudio/CodeDocument.h +++ b/DevTools/HackStudio/CodeDocument.h @@ -26,6 +26,8 @@ #pragma once +#include "Language.h" +#include <AK/LexicalPath.h> #include <LibGUI/TextDocument.h> namespace HackStudio { @@ -33,6 +35,7 @@ namespace HackStudio { class CodeDocument final : public GUI::TextDocument { public: virtual ~CodeDocument() override; + static NonnullRefPtr<CodeDocument> create(const LexicalPath& file_path, Client* client = nullptr); static NonnullRefPtr<CodeDocument> create(Client* client = nullptr); const Vector<size_t>& breakpoint_lines() const { return m_breakpoint_lines; } @@ -40,12 +43,17 @@ public: Optional<size_t> execution_position() const { return m_execution_position; } void set_execution_position(size_t line) { m_execution_position = line; } void clear_execution_position() { m_execution_position.clear(); } + const LexicalPath& file_path() const { return m_file_path; } + Language language() const { return m_language; } virtual bool is_code_document() const override final { return true; } private: - explicit CodeDocument(Client* client); + explicit CodeDocument(const LexicalPath& file_path, Client* client = nullptr); + explicit CodeDocument(Client* client = nullptr); + LexicalPath m_file_path; + Language m_language { Language::Unknown }; Vector<size_t> m_breakpoint_lines; Optional<size_t> m_execution_position; }; diff --git a/DevTools/HackStudio/Editor.cpp b/DevTools/HackStudio/Editor.cpp index 2e1a0fa188..cfd815ef83 100644 --- a/DevTools/HackStudio/Editor.cpp +++ b/DevTools/HackStudio/Editor.cpp @@ -25,16 +25,22 @@ */ #include "Editor.h" -#include "CppAutoComplete.h" +#include "Debugger/Debugger.h" #include "EditorWrapper.h" +#include "HackStudio.h" +#include "Language.h" #include <AK/ByteBuffer.h> #include <AK/LexicalPath.h> #include <LibCore/DirIterator.h> #include <LibCore/File.h> #include <LibGUI/Application.h> +#include <LibGUI/CppSyntaxHighlighter.h> +#include <LibGUI/INISyntaxHighlighter.h> +#include <LibGUI/JSSyntaxHighlighter.h> #include <LibGUI/Label.h> #include <LibGUI/Painter.h> #include <LibGUI/ScrollBar.h> +#include <LibGUI/ShellSyntaxHighlighter.h> #include <LibGUI/SyntaxHighlighter.h> #include <LibGUI/Window.h> #include <LibMarkdown/Document.h> @@ -269,10 +275,10 @@ void Editor::mousedown_event(GUI::MouseEvent& event) if (event.button() == GUI::MouseButton::Left && event.position().x() < ruler_line_rect.width()) { if (!breakpoint_lines().contains_slow(text_position.line())) { breakpoint_lines().append(text_position.line()); - on_breakpoint_change(wrapper().filename_label().text(), text_position.line(), BreakpointChange::Added); + Debugger::on_breakpoint_change(wrapper().filename_label().text(), text_position.line(), BreakpointChange::Added); } else { breakpoint_lines().remove_first_matching([&](size_t line) { return line == text_position.line(); }); - on_breakpoint_change(wrapper().filename_label().text(), text_position.line(), BreakpointChange::Removed); + Debugger::on_breakpoint_change(wrapper().filename_label().text(), text_position.line(), BreakpointChange::Removed); } } @@ -462,6 +468,25 @@ void Editor::set_document(GUI::TextDocument& doc) { ASSERT(doc.is_code_document()); GUI::TextEditor::set_document(doc); + + CodeDocument& code_document = static_cast<CodeDocument&>(doc); + switch (code_document.language()) { + case Language::Cpp: + set_syntax_highlighter(make<GUI::CppSyntaxHighlighter>()); + cpp_Language_server_connection().post_message(Messages::CppLanguageServer::FileOpened(code_document.file_path().string())); + break; + case Language::JavaScript: + set_syntax_highlighter(make<GUI::JSSyntaxHighlighter>()); + break; + case Language::Ini: + set_syntax_highlighter(make<GUI::IniSyntaxHighlighter>()); + break; + case Language::Shell: + set_syntax_highlighter(make<GUI::ShellSyntaxHighlighter>()); + break; + default: + set_syntax_highlighter(nullptr); + } } Optional<Editor::AutoCompleteRequestData> Editor::get_autocomplete_request_data() @@ -492,8 +517,15 @@ Optional<Editor::AutoCompleteRequestData> Editor::get_autocomplete_request_data( 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 (code_document().language() != Language::Cpp) + return; + auto autocomplete_response = cpp_Language_server_connection().send_sync<Messages::CppLanguageServer::AutoCompleteSuggestions>( + code_document().file_path().string(), + data.position.line(), + data.position.column()); + ASSERT(autocomplete_response); + + auto suggestions = autocomplete_response->suggestions(); if (suggestions.is_empty()) { close_autocomplete(); return; @@ -515,4 +547,58 @@ void Editor::close_autocomplete() m_autocomplete_in_focus = false; } +void Editor::on_edit_action(const GUI::Command& command) +{ + if (code_document().language() != Language::Cpp) + return; + + if (command.is_insert_text()) { + const GUI::InsertTextCommand& insert_command = static_cast<const GUI::InsertTextCommand&>(command); + cpp_Language_server_connection().post_message( + Messages::CppLanguageServer::FileEditInsertText( + code_document().file_path().string(), + insert_command.text(), + insert_command.range().start().line(), + insert_command.range().start().column())); + return; + } + + if (command.is_remove_text()) { + const GUI::RemoveTextCommand& remove_command = static_cast<const GUI::RemoveTextCommand&>(command); + cpp_Language_server_connection().post_message( + Messages::CppLanguageServer::FileEditRemoveText( + code_document().file_path().string(), + remove_command.range().start().line(), + remove_command.range().start().column(), + remove_command.range().end().line(), + remove_command.range().end().column())); + return; + } + + ASSERT_NOT_REACHED(); +} + +void Editor::undo() +{ + TextEditor::undo(); + flush_file_content_to_langauge_server(); +} + +void Editor::redo() +{ + TextEditor::redo(); + flush_file_content_to_langauge_server(); +} + +void Editor::flush_file_content_to_langauge_server() +{ + if (code_document().language() != Language::Cpp) + return; + + cpp_Language_server_connection().post_message( + Messages::CppLanguageServer::SetFileContent( + code_document().file_path().string(), + document().text())); +} + } diff --git a/DevTools/HackStudio/Editor.h b/DevTools/HackStudio/Editor.h index e034772c8d..76dce15b56 100644 --- a/DevTools/HackStudio/Editor.h +++ b/DevTools/HackStudio/Editor.h @@ -55,13 +55,16 @@ public: void set_execution_position(size_t line_number); void clear_execution_position(); - BreakpointChangeCallback on_breakpoint_change; - const CodeDocument& code_document() const; CodeDocument& code_document(); virtual void set_document(GUI::TextDocument&) override; + virtual void on_edit_action(const GUI::Command&) override; + + virtual void undo() override; + virtual void redo() override; + private: virtual void focusin_event(GUI::FocusEvent&) override; virtual void focusout_event(GUI::FocusEvent&) override; @@ -91,6 +94,8 @@ private: void show_autocomplete(const AutoCompleteRequestData&); void close_autocomplete(); + void flush_file_content_to_langauge_server(); + explicit Editor(); RefPtr<GUI::Window> m_documentation_tooltip_window; diff --git a/DevTools/HackStudio/EditorWrapper.cpp b/DevTools/HackStudio/EditorWrapper.cpp index c02816a9eb..789b43f098 100644 --- a/DevTools/HackStudio/EditorWrapper.cpp +++ b/DevTools/HackStudio/EditorWrapper.cpp @@ -35,7 +35,7 @@ namespace HackStudio { -EditorWrapper::EditorWrapper(BreakpointChangeCallback breakpoint_change_callback) +EditorWrapper::EditorWrapper() { set_layout<GUI::VerticalBoxLayout>(); @@ -72,8 +72,6 @@ EditorWrapper::EditorWrapper(BreakpointChangeCallback breakpoint_change_callback m_editor->on_open = [](String path) { open_file(path); }; - - m_editor->on_breakpoint_change = move(breakpoint_change_callback); } EditorWrapper::~EditorWrapper() diff --git a/DevTools/HackStudio/EditorWrapper.h b/DevTools/HackStudio/EditorWrapper.h index c286a4d139..38f24c0984 100644 --- a/DevTools/HackStudio/EditorWrapper.h +++ b/DevTools/HackStudio/EditorWrapper.h @@ -50,7 +50,7 @@ public: void set_editor_has_focus(Badge<Editor>, bool); private: - explicit EditorWrapper(BreakpointChangeCallback); + EditorWrapper(); RefPtr<GUI::Label> m_filename_label; RefPtr<GUI::Label> m_cursor_label; diff --git a/DevTools/HackStudio/HackStudio.h b/DevTools/HackStudio/HackStudio.h index 9216bf3c2c..7aebad117a 100644 --- a/DevTools/HackStudio/HackStudio.h +++ b/DevTools/HackStudio/HackStudio.h @@ -27,6 +27,7 @@ #pragma once #include "EditorWrapper.h" +#include "LanguageClients/Cpp/ServerConnection.h" #include "Project.h" #include <AK/String.h> #include <LibGUI/TextEditor.h> @@ -40,5 +41,6 @@ void open_file(const String&); Project& project(); String currently_open_file(); void set_current_editor_wrapper(RefPtr<EditorWrapper>); +LanguageClients::Cpp::ServerConnection& cpp_Language_server_connection(); } diff --git a/DevTools/HackStudio/HackStudioWidget.cpp b/DevTools/HackStudio/HackStudioWidget.cpp index 190fecf122..00c225f2a4 100644 --- a/DevTools/HackStudio/HackStudioWidget.cpp +++ b/DevTools/HackStudio/HackStudioWidget.cpp @@ -57,16 +57,12 @@ #include <LibGUI/Application.h> #include <LibGUI/BoxLayout.h> #include <LibGUI/Button.h> -#include <LibGUI/CppSyntaxHighlighter.h> #include <LibGUI/FilePicker.h> -#include <LibGUI/INISyntaxHighlighter.h> #include <LibGUI/InputBox.h> -#include <LibGUI/JSSyntaxHighlighter.h> #include <LibGUI/Label.h> #include <LibGUI/Menu.h> #include <LibGUI/MenuBar.h> #include <LibGUI/MessageBox.h> -#include <LibGUI/ShellSyntaxHighlighter.h> #include <LibGUI/Splitter.h> #include <LibGUI/StackWidget.h> #include <LibGUI/TabWidget.h> @@ -211,17 +207,6 @@ void HackStudioWidget::open_file(const String& filename) current_editor().set_mode(GUI::TextEditor::ReadOnly); } - if (filename.ends_with(".cpp") || filename.ends_with(".h")) - current_editor().set_syntax_highlighter(make<GUI::CppSyntaxHighlighter>()); - else if (filename.ends_with(".js")) - current_editor().set_syntax_highlighter(make<GUI::JSSyntaxHighlighter>()); - else if (filename.ends_with(".ini")) - current_editor().set_syntax_highlighter(make<GUI::IniSyntaxHighlighter>()); - else if (filename.ends_with(".sh")) - current_editor().set_syntax_highlighter(make<GUI::ShellSyntaxHighlighter>()); - else - current_editor().set_syntax_highlighter(nullptr); - if (filename.ends_with(".frm")) { set_edit_mode(EditMode::Form); } else { @@ -366,7 +351,7 @@ NonnullRefPtr<GUI::Action> HackStudioWidget::create_delete_action() void HackStudioWidget::add_new_editor(GUI::Widget& parent) { - auto wrapper = EditorWrapper::construct(Debugger::on_breakpoint_change); + auto wrapper = EditorWrapper::construct(); if (m_action_tab_widget) { parent.insert_child_before(wrapper, *m_action_tab_widget); } else { diff --git a/DevTools/HackStudio/HackStudioWidget.h b/DevTools/HackStudio/HackStudioWidget.h index bf219d9e1c..c7f254446b 100644 --- a/DevTools/HackStudio/HackStudioWidget.h +++ b/DevTools/HackStudio/HackStudioWidget.h @@ -123,6 +123,7 @@ private: String m_currently_open_file; OwnPtr<Project> m_project; + RefPtr<GUI::TreeView> m_project_tree_view; RefPtr<GUI::VerticalSplitter> m_right_hand_splitter; RefPtr<GUI::StackWidget> m_right_hand_stack; diff --git a/DevTools/HackStudio/Language.h b/DevTools/HackStudio/Language.h new file mode 100644 index 0000000000..6295fcde25 --- /dev/null +++ b/DevTools/HackStudio/Language.h @@ -0,0 +1,37 @@ +/* + * 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 + +namespace HackStudio { +enum class Language { + Unknown, + Cpp, + JavaScript, + Ini, + Shell, +}; +} diff --git a/DevTools/HackStudio/ProjectFile.cpp b/DevTools/HackStudio/ProjectFile.cpp index bd1b894242..4f15bad20f 100644 --- a/DevTools/HackStudio/ProjectFile.cpp +++ b/DevTools/HackStudio/ProjectFile.cpp @@ -38,7 +38,7 @@ ProjectFile::ProjectFile(const String& name) const GUI::TextDocument& ProjectFile::document() const { if (!m_document) { - m_document = CodeDocument::create(nullptr); + m_document = CodeDocument::create(LexicalPath(m_name)); auto file = Core::File::construct(m_name); if (!file->open(Core::File::ReadOnly)) { ASSERT_NOT_REACHED(); diff --git a/DevTools/HackStudio/main.cpp b/DevTools/HackStudio/main.cpp index 0a10f45715..8cbf81ea26 100644 --- a/DevTools/HackStudio/main.cpp +++ b/DevTools/HackStudio/main.cpp @@ -26,6 +26,7 @@ #include "HackStudio.h" #include "HackStudioWidget.h" +#include "LanguageClients/Cpp/ServerConnection.h" #include "Project.h" #include <AK/StringBuilder.h> #include <LibCore/ArgsParser.h> @@ -51,22 +52,24 @@ using namespace HackStudio; static RefPtr<GUI::Window> s_window; static RefPtr<HackStudioWidget> s_hack_studio_widget; +static RefPtr<LanguageClients::Cpp::ServerConnection> s_cpp_Language_server_connection; static bool make_is_available(); static void update_path_environment_variable(); static String path_to_project(const String& path_argument_absolute_path); static void open_default_project_file(const String& project_path); +static void initialize_connections_to_language_servers(const String& project_path); int main(int argc, char** argv) { - if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec unix fattr thread", nullptr) < 0) { + if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec unix fattr thread unix", nullptr) < 0) { perror("pledge"); return 1; } auto app = GUI::Application::construct(argc, argv); - if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec fattr thread", nullptr) < 0) { + if (pledge("stdio tty accept rpath cpath wpath shared_buffer proc exec fattr thread unix", nullptr) < 0) { perror("pledge"); return 1; } @@ -89,7 +92,10 @@ int main(int argc, char** argv) auto argument_absolute_path = Core::File::real_path_for(path_argument); auto menubar = GUI::MenuBar::construct(); - s_hack_studio_widget = s_window->set_main_widget<HackStudioWidget>(path_to_project(argument_absolute_path)); + auto project_path = path_to_project(argument_absolute_path); + s_hack_studio_widget = s_window->set_main_widget<HackStudioWidget>(project_path); + + initialize_connections_to_language_servers(project_path); s_hack_studio_widget->initialize_menubar(menubar); app->set_menubar(menubar); @@ -146,6 +152,13 @@ static void open_default_project_file(const String& project_path) open_file(s_hack_studio_widget->project().default_file()); } +static void initialize_connections_to_language_servers(const String& project_path) +{ + LexicalPath project_root_dir(LexicalPath(project_path).dirname()); + s_cpp_Language_server_connection = LanguageClients::Cpp::ServerConnection::construct(project_root_dir.string()); + s_cpp_Language_server_connection->handshake(); +} + namespace HackStudio { GUI::TextEditor& current_editor() @@ -182,4 +195,10 @@ void set_current_editor_wrapper(RefPtr<EditorWrapper> wrapper) s_hack_studio_widget->set_current_editor_wrapper(wrapper); } +LanguageClients::Cpp::ServerConnection& cpp_Language_server_connection() +{ + ASSERT(s_cpp_Language_server_connection); + return *s_cpp_Language_server_connection; +} + } diff --git a/Libraries/LibGUI/TextEditor.h b/Libraries/LibGUI/TextEditor.h index f451b16b10..519f0af3cd 100644 --- a/Libraries/LibGUI/TextEditor.h +++ b/Libraries/LibGUI/TextEditor.h @@ -131,8 +131,8 @@ public: void do_delete(); void delete_current_line(); void select_all(); - void undo() { document().undo(); } - void redo() { document().redo(); } + virtual void undo() { document().undo(); } + virtual void redo() { document().redo(); } Function<void()> on_change; Function<void()> on_mousedown; @@ -263,10 +263,13 @@ private: inline void execute(Args&&... args) { auto command = make<T>(*m_document, forward<Args>(args)...); + on_edit_action(*command); command->execute_from(*this); m_document->add_to_undo_stack(move(command)); } + virtual void on_edit_action(const Command&) { } + Type m_type { MultiLine }; Mode m_mode { Editable }; |