Selaa lähdekoodia

LanguageServers/Cpp: Use FileDB and AutoCompleteEngine base class

Itamar 4 vuotta sitten
vanhempi
commit
02038a0ede

+ 36 - 0
Userland/DevTools/HackStudio/LanguageServers/Cpp/AutoCompleteEngine.cpp

@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
+ * 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.
+ */
+
+#include "AutoCompleteEngine.h"
+
+AutoCompleteEngine::AutoCompleteEngine(const FileDB& filedb)
+    : m_filedb(filedb)
+{
+}
+
+AutoCompleteEngine::~AutoCompleteEngine()
+{
+}

+ 49 - 0
Userland/DevTools/HackStudio/LanguageServers/Cpp/AutoCompleteEngine.h

@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2021, Itamar S. <itamar8910@gmail.com>
+ * 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 "FileDB.h"
+#include <DevTools/HackStudio/AutoCompleteResponse.h>
+#include <LibGUI/TextPosition.h>
+
+class AutoCompleteEngine {
+public:
+    AutoCompleteEngine(const FileDB& filedb);
+    virtual ~AutoCompleteEngine();
+
+    virtual Vector<GUI::AutocompleteProvider::Entry> get_suggestions(const String& file, const GUI::TextPosition& autocomplete_position) = 0;
+
+    // TODO: In the future we can pass the range that was edited and only re-parse what we have to.
+    virtual void on_edit([[maybe_unused]] const String& file) {};
+    virtual void file_opened([[maybe_unused]] const String& file) {};
+
+protected:
+    const FileDB& filedb() const { return m_filedb; }
+
+private:
+    const FileDB& m_filedb;
+};

+ 3 - 1
Userland/DevTools/HackStudio/LanguageServers/Cpp/CMakeLists.txt

