| 
									
										
										
										
											2020-01-18 09:38:21 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> | 
					
						
							|  |  |  |  * 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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-27 18:00:07 +01:00
										 |  |  | #include <AK/HashTable.h>
 | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | #include <AK/NonnullOwnPtrVector.h>
 | 
					
						
							|  |  |  | #include <AK/NonnullRefPtr.h>
 | 
					
						
							| 
									
										
										
										
											2020-02-14 23:28:42 +01:00
										 |  |  | #include <AK/Optional.h>
 | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | #include <AK/RefCounted.h>
 | 
					
						
							| 
									
										
										
										
											2020-02-16 09:17:49 +01:00
										 |  |  | #include <LibCore/Forward.h>
 | 
					
						
							|  |  |  | #include <LibGUI/Command.h>
 | 
					
						
							|  |  |  | #include <LibGUI/Forward.h>
 | 
					
						
							| 
									
										
										
										
											2020-02-06 20:33:02 +01:00
										 |  |  | #include <LibGUI/TextRange.h>
 | 
					
						
							|  |  |  | #include <LibGUI/UndoStack.h>
 | 
					
						
							| 
									
										
										
										
											2020-02-14 23:28:42 +01:00
										 |  |  | #include <LibGfx/Color.h>
 | 
					
						
							| 
									
										
										
										
											2020-02-15 00:24:14 +01:00
										 |  |  | #include <LibGfx/Forward.h>
 | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  | namespace GUI { | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  | struct TextDocumentSpan { | 
					
						
							|  |  |  |     TextRange range; | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  |     Color color; | 
					
						
							| 
									
										
										
										
											2019-11-18 19:10:06 +01:00
										 |  |  |     Optional<Color> background_color; | 
					
						
							| 
									
										
										
										
											2019-11-15 20:36:45 +01:00
										 |  |  |     bool is_skippable { false }; | 
					
						
							| 
									
										
										
										
											2020-02-06 11:56:38 +01:00
										 |  |  |     const Gfx::Font* font { nullptr }; | 
					
						
							| 
									
										
										
										
											2019-11-18 19:10:06 +01:00
										 |  |  |     void* data { nullptr }; | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  | class TextDocument : public RefCounted<TextDocument> { | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:39 +01:00
										 |  |  |     enum class SearchShouldWrap { | 
					
						
							|  |  |  |         No = 0, | 
					
						
							|  |  |  |         Yes | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-27 18:00:07 +01:00
										 |  |  |     class Client { | 
					
						
							|  |  |  |     public: | 
					
						
							|  |  |  |         virtual ~Client(); | 
					
						
							|  |  |  |         virtual void document_did_append_line() = 0; | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |         virtual void document_did_insert_line(size_t) = 0; | 
					
						
							|  |  |  |         virtual void document_did_remove_line(size_t) = 0; | 
					
						
							| 
									
										
										
										
											2019-10-27 18:00:07 +01:00
										 |  |  |         virtual void document_did_remove_all_lines() = 0; | 
					
						
							| 
									
										
										
										
											2019-10-27 19:36:59 +01:00
										 |  |  |         virtual void document_did_change() = 0; | 
					
						
							| 
									
										
										
										
											2019-11-23 17:41:14 +01:00
										 |  |  |         virtual void document_did_set_text() = 0; | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |         virtual void document_did_set_cursor(const TextPosition&) = 0; | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         virtual bool is_automatic_indentation_enabled() const = 0; | 
					
						
							|  |  |  |         virtual int soft_tab_width() const = 0; | 
					
						
							| 
									
										
										
										
											2019-10-27 18:00:07 +01:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-16 09:17:49 +01:00
										 |  |  |     static NonnullRefPtr<TextDocument> create(Client* client = nullptr); | 
					
						
							|  |  |  |     ~TextDocument(); | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |     size_t line_count() const { return (size_t)m_lines.size(); } | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     const TextDocumentLine& line(size_t line_index) const { return m_lines[(int)line_index]; } | 
					
						
							|  |  |  |     TextDocumentLine& line(size_t line_index) { return m_lines[(int)line_index]; } | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     void set_spans(const Vector<TextDocumentSpan>& spans) { m_spans = spans; } | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     void set_text(const StringView&); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     const NonnullOwnPtrVector<TextDocumentLine>& lines() const { return m_lines; } | 
					
						
							|  |  |  |     NonnullOwnPtrVector<TextDocumentLine>& lines() { return m_lines; } | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     bool has_spans() const { return !m_spans.is_empty(); } | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     const Vector<TextDocumentSpan>& spans() const { return m_spans; } | 
					
						
							|  |  |  |     void set_span_at_index(size_t index, TextDocumentSpan span) { m_spans[(int)index] = move(span); } | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     void append_line(NonnullOwnPtr<TextDocumentLine>); | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |     void remove_line(size_t line_index); | 
					
						
							| 
									
										
										
										
											2019-10-27 18:00:07 +01:00
										 |  |  |     void remove_all_lines(); | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     void insert_line(size_t line_index, NonnullOwnPtr<TextDocumentLine>); | 
					
						
							| 
									
										
										
										
											2019-10-27 18:00:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     void register_client(Client&); | 
					
						
							|  |  |  |     void unregister_client(Client&); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     void update_views(Badge<TextDocumentLine>); | 
					
						
							| 
									
										
										
										
											2019-10-27 19:36:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     String text_in_range(const TextRange&) const; | 
					
						
							| 
									
										
										
										
											2019-10-29 21:36:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     Vector<TextRange> find_all(const StringView& needle) const; | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     TextRange find_next(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const; | 
					
						
							|  |  |  |     TextRange find_previous(const StringView&, const TextPosition& start = {}, SearchShouldWrap = SearchShouldWrap::Yes) const; | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     TextPosition next_position_after(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const; | 
					
						
							|  |  |  |     TextPosition previous_position_before(const TextPosition&, SearchShouldWrap = SearchShouldWrap::Yes) const; | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     char character_at(const TextPosition&) const; | 
					
						
							| 
									
										
										
										
											2019-11-01 19:19:39 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     TextRange range_for_entire_line(size_t line_index) const; | 
					
						
							| 
									
										
										
										
											2019-12-10 22:06:31 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     Optional<TextDocumentSpan> first_non_skippable_span_before(const TextPosition&) const; | 
					
						
							|  |  |  |     Optional<TextDocumentSpan> first_non_skippable_span_after(const TextPosition&) const; | 
					
						
							| 
									
										
										
										
											2019-11-15 20:36:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     void add_to_undo_stack(NonnullOwnPtr<TextDocumentUndoCommand>); | 
					
						
							| 
									
										
										
										
											2019-11-30 13:05:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-30 15:36:17 +01:00
										 |  |  |     bool can_undo() const { return m_undo_stack.can_undo(); } | 
					
						
							|  |  |  |     bool can_redo() const { return m_undo_stack.can_redo(); } | 
					
						
							| 
									
										
										
										
											2019-11-30 13:05:17 +01:00
										 |  |  |     void undo(); | 
					
						
							|  |  |  |     void redo(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     void notify_did_change(); | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     void set_all_cursors(const TextPosition&); | 
					
						
							| 
									
										
										
										
											2019-11-30 13:05:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     TextPosition insert_at(const TextPosition&, char, const Client* = nullptr); | 
					
						
							|  |  |  |     TextPosition insert_at(const TextPosition&, const StringView&, const Client* = nullptr); | 
					
						
							|  |  |  |     void remove(const TextRange&); | 
					
						
							| 
									
										
										
										
											2019-11-30 16:50:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | private: | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     explicit TextDocument(Client* client); | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-30 13:05:17 +01:00
										 |  |  |     void update_undo_timer(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     NonnullOwnPtrVector<TextDocumentLine> m_lines; | 
					
						
							|  |  |  |     Vector<TextDocumentSpan> m_spans; | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-27 18:00:07 +01:00
										 |  |  |     HashTable<Client*> m_clients; | 
					
						
							| 
									
										
										
										
											2019-11-23 17:41:14 +01:00
										 |  |  |     bool m_client_notifications_enabled { true }; | 
					
						
							| 
									
										
										
										
											2019-11-30 13:05:17 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     UndoStack m_undo_stack; | 
					
						
							| 
									
										
										
										
											2020-02-02 12:34:39 +01:00
										 |  |  |     RefPtr<Core::Timer> m_undo_timer; | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  | class TextDocumentLine { | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  |     friend class GTextEditor; | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     friend class TextDocument; | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     explicit TextDocumentLine(TextDocument&); | 
					
						
							|  |  |  |     explicit TextDocumentLine(TextDocument&, const StringView&); | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |     StringView view() const { return { characters(), (size_t)length() }; } | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  |     const char* characters() const { return m_text.data(); } | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |     size_t length() const { return (size_t)m_text.size() - 1; } | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     void set_text(TextDocument&, const StringView&); | 
					
						
							|  |  |  |     void append(TextDocument&, char); | 
					
						
							|  |  |  |     void prepend(TextDocument&, char); | 
					
						
							|  |  |  |     void insert(TextDocument&, size_t index, char); | 
					
						
							|  |  |  |     void remove(TextDocument&, size_t index); | 
					
						
							|  |  |  |     void append(TextDocument&, const char*, size_t); | 
					
						
							|  |  |  |     void truncate(TextDocument&, size_t length); | 
					
						
							|  |  |  |     void clear(TextDocument&); | 
					
						
							| 
									
										
										
										
											2019-12-09 17:45:40 +01:00
										 |  |  |     size_t first_non_whitespace_column() const; | 
					
						
							| 
									
										
										
										
											2019-10-27 16:10:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     // NOTE: This vector is null terminated.
 | 
					
						
							|  |  |  |     Vector<char> m_text; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  | class TextDocumentUndoCommand : public Command { | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     TextDocumentUndoCommand(TextDocument&); | 
					
						
							|  |  |  |     virtual ~TextDocumentUndoCommand(); | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     void execute_from(const TextDocument::Client& client) | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  |     { | 
					
						
							|  |  |  |         m_client = &client; | 
					
						
							|  |  |  |         redo(); | 
					
						
							|  |  |  |         m_client = nullptr; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | protected: | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     TextDocument& m_document; | 
					
						
							|  |  |  |     const TextDocument::Client* m_client { nullptr }; | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  | class InsertTextCommand : public TextDocumentUndoCommand { | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     InsertTextCommand(TextDocument&, const String&, const TextPosition&); | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  |     virtual void undo() override; | 
					
						
							|  |  |  |     virtual void redo() override; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     String m_text; | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     TextRange m_range; | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  | class RemoveTextCommand : public TextDocumentUndoCommand { | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | public: | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     RemoveTextCommand(TextDocument&, const String&, const TextRange&); | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  |     virtual void undo() override; | 
					
						
							|  |  |  |     virtual void redo() override; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | private: | 
					
						
							|  |  |  |     String m_text; | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  |     TextRange m_range; | 
					
						
							| 
									
										
										
										
											2020-01-23 21:04:59 +01:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2020-02-02 15:07:41 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | } |