Procházet zdrojové kódy

LibLine: Do a whole bunch of internal error propagation

Tim Schumacher před 2 roky
rodič
revize
40cb41a16c

+ 143 - 118
Userland/Libraries/LibLine/Editor.cpp

@@ -566,11 +566,11 @@ void Editor::initialize()
 
     if (m_configuration.m_signal_mode == Configuration::WithSignalHandlers) {
         m_signal_handlers.append(Core::EventLoop::register_signal(SIGINT, [this](int) {
-            interrupted();
+            interrupted().release_value_but_fixme_should_propagate_errors();
         }));
 
         m_signal_handlers.append(Core::EventLoop::register_signal(SIGWINCH, [this](int) {
-            resized();
+            resized().release_value_but_fixme_should_propagate_errors();
         }));
     }
 
@@ -587,26 +587,26 @@ void Editor::refetch_default_termios()
     m_termios = termios;
 }
 
-void Editor::interrupted()
+ErrorOr<void> Editor::interrupted()
 {
     if (m_is_searching)
         return m_search_editor->interrupted();
 
     if (!m_is_editing)
-        return;
+        return {};
 
     m_was_interrupted = true;
     handle_interrupt_event();
     if (!m_finish || !m_previous_interrupt_was_handled_as_interrupt)
-        return;
+        return {};
 
     m_finish = false;
     {
-        auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
-        reposition_cursor(*stderr_stream, true);
-        if (m_suggestion_display->cleanup())
-            reposition_cursor(*stderr_stream, true);
-        stderr_stream->write_entire_buffer("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+        auto stderr_stream = TRY(Core::Stream::File::standard_error());
+        TRY(reposition_cursor(*stderr_stream, true));
+        if (TRY(m_suggestion_display->cleanup()))
+            TRY(reposition_cursor(*stderr_stream, true));
+        TRY(stderr_stream->write_entire_buffer("\n"sv.bytes()));
     }
     m_buffer.clear();
     m_chars_touched_in_the_middle = buffer().size();
@@ -615,9 +615,10 @@ void Editor::interrupted()
     m_notifier->set_enabled(false);
     m_notifier = nullptr;
     Core::EventLoop::current().quit(Retry);
+    return {};
 }
 
-void Editor::resized()
+ErrorOr<void> Editor::resized()
 {
     m_was_resized = true;
     m_previous_num_columns = m_num_columns;
@@ -626,42 +627,47 @@ void Editor::resized()
     if (!m_has_origin_reset_scheduled) {
         // Reset the origin, but make sure it doesn't blow up if we can't read it
         if (set_origin(false)) {
-            handle_resize_event(false);
+            TRY(handle_resize_event(false));
         } else {
-            deferred_invoke([this] { handle_resize_event(true); });
+            deferred_invoke([this] { handle_resize_event(true).release_value_but_fixme_should_propagate_errors(); });
             m_has_origin_reset_scheduled = true;
         }
     }
+
+    return {};
 }
 
-void Editor::handle_resize_event(bool reset_origin)
+ErrorOr<void> Editor::handle_resize_event(bool reset_origin)
 {
     m_has_origin_reset_scheduled = false;
     if (reset_origin && !set_origin(false)) {
         m_has_origin_reset_scheduled = true;
-        return deferred_invoke([this] { handle_resize_event(true); });
+        deferred_invoke([this] { handle_resize_event(true).release_value_but_fixme_should_propagate_errors(); });
+        return {};
     }
 
     set_origin(m_origin_row, 1);
 
-    auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
+    auto stderr_stream = TRY(Core::Stream::File::standard_error());
 
-    reposition_cursor(*stderr_stream, true);
-    m_suggestion_display->redisplay(m_suggestion_manager, m_num_lines, m_num_columns);
+    TRY(reposition_cursor(*stderr_stream, true));
+    TRY(m_suggestion_display->redisplay(m_suggestion_manager, m_num_lines, m_num_columns));
     m_origin_row = m_suggestion_display->origin_row();
-    reposition_cursor(*stderr_stream);
+    TRY(reposition_cursor(*stderr_stream));
 
     if (m_is_searching)
-        m_search_editor->resized();
+        TRY(m_search_editor->resized());
+
+    return {};
 }
 
-void Editor::really_quit_event_loop()
+ErrorOr<void> Editor::really_quit_event_loop()
 {
     m_finish = false;
     {
-        auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
-        reposition_cursor(*stderr_stream, true);
-        stderr_stream->write_entire_buffer("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+        auto stderr_stream = TRY(Core::Stream::File::standard_error());
+        TRY(reposition_cursor(*stderr_stream, true));
+        TRY(stderr_stream->write_entire_buffer("\n"sv.bytes()));
     }
     auto string = line();
     m_buffer.clear();
@@ -675,6 +681,7 @@ void Editor::really_quit_event_loop()
     m_notifier->set_enabled(false);
     m_notifier = nullptr;
     Core::EventLoop::current().quit(Exit);
+    return {};
 }
 
 auto Editor::get_line(DeprecatedString const& prompt) -> Result<DeprecatedString, Editor::Error>
@@ -729,22 +736,22 @@ auto Editor::get_line(DeprecatedString const& prompt) -> Result<DeprecatedString
         for (size_t i = 0; i < prompt_lines; ++i)
             stderr_stream->write_entire_buffer("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
 
-        VT::move_relative(-static_cast<int>(prompt_lines), 0, *stderr_stream);
+        VT::move_relative(-static_cast<int>(prompt_lines), 0, *stderr_stream).release_value_but_fixme_should_propagate_errors();
     }
 
     set_origin();
 
     m_history_cursor = m_history.size();
 
-    refresh_display();
+    refresh_display().release_value_but_fixme_should_propagate_errors();
 
     Core::EventLoop loop;
 
     m_notifier = Core::Notifier::construct(STDIN_FILENO, Core::Notifier::Read);
 
-    m_notifier->on_ready_to_read = [&] { try_update_once(); };
+    m_notifier->on_ready_to_read = [&] { try_update_once().release_value_but_fixme_should_propagate_errors(); };
     if (!m_incomplete_data.is_empty())
-        deferred_invoke([&] { try_update_once(); });
+        deferred_invoke([&] { try_update_once().release_value_but_fixme_should_propagate_errors(); });
 
     if (loop.exec() == Retry)
         return get_line(prompt);
@@ -770,21 +777,23 @@ void Editor::save_to(JsonObject& object)
     object.set("used_display_area", move(display_area));
 }
 
-void Editor::try_update_once()
+ErrorOr<void> Editor::try_update_once()
 {
     if (m_was_interrupted) {
         handle_interrupt_event();
     }
 
-    handle_read_event();
+    TRY(handle_read_event());
 
     if (m_always_refresh)
         m_refresh_needed = true;
 
-    refresh_display();
+    TRY(refresh_display());
 
     if (m_finish)
-        really_quit_event_loop();
+        TRY(really_quit_event_loop());
+
+    return {};
 }
 
 void Editor::handle_interrupt_event()
@@ -811,11 +820,11 @@ void Editor::handle_interrupt_event()
     finish();
 }
 
