1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 04:48:14 +00:00

LibLine: Do a whole bunch of internal error propagation

This commit is contained in:
Tim Schumacher 2023-01-13 12:29:46 +01:00 committed by Sam Atkins
parent f9f1e1dd49
commit 40cb41a16c
8 changed files with 212 additions and 187 deletions

View file

@ -566,11 +566,11 @@ void Editor::initialize()
if (m_configuration.m_signal_mode == Configuration::WithSignalHandlers) { if (m_configuration.m_signal_mode == Configuration::WithSignalHandlers) {
m_signal_handlers.append(Core::EventLoop::register_signal(SIGINT, [this](int) { 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) { 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; m_termios = termios;
} }
void Editor::interrupted() ErrorOr<void> Editor::interrupted()
{ {
if (m_is_searching) if (m_is_searching)
return m_search_editor->interrupted(); return m_search_editor->interrupted();
if (!m_is_editing) if (!m_is_editing)
return; return {};
m_was_interrupted = true; m_was_interrupted = true;
handle_interrupt_event(); handle_interrupt_event();
if (!m_finish || !m_previous_interrupt_was_handled_as_interrupt) if (!m_finish || !m_previous_interrupt_was_handled_as_interrupt)
return; return {};
m_finish = false; m_finish = false;
{ {
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); TRY(reposition_cursor(*stderr_stream, true));
if (m_suggestion_display->cleanup()) if (TRY(m_suggestion_display->cleanup()))
reposition_cursor(*stderr_stream, true); TRY(reposition_cursor(*stderr_stream, true));
stderr_stream->write_entire_buffer("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors(); TRY(stderr_stream->write_entire_buffer("\n"sv.bytes()));
} }
m_buffer.clear(); m_buffer.clear();
m_chars_touched_in_the_middle = buffer().size(); m_chars_touched_in_the_middle = buffer().size();
@ -615,9 +615,10 @@ void Editor::interrupted()
m_notifier->set_enabled(false); m_notifier->set_enabled(false);
m_notifier = nullptr; m_notifier = nullptr;
Core::EventLoop::current().quit(Retry); Core::EventLoop::current().quit(Retry);
return {};
} }
void Editor::resized() ErrorOr<void> Editor::resized()
{ {
m_was_resized = true; m_was_resized = true;
m_previous_num_columns = m_num_columns; m_previous_num_columns = m_num_columns;
@ -626,42 +627,47 @@ void Editor::resized()
if (!m_has_origin_reset_scheduled) { if (!m_has_origin_reset_scheduled) {
// Reset the origin, but make sure it doesn't blow up if we can't read it // Reset the origin, but make sure it doesn't blow up if we can't read it
if (set_origin(false)) { if (set_origin(false)) {
handle_resize_event(false); TRY(handle_resize_event(false));
} else { } 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; 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; m_has_origin_reset_scheduled = false;
if (reset_origin && !set_origin(false)) { if (reset_origin && !set_origin(false)) {
m_has_origin_reset_scheduled = true; 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); 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); TRY(reposition_cursor(*stderr_stream, true));
m_suggestion_display->redisplay(m_suggestion_manager, m_num_lines, m_num_columns); TRY(m_suggestion_display->redisplay(m_suggestion_manager, m_num_lines, m_num_columns));
m_origin_row = m_suggestion_display->origin_row(); m_origin_row = m_suggestion_display->origin_row();
reposition_cursor(*stderr_stream); TRY(reposition_cursor(*stderr_stream));
if (m_is_searching) 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; m_finish = false;
{ {
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); TRY(reposition_cursor(*stderr_stream, true));
stderr_stream->write_entire_buffer("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors(); TRY(stderr_stream->write_entire_buffer("\n"sv.bytes()));
} }
auto string = line(); auto string = line();
m_buffer.clear(); m_buffer.clear();
@ -675,6 +681,7 @@ void Editor::really_quit_event_loop()
m_notifier->set_enabled(false); m_notifier->set_enabled(false);
m_notifier = nullptr; m_notifier = nullptr;
Core::EventLoop::current().quit(Exit); Core::EventLoop::current().quit(Exit);
return {};
} }
auto Editor::get_line(DeprecatedString const& prompt) -> Result<DeprecatedString, Editor::Error> 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) for (size_t i = 0; i < prompt_lines; ++i)
stderr_stream->write_entire_buffer("\n"sv.bytes()).release_value_but_fixme_should_propagate_errors(); 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(); set_origin();
m_history_cursor = m_history.size(); m_history_cursor = m_history.size();
refresh_display(); refresh_display().release_value_but_fixme_should_propagate_errors();
Core::EventLoop loop; Core::EventLoop loop;
m_notifier = Core::Notifier::construct(STDIN_FILENO, Core::Notifier::Read); 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()) 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) if (loop.exec() == Retry)
return get_line(prompt); return get_line(prompt);
@ -770,21 +777,23 @@ void Editor::save_to(JsonObject& object)
object.set("used_display_area", move(display_area)); object.set("used_display_area", move(display_area));
} }
void Editor::try_update_once() ErrorOr<void> Editor::try_update_once()
{ {
if (m_was_interrupted) { if (m_was_interrupted) {
handle_interrupt_event(); handle_interrupt_event();
} }
handle_read_event(); TRY(handle_read_event());
if (m_always_refresh) if (m_always_refresh)
m_refresh_needed = true; m_refresh_needed = true;
refresh_display(); TRY(refresh_display());
if (m_finish) if (m_finish)
really_quit_event_loop(); TRY(really_quit_event_loop());
return {};
} }
void Editor::handle_interrupt_event() void Editor::handle_interrupt_event()
@ -811,11 +820,11 @@ void Editor::handle_interrupt_event()
finish(); finish();
} }
void Editor::handle_read_event() ErrorOr<void> Editor::handle_read_event()
{ {
if (m_prohibit_input_processing) { if (m_prohibit_input_processing) {
m_have_unprocessed_read_event = true; m_have_unprocessed_read_event = true;
return; return {};
} }
auto prohibit_scope = prohibit_input(); auto prohibit_scope = prohibit_input();
@ -830,14 +839,14 @@ void Editor::handle_read_event()
if (errno == EINTR) { if (errno == EINTR) {
if (!m_was_interrupted) { if (!m_was_interrupted) {
if (m_was_resized) if (m_was_resized)
return; return {};
finish(); finish();
return; return {};
} }
handle_interrupt_event(); handle_interrupt_event();
return; return {};
} }
ScopedValueRollback errno_restorer(errno); ScopedValueRollback errno_restorer(errno);
@ -845,7 +854,7 @@ void Editor::handle_read_event()
m_input_error = Error::ReadFailure; m_input_error = Error::ReadFailure;
finish(); finish();
return; return {};
} }
m_incomplete_data.append(keybuf, nread); m_incomplete_data.append(keybuf, nread);
@ -854,7 +863,7 @@ void Editor::handle_read_event()
if (available_bytes == 0) { if (available_bytes == 0) {
m_input_error = Error::Empty; m_input_error = Error::Empty;
finish(); finish();
return; return {};
} }
auto reverse_tab = false; auto reverse_tab = false;
@ -900,7 +909,7 @@ void Editor::handle_read_event()
default: { default: {
m_callback_machine.key_pressed(*this, { code_point, Key::Alt }); m_callback_machine.key_pressed(*this, { code_point, Key::Alt });
m_state = InputState::Free; m_state = InputState::Free;
cleanup_suggestions(); TRY(cleanup_suggestions());
continue; continue;
} }
} }
@ -958,7 +967,7 @@ void Editor::handle_read_event()
reverse_tab = true; reverse_tab = true;
break; break;
} }
cleanup_suggestions(); TRY(cleanup_suggestions());
switch (csi_final) { switch (csi_final) {
case 'A': // ^[[A: arrow up 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. // 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. // 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. // 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) for (auto& view : completion_result.insert)
insert(view); insert(view);
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); TRY(reposition_cursor(*stderr_stream));
if (completion_result.style_to_apply.has_value()) { if (completion_result.style_to_apply.has_value()) {
// Apply the style of the last suggestion. // 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_times_tab_pressed > 1 && m_suggestion_manager.count() > 0) {
if (m_suggestion_display->cleanup()) if (TRY(m_suggestion_display->cleanup()))
reposition_cursor(*stderr_stream); TRY(reposition_cursor(*stderr_stream));
m_suggestion_display->set_initial_prompt_lines(m_prompt_lines_at_suggestion_initiation); 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(); 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 have none, or just one suggestion,
// we should just commit that and continue // we should just commit that and continue
// after it, as if it were auto-completed. // after it, as if it were auto-completed.
reposition_cursor(*stderr_stream, true); TRY(reposition_cursor(*stderr_stream, true));
cleanup_suggestions(); TRY(cleanup_suggestions());
m_remembered_suggestion_static_data.clear_with_capacity(); m_remembered_suggestion_static_data.clear_with_capacity();
} }
continue; 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. // If we got here, manually cleanup the suggestions and then insert the new code point.
m_remembered_suggestion_static_data.clear_with_capacity(); m_remembered_suggestion_static_data.clear_with_capacity();
suggestion_cleanup.disarm(); suggestion_cleanup.disarm();
cleanup_suggestions(); TRY(cleanup_suggestions());
insert(code_point); insert(code_point);
} }
@ -1221,10 +1230,12 @@ void Editor::handle_read_event()
} }
if (!m_incomplete_data.is_empty() && !m_finish) 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) { if (m_times_tab_pressed != 0) {
// Apply the style of the last suggestion. // 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); 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, // We probably have some suggestions drawn,
// let's clean them up. // let's clean them up.
if (m_suggestion_display->cleanup()) { if (TRY(m_suggestion_display->cleanup())) {
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); TRY(reposition_cursor(*stderr_stream));
m_refresh_needed = true; m_refresh_needed = true;
} }
m_suggestion_manager.reset(); m_suggestion_manager.reset();
m_suggestion_display->finish(); m_suggestion_display->finish();
} }
m_times_tab_pressed = 0; // Safe to say if we get here, the user didn't press TAB 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) 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 // but that will be calculated and applied at the next
// refresh cycle. // refresh cycle.
} }
void Editor::cleanup()
ErrorOr<void> Editor::cleanup()
{ {
auto current_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks); 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); auto new_lines = current_prompt_metrics().lines_with_addition(current_buffer_metrics, m_num_columns);
if (new_lines < m_shown_lines) if (new_lines < m_shown_lines)
m_extra_forward_lines = max(m_shown_lines - new_lines, m_extra_forward_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(); auto stderr_stream = TRY(Core::Stream::File::standard_error());
reposition_cursor(*stderr_stream, true); TRY(reposition_cursor(*stderr_stream, true));
auto current_line = num_lines() - 1; 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; 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; Core::Stream::AllocatingMemoryStream output_stream;
ScopeGuard flush_stream { ScopeGuard flush_stream {
@ -1339,7 +1353,7 @@ void Editor::refresh_display()
m_refresh_needed = true; m_refresh_needed = true;
swap(m_previous_num_columns, m_num_columns); swap(m_previous_num_columns, m_num_columns);
recalculate_origin(); recalculate_origin();
cleanup(); TRY(cleanup());
swap(m_previous_num_columns, m_num_columns); swap(m_previous_num_columns, m_num_columns);
has_cleaned_up = true; has_cleaned_up = true;
} }
@ -1353,22 +1367,22 @@ void Editor::refresh_display()
if (m_origin_row + current_num_lines > m_num_lines) { if (m_origin_row + current_num_lines > m_num_lines) {
if (current_num_lines > m_num_lines) { if (current_num_lines > m_num_lines) {
for (size_t i = 0; i < m_num_lines; ++i) 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; m_origin_row = 0;
} else { } else {
auto old_origin_row = m_origin_row; auto old_origin_row = m_origin_row;
m_origin_row = m_num_lines - current_num_lines + 1; m_origin_row = m_num_lines - current_num_lines + 1;
for (size_t i = 0; i < old_origin_row - m_origin_row; ++i) 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. // Do not call hook on pure cursor movement.
if (m_cached_prompt_valid && !m_refresh_needed && m_pending_chars.size() == 0) { if (m_cached_prompt_valid && !m_refresh_needed && m_pending_chars.size() == 0) {
// Probably just moving around. // 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_cached_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks);
m_drawn_end_of_line_offset = m_buffer.size(); m_drawn_end_of_line_offset = m_buffer.size();
return; return {};
} }
if (on_display_refresh) if (on_display_refresh)
@ -1378,17 +1392,17 @@ void Editor::refresh_display()
if (!m_refresh_needed && m_cursor == m_buffer.size()) { if (!m_refresh_needed && m_cursor == m_buffer.size()) {
// Just write the characters out and continue, // Just write the characters out and continue,
// no need to refresh the entire line. // 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_pending_chars.clear();
m_drawn_cursor = m_cursor; m_drawn_cursor = m_cursor;
m_drawn_end_of_line_offset = m_buffer.size(); m_drawn_end_of_line_offset = m_buffer.size();
m_cached_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks); m_cached_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks);
m_drawn_spans = m_current_spans; 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 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); 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); style.unify_with(applicable_style.value);
// Disable any style that should be turned off. // 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. // Reapply styles for overlapping spans that include this one.
style = find_applicable_style(i); 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()) { if (starts.size() || anchored_starts.size()) {
Style style; Style style;
@ -1421,8 +1435,10 @@ void Editor::refresh_display()
style.unify_with(applicable_style.value); style.unify_with(applicable_style.value);
// Set new styles. // 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) { auto print_character_at = [&](size_t i) {
@ -1445,7 +1461,7 @@ void Editor::refresh_display()
} else { } else {
c = m_buffer[i]; c = m_buffer[i];
} }
auto print_single_character = [&](auto c) { auto print_single_character = [&](auto c) -> ErrorOr<void> {
StringBuilder builder; StringBuilder builder;
bool should_print_masked = is_ascii_control(c) && c != '\n'; bool should_print_masked = is_ascii_control(c) && c != '\n';
bool should_print_caret = c < 64 && should_print_masked; bool should_print_caret = c < 64 && should_print_masked;
@ -1457,30 +1473,32 @@ void Editor::refresh_display()
builder.append(Utf32View { &c, 1 }); builder.append(Utf32View { &c, 1 });
if (should_print_masked) 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) 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( c.visit(
[&](u32 c) { 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); }); [&](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) // If there have been no changes to previous sections of the line (style or text)
// just append the new text with the appropriate styles. // 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)) { 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); 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) { 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); 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_pending_chars.clear();
m_refresh_needed = false; m_refresh_needed = false;
m_cached_buffer_metrics = actual_rendered_string_metrics(buffer_view(), m_current_masks); 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(); m_drawn_end_of_line_offset = m_buffer.size();
// No need to reposition the cursor, the cursor is already where it needs to be. // No need to reposition the cursor, the cursor is already where it needs to be.
return; return {};
} }
if constexpr (LINE_EDITOR_DEBUG) { if constexpr (LINE_EDITOR_DEBUG) {
@ -1514,20 +1532,20 @@ void Editor::refresh_display()
// Ouch, reflow entire line. // Ouch, reflow entire line.
if (!has_cleaned_up) { 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; StringBuilder builder;
for (size_t i = 0; i < m_buffer.size(); ++i) { for (size_t i = 0; i < m_buffer.size(); ++i) {
apply_styles(i); TRY(apply_styles(i));
print_character_at(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_pending_chars.clear();
m_refresh_needed = false; m_refresh_needed = false;
@ -1537,7 +1555,8 @@ void Editor::refresh_display()
m_drawn_end_of_line_offset = m_buffer.size(); m_drawn_end_of_line_offset = m_buffer.size();
m_cached_prompt_valid = true; m_cached_prompt_valid = true;
reposition_cursor(output_stream); TRY(reposition_cursor(output_stream));
return {};
} }
void Editor::strip_styles(bool strip_anchored) void Editor::strip_styles(bool strip_anchored)
@ -1555,7 +1574,7 @@ void Editor::strip_styles(bool strip_anchored)
m_refresh_needed = true; 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 cursor = m_cursor;
auto saved_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); ensure_free_lines_from_origin(line);
VERIFY(column + m_origin_column <= m_num_columns); 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; 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'; 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; col = -col;
if (row > 0) 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) 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 Style Editor::find_applicable_style(size_t offset) const
@ -1731,53 +1753,56 @@ DeprecatedString Style::to_deprecated_string() const
return builder.build(); 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) { if (is_starting) {
stream.write_entire_buffer(DeprecatedString::formatted("\033[{};{};{}m{}{}{}", TRY(stream.write_entire_buffer(DeprecatedString::formatted("\033[{};{};{}m{}{}{}",
style.bold() ? 1 : 22, style.bold() ? 1 : 22,
style.underline() ? 4 : 24, style.underline() ? 4 : 24,
style.italic() ? 3 : 23, style.italic() ? 3 : 23,
style.background().to_vt_escape(), style.background().to_vt_escape(),
style.foreground().to_vt_escape(), style.foreground().to_vt_escape(),
style.hyperlink().to_vt_escape(true)) style.hyperlink().to_vt_escape(true))
.bytes()) .bytes()));
.release_value_but_fixme_should_propagate_errors();
} else { } 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) { 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 { } else {
// Go down count_below lines. // Go down count_below lines.
if (count_below > 0) 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. // Then clear lines going upwards.
for (size_t i = count_below + count_above; i > 0; --i) { 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) 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 { enum VTState {

View file

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

View file

@ -159,7 +159,7 @@ void Editor::finish_edit()
if (!m_always_refresh) { if (!m_always_refresh) {
m_input_error = Error::Eof; m_input_error = Error::Eof;
finish(); 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) { 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). // 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; StringBuilder builder;
builder.append(Utf32View { search_editor.buffer().data(), search_editor.buffer().size() }); builder.append(Utf32View { search_editor.buffer().data(), search_editor.buffer().size() });
@ -244,7 +244,7 @@ void Editor::enter_search()
m_cursor = 0; 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. // 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); 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(); search_editor.finish();
m_reset_buffer_on_search_end = true; m_reset_buffer_on_search_end = true;
search_editor.end_search(); 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; return false;
}); });
@ -293,7 +293,7 @@ void Editor::enter_search()
TemporaryChange refresh_change { m_always_refresh, true }; TemporaryChange refresh_change { m_always_refresh, true };
set_origin(1, 1); set_origin(1, 1);
m_refresh_needed = true; m_refresh_needed = true;
refresh_display(); refresh_display().release_value_but_fixme_should_propagate_errors();
} }
// move the search prompt below ours // move the search prompt below ours
@ -342,12 +342,12 @@ void Editor::enter_search()
// Manually cleanup the search line. // Manually cleanup the search line.
auto stderr_stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors(); 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 search_metrics = actual_rendered_string_metrics(search_string, {});
auto metrics = actual_rendered_string_metrics(search_prompt, {}); 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_refresh_needed = true;
m_cached_prompt_valid = false; m_cached_prompt_valid = false;
@ -434,7 +434,7 @@ void Editor::clear_screen()
{ {
warn("\033[3J\033[H\033[2J"); warn("\033[3J\033[H\033[2J");
auto stream = Core::Stream::File::standard_error().release_value_but_fixme_should_propagate_errors(); 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); set_origin(1, 1);
m_refresh_needed = true; m_refresh_needed = true;
m_cached_prompt_valid = false; 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); auto file = Core::Stream::File::open({ file_path, strlen(file_path) }, Core::Stream::OpenMode::Read).release_value_but_fixme_should_propagate_errors();
if (file_or_error.is_error())
return;
auto file = file_or_error.release_value();
auto contents = file->read_until_eof().release_value_but_fixme_should_propagate_errors(); auto contents = file->read_until_eof().release_value_but_fixme_should_propagate_errors();
StringView data { contents }; StringView data { contents };
while (data.ends_with('\n')) while (data.ends_with('\n'))

View file

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

View file

@ -182,13 +182,13 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion
return result; 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 }; size_t start_index { 0 };
for (auto& suggestion : m_suggestions) { for (auto& suggestion : m_suggestions) {
if (start_index++ < m_last_displayed_suggestion_index) if (start_index++ < m_last_displayed_suggestion_index)
continue; continue;
if (callback(suggestion, start_index - 1) == IterationDecision::Break) if (TRY(callback(suggestion, start_index - 1)) == IterationDecision::Break)
break; break;
} }
return start_index; return start_index;

View file

@ -78,7 +78,7 @@ public:
size_t next_index() const { return m_next_suggestion_index; } size_t next_index() const { return m_next_suggestion_index; }
void set_start_index(size_t index) const { m_last_displayed_suggestion_index = 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 { enum CompletionMode {
DontComplete, DontComplete,

View file

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

View file

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