@@ -1,8 +1,10 @@
 set(SOURCES
+    AutoCompleteEngine.cpp
     ClientConnection.cpp
-    main.cpp
+    FileDB.cpp
     LexerAutoComplete.cpp
     ParserAutoComplete.cpp
+    main.cpp
 )
 
 set(GENERATED_SOURCES

+ 15 - 78
Userland/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.cpp

@@ -40,6 +40,7 @@ ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int
     : IPC::ClientConnection<LanguageClientEndpoint, LanguageServerEndpoint>(*this, move(socket), client_id)
 {
     s_connections.set(client_id, *this);
+    m_autocomplete_engine = make<LexerAutoComplete>(m_filedb);
 }
 
 ClientConnection::~ClientConnection()
@@ -66,38 +67,13 @@ OwnPtr<Messages::LanguageServer::GreetResponse> ClientConnection::handle(const M
     return make<Messages::LanguageServer::GreetResponse>();
 }
 
-class DefaultDocumentClient final : public GUI::TextDocument::Client {
-public:
-    virtual ~DefaultDocumentClient() override = default;
-    virtual void document_did_append_line() override {};
-    virtual void document_did_insert_line(size_t) override {};
-    virtual void document_did_remove_line(size_t) override {};
-    virtual void document_did_remove_all_lines() override {};
-    virtual void document_did_change() override {};
-    virtual void document_did_set_text() override {};
-    virtual void document_did_set_cursor(const GUI::TextPosition&) override {};
-
-    virtual bool is_automatic_indentation_enabled() const override { return false; }
-    virtual int soft_tab_width() const override { return 4; }
-};
-
-static DefaultDocumentClient s_default_document_client;
-
 void ClientConnection::handle(const Messages::LanguageServer::FileOpened& message)
 {
-    auto file = Core::File::construct(this);
-    if (!file->open(message.file().take_fd(), Core::IODevice::ReadOnly, Core::File::ShouldCloseFileDescriptor::Yes)) {
-        errno = file->error();
-        perror("open");
-        dbgln("Failed to open project file");
+    if (m_filedb.is_open(message.file_name())) {
         return;
     }
-    auto content = file->read_all();
-    StringView content_view(content);
-    auto document = GUI::TextDocument::create(&s_default_document_client);
-    document->set_text(content_view);
-    m_open_files.set(message.file_name(), document);
-    dbgln_if(FILE_CONTENT_DEBUG, "{}", document->text());
+    m_filedb.add(message.file_name(), message.file().take_fd());
+    m_autocomplete_engine->file_opened(message.file_name());
 }
 
 void ClientConnection::handle(const Messages::LanguageServer::FileEditInsertText& message)
@@ -107,16 +83,8 @@ void ClientConnection::handle(const Messages::LanguageServer::FileEditInsertText
     dbgln("Text: {}", message.text());
     dbgln("[{}:{}]", message.start_line(), message.start_column());
 #endif
-    auto document = document_for(message.file_name());
-    if (!document) {
-        dbgln("file {} has not been opened", message.file_name());
-        return;
-    }
-    GUI::TextPosition start_position { (size_t)message.start_line(), (size_t)message.start_column() };
-    document->insert_at(start_position, message.text(), &s_default_document_client);
-#if FILE_CONTENT_DEBUG
-    dbgln("{}", document->text());
-#endif
+    m_filedb.on_file_edit_insert_text(message.file_name(), message.text(), message.start_line(), message.start_column());
+    m_autocomplete_engine->on_edit(message.file_name());
 }
 
 void ClientConnection::handle(const Messages::LanguageServer::FileEditRemoveText& message)
@@ -125,23 +93,8 @@ void ClientConnection::handle(const Messages::LanguageServer::FileEditRemoveText
     dbgln("RemoveText for file: {}", message.file_name());
     dbgln("[{}:{} - {}:{}]", message.start_line(), message.start_column(), message.end_line(), message.end_column());
 #endif
-    auto document = document_for(message.file_name());
-    if (!document) {
-        dbgln("file {} has not been opened", message.file_name());
-        return;
-    }
-    GUI::TextPosition start_position { (size_t)message.start_line(), (size_t)message.start_column() };
-    GUI::TextRange range {
-        GUI::TextPosition { (size_t)message.start_line(),
-            (size_t)message.start_column() },
-        GUI::TextPosition { (size_t)message.end_line(),
-            (size_t)message.end_column() }
-    };
-
-    document->remove(range);
-#if FILE_CONTENT_DEBUG
-    dbgln("{}", document->text());
-#endif
+    m_filedb.on_file_edit_remove_text(message.file_name(), message.start_line(), message.start_column(), message.end_line(), message.end_column());
+    m_autocomplete_engine->on_edit(message.file_name());
 }
 
 void ClientConnection::handle(const Messages::LanguageServer::AutoCompleteSuggestions& message)
@@ -150,43 +103,27 @@ void ClientConnection::handle(const Messages::LanguageServer::AutoCompleteSugges
     dbgln("AutoCompleteSuggestions for: {} {}:{}", message.file_name(), message.cursor_line(), message.cursor_column());
 #endif
 
-    auto document = document_for(message.file_name());
+    auto document = m_filedb.get(message.file_name());
     if (!document) {
         dbgln("file {} has not been opened", message.file_name());
         return;
     }
 
-    Vector<GUI::AutocompleteProvider::Entry> suggestions;
-    switch (m_auto_complete_mode) {
-    case AutoCompleteMode::Lexer:
-        suggestions = LexerAutoComplete::get_suggestions(document->text(), { (size_t)message.cursor_line(), (size_t)max(message.cursor_column(), message.cursor_column() - 1) });
-        break;
-    case AutoCompleteMode::Parser: {
-        auto engine = ParserAutoComplete(document->text());
-        suggestions = engine.get_suggestions({ (size_t)message.cursor_line(), (size_t)max(message.cursor_column(), message.cursor_column() - 1) });
-    }
-    }
+    GUI::TextPosition autocomplete_position = { (size_t)message.cursor_line(), (size_t)max(message.cursor_column(), message.cursor_column() - 1) };
+    Vector<GUI::AutocompleteProvider::Entry> suggestions = m_autocomplete_engine->get_suggestions(message.file_name(), autocomplete_position);
     post_message(Messages::LanguageClient::AutoCompleteSuggestions(move(suggestions)));
 }
 
-RefPtr<GUI::TextDocument> ClientConnection::document_for(const String& file_name)
-{
-    auto document_optional = m_open_files.get(file_name);
-    if (!document_optional.has_value())
-        return nullptr;
-
-    return document_optional.value();
-}
-
 void ClientConnection::handle(const Messages::LanguageServer::SetFileContent& message)
 {
-    auto document = document_for(message.file_name());
+    auto document = m_filedb.get(message.file_name());
     if (!document) {
         dbgln("file {} has not been opened", message.file_name());
         return;
     }
     auto content = message.content();
     document->set_text(content.view());
+    m_autocomplete_engine->on_edit(message.file_name());
 }
 
 void ClientConnection::handle(const Messages::LanguageServer::SetAutoCompleteMode& message)
@@ -195,9 +132,9 @@ void ClientConnection::handle(const Messages::LanguageServer::SetAutoCompleteMod
     dbgln("SetAutoCompleteMode: {}", message.mode());
 #endif
     if (message.mode() == "Parser")
-        m_auto_complete_mode = AutoCompleteMode::Parser;
+        m_autocomplete_engine = make<ParserAutoComplete>(m_filedb);
     else
-        m_auto_complete_mode = AutoCompleteMode::Lexer;
+        m_autocomplete_engine = make<LexerAutoComplete>(m_filedb);
 }
 
 }

+ 4 - 11
Userland/DevTools/HackStudio/LanguageServers/Cpp/ClientConnection.h

@@ -26,10 +26,11 @@
 
 #pragma once
 
+#include "AutoCompleteEngine.h"
+#include "FileDB.h"
 #include <AK/HashMap.h>
 #include <AK/LexicalPath.h>
 #include <DevTools/HackStudio/AutoCompleteResponse.h>
-#include <LibGUI/TextDocument.h>
 #include <LibIPC/ClientConnection.h>
 
 #include <DevTools/HackStudio/LanguageServers/LanguageClientEndpoint.h>
@@ -57,16 +58,8 @@ private:
     virtual void handle(const Messages::LanguageServer::AutoCompleteSuggestions&) override;
     virtual void handle(const Messages::LanguageServer::SetAutoCompleteMode&) override;
 
-    RefPtr<GUI::TextDocument> document_for(const String& file_name);
-
-    HashMap<String, NonnullRefPtr<GUI::TextDocument>> m_open_files;
-
-    enum class AutoCompleteMode {
-        Lexer,
-        Parser
-    };
-
-    AutoCompleteMode m_auto_complete_mode { AutoCompleteMode::Lexer };
+    FileDB m_filedb;
+    OwnPtr<AutoCompleteEngine> m_autocomplete_engine;
 };
 
 }