-void Editor::handle_read_event()
+ErrorOr<void> Editor::handle_read_event()
 {
     if (m_prohibit_input_processing) {
         m_have_unprocessed_read_event = true;
-        return;
+        return {};
     }
 
     auto prohibit_scope = prohibit_input();
@@ -830,14 +839,14 @@ void Editor::handle_read_event()
         if (errno == EINTR) {
             if (!m_was_interrupted) {
                 if (m_was_resized)
-                    return;
+                    return {};
 
                 finish();
-                return;
+                return {};
             }
 
             handle_interrupt_event();
-            return;
+            return {};
         }
 
         ScopedValueRollback errno_restorer(errno);
@@ -845,7 +854,7 @@ void Editor::handle_read_event()
 
         m_input_error = Error::ReadFailure;
         finish();
-        return;
+        return {};
     }
 
     m_incomplete_data.append(keybuf, nread);
@@ -854,7 +863,7 @@ void Editor::handle_read_event()
     if (available_bytes == 0) {
         m_input_error = Error::Empty;
         finish();
-        return;
+        return {};
     }
 
     auto reverse_tab = false;
@@ -900,7 +909,7 @@ void Editor::handle_read_event()
             default: {
                 m_callback_machine.key_pressed(*this, { code_point, Key::Alt });
                 m_state = InputState::Free;
-                cleanup_suggestions();
+                TRY(cleanup_suggestions());
                 continue;
             }
             }
@@ -958,7 +967,7 @@ void Editor::handle_read_event()
                 reverse_tab = true;
                 break;
             }
-            cleanup_suggestions();
+            TRY(cleanup_suggestions());
 
             switch (csi_final) {
             case 'A': // ^[[A: arrow up
@@ -1064,7 +1073,7 @@ void Editor::handle_read_event()
         }
 
         // There are no sequences past this point, so short of 'tab', we will want to cleanup the suggestions.
-        ArmedScopeGuard suggestion_cleanup { [this] { cleanup_suggestions(); } };
+        ArmedScopeGuard suggestion_cleanup { [this] { cleanup_suggestions().release_value_but_fixme_should_propagate_errors(); } };
 
         // Normally ^D. `stty eof \^n` can change it to ^N (or something else), but Serenity doesn't have `stty` yet.
         // Process this here since the keybinds might override its behavior.
@@ -1156,8 +1165,8 @@ void Editor::handle_read_event()
             for (auto& view : completion_result.insert)
                 insert(view);
 
-            auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
-            reposition_cursor(*stderr_stream);
+            auto stderr_stream = TRY(Core::Stream::File::standard_error());
+            TRY(reposition_cursor(*stderr_stream));
 
             if (completion_result.style_to_apply.has_value()) {
                 // Apply the style of the last suggestion.
@@ -1178,12 +1187,12 @@ void Editor::handle_read_event()
             }
 
             if (m_times_tab_pressed > 1 && m_suggestion_manager.count() > 0) {
-                if (m_suggestion_display->cleanup())
-                    reposition_cursor(*stderr_stream);
+                if (TRY(m_suggestion_display->cleanup()))
+                    TRY(reposition_cursor(*stderr_stream));
 
                 m_suggestion_display->set_initial_prompt_lines(m_prompt_lines_at_suggestion_initiation);
 
-                m_suggestion_display->display(m_suggestion_manager);
+                TRY(m_suggestion_display->display(m_suggestion_manager));
 
                 m_origin_row = m_suggestion_display->origin_row();
             }
@@ -1199,8 +1208,8 @@ void Editor::handle_read_event()
                 // We have none, or just one suggestion,
                 // we should just commit that and continue
                 // after it, as if it were auto-completed.
-                reposition_cursor(*stderr_stream, true);
-                cleanup_suggestions();
+                TRY(reposition_cursor(*stderr_stream, true));
+                TRY(cleanup_suggestions());
                 m_remembered_suggestion_static_data.clear_with_capacity();
             }
             continue;
@@ -1209,7 +1218,7 @@ void Editor::handle_read_event()
         // If we got here, manually cleanup the suggestions and then insert the new code point.
         m_remembered_suggestion_static_data.clear_with_capacity();
         suggestion_cleanup.disarm();
-        cleanup_suggestions();
+        TRY(cleanup_suggestions());
         insert(code_point);
     }
 
@@ -1221,10 +1230,12 @@ void Editor::handle_read_event()
     }
 
     if (!m_incomplete_data.is_empty() && !m_finish)
