Serenity Operating System
at master 161 lines 5.6 kB view raw
1/* 2 * Copyright (c) 2021, Itamar S. <itamar8910@gmail.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include "FileDB.h" 8 9#include <AK/Debug.h> 10#include <AK/LexicalPath.h> 11#include <AK/NonnullRefPtr.h> 12#include <LibCore/File.h> 13 14namespace LanguageServers { 15 16RefPtr<const GUI::TextDocument> FileDB::get_document(DeprecatedString const& filename) const 17{ 18 auto absolute_path = to_absolute_path(filename); 19 auto document_optional = m_open_files.get(absolute_path); 20 if (!document_optional.has_value()) 21 return nullptr; 22 23 return *document_optional.value(); 24} 25 26RefPtr<GUI::TextDocument> FileDB::get_document(DeprecatedString const& filename) 27{ 28 auto document = reinterpret_cast<FileDB const*>(this)->get_document(filename); 29 if (document.is_null()) 30 return nullptr; 31 return adopt_ref(*const_cast<GUI::TextDocument*>(document.leak_ref())); 32} 33 34Optional<DeprecatedString> FileDB::get_or_read_from_filesystem(StringView filename) const 35{ 36 auto absolute_path = to_absolute_path(filename); 37 auto document = get_document(absolute_path); 38 if (document) 39 return document->text(); 40 41 auto document_or_error = create_from_filesystem(absolute_path); 42 if (document_or_error.is_error()) { 43 dbgln("Failed to create document '{}': {}", absolute_path, document_or_error.error()); 44 return {}; 45 } 46 return document_or_error.value()->text(); 47} 48 49bool FileDB::is_open(DeprecatedString const& filename) const 50{ 51 return m_open_files.contains(to_absolute_path(filename)); 52} 53 54bool FileDB::add(DeprecatedString const& filename, int fd) 55{ 56 auto document_or_error = create_from_fd(fd); 57 if (document_or_error.is_error()) { 58 dbgln("Failed to create document: {}", document_or_error.error()); 59 return false; 60 } 61 62 m_open_files.set(to_absolute_path(filename), document_or_error.release_value()); 63 return true; 64} 65 66DeprecatedString FileDB::to_absolute_path(DeprecatedString const& filename) const 67{ 68 if (LexicalPath { filename }.is_absolute()) { 69 return filename; 70 } 71 if (m_project_root.is_null()) 72 return filename; 73 return LexicalPath { DeprecatedString::formatted("{}/{}", m_project_root, filename) }.string(); 74} 75 76ErrorOr<NonnullRefPtr<GUI::TextDocument>> FileDB::create_from_filesystem(DeprecatedString const& filename) const 77{ 78 auto file = TRY(Core::File::open(to_absolute_path(filename), Core::File::OpenMode::Read)); 79 return create_from_file(move(file)); 80} 81 82ErrorOr<NonnullRefPtr<GUI::TextDocument>> FileDB::create_from_fd(int fd) const 83{ 84 auto file = TRY(Core::File::adopt_fd(fd, Core::File::OpenMode::Read)); 85 return create_from_file(move(file)); 86} 87 88class DefaultDocumentClient final : public GUI::TextDocument::Client { 89public: 90 virtual ~DefaultDocumentClient() override = default; 91 virtual void document_did_append_line() override {}; 92 virtual void document_did_insert_line(size_t) override {}; 93 virtual void document_did_remove_line(size_t) override {}; 94 virtual void document_did_remove_all_lines() override {}; 95 virtual void document_did_change(GUI::AllowCallback) override {}; 96 virtual void document_did_set_text(GUI::AllowCallback) override {}; 97 virtual void document_did_set_cursor(const GUI::TextPosition&) override {}; 98 virtual void document_did_update_undo_stack() override { } 99 100 virtual bool is_automatic_indentation_enabled() const override { return false; } 101 virtual int soft_tab_width() const override { return 4; } 102}; 103static DefaultDocumentClient s_default_document_client; 104 105ErrorOr<NonnullRefPtr<GUI::TextDocument>> FileDB::create_from_file(NonnullOwnPtr<Core::File> file) const 106{ 107 auto content = TRY(file->read_until_eof()); 108 auto document = GUI::TextDocument::create(&s_default_document_client); 109 document->set_text(content); 110 return document; 111} 112 113void FileDB::on_file_edit_insert_text(DeprecatedString const& filename, DeprecatedString const& inserted_text, size_t start_line, size_t start_column) 114{ 115 VERIFY(is_open(filename)); 116 auto document = get_document(filename); 117 VERIFY(document); 118 GUI::TextPosition start_position { start_line, start_column }; 119 document->insert_at(start_position, inserted_text, &s_default_document_client); 120 121 dbgln_if(FILE_CONTENT_DEBUG, "{}", document->text()); 122} 123 124void FileDB::on_file_edit_remove_text(DeprecatedString const& filename, size_t start_line, size_t start_column, size_t end_line, size_t end_column) 125{ 126 // TODO: If file is not open - need to get its contents 127 // Otherwise- somehow verify that respawned language server is synced with all file contents 128 VERIFY(is_open(filename)); 129 auto document = get_document(filename); 130 VERIFY(document); 131 GUI::TextPosition start_position { start_line, start_column }; 132 GUI::TextRange range { 133 GUI::TextPosition { start_line, start_column }, 134 GUI::TextPosition { end_line, end_column } 135 }; 136 137 document->remove(range); 138 dbgln_if(FILE_CONTENT_DEBUG, "{}", document->text()); 139} 140 141RefPtr<GUI::TextDocument> FileDB::create_with_content(DeprecatedString const& content) 142{ 143 StringView content_view(content); 144 auto document = GUI::TextDocument::create(&s_default_document_client); 145 document->set_text(content_view); 146 return document; 147} 148 149bool FileDB::add(DeprecatedString const& filename, DeprecatedString const& content) 150{ 151 auto document = create_with_content(content); 152 if (!document) { 153 VERIFY_NOT_REACHED(); 154 return false; 155 } 156 157 m_open_files.set(to_absolute_path(filename), document.release_nonnull()); 158 return true; 159} 160 161}