+ 12 - 1
Userland/DevTools/HackStudio/LanguageServers/Cpp/LexerAutoComplete.cpp

@@ -31,8 +31,19 @@
 
 namespace LanguageServers::Cpp {
 
-Vector<GUI::AutocompleteProvider::Entry> LexerAutoComplete::get_suggestions(const String& code, const GUI::TextPosition& autocomplete_position)
+LexerAutoComplete::LexerAutoComplete(const FileDB& filedb)
+    : AutoCompleteEngine(filedb)
 {
+}
+
+Vector<GUI::AutocompleteProvider::Entry> LexerAutoComplete::get_suggestions(const String& file, const GUI::TextPosition& autocomplete_position)
+{
+    auto document = filedb().get(file);
+    if (!document) {
+        dbgln("didn't find document for {}", file);
+        return {};
+    }
+    auto code = document->text();
     auto lines = code.split('\n', true);
     Cpp::Lexer lexer(code);
     auto tokens = lexer.lex();

+ 7 - 6
Userland/DevTools/HackStudio/LanguageServers/Cpp/LexerAutoComplete.h

@@ -26,6 +26,7 @@
 
 #pragma once
 
+#include "AutoCompleteEngine.h"
 #include <AK/String.h>
 #include <AK/Vector.h>
 #include <DevTools/HackStudio/AutoCompleteResponse.h>
@@ -36,16 +37,16 @@ namespace LanguageServers::Cpp {
 
 using namespace ::Cpp;
 
-class LexerAutoComplete {
+class LexerAutoComplete : public AutoCompleteEngine {
 public:
-    LexerAutoComplete() = delete;
+    LexerAutoComplete(const FileDB& filedb);
 
-    static Vector<GUI::AutocompleteProvider::Entry> get_suggestions(const String& code, const GUI::TextPosition& autocomplete_position);
+    virtual Vector<GUI::AutocompleteProvider::Entry> get_suggestions(const String& file, const GUI::TextPosition& autocomplete_position) override;
 
 private:
-    static Optional<size_t> token_in_position(const Vector<Cpp::Token>&, const GUI::TextPosition&);
-    static StringView text_of_token(const Vector<String>& lines, const Cpp::Token&);
-    static Vector<GUI::AutocompleteProvider::Entry> identifier_prefixes(const Vector<String>& lines, const Vector<Cpp::Token>&, size_t target_token_index);
+    Optional<size_t> token_in_position(const Vector<Cpp::Token>&, const GUI::TextPosition&);
+    StringView text_of_token(const Vector<String>& lines, const Cpp::Token&);
+    Vector<GUI::AutocompleteProvider::Entry> identifier_prefixes(const Vector<String>& lines, const Vector<Cpp::Token>&, size_t target_token_index);
 };
 
 }