-        deferred_invoke([&] { try_update_once(); });
+        deferred_invoke([&] { try_update_once().release_value_but_fixme_should_propagate_errors(); });
+
+    return {};
 }
 
-void Editor::cleanup_suggestions()
+ErrorOr<void> Editor::cleanup_suggestions()
 {
     if (m_times_tab_pressed != 0) {
         // Apply the style of the last suggestion.
@@ -1232,15 +1243,16 @@ void Editor::cleanup_suggestions()
         stylize({ m_suggestion_manager.current_suggestion().start_index, m_cursor, Span::Mode::CodepointOriented }, m_suggestion_manager.current_suggestion().style);
         // We probably have some suggestions drawn,
         // let's clean them up.
-        if (m_suggestion_display->cleanup()) {
-            auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
-            reposition_cursor(*stderr_stream);
+        if (TRY(m_suggestion_display->cleanup())) {
+            auto stderr_stream = TRY(Core::Stream::File::standard_error());
+            TRY(reposition_cursor(*stderr_stream));
             m_refresh_needed = true;
         }
         m_suggestion_manager.reset();
         m_suggestion_display->finish();
     }
     m_times_tab_pressed = 0; // Safe to say if we get here, the user didn't press TAB
+    return {};
 }
 
 bool Editor::search(StringView phrase, bool allow_empty, bool from_beginning)
@@ -1298,22 +1310,24 @@ void Editor::recalculate_origin()
     // but that will be calculated and applied at the next
     // refresh cycle.
 }
-void Editor::cleanup()
+
+ErrorOr<void> Editor::cleanup()
 {
     auto current_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks);
     auto new_lines = current_prompt_metrics().lines_with_addition(current_buffer_metrics, m_num_columns);
     if (new_lines < m_shown_lines)
         m_extra_forward_lines = max(m_shown_lines - new_lines, m_extra_forward_lines);
 
-    auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
-    reposition_cursor(*stderr_stream, true);
+    auto stderr_stream = TRY(Core::Stream::File::standard_error());
+    TRY(reposition_cursor(*stderr_stream, true));
     auto current_line = num_lines() - 1;
-    VT::clear_lines(current_line, m_extra_forward_lines, *stderr_stream);
+    TRY(VT::clear_lines(current_line, m_extra_forward_lines, *stderr_stream));
     m_extra_forward_lines = 0;
-    reposition_cursor(*stderr_stream);
+    TRY(reposition_cursor(*stderr_stream));
+    return {};
 };
 
-void Editor::refresh_display()
+ErrorOr<void> Editor::refresh_display()
 {
     Core::Stream::AllocatingMemoryStream output_stream;
     ScopeGuard flush_stream {
@@ -1339,7 +1353,7 @@ void Editor::refresh_display()
             m_refresh_needed = true;
             swap(m_previous_num_columns, m_num_columns);
             recalculate_origin();
-            cleanup();
+            TRY(cleanup());
             swap(m_previous_num_columns, m_num_columns);
             has_cleaned_up = true;
         }
@@ -1353,22 +1367,22 @@ void Editor::refresh_display()
     if (m_origin_row + current_num_lines > m_num_lines) {
         if (current_num_lines > m_num_lines) {
             for (size_t i = 0; i < m_num_lines; ++i)
-                output_stream.write_entire_buffer("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+                TRY(output_stream.write_entire_buffer("\n"sv.bytes()));
             m_origin_row = 0;
         } else {
             auto old_origin_row = m_origin_row;
             m_origin_row = m_num_lines - current_num_lines + 1;
             for (size_t i = 0; i < old_origin_row - m_origin_row; ++i)
-                output_stream.write_entire_buffer("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+                TRY(output_stream.write_entire_buffer("\n"sv.bytes()));
         }
     }
     // Do not call hook on pure cursor movement.
     if (m_cached_prompt_valid && !m_refresh_needed && m_pending_chars.size() == 0) {
         // Probably just moving around.
-        reposition_cursor(output_stream);
+        TRY(reposition_cursor(output_stream));
         m_cached_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks);
         m_drawn_end_of_line_offset = m_buffer.size();
-        return;
+        return {};
     }
 
     if (on_display_refresh)
@@ -1378,17 +1392,17 @@ void Editor::refresh_display()
         if (!m_refresh_needed && m_cursor == m_buffer.size()) {
             // Just write the characters out and continue,
             // no need to refresh the entire line.
-            output_stream.write_entire_buffer(m_pending_chars).release_value_but_fixme_should_propagate_errors();
+            TRY(output_stream.write_entire_buffer(m_pending_chars));
             m_pending_chars.clear();
             m_drawn_cursor = m_cursor;
             m_drawn_end_of_line_offset = m_buffer.size();
             m_cached_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks);
             m_drawn_spans = m_current_spans;
-            return;
+            return {};
         }
     }
 
-    auto apply_styles = [&, empty_styles = HashMap<u32, Style> {}](size_t i) {
+    auto apply_styles = [&, empty_styles = HashMap<u32, Style> {}](size_t i) -> ErrorOr<void> {
         auto ends = m_current_spans.m_spans_ending.get(i).value_or(empty_styles);
         auto starts = m_current_spans.m_spans_starting.get(i).value_or(empty_styles);
 
@@ -1405,11 +1419,11 @@ void Editor::refresh_display()
                 style.unify_with(applicable_style.value);
 
             // Disable any style that should be turned off.
-            VT::apply_style(style, output_stream, false);
+            TRY(VT::apply_style(style, output_stream, false));
 
             // Reapply styles for overlapping spans that include this one.
             style = find_applicable_style(i);
-            VT::apply_style(style, output_stream, true);
+            TRY(VT::apply_style(style, output_stream, true));
         }
         if (starts.size() || anchored_starts.size()) {
             Style style;
@@ -1421,8 +1435,10 @@ void Editor::refresh_display()
                 style.unify_with(applicable_style.value);
 
             // Set new styles.
-            VT::apply_style(style, output_stream, true);
+            TRY(VT::apply_style(style, output_stream, true));
         }
+
+        return {};
     };
 
     auto print_character_at = [&](size_t i) {
@@ -1445,7 +1461,7 @@ void Editor::refresh_display()
         } else {
             c = m_buffer[i];
         }
