Serenity Operating System
at portability 214 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 const Gfx::Font* font { nullptr }; 50 void* data { nullptr }; 51}; 52 53class TextDocument : public RefCounted<TextDocument> { 54public: 55 enum class SearchShouldWrap { 56 No = 0, 57 Yes 58 }; 59 60 class Client { 61 public: 62 virtual ~Client(); 63 virtual void document_did_append_line() = 0; 64 virtual void document_did_insert_line(size_t) = 0; 65 virtual void document_did_remove_line(size_t) = 0; 66 virtual void document_did_remove_all_lines() = 0; 67 virtual void document_did_change() = 0; 68 virtual void document_did_set_text() = 0; 69 virtual void document_did_set_cursor(const TextPosition&) = 0; 70 71 virtual bool is_automatic_indentation_enabled() const = 0; 72 virtual int soft_tab_width() const = 0; 73 }; 74 75 static NonnullRefPtr<TextDocument> create(Client* client = nullptr); 76 ~TextDocument(); 77 78 size_t line_count() const { return (size_t)m_lines.size(); } 79 const TextDocumentLine& line(size_t line_index) const { return m_lines[(int)line_index]; } 80 TextDocumentLine& line(size_t line_index) { return m_lines[(int)line_index]; } 81 82 void set_spans(const Vector<TextDocumentSpan>& spans) { m_spans = spans; } 83 84 void set_text(const StringView&); 85 86 const NonnullOwnPtrVector<TextDocumentLine>& lines() const { return m_lines; } 87 NonnullOwnPtrVector<TextDocumentLine>& lines() { return m_lines; } 88 89 bool has_spans() const { return !m_spans.is_empty(); } 90 const Vector<TextDocumentSpan>& spans() const { return m_spans; } 91 void set_span_at_index(size_t index, TextDocumentSpan span) { m_spans[(int)index] = move(span); } 92 93 void append_line(NonnullOwnPtr<TextDocumentLine>); 94 void remove_line(size_t line_index); 95 void remove_all_lines(); 96 void insert_line(size_t line_index, NonnullOwnPtr<TextDocumentLine>); 97 98 void register_client(Client&); 99 void unregister_client(Client&); 100 101 void update_views(Badge<TextDocumentLine>); 102 103 String text_in_range(const TextRange&) const; 104 105 Vector<TextRange> find_all(const StringView& needle) const; 106 107 TextRange find_next(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const; 108 TextRange find_previous(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const; 109 110 TextPosition next_position_after(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const; 111 TextPosition previous_position_before(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const; 112 113 char character_at(const TextPosition&) const; 114 115 TextRange range_for_entire_line(size_t line_index) const; 116 117 Optional<TextDocumentSpan> first_non_skippable_span_before(const TextPosition&) const; 118 Optional<TextDocumentSpan> first_non_skippable_span_after(const TextPosition&) const; 119 120 void add_to_undo_stack(NonnullOwnPtr<TextDocumentUndoCommand>); 121 122 bool can_undo() const { return m_undo_stack.can_undo(); } 123 bool can_redo() const { return m_undo_stack.can_redo(); } 124 void undo(); 125 void redo(); 126 127 void notify_did_change(); 128 void set_all_cursors(const TextPosition&); 129 130 TextPosition insert_at(const TextPosition&, char, const Client* = nullptr); 131 TextPosition insert_at(const TextPosition&, const StringView&, const Client* = nullptr); 132 void remove(const TextRange&); 133 134private: 135 explicit TextDocument(Client* client); 136 137 void update_undo_timer(); 138 139 NonnullOwnPtrVector<TextDocumentLine> m_lines; 140 Vector<TextDocumentSpan> m_spans; 141 142 HashTable<Client*> m_clients; 143 bool m_client_notifications_enabled { true }; 144 145 UndoStack m_undo_stack; 146 RefPtr<Core::Timer> m_undo_timer; 147}; 148 149class TextDocumentLine { 150 friend class GTextEditor; 151 friend class TextDocument; 152 153public: 154 explicit TextDocumentLine(TextDocument&); 155 explicit TextDocumentLine(TextDocument&, const StringView&); 156 157 StringView view() const { return { characters(), (size_t)length() }; } 158 const char* characters() const { return m_text.data(); } 159 size_t length() const { return (size_t)m_text.size() - 1; } 160 void set_text(TextDocument&, const StringView&); 161 void append(TextDocument&, char); 162 void prepend(TextDocument&, char); 163 void insert(TextDocument&, size_t index, char); 164 void remove(TextDocument&, size_t index); 165 void append(TextDocument&, const char*, size_t); 166 void truncate(TextDocument&, size_t length); 167 void clear(TextDocument&); 168 size_t first_non_whitespace_column() const; 169 170private: 171 // NOTE: This vector is null terminated. 172 Vector<char> m_text; 173}; 174 175class TextDocumentUndoCommand : public Command { 176public: 177 TextDocumentUndoCommand(TextDocument&); 178 virtual ~TextDocumentUndoCommand(); 179 180 void execute_from(const TextDocument::Client& client) 181 { 182 m_client = &client; 183 redo(); 184 m_client = nullptr; 185 } 186 187protected: 188 TextDocument& m_document; 189 const TextDocument::Client* m_client { nullptr }; 190}; 191 192class InsertTextCommand : public TextDocumentUndoCommand { 193public: 194 InsertTextCommand(TextDocument&, const String&, const TextPosition&); 195 virtual void undo() override; 196 virtual void redo() override; 197 198private: 199 String m_text; 200 TextRange m_range; 201}; 202 203class RemoveTextCommand : public TextDocumentUndoCommand { 204public: 205 RemoveTextCommand(TextDocument&, const String&, const TextRange&); 206 virtual void undo() override; 207 virtual void redo() override; 208 209private: 210 String m_text; 211 TextRange m_range; 212}; 213 214}