Coverage Report

Created: 2026-09-26 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibLine/Editor.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3
 * Copyright (c) 2021, the SerenityOS developers.
4
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#pragma once
9
10
#include <AK/BinarySearch.h>
11
#include <AK/ByteBuffer.h>
12
#include <AK/ByteString.h>
13
#include <AK/Function.h>
14
#include <AK/HashMap.h>
15
#include <AK/OwnPtr.h>
16
#include <AK/RedBlackTree.h>
17
#include <AK/Result.h>
18
#include <AK/Traits.h>
19
#include <AK/Utf32View.h>
20
#include <AK/Utf8View.h>
21
#include <AK/Vector.h>
22
#include <LibCore/DirIterator.h>
23
#include <LibCore/EventLoop.h>
24
#include <LibCore/EventReceiver.h>
25
#include <LibCore/Notifier.h>
26
#include <LibLine/KeyCallbackMachine.h>
27
#include <LibLine/Span.h>
28
#include <LibLine/StringMetrics.h>
29
#include <LibLine/Style.h>
30
#include <LibLine/SuggestionDisplay.h>
31
#include <LibLine/SuggestionManager.h>
32
#include <LibLine/VT.h>
33
#include <sys/ioctl.h>
34
#include <sys/stat.h>
35
#include <termios.h>
36
37
namespace Line {
38
39
0
static constexpr u32 ctrl(char c) { return c & 0x3f; }
Unexecuted instantiation: FuzzShell.cpp:Line::ctrl(char)
Unexecuted instantiation: Parser.cpp:Line::ctrl(char)
Unexecuted instantiation: AST.cpp:Line::ctrl(char)
Unexecuted instantiation: ImmediateFunctions.cpp:Line::ctrl(char)
Unexecuted instantiation: Formatter.cpp:Line::ctrl(char)
Unexecuted instantiation: NodeVisitor.cpp:Line::ctrl(char)
Unexecuted instantiation: PosixParser.cpp:Line::ctrl(char)
Unexecuted instantiation: PosixLexer.cpp:Line::ctrl(char)
Unexecuted instantiation: Shell.cpp:Line::ctrl(char)
Unexecuted instantiation: Builtin.cpp:Line::ctrl(char)
Unexecuted instantiation: Job.cpp:Line::ctrl(char)
Unexecuted instantiation: URLHighlight.cpp:Line::ctrl(char)
Unexecuted instantiation: Editor.cpp:Line::ctrl(char)
Unexecuted instantiation: InternalFunctions.cpp:Line::ctrl(char)
Unexecuted instantiation: KeyCallbackMachine.cpp:Line::ctrl(char)
Unexecuted instantiation: FuzzShellPosix.cpp:Line::ctrl(char)
40
41
struct KeyBinding {
42
    Vector<Key> keys;
43
    enum class Kind {
44
        InternalFunction,
45
        Insertion,
46
    } kind { Kind::InternalFunction };
47
    ByteString binding;
48
};
49
50
struct Configuration {
51
    enum RefreshBehavior {
52
        Lazy,
53
        Eager,
54
    };
55
    enum OperationMode {
56
        Unset,
57
        Full,
58
        NoEscapeSequences,
59
        NonInteractive,
60
    };
61
    enum SignalHandler {
62
        WithSignalHandlers,
63
        NoSignalHandlers,
64
    };
65
66
    enum Flags : u32 {
67
        None = 0,
68
        BracketedPaste = 1,
69
    };
70
71
    struct DefaultTextEditor {
72
        ByteString command;
73
    };
74
75
    Configuration()
76
0
    {
77
0
    }
78
79
    template<typename Arg, typename... Rest>
80
    Configuration(Arg arg, Rest... rest)
81
0
        : Configuration(rest...)
82
0
    {
83
0
        set(arg);
84
0
    }
Unexecuted instantiation: Line::Configuration::Configuration<Line::Configuration::RefreshBehavior, Line::Configuration::SignalHandler>(Line::Configuration::RefreshBehavior, Line::Configuration::SignalHandler)
Unexecuted instantiation: Line::Configuration::Configuration<Line::Configuration::SignalHandler>(Line::Configuration::SignalHandler)
85
86
0
    void set(RefreshBehavior refresh) { refresh_behavior = refresh; }
87
0
    void set(OperationMode mode) { operation_mode = mode; }
88
0
    void set(SignalHandler mode) { m_signal_mode = mode; }
89
0
    void set(KeyBinding const& binding) { keybindings.append(binding); }
90
0
    void set(DefaultTextEditor editor) { m_default_text_editor = move(editor.command); }
91
    void set(Flags flags)
92
0
    {
93
0
        enable_bracketed_paste = flags & Flags::BracketedPaste;
94
0
    }
95
96
    static Configuration from_config(StringView libname = "line"sv);
97
98
    RefreshBehavior refresh_behavior { RefreshBehavior::Lazy };
99
    SignalHandler m_signal_mode { SignalHandler::WithSignalHandlers };
100
    OperationMode operation_mode { OperationMode::Unset };
101
    Vector<KeyBinding> keybindings;
102
    ByteString m_default_text_editor {};
103
    bool enable_bracketed_paste { false };
104
};
105
106
#define ENUMERATE_EDITOR_INTERNAL_FUNCTIONS(M) \
107
0
    M(clear_screen)                            \
108
0
    M(cursor_left_character)                   \
109
0
    M(cursor_left_word)                        \
110
0
    M(cursor_left_nonspace_word)               \
111
0
    M(cursor_right_character)                  \
112
0
    M(cursor_right_word)                       \
113
0
    M(cursor_right_nonspace_word)              \
114
0
    M(enter_search)                            \
115
0
    M(search_character_backwards)              \
116
0
    M(search_character_forwards)               \
117
0
    M(erase_character_backwards)               \
118
0
    M(erase_character_forwards)                \
119
0
    M(erase_to_beginning)                      \
120
0
    M(erase_to_end)                            \
121
0
    M(erase_word_backwards)                    \
122
0
    M(finish_edit)                             \
123
0
    M(go_end)                                  \
124
0
    M(go_home)                                 \
125
0
    M(kill_line)                               \
126
0
    M(search_backwards)                        \
127
0
    M(search_forwards)                         \
128
0
    M(transpose_characters)                    \
129
0
    M(transpose_words)                         \
130
0
    M(insert_last_words)                       \
131
0
    M(insert_last_erased)                      \
132
0
    M(erase_alnum_word_backwards)              \
133
0
    M(erase_alnum_word_forwards)               \
134
0
    M(erase_spaces)                            \
135
0
    M(capitalize_word)                         \
136
0
    M(lowercase_word)                          \
137
0
    M(uppercase_word)                          \
138
0
    M(edit_in_external_editor)
139
140
#define EDITOR_INTERNAL_FUNCTION(name) \
141
0
    [](auto& editor) { editor.name();  return false; }
Unexecuted instantiation: Shell.cpp:auto Shell::Shell::setup_keybinds()::$_0::operator()(Line::Editor&) const::{lambda(auto:1&)#1}::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_0::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_1::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_2::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_3::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_4::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_5::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_6::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_7::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_8::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_9::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_10::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_11::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_12::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_13::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_14::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_15::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_16::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_17::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_18::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_19::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_20::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_21::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_22::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_23::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_24::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_25::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_26::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_27::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_28::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_29::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_30::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_31::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: Editor.cpp:auto Line::Editor::set_default_keybinds()::$_32::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_0::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_1::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_2::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_3::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_4::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_5::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_6::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_7::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_8::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_9::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_10::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_11::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_12::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_13::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_14::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_15::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_16::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_17::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_18::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_19::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_20::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_21::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_22::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_23::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_24::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_25::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_26::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_27::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_28::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_29::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_30::operator()<Line::Editor>(Line::Editor&) const
Unexecuted instantiation: InternalFunctions.cpp:auto Line::Editor::find_internal_function(AK::StringView)::$_31::operator()<Line::Editor>(Line::Editor&) const
142
143
class Editor : public Core::EventReceiver {
144
    C_OBJECT(Editor);
145
146
public:
147
    enum class Error {
148
        ReadFailure,
149
        Empty,
150
        Eof,
151
    };
152
153
    ~Editor();
154
155
    Result<ByteString, Error> get_line(ByteString const& prompt);
156
157
    void initialize();
158
159
    void refetch_default_termios();
160
161
    void add_to_history(ByteString const& line);
162
    bool load_history(ByteString const& path);
163
    bool save_history(ByteString const& path);
164
0
    auto const& history() const { return m_history; }
165
0
    bool is_history_dirty() const { return m_history_dirty; }
166
167
    void register_key_input_callback(KeyBinding const&);
168
0
    void register_key_input_callback(Vector<Key> keys, Function<bool(Editor&)> callback) { m_callback_machine.register_key_input_callback(move(keys), move(callback)); }
169
0
    void register_key_input_callback(Key key, Function<bool(Editor&)> callback) { register_key_input_callback(Vector<Key> { key }, move(callback)); }
170
171
    static StringMetrics actual_rendered_string_metrics(StringView, RedBlackTree<u32, Optional<Style::Mask>> const& masks = {}, Optional<size_t> maximum_line_width = {});
172
    static StringMetrics actual_rendered_string_metrics(Utf32View const&, RedBlackTree<u32, Optional<Style::Mask>> const& masks = {}, Optional<size_t> maximum_line_width = {});
173
174
    Function<Vector<CompletionSuggestion>(Editor const&)> on_tab_complete;
175
    Function<void(Utf32View, Editor&)> on_paste;
176
    Function<void()> on_interrupt_handled;
177
    Function<void(Editor&)> on_display_refresh;
178
179
    static Function<bool(Editor&)> find_internal_function(StringView name);
180
    enum class CaseChangeOp {
181
        Lowercase,
182
        Uppercase,
183
        Capital,
184
    };
185
    void case_change_word(CaseChangeOp);
186
#define __ENUMERATE_EDITOR_INTERNAL_FUNCTION(name) \
187
    void name();
188
189
    ENUMERATE_EDITOR_INTERNAL_FUNCTIONS(__ENUMERATE_EDITOR_INTERNAL_FUNCTION)
190
191
#undef __ENUMERATE_EDITOR_INTERNAL_FUNCTION
192
193
    ErrorOr<void> interrupted();
194
    ErrorOr<void> resized();
195
196
0
    size_t cursor() const { return m_cursor; }
197
    void set_cursor(size_t cursor)
198
0
    {
199
0
        if (cursor > m_buffer.size())
200
0
            cursor = m_buffer.size();
201
0
        m_cursor = cursor;
202
0
    }
203
0
    Vector<u32, 1024> const& buffer() const { return m_buffer; }
204
0
    u32 buffer_at(size_t pos) const { return m_buffer.at(pos); }
205
0
    ByteString line() const { return line(m_buffer.size()); }
206
    ByteString line(size_t up_to_index) const;
207
208
    // Only makes sense inside a character_input callback or on_* callback.
209
    void set_prompt(ByteString const& prompt)
210
0
    {
211
0
        if (m_cached_prompt_valid)
212
0
            m_old_prompt_metrics = m_cached_prompt_metrics;
213
0
        m_cached_prompt_valid = false;
214
0
        m_cached_prompt_metrics = actual_rendered_string_metrics(prompt, {});
215
0
        m_new_prompt = prompt;
216
0
    }
217
218
    void clear_line();
219
    void insert(ByteString const&);
220
    void insert(StringView);
221
    void insert(Utf8View&);
222
    void insert(Utf32View const&);
223
    void insert(u32 const);
224
    void stylize(Span const&, Style const&);
225
    void strip_styles(bool strip_anchored = false);
226
227
    // Invariant Offset is an offset into the suggested data, hinting the editor what parts of the suggestion will not change
228
    // Static Offset is an offset into the token, signifying where the suggestions start
229
    // e.g.
230
    //    foobar<suggestion initiated>, on_tab_complete returns "barx", "bary", "barz"
231
    //       ^ ^
232
    //       +-|- static offset: the suggestions start here
233
    //         +- invariant offset: the suggestions do not change up to here
234
    //
235
    void transform_suggestion_offsets(size_t& invariant_offset, size_t& static_offset, Span::Mode offset_mode = Span::ByteOriented) const;
236
237
0
    const struct termios& termios() const { return m_termios; }
238
0
    const struct termios& default_termios() const { return m_default_termios; }
239
    struct winsize terminal_size() const
240
0
    {
241
0
        winsize ws { (u16)m_num_lines, (u16)m_num_columns, 0, 0 };
242
0
        return ws;
243
0
    }
244
245
    void finish()
246
0
    {
247
0
        m_finish = true;
248
0
    }
249
250
0
    bool is_editing() const { return m_is_editing; }
251
252
0
    Utf32View const buffer_view() const { return { m_buffer.data(), m_buffer.size() }; }
253
254
    auto prohibit_input()
255
0
    {
256
0
        auto previous_value = m_prohibit_input_processing;
257
0
        m_prohibit_input_processing = true;
258
0
        m_have_unprocessed_read_event = false;
259
0
        return ScopeGuard {
260
0
            [this, previous_value] {
261
0
                m_prohibit_input_processing = previous_value;
262
0
                if (!m_prohibit_input_processing && m_have_unprocessed_read_event)
263
0
                    handle_read_event().release_value_but_fixme_should_propagate_errors();
264
0
            }
265
0
        };
266
0
    }
267
268
private:
269
    explicit Editor(Configuration configuration = Configuration::from_config());
270
271
    void set_default_keybinds();
272
273
    enum LoopExitCode {
274
        Exit = 0,
275
        Retry
276
    };
277
278
    ErrorOr<void> try_update_once();
279
    void handle_interrupt_event();
280
    ErrorOr<void> handle_read_event();
281
    ErrorOr<void> handle_resize_event(bool reset_origin);
282
283
    void ensure_free_lines_from_origin(size_t count);
284
285
    Result<Vector<size_t, 2>, Error> vt_dsr();
286
    void remove_at_index(size_t);
287
288
    enum class ModificationKind {
289
        Insertion,
290
        Removal,
291
        ForcedOverlapRemoval,
292
    };
293
    void readjust_anchored_styles(size_t hint_index, ModificationKind);
294
295
    Style find_applicable_style(size_t offset) const;
296
297
    bool search(StringView, bool allow_empty = false, bool from_beginning = true);
298
    inline void end_search()
299
0
    {
300
0
        m_is_searching = false;
301
0
        m_refresh_needed = true;
302
0
        m_search_offset = 0;
303
0
        if (m_reset_buffer_on_search_end) {
304
0
            m_buffer.clear();
305
0
            for (auto ch : m_pre_search_buffer)
306
0
                m_buffer.append(ch);
307
0
            m_cursor = m_pre_search_cursor;
308
0
        }
309
0
        m_reset_buffer_on_search_end = true;
310
0
        m_search_editor = nullptr;
311
0
    }
312
313
    void reset()
314
0
    {
315
0
        m_cached_buffer_metrics.reset();
316
0
        m_cached_prompt_valid = false;
317
0
        m_cursor = 0;
318
0
        m_drawn_cursor = 0;
319
0
        m_inline_search_cursor = 0;
320
0
        m_search_offset = 0;
321
0
        m_search_offset_state = SearchOffsetState::Unbiased;
322
0
        m_old_prompt_metrics = m_cached_prompt_metrics;
323
0
        set_origin(0, 0);
324
0
        m_prompt_lines_at_suggestion_initiation = 0;
325
0
        m_refresh_needed = true;
326
0
        m_input_error.clear();
327
0
        m_returned_line = ByteString::empty();
328
0
        m_chars_touched_in_the_middle = 0;
329
0
        m_drawn_end_of_line_offset = 0;
330
0
        m_drawn_spans = {};
331
0
        m_paste_buffer.clear_with_capacity();
332
0
        m_shown_lines = 0;
333
0
        m_extra_forward_lines = 0;
334
0
    }
335
336
    ErrorOr<void> refresh_display();
337
    ErrorOr<void> cleanup();
338
    ErrorOr<void> cleanup_suggestions();
339
    ErrorOr<void> really_quit_event_loop();
340
341
    void restore()
342
0
    {
343
0
        VERIFY(m_initialized);
344
0
        tcsetattr(0, TCSANOW, &m_default_termios);
345
0
        m_initialized = false;
346
0
        if (m_configuration.enable_bracketed_paste)
347
0
            warn("\x1b[?2004l");
348
0
        for (auto id : m_signal_handlers)
349
0
            Core::EventLoop::unregister_signal(id);
350
0
    }
351
352
    StringMetrics const& current_prompt_metrics() const
353
0
    {
354
0
        return m_cached_prompt_valid ? m_cached_prompt_metrics : m_old_prompt_metrics;
355
0
    }
356
357
    size_t num_lines() const
358
0
    {
359
0
        return current_prompt_metrics().lines_with_addition(m_cached_buffer_metrics, m_num_columns);
360
0
    }
361
362
    size_t cursor_line() const
363
0
    {
364
0
        auto cursor = m_drawn_cursor;
365
0
        if (cursor > m_cursor)
366
0
            cursor = m_cursor;
367
0
        return current_prompt_metrics().lines_with_addition(
368
0
            actual_rendered_string_metrics(buffer_view().substring_view(0, cursor), m_current_masks),
369
0
            m_num_columns);
370
0
    }
371
372
    size_t offset_in_line() const
373
0
    {
374
0
        auto cursor = m_drawn_cursor;
375
0
        if (cursor > m_cursor)
376
0
            cursor = m_cursor;
377
0
        auto buffer_metrics = actual_rendered_string_metrics(buffer_view().substring_view(0, cursor), m_current_masks);
378
0
        return current_prompt_metrics().offset_with_addition(buffer_metrics, m_num_columns);
379
0
    }
380
381
    bool set_origin(bool quit_on_error = true)
382
0
    {
383
0
        auto position = vt_dsr();
384
0
        if (!position.is_error()) {
385
0
            set_origin(position.value()[0], position.value()[1]);
386
0
            return true;
387
0
        }
388
0
        if (quit_on_error && position.is_error()) {
389
0
            m_input_error = position.error();
390
0
            finish();
391
0
        }
392
0
        return false;
393
0
    }
394
395
    void set_origin(int row, int col)
396
0
    {
397
0
        m_origin_row = row;
398
0
        m_origin_column = col;
399
0
        m_suggestion_display->set_origin(row, col, {});
400
0
    }
401
402
    void recalculate_origin();
403
    ErrorOr<void> reposition_cursor(Stream&, bool to_end = false);
404
405
    struct CodepointRange {
406
        size_t start { 0 };
407
        size_t end { 0 };
408
    };
409
    CodepointRange byte_offset_range_to_code_point_offset_range(size_t byte_start, size_t byte_end, size_t code_point_scan_offset, bool reverse = false) const;
410
411
    void get_terminal_size();
412
413
    bool m_finish { false };
414
415
    RefPtr<Editor> m_search_editor;
416
    bool m_is_searching { false };
417
    bool m_reset_buffer_on_search_end { true };
418
    size_t m_search_offset { 0 };
419
    enum class SearchOffsetState {
420
        Unbiased,
421
        Backwards,
422
        Forwards,
423
    } m_search_offset_state { SearchOffsetState::Unbiased };
424
    size_t m_pre_search_cursor { 0 };
425
    Vector<u32, 1024> m_pre_search_buffer;
426
427
    Vector<u32, 1024> m_buffer;
428
    ByteBuffer m_pending_chars;
429
    Vector<char, 512> m_incomplete_data;
430
    Optional<Error> m_input_error;
431
    ByteString m_returned_line;
432
433
    size_t m_cursor { 0 };
434
    size_t m_drawn_cursor { 0 };
435
    size_t m_drawn_end_of_line_offset { 0 };
436
    size_t m_inline_search_cursor { 0 };
437
    size_t m_chars_touched_in_the_middle { 0 };
438
    size_t m_times_tab_pressed { 0 };
439
    size_t m_num_columns { 0 };
440
    size_t m_num_lines { 1 };
441
    size_t m_previous_num_columns { 0 };
442
    size_t m_extra_forward_lines { 0 };
443
    size_t m_shown_lines { 0 };
444
    StringMetrics m_cached_prompt_metrics;
445
    StringMetrics m_old_prompt_metrics;
446
    StringMetrics m_cached_buffer_metrics;
447
    size_t m_prompt_lines_at_suggestion_initiation { 0 };
448
    bool m_cached_prompt_valid { false };
449
450
    // Exact position before our prompt in the terminal.
451
    size_t m_origin_row { 0 };
452
    size_t m_origin_column { 0 };
453
    bool m_expected_origin_changed { false };
454
    bool m_has_origin_reset_scheduled { false };
455
456
    OwnPtr<SuggestionDisplay> m_suggestion_display;
457
    Vector<u32, 32> m_remembered_suggestion_static_data;
458
459
    ByteString m_new_prompt;
460
461
    SuggestionManager m_suggestion_manager;
462
463
    bool m_always_refresh { false };
464
465
    enum class TabDirection {
466
        Forward,
467
        Backward,
468
    };
469
    TabDirection m_tab_direction { TabDirection::Forward };
470
471
    KeyCallbackMachine m_callback_machine;
472
473
    struct termios m_termios {};
474
    struct termios m_default_termios {};
475
    bool m_was_interrupted { false };
476
    bool m_previous_interrupt_was_handled_as_interrupt { true };
477
    bool m_was_resized { false };
478
479
    // FIXME: This should be something more take_first()-friendly.
480
    struct HistoryEntry {
481
        ByteString entry;
482
        time_t timestamp;
483
    };
484
    Vector<HistoryEntry> m_history;
485
    size_t m_history_cursor { 0 };
486
    size_t m_history_capacity { 1024 };
487
    bool m_history_dirty { false };
488
    static ErrorOr<Vector<HistoryEntry>> try_load_history(StringView path);
489
490
    enum class InputState {
491
        Free,
492
        Verbatim,
493
        Paste,
494
        GotEscape,
495
        SS3ExpectFinal,
496
        CSIExpectParameter,
497
        CSIExpectIntermediate,
498
        CSIExpectFinal,
499
    };
500
    InputState m_state { InputState::Free };
501
    InputState m_previous_free_state { InputState::Free };
502
503
    struct Spans {
504
        HashMap<u32, HashMap<u32, Style>> m_spans_starting;
505
        HashMap<u32, HashMap<u32, Style>> m_spans_ending;
506
        HashMap<u32, HashMap<u32, Style>> m_anchored_spans_starting;
507
        HashMap<u32, HashMap<u32, Style>> m_anchored_spans_ending;
508
509
        bool contains_up_to_offset(Spans const& other, size_t offset) const;
510
    } m_drawn_spans, m_current_spans;
511
512
    RedBlackTree<u32, Optional<Style::Mask>> m_current_masks;
513
514
    RefPtr<Core::Notifier> m_notifier;
515
516
    Vector<u32> m_paste_buffer;
517
    Vector<u32> m_last_erased;
518
519
    bool m_initialized { false };
520
    bool m_refresh_needed { false };
521
    Vector<int, 2> m_signal_handlers;
522
523
    bool m_is_editing { false };
524
    bool m_prohibit_input_processing { false };
525
    bool m_have_unprocessed_read_event { false };
526
527
    Configuration m_configuration;
528
};
529
530
}