-        auto print_single_character = [&](auto c) {
+        auto print_single_character = [&](auto c) -> ErrorOr<void> {
             StringBuilder builder;
             bool should_print_masked = is_ascii_control(c) && c != '\n';
             bool should_print_caret = c < 64 && should_print_masked;
@@ -1457,30 +1473,32 @@ void Editor::refresh_display()
                 builder.append(Utf32View { &c, 1 });
 
             if (should_print_masked)
-                output_stream.write_entire_buffer("\033[7m"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+                TRY(output_stream.write_entire_buffer("\033[7m"sv.bytes()));
 
-            output_stream.write_entire_buffer(builder.string_view().bytes()).release_value_but_fixme_should_propagate_errors();
+            TRY(output_stream.write_entire_buffer(builder.string_view().bytes()));
 
             if (should_print_masked)
-                output_stream.write_entire_buffer("\033[27m"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+                TRY(output_stream.write_entire_buffer("\033[27m"sv.bytes()));
+
+            return {};
         };
         c.visit(
-            [&](u32 c) { print_single_character(c); },
-            [&](auto& view) { for (auto c : view) print_single_character(c); });
+            [&](u32 c) { print_single_character(c).release_value_but_fixme_should_propagate_errors(); },
+            [&](auto& view) { for (auto c : view) print_single_character(c).release_value_but_fixme_should_propagate_errors(); });
     };
 
     // If there have been no changes to previous sections of the line (style or text)
     // just append the new text with the appropriate styles.
     if (!m_always_refresh && m_cached_prompt_valid && m_chars_touched_in_the_middle == 0 && m_drawn_spans.contains_up_to_offset(m_current_spans, m_drawn_cursor)) {
         auto initial_style = find_applicable_style(m_drawn_end_of_line_offset);
-        VT::apply_style(initial_style, output_stream);
+        TRY(VT::apply_style(initial_style, output_stream));
 
         for (size_t i = m_drawn_end_of_line_offset; i < m_buffer.size(); ++i) {
-            apply_styles(i);
+            TRY(apply_styles(i));
             print_character_at(i);
         }
 
-        VT::apply_style(Style::reset_style(), output_stream);
+        TRY(VT::apply_style(Style::reset_style(), output_stream));
         m_pending_chars.clear();
         m_refresh_needed = false;
         m_cached_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks);
@@ -1489,7 +1507,7 @@ void Editor::refresh_display()
         m_drawn_end_of_line_offset = m_buffer.size();
 
         // No need to reposition the cursor, the cursor is already where it needs to be.
-        return;
+        return {};
     }
 
     if constexpr (LINE_EDITOR_DEBUG) {
@@ -1514,20 +1532,20 @@ void Editor::refresh_display()
 
     // Ouch, reflow entire line.
     if (!has_cleaned_up) {
-        cleanup();
+        TRY(cleanup());
     }
-    VT::move_absolute(m_origin_row, m_origin_column, output_stream);
+    TRY(VT::move_absolute(m_origin_row, m_origin_column, output_stream));
 
-    output_stream.write_entire_buffer(m_new_prompt.bytes()).release_value_but_fixme_should_propagate_errors();
+    TRY(output_stream.write_entire_buffer(m_new_prompt.bytes()));
 
-    VT::clear_to_end_of_line(output_stream);
+    TRY(VT::clear_to_end_of_line(output_stream));
     StringBuilder builder;
     for (size_t i = 0; i < m_buffer.size(); ++i) {
-        apply_styles(i);
+        TRY(apply_styles(i));
         print_character_at(i);
     }
 
-    VT::apply_style(Style::reset_style(), output_stream); // don't bleed to EOL
+    TRY(VT::apply_style(Style::reset_style(), output_stream)); // don't bleed to EOL
 
     m_pending_chars.clear();
     m_refresh_needed = false;
@@ -1537,7 +1555,8 @@ void Editor::refresh_display()
     m_drawn_end_of_line_offset = m_buffer.size();
     m_cached_prompt_valid = true;
 
-    reposition_cursor(output_stream);
+    TRY(reposition_cursor(output_stream));
+    return {};
 }
 
 void Editor::strip_styles(bool strip_anchored)
@@ -1555,7 +1574,7 @@ void Editor::strip_styles(bool strip_anchored)
     m_refresh_needed = true;
 }
 
-void Editor::reposition_cursor(Core::Stream::Stream& stream, bool to_end)
+ErrorOr<void> Editor::reposition_cursor(Core::Stream::Stream& stream, bool to_end)
 {
     auto cursor = m_cursor;
     auto saved_cursor = m_cursor;
@@ -1571,17 +1590,18 @@ void Editor::reposition_cursor(Core::Stream::Stream& stream, bool to_end)
     ensure_free_lines_from_origin(line);
 
     VERIFY(column + m_origin_column <= m_num_columns);
-    VT::move_absolute(line + m_origin_row, column + m_origin_column, stream);
+    TRY(VT::move_absolute(line + m_origin_row, column + m_origin_column, stream));
 
     m_cursor = saved_cursor;
+    return {};
 }
 
-void VT::move_absolute(u32 row, u32 col, Core::Stream::Stream& stream)
+ErrorOr<void> VT::move_absolute(u32 row, u32 col, Core::Stream::Stream& stream)
 {
-    stream.write_entire_buffer(DeprecatedString::formatted("\033[{};{}H", row, col).bytes()).release_value_but_fixme_should_propagate_errors();
+    return stream.write_entire_buffer(DeprecatedString::formatted("\033[{};{}H", row, col).bytes());
 }
 
