Serenity Operating System
at hosted 175 lines 5.7 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/BinarySearch.h> 30#include <AK/ByteBuffer.h> 31#include <AK/FileSystemPath.h> 32#include <AK/Function.h> 33#include <AK/HashMap.h> 34#include <AK/NonnullOwnPtr.h> 35#include <AK/QuickSort.h> 36#include <AK/String.h> 37#include <AK/Vector.h> 38#include <LibCore/DirIterator.h> 39#include <Libraries/LibLine/Span.h> 40#include <Libraries/LibLine/Style.h> 41#include <sys/stat.h> 42#include <termios.h> 43 44namespace Line { 45 46class Editor; 47 48struct KeyCallback { 49 KeyCallback(Function<bool(Editor&)> cb) 50 : callback(move(cb)) 51 { 52 } 53 Function<bool(Editor&)> callback; 54}; 55 56class Editor { 57public: 58 Editor(); 59 ~Editor(); 60 61 void initialize() 62 { 63 ASSERT(!m_initialized); 64 struct termios termios; 65 tcgetattr(0, &termios); 66 m_default_termios = termios; // grab a copy to restore 67 // Because we use our own line discipline which includes echoing, 68 // we disable ICANON and ECHO. 69 termios.c_lflag &= ~(ECHO | ICANON); 70 tcsetattr(0, TCSANOW, &termios); 71 m_termios = termios; 72 m_initialized = true; 73 } 74 75 String get_line(const String& prompt); 76 77 void add_to_history(const String&); 78 const Vector<String>& history() const { return m_history; } 79 80 void register_character_input_callback(char ch, Function<bool(Editor&)> callback); 81 82 Function<Vector<String>(const String&)> on_tab_complete_first_token; 83 Function<Vector<String>(const String&)> on_tab_complete_other_token; 84 Function<void(Editor&)> on_display_refresh; 85 86 // FIXME: we will have to kindly ask our instantiators to set our signal handlers 87 // since we can not do this cleanly ourselves (signal() limitation: cannot give member functions) 88 void interrupted() { m_was_interrupted = true; } 89 void resized() { m_was_resized = true; } 90 91 size_t cursor() const { return m_cursor; } 92 const Vector<char, 1024>& buffer() const { return m_buffer; } 93 char buffer_at(size_t pos) const { return m_buffer.at(pos); } 94 95 void clear_line(); 96 void insert(const String&); 97 void insert(const char); 98 void cut_mismatching_chars(String& completion, const String& other, size_t start_compare); 99 void stylize(const Span&, const Style&); 100 void strip_styles() 101 { 102 m_spans_starting.clear(); 103 m_spans_ending.clear(); 104 m_refresh_needed = true; 105 } 106 107 const struct termios& termios() const { return m_termios; } 108 const struct termios& default_termios() const { return m_default_termios; } 109 110private: 111 void vt_save_cursor(); 112 void vt_restore_cursor(); 113 void vt_clear_to_end_of_line(); 114 void vt_clear_lines(size_t count_above, size_t count_below = 0); 115 void vt_move_relative(int x, int y); 116 void vt_apply_style(const Style&); 117 118 Style find_applicable_style(size_t offset) const; 119 120 void refresh_display(); 121 122 // FIXME: These three will report the wrong value because they do not 123 // take the length of the prompt into consideration, and it does not 124 // appear that we can figure that out easily 125 size_t num_lines() const 126 { 127 return (m_buffer.size() + m_num_columns) / m_num_columns; 128 } 129 130 size_t cursor_line() const 131 { 132 return (m_cursor + m_num_columns) / m_num_columns; 133 } 134 135 size_t offset_in_line() const 136 { 137 auto offset = m_cursor % m_num_columns; 138 return offset; 139 } 140 141 Vector<char, 1024> m_buffer; 142 ByteBuffer m_pending_chars; 143 size_t m_cursor { 0 }; 144 size_t m_chars_inserted_in_the_middle { 0 }; 145 size_t m_times_tab_pressed { 0 }; 146 size_t m_num_columns { 0 }; 147 148 HashMap<char, NonnullOwnPtr<KeyCallback>> m_key_callbacks; 149 150 // TODO: handle signals internally 151 struct termios m_termios, m_default_termios; 152 bool m_was_interrupted = false; 153 bool m_was_resized = false; 154 155 // FIXME: This should be something more take_first()-friendly. 156 Vector<String> m_history; 157 size_t m_history_cursor { 0 }; 158 size_t m_history_capacity { 100 }; 159 160 enum class InputState { 161 Free, 162 ExpectBracket, 163 ExpectFinal, 164 ExpectTerminator, 165 }; 166 InputState m_state { InputState::Free }; 167 168 HashMap<u32, HashMap<u32, Style>> m_spans_starting; 169 HashMap<u32, HashMap<u32, Style>> m_spans_ending; 170 171 bool m_initialized { false }; 172 bool m_refresh_needed { false }; 173}; 174 175}