Serenity Operating System
at hosted 213 lines 7.4 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#pragma once 28 29#include <AK/HashTable.h> 30#include <AK/NonnullOwnPtrVector.h> 31#include <AK/NonnullRefPtr.h> 32#include <AK/Optional.h> 33#include <AK/RefCounted.h> 34#include <LibCore/Forward.h> 35#include <LibGUI/Command.h> 36#include <LibGUI/Forward.h> 37#include <LibGUI/TextRange.h> 38#include <LibGUI/UndoStack.h> 39#include <LibGfx/Color.h> 40#include <LibGfx/Forward.h> 41 42namespace GUI { 43 44struct TextDocumentSpan { 45 TextRange range; 46 Color color; 47 Optional<Color> background_color; 48 bool is_skippable { false }; 49 bool is_underlined { false }; 50 const Gfx::Font* font { nullptr }; 51 void* data { nullptr }; 52}; 53 54class TextDocument : public RefCounted<TextDocument> { 55public: 56 enum class SearchShouldWrap { 57 No = 0, 58 Yes 59 }; 60 61 class Client { 62 public: 63 virtual ~Client(); 64 virtual void document_did_append_line() = 0; 65 virtual void document_did_insert_line(size_t) = 0; 66 virtual void document_did_remove_line(size_t) = 0; 67 virtual void document_did_remove_all_lines() = 0; 68 virtual void document_did_change() = 0; 69 virtual void document_did_set_text() = 0; 70 virtual void document_did_set_cursor(const TextPosition&) = 0; 71 72 virtual bool is_automatic_indentation_enabled() const = 0; 73 virtual int soft_tab_width() const = 0; 74 }; 75 76 static NonnullRefPtr<TextDocument> create(Client* client = nullptr); 77 ~TextDocument(); 78 79 size_t line_count() const { return m_lines.size(); } 80 const TextDocumentLine& line(size_t line_index) const { return m_lines[line_index]; } 81 TextDocumentLine& line(size_t line_index) { return m_lines[line_index]; } 82 83 void set_spans(const Vector<TextDocumentSpan>& spans) { m_spans = spans; } 84 85 void set_text(const StringView&); 86 87 const NonnullOwnPtrVector<TextDocumentLine>& lines() const { return m_lines; } 88 NonnullOwnPtrVector<TextDocumentLine>& lines() { return m_lines; } 89 90 bool has_spans() const { return !m_spans.is_empty(); } 91 Vector<TextDocumentSpan>& spans() { return m_spans; } 92 const Vector<TextDocumentSpan>& spans() const { return m_spans; } 93 void set_span_at_index(size_t index, TextDocumentSpan span) { m_spans[index] = move(span); } 94 95 void append_line(NonnullOwnPtr<TextDocumentLine>); 96 void remove_line(size_t line_index); 97 void remove_all_lines(); 98 void insert_line(size_t line_index, NonnullOwnPtr<TextDocumentLine>); 99 100 void register_client(Client&); 101 void unregister_client(Client&); 102 103 void update_views(Badge<TextDocumentLine>); 104 105 String text_in_range(const TextRange&) const; 106 107 Vector<TextRange> find_all(const StringView& needle) const; 108 109 TextRange find_next(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const; 110 TextRange find_previous(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const; 111 112 TextPosition next_position_after(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const; 113 TextPosition previous_position_before(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const; 114 115 char character_at(const TextPosition&) const; 116 117 TextRange range_for_entire_line(size_t line_index) const; 118 119 Optional<TextDocumentSpan> first_non_skippable_span_before(const TextPosition&) const; 120 Optional<TextDocumentSpan> first_non_skippable_span_after(const TextPosition&) const; 121 122 void add_to_undo_stack(NonnullOwnPtr<TextDocumentUndoCommand>); 123 124 bool can_undo() const { return m_undo_stack.can_undo(); } 125 bool can_redo() const { return m_undo_stack.can_redo(); } 126 void undo(); 127 void redo(); 128 129 void notify_did_change(); 130 void set_all_cursors(const TextPosition&); 131 132 TextPosition insert_at(const TextPosition&, char, const Client* = nullptr); 133 TextPosition insert_at(const TextPosition&, const StringView&, const Client* = nullptr); 134 void remove(const TextRange&); 135 136private: 137 explicit TextDocument(Client* client); 138 139 void update_undo_timer(); 140 141 NonnullOwnPtrVector<TextDocumentLine> m_lines; 142 Vector<TextDocumentSpan> m_spans; 143 144 HashTable<Client*> m_clients; 145 bool m_client_notifications_enabled { true }; 146 147 UndoStack m_undo_stack; 148 RefPtr<Core::Timer> m_undo_timer; 149}; 150 151class TextDocumentLine { 152public: 153 explicit TextDocumentLine(TextDocument&); 154 explicit TextDocumentLine(TextDocument&, const StringView&); 155 156 StringView view() const { return { characters(), (size_t)length() }; } 157 const char* characters() const { return m_text.data(); } 158 size_t length() const { return m_text.size() - 1; } 159 void set_text(TextDocument&, const StringView&); 160 void append(TextDocument&, char); 161 void prepend(TextDocument&, char); 162 void insert(TextDocument&, size_t index, char); 163 void remove(TextDocument&, size_t index); 164 void append(TextDocument&, const char*, size_t); 165 void truncate(TextDocument&, size_t length); 166 void clear(TextDocument&); 167 size_t first_non_whitespace_column() const; 168 169private: 170 // NOTE: This vector is null terminated. 171 Vector<char> m_text; 172}; 173 174class TextDocumentUndoCommand : public Command { 175public: 176 TextDocumentUndoCommand(TextDocument&); 177 virtual ~TextDocumentUndoCommand(); 178 179 void execute_from(const TextDocument::Client& client) 180 { 181 m_client = &client; 182 redo(); 183 m_client = nullptr; 184 } 185 186protected: 187 TextDocument& m_document; 188 const TextDocument::Client* m_client { nullptr }; 189}; 190 191class InsertTextCommand : public TextDocumentUndoCommand { 192public: 193 InsertTextCommand(TextDocument&, const String&, const TextPosition&); 194 virtual void undo() override; 195 virtual void redo() override; 196 197private: 198 String m_text; 199 TextRange m_range; 200}; 201 202class RemoveTextCommand : public TextDocumentUndoCommand { 203public: 204 RemoveTextCommand(TextDocument&, const String&, const TextRange&); 205 virtual void undo() override; 206 virtual void redo() override; 207 208private: 209 String m_text; 210 TextRange m_range; 211}; 212 213}