-void VT::move_relative(int row, int col, Core::Stream::Stream& stream)
+ErrorOr<void> VT::move_relative(int row, int col, Core::Stream::Stream& stream)
 {
     char x_op = 'A', y_op = 'D';
 
@@ -1595,9 +1615,11 @@ void VT::move_relative(int row, int col, Core::Stream::Stream& stream)
         col = -col;
 
     if (row > 0)
-        stream.write_entire_buffer(DeprecatedString::formatted("\033[{}{}", row, x_op).bytes()).release_value_but_fixme_should_propagate_errors();
+        TRY(stream.write_entire_buffer(DeprecatedString::formatted("\033[{}{}", row, x_op).bytes()));
     if (col > 0)
-        stream.write_entire_buffer(DeprecatedString::formatted("\033[{}{}", col, y_op).bytes()).release_value_but_fixme_should_propagate_errors();
+        TRY(stream.write_entire_buffer(DeprecatedString::formatted("\033[{}{}", col, y_op).bytes()));
+
+    return {};
 }
 
 Style Editor::find_applicable_style(size_t offset) const
@@ -1731,53 +1753,56 @@ DeprecatedString Style::to_deprecated_string() const
     return builder.build();
 }
 
-void VT::apply_style(Style const& style, Core::Stream::Stream& stream, bool is_starting)
+ErrorOr<void> VT::apply_style(Style const& style, Core::Stream::Stream& stream, bool is_starting)
 {
     if (is_starting) {
-        stream.write_entire_buffer(DeprecatedString::formatted("\033[{};{};{}m{}{}{}",
-                                       style.bold() ? 1 : 22,
-                                       style.underline() ? 4 : 24,
-                                       style.italic() ? 3 : 23,
-                                       style.background().to_vt_escape(),
-                                       style.foreground().to_vt_escape(),
-                                       style.hyperlink().to_vt_escape(true))
-                                       .bytes())
-            .release_value_but_fixme_should_propagate_errors();
+        TRY(stream.write_entire_buffer(DeprecatedString::formatted("\033[{};{};{}m{}{}{}",
+            style.bold() ? 1 : 22,
+            style.underline() ? 4 : 24,
+            style.italic() ? 3 : 23,
+            style.background().to_vt_escape(),
+            style.foreground().to_vt_escape(),
+            style.hyperlink().to_vt_escape(true))
+                                           .bytes()));
     } else {
-        stream.write_entire_buffer(style.hyperlink().to_vt_escape(false).bytes()).release_value_but_fixme_should_propagate_errors();
+        TRY(stream.write_entire_buffer(style.hyperlink().to_vt_escape(false).bytes()));
     }
+
+    return {};
 }
 
-void VT::clear_lines(size_t count_above, size_t count_below, Core::Stream::Stream& stream)
+ErrorOr<void> VT::clear_lines(size_t count_above, size_t count_below, Core::Stream::Stream& stream)
 {
     if (count_below + count_above == 0) {
-        stream.write_entire_buffer("\033[2K"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+        TRY(stream.write_entire_buffer("\033[2K"sv.bytes()));
     } else {
         // Go down count_below lines.
         if (count_below > 0)
-            stream.write_entire_buffer(DeprecatedString::formatted("\033[{}B", count_below).bytes()).release_value_but_fixme_should_propagate_errors();
+            TRY(stream.write_entire_buffer(DeprecatedString::formatted("\033[{}B", count_below).bytes()));
         // Then clear lines going upwards.
         for (size_t i = count_below + count_above; i > 0; --i) {
-            stream.write_entire_buffer("\033[2K"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+            TRY(stream.write_entire_buffer("\033[2K"sv.bytes()));
             if (i != 1)
-                stream.write_entire_buffer("\033[A"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+                TRY(stream.write_entire_buffer("\033[A"sv.bytes()));
         }
     }
+
+    return {};
 }
 
-void VT::save_cursor(Core::Stream::Stream& stream)
+ErrorOr<void> VT::save_cursor(Core::Stream::Stream& stream)
 {
-    stream.write_entire_buffer("\033[s"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+    return stream.write_entire_buffer("\033[s"sv.bytes());
 }
 
-void VT::restore_cursor(Core::Stream::Stream& stream)
+ErrorOr<void> VT::restore_cursor(Core::Stream::Stream& stream)
 {
-    stream.write_entire_buffer("\033[u"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+    return stream.write_entire_buffer("\033[u"sv.bytes());
 }
 
-void VT::clear_to_end_of_line(Core::Stream::Stream& stream)
+ErrorOr<void> VT::clear_to_end_of_line(Core::Stream::Stream& stream)
 {
-    stream.write_entire_buffer("\033[K"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+    return stream.write_entire_buffer("\033[K"sv.bytes());
 }
 
 enum VTState {

+ 11 - 11
Userland/Libraries/LibLine/Editor.h

@@ -183,8 +183,8 @@ public:
 
 #undef __ENUMERATE_EDITOR_INTERNAL_FUNCTION
 
-    void interrupted();
-    void resized();
+    ErrorOr<void> interrupted();
+    ErrorOr<void> resized();
 
     size_t cursor() const { return m_cursor; }
     void set_cursor(size_t cursor)
@@ -252,7 +252,7 @@ public:
             [this, previous_value] {
                 m_prohibit_input_processing = previous_value;
                 if (!m_prohibit_input_processing && m_have_unprocessed_read_event)
-                    handle_read_event();
+                    handle_read_event().release_value_but_fixme_should_propagate_errors();
             }
         };
     }
@@ -270,10 +270,10 @@ private:
     // FIXME: Port to Core::Property
     void save_to(JsonObject&);
 
-    void try_update_once();
+    ErrorOr<void> try_update_once();
     void handle_interrupt_event();
-    void handle_read_event();
-    void handle_resize_event(bool reset_origin);
+    ErrorOr<void> handle_read_event();
+    ErrorOr<void> handle_resize_event(bool reset_origin);
 
     void ensure_free_lines_from_origin(size_t count);
 
@@ -326,10 +326,10 @@ private:
         m_paste_buffer.clear_with_capacity();
     }
 
-    void refresh_display();
-    void cleanup();
-    void cleanup_suggestions();
-    void really_quit_event_loop();
+    ErrorOr<void> refresh_display();
+    ErrorOr<void> cleanup();
+    ErrorOr<void> cleanup_suggestions();
+    ErrorOr<void> really_quit_event_loop();
 
     void restore()
     {
@@ -393,7 +393,7 @@ private:
     }
 
     void recalculate_origin();
-    void reposition_cursor(Core::Stream::Stream&, bool to_end = false);
+    ErrorOr<void> reposition_cursor(Core::Stream::Stream&, bool to_end = false);
 
     struct CodepointRange {
         size_t start { 0 };

+ 10 - 14
Userland/Libraries/LibLine/InternalFunctions.cpp

@@ -159,7 +159,7 @@ void Editor::finish_edit()
     if (!m_always_refresh) {
         m_input_error = Error::Eof;
         finish();
-        really_quit_event_loop();
+        really_quit_event_loop().release_value_but_fixme_should_propagate_errors();
     }
 }
 
@@ -233,7 +233,7 @@ void Editor::enter_search()
 
         m_search_editor->on_display_refresh = [this](Editor& search_editor) {
             // Remove the search editor prompt before updating ourselves (this avoids artifacts when we move the search editor around).
-            search_editor.cleanup();
+            search_editor.cleanup().release_value_but_fixme_should_propagate_errors();
 
             StringBuilder builder;
             builder.append(Utf32View { search_editor.buffer().data(), search_editor.buffer().size() });
@@ -244,7 +244,7 @@ void Editor::enter_search()
                 m_cursor = 0;
             }
 
-            refresh_display();
+            refresh_display().release_value_but_fixme_should_propagate_errors();
 
             // Move the search prompt below ours and tell it to redraw itself.
             auto prompt_end_line = current_prompt_metrics().lines_with_addition(m_cached_buffer_metrics, m_num_columns);
@@ -264,7 +264,7 @@ void Editor::enter_search()
             search_editor.finish();
             m_reset_buffer_on_search_end = true;
             search_editor.end_search();
-            search_editor.deferred_invoke([&search_editor] { search_editor.really_quit_event_loop(); });
+            search_editor.deferred_invoke([&search_editor] { search_editor.really_quit_event_loop().release_value_but_fixme_should_propagate_errors(); });
             return false;
         });
 
@@ -293,7 +293,7 @@ void Editor::enter_search()
                 TemporaryChange refresh_change { m_always_refresh, true };
                 set_origin(1, 1);
                 m_refresh_needed = true;
-                refresh_display();
+                refresh_display().release_value_but_fixme_should_propagate_errors();
             }
 
             // move the search prompt below ours
@@ -342,12 +342,12 @@ void Editor::enter_search()
 
         // Manually cleanup the search line.
         auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
-        reposition_cursor(*stderr_stream);
+        reposition_cursor(*stderr_stream).release_value_but_fixme_should_propagate_errors();
         auto search_metrics = actual_rendered_string_metrics(search_string, {});
         auto metrics = actual_rendered_string_metrics(search_prompt, {});
-        VT::clear_lines(0, metrics.lines_with_addition(search_metrics, m_num_columns) + search_end_row - m_origin_row - 1, *stderr_stream);
+        VT::clear_lines(0, metrics.lines_with_addition(search_metrics, m_num_columns) + search_end_row - m_origin_row - 1, *stderr_stream).release_value_but_fixme_should_propagate_errors();
 
-        reposition_cursor(*stderr_stream);
+        reposition_cursor(*stderr_stream).release_value_but_fixme_should_propagate_errors();
 
         m_refresh_needed = true;
         m_cached_prompt_valid = false;
@@ -434,7 +434,7 @@ void Editor::clear_screen()
 {
     warn("\033[3J\033[H\033[2J");
     auto stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
-    VT::move_absolute(1, 1, *stream);
+    VT::move_absolute(1, 1, *stream).release_value_but_fixme_should_propagate_errors();
     set_origin(1, 1);
     m_refresh_needed = true;
     m_cached_prompt_valid = false;
@@ -569,11 +569,7 @@ void Editor::edit_in_external_editor()
     }
 
     {
-        auto file_or_error = Core::Stream::File::open({ file_path, strlen(file_path) }, Core::Stream::OpenMode::Read);
-        if (file_or_error.is_error())
-            return;
-
-        auto file = file_or_error.release_value();
+        auto file = Core::Stream::File::open({ file_path, strlen(file_path) }, Core::Stream::OpenMode::Read).release_value_but_fixme_should_propagate_errors();
         auto contents = file->read_until_eof().release_value_but_fixme_should_propagate_errors();
         StringView data { contents };
         while (data.ends_with('\n'))

+ 9 - 7
Userland/Libraries/LibLine/SuggestionDisplay.h

@@ -18,20 +18,22 @@ class Editor;
 class SuggestionDisplay {
 public:
     virtual ~SuggestionDisplay() = default;
-    virtual void display(SuggestionManager const&) = 0;
-    virtual bool cleanup() = 0;
+    virtual ErrorOr<void> display(SuggestionManager const&) = 0;
+    virtual ErrorOr<bool> cleanup() = 0;
     virtual void finish() = 0;
     virtual void set_initial_prompt_lines(size_t) = 0;
 
-    void redisplay(SuggestionManager const& manager, size_t lines, size_t columns)
+    ErrorOr<void> redisplay(SuggestionManager const& manager, size_t lines, size_t columns)
     {
         if (m_is_showing_suggestions) {
-            cleanup();
+            TRY(cleanup());
             set_vt_size(lines, columns);
-            display(manager);
+            TRY(display(manager));
         } else {
             set_vt_size(lines, columns);
         }
+
+        return {};
     }
 
     virtual void set_vt_size(size_t lines, size_t columns) = 0;
@@ -62,8 +64,8 @@ public:
     {
     }
     virtual ~XtermSuggestionDisplay() override = default;
-    virtual void display(SuggestionManager const&) override;
-    virtual bool cleanup() override;
+    virtual ErrorOr<void> display(SuggestionManager const&) override;
+    virtual ErrorOr<bool> cleanup() override;
     virtual void finish() override
     {
         m_pages.clear();

+ 2 - 2
Userland/Libraries/LibLine/SuggestionManager.cpp

@@ -182,13 +182,13 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion
     return result;
 }
 
-size_t SuggestionManager::for_each_suggestion(Function<IterationDecision(CompletionSuggestion const&, size_t)> callback) const
+ErrorOr<size_t> SuggestionManager::for_each_suggestion(Function<ErrorOr<IterationDecision>(CompletionSuggestion const&, size_t)> callback) const
 {
     size_t start_index { 0 };
     for (auto& suggestion : m_suggestions) {
         if (start_index++ < m_last_displayed_suggestion_index)
             continue;
-        if (callback(suggestion, start_index - 1) == IterationDecision::Break)
+        if (TRY(callback(suggestion, start_index - 1)) == IterationDecision::Break)
             break;
     }
     return start_index;

+ 1 - 1
Userland/Libraries/LibLine/SuggestionManager.h

@@ -78,7 +78,7 @@ public:
     size_t next_index() const { return m_next_suggestion_index; }
     void set_start_index(size_t index) const { m_last_displayed_suggestion_index = index; }
 
-    size_t for_each_suggestion(Function<IterationDecision(CompletionSuggestion const&, size_t)>) const;
+    ErrorOr<size_t> for_each_suggestion(Function<ErrorOr<IterationDecision>(CompletionSuggestion const&, size_t)>) const;
 
     enum CompletionMode {
         DontComplete,

+ 7 - 7
Userland/Libraries/LibLine/VT.h

@@ -13,13 +13,13 @@
 namespace Line {
 namespace VT {
 
-void save_cursor(Core::Stream::Stream&);
-void restore_cursor(Core::Stream::Stream&);
-void clear_to_end_of_line(Core::Stream::Stream&);
-void clear_lines(size_t count_above, size_t count_below, Core::Stream::Stream&);
-void move_relative(int x, int y, Core::Stream::Stream&);
-void move_absolute(u32 x, u32 y, Core::Stream::Stream&);
-void apply_style(Style const&, Core::Stream::Stream&, bool is_starting = true);
+ErrorOr<void> save_cursor(Core::Stream::Stream&);
+ErrorOr<void> restore_cursor(Core::Stream::Stream&);
+ErrorOr<void> clear_to_end_of_line(Core::Stream::Stream&);
+ErrorOr<void> clear_lines(size_t count_above, size_t count_below, Core::Stream::Stream&);
+ErrorOr<void> move_relative(int x, int y, Core::Stream::Stream&);
+ErrorOr<void> move_absolute(u32 x, u32 y, Core::Stream::Stream&);
+ErrorOr<void> apply_style(Style const&, Core::Stream::Stream&, bool is_starting = true);
 
 }
 }

+ 29 - 27
Userland/Libraries/LibLine/XtermSuggestionDisplay.cpp

@@ -13,30 +13,30 @@
 
 namespace Line {
 
-void XtermSuggestionDisplay::display(SuggestionManager const& manager)
+ErrorOr<void> XtermSuggestionDisplay::display(SuggestionManager const& manager)
 {
     did_display();
 
-    auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
+    auto stderr_stream = TRY(Core::Stream::File::standard_error());
 
     size_t longest_suggestion_length = 0;
     size_t longest_suggestion_byte_length = 0;
     size_t longest_suggestion_byte_length_without_trivia = 0;
 
     manager.set_start_index(0);
-    manager.for_each_suggestion([&](auto& suggestion, auto) {
+    TRY(manager.for_each_suggestion([&](auto& suggestion, auto) {
         longest_suggestion_length = max(longest_suggestion_length, suggestion.text_view.length() + suggestion.display_trivia_view.length());
         longest_suggestion_byte_length = max(longest_suggestion_byte_length, suggestion.text_string.length() + suggestion.display_trivia_string.length());
         longest_suggestion_byte_length_without_trivia = max(longest_suggestion_byte_length_without_trivia, suggestion.text_string.length());
         return IterationDecision::Continue;
-    });
+    }));
 
     size_t num_printed = 0;
     size_t lines_used = 1;
 
-    VT::save_cursor(*stderr_stream);
-    VT::clear_lines(0, m_lines_used_for_last_suggestions, *stderr_stream);
-    VT::restore_cursor(*stderr_stream);
+    TRY(VT::save_cursor(*stderr_stream));
+    TRY(VT::clear_lines(0, m_lines_used_for_last_suggestions, *stderr_stream));
+    TRY(VT::restore_cursor(*stderr_stream));
 
     auto spans_entire_line { false };
     Vector<StringMetrics::LineMetrics> lines;
@@ -50,12 +50,12 @@ void XtermSuggestionDisplay::display(SuggestionManager const& manager)
         // the suggestion list to fit in the prompt line.
         auto start = max_line_count - m_prompt_lines_at_suggestion_initiation;
         for (size_t i = start; i < max_line_count; ++i)
-            stderr_stream->write("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+            TRY(stderr_stream->write("\n"sv.bytes()));
         lines_used += max_line_count;
         longest_suggestion_length = 0;
     }
 
-    VT::move_absolute(max_line_count + m_origin_row, 1, *stderr_stream);
+    TRY(VT::move_absolute(max_line_count + m_origin_row, 1, *stderr_stream));
 
     if (m_pages.is_empty()) {
         size_t num_printed = 0;
@@ -63,7 +63,7 @@ void XtermSuggestionDisplay::display(SuggestionManager const& manager)
         // Cache the pages.
         manager.set_start_index(0);
         size_t page_start = 0;
-        manager.for_each_suggestion([&](auto& suggestion, auto index) {
+        TRY(manager.for_each_suggestion([&](auto& suggestion, auto index) {
             size_t next_column = num_printed + suggestion.text_view.length() + longest_suggestion_length + 2;
             if (next_column > m_num_columns) {
                 auto lines = (suggestion.text_view.length() + m_num_columns - 1) / m_num_columns;
@@ -84,7 +84,7 @@ void XtermSuggestionDisplay::display(SuggestionManager const& manager)
                 num_printed += longest_suggestion_length + 2;
 
             return IterationDecision::Continue;
-        });
+        }));
         // Append the last page.
         m_pages.append({ page_start, manager.count() });
     }
@@ -92,13 +92,13 @@ void XtermSuggestionDisplay::display(SuggestionManager const& manager)
     auto page_index = fit_to_page_boundary(manager.next_index());
 
     manager.set_start_index(m_pages[page_index].start);
-    manager.for_each_suggestion([&](auto& suggestion, auto index) {
+    TRY(manager.for_each_suggestion([&](auto& suggestion, auto index) -> ErrorOr<IterationDecision> {
         size_t next_column = num_printed + suggestion.text_view.length() + longest_suggestion_length + 2;
 
         if (next_column > m_num_columns) {
             auto lines = (suggestion.text_view.length() + m_num_columns - 1) / m_num_columns;
             lines_used += lines;
-            stderr_stream->write("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors();
+            TRY(stderr_stream->write("\n"sv.bytes()));
             num_printed = 0;
         }
 
@@ -109,23 +109,23 @@ void XtermSuggestionDisplay::display(SuggestionManager const& manager)
 
         // Only apply color to the selection if something is *actually* added to the buffer.
         if (manager.is_current_suggestion_complete() && index == manager.next_index()) {
-            VT::apply_style({ Style::Foreground(Style::XtermColor::Blue) }, *stderr_stream);
+            TRY(VT::apply_style({ Style::Foreground(Style::XtermColor::Blue) }, *stderr_stream));
         }
 
         if (spans_entire_line) {
             num_printed += m_num_columns;
-            stderr_stream->write(suggestion.text_string.bytes()).release_value_but_fixme_should_propagate_errors();
-            stderr_stream->write(suggestion.display_trivia_string.bytes()).release_value_but_fixme_should_propagate_errors();
+            TRY(stderr_stream->write(suggestion.text_string.bytes()));
+            TRY(stderr_stream->write(suggestion.display_trivia_string.bytes()));
         } else {
             auto field = DeprecatedString::formatted("{: <{}}  {}", suggestion.text_string, longest_suggestion_byte_length_without_trivia, suggestion.display_trivia_string);
-            stderr_stream->write(DeprecatedString::formatted("{: <{}}", field, longest_suggestion_byte_length + 2).bytes()).release_value_but_fixme_should_propagate_errors();
+            TRY(stderr_stream->write(DeprecatedString::formatted("{: <{}}", field, longest_suggestion_byte_length + 2).bytes()));
             num_printed += longest_suggestion_length + 2;
         }
 
         if (manager.is_current_suggestion_complete() && index == manager.next_index())
-            VT::apply_style(Style::reset_style(), *stderr_stream);
+            TRY(VT::apply_style(Style::reset_style(), *stderr_stream));
         return IterationDecision::Continue;
-    });
+    }));
 
     m_lines_used_for_last_suggestions = lines_used;
 
@@ -144,23 +144,25 @@ void XtermSuggestionDisplay::display(SuggestionManager const& manager)
 
         if (string.length() > m_num_columns - 1) {
             // This would overflow into the next line, so just don't print an indicator.
-            return;
+            return {};
         }
 
-        VT::move_absolute(m_origin_row + lines_used, m_num_columns - string.length() - 1, *stderr_stream);
-        VT::apply_style({ Style::Background(Style::XtermColor::Green) }, *stderr_stream);
-        stderr_stream->write(string.bytes()).release_value_but_fixme_should_propagate_errors();
-        VT::apply_style(Style::reset_style(), *stderr_stream);
+        TRY(VT::move_absolute(m_origin_row + lines_used, m_num_columns - string.length() - 1, *stderr_stream));
+        TRY(VT::apply_style({ Style::Background(Style::XtermColor::Green) }, *stderr_stream));
+        TRY(stderr_stream->write(string.bytes()));
+        TRY(VT::apply_style(Style::reset_style(), *stderr_stream));
     }
+
+    return {};
 }
 
-bool XtermSuggestionDisplay::cleanup()
+ErrorOr<bool> XtermSuggestionDisplay::cleanup()
 {
     did_cleanup();
 
     if (m_lines_used_for_last_suggestions) {
-        auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors();
-        VT::clear_lines(0, m_lines_used_for_last_suggestions, *stderr_stream);
+        auto stderr_stream = TRY(Core::Stream::File::standard_error());
+        TRY(VT::clear_lines(0, m_lines_used_for_last_suggestions, *stderr_stream));
         m_lines_used_for_last_suggestions = 0;
         return true;
     }