From 299ca10fd547107af5c3dabcf18a940c8dcd66cb Mon Sep 17 00:00:00 2001 From: Lucas CHOLLET Date: Tue, 8 Nov 2022 01:58:04 +0100 Subject: [PATCH] LibLine: Support wrapping lines in `actual_rendered_string_metrics` This will allow us to use this API to split an input to visible lines of a specified width. --- Userland/Libraries/LibLine/Editor.cpp | 46 ++++++++++++++++++---- Userland/Libraries/LibLine/Editor.h | 2 +- Userland/Libraries/LibLine/StringMetrics.h | 2 + 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/Userland/Libraries/LibLine/Editor.cpp b/Userland/Libraries/LibLine/Editor.cpp index d5ee6097bd..2744f4a1ad 100644 --- a/Userland/Libraries/LibLine/Editor.cpp +++ b/Userland/Libraries/LibLine/Editor.cpp @@ -1779,7 +1779,7 @@ enum VTState { BracketArgsSemi = 7, Title = 9, }; -static VTState actual_rendered_string_length_step(StringMetrics& metrics, size_t index, StringMetrics::LineMetrics& current_line, u32 c, u32 next_c, VTState state, Optional const& mask); +static VTState actual_rendered_string_length_step(StringMetrics& metrics, size_t index, StringMetrics::LineMetrics& current_line, u32 c, u32 next_c, VTState state, Optional const& mask, Optional const& maximum_line_width = {}, Optional last_return = {}); enum class MaskedSelectionDecision { Skip, @@ -1813,12 +1813,13 @@ static MaskedSelectionDecision resolve_masked_selection(Optional& m return MaskedSelectionDecision::Continue; } -StringMetrics Editor::actual_rendered_string_metrics(StringView string, RedBlackTree> const& masks) +StringMetrics Editor::actual_rendered_string_metrics(StringView string, RedBlackTree> const& masks, Optional maximum_line_width) { StringMetrics metrics; StringMetrics::LineMetrics current_line; VTState state { Free }; Utf8View view { string }; + size_t last_return {}; auto it = view.begin(); Optional mask; size_t i = 0; @@ -1835,7 +1836,7 @@ StringMetrics Editor::actual_rendered_string_metrics(StringView string, RedBlack continue; auto next_c = it_copy == view.end() ? 0 : *it_copy; - state = actual_rendered_string_length_step(metrics, view.iterator_offset(it), current_line, c, next_c, state, mask); + state = actual_rendered_string_length_step(metrics, view.iterator_offset(it), current_line, c, next_c, state, mask, maximum_line_width, last_return); if (!mask_it.is_end() && mask_it.key() <= i) { auto mask_it_peek = mask_it; ++mask_it_peek; @@ -1890,8 +1891,31 @@ StringMetrics Editor::actual_rendered_string_metrics(Utf32View const& view, RedB return metrics; } -VTState actual_rendered_string_length_step(StringMetrics& metrics, size_t index, StringMetrics::LineMetrics& current_line, u32 c, u32 next_c, VTState state, Optional const& mask) +VTState actual_rendered_string_length_step(StringMetrics& metrics, size_t index, StringMetrics::LineMetrics& current_line, u32 c, u32 next_c, VTState state, Optional const& mask, Optional const& maximum_line_width, Optional last_return) { + auto const save_line = [&metrics, ¤t_line, &last_return, &index]() { + if (last_return.has_value()) { + auto const last_index = index - 1; + current_line.bit_length = last_index - *last_return + 1; + last_return.value() = last_index + 1; + } + metrics.line_metrics.append(current_line); + + current_line.masked_chars = {}; + current_line.length = 0; + current_line.visible_length = 0; + current_line.bit_length = {}; + }; + + // FIXME: current_line.visible_length can go above maximum_line_width when using masks + if (maximum_line_width.has_value() && maximum_line_width.value() >= current_line.visible_length) + save_line(); + + ScopeGuard bit_length_update { [&last_return, ¤t_line, &index]() { + if (last_return.has_value()) + current_line.bit_length = index - *last_return + 1; + } }; + switch (state) { case Free: { if (c == '\x1b') { // escape @@ -1900,14 +1924,19 @@ VTState actual_rendered_string_length_step(StringMetrics& metrics, size_t index, if (c == '\r') { // carriage return current_line.masked_chars = {}; current_line.length = 0; + current_line.visible_length = 0; if (!metrics.line_metrics.is_empty()) metrics.line_metrics.last() = { {}, 0 }; return state; } if (c == '\n') { // return - metrics.line_metrics.append(current_line); - current_line.masked_chars = {}; - current_line.length = 0; + save_line(); + return state; + } + if (c == '\t') { + // Tabs are a special case, because their width is variable. + ++current_line.length; + current_line.visible_length += (8 - (current_line.visible_length % 8)); return state; } auto is_control = is_ascii_control(c); @@ -1920,12 +1949,15 @@ VTState actual_rendered_string_length_step(StringMetrics& metrics, size_t index, // FIXME: This will not support anything sophisticated if (mask.has_value()) { current_line.length += mask->replacement_view.length(); + current_line.visible_length += mask->replacement_view.length(); metrics.total_length += mask->replacement_view.length(); } else if (is_control) { current_line.length += current_line.masked_chars.last().masked_length; + current_line.visible_length += current_line.masked_chars.last().masked_length; metrics.total_length += current_line.masked_chars.last().masked_length; } else { ++current_line.length; + ++current_line.visible_length; ++metrics.total_length; } return state; diff --git a/Userland/Libraries/LibLine/Editor.h b/Userland/Libraries/LibLine/Editor.h index 063e4db9ae..005519317f 100644 --- a/Userland/Libraries/LibLine/Editor.h +++ b/Userland/Libraries/LibLine/Editor.h @@ -160,7 +160,7 @@ public: void register_key_input_callback(Vector keys, Function callback) { m_callback_machine.register_key_input_callback(move(keys), move(callback)); } void register_key_input_callback(Key key, Function callback) { register_key_input_callback(Vector { key }, move(callback)); } - static StringMetrics actual_rendered_string_metrics(StringView, RedBlackTree> const& masks = {}); + static StringMetrics actual_rendered_string_metrics(StringView, RedBlackTree> const& masks = {}, Optional maximum_line_width = {}); static StringMetrics actual_rendered_string_metrics(Utf32View const&, RedBlackTree> const& masks = {}); Function(Editor const&)> on_tab_complete; diff --git a/Userland/Libraries/LibLine/StringMetrics.h b/Userland/Libraries/LibLine/StringMetrics.h index f3c1525b5d..f312f389e4 100644 --- a/Userland/Libraries/LibLine/StringMetrics.h +++ b/Userland/Libraries/LibLine/StringMetrics.h @@ -20,6 +20,8 @@ struct StringMetrics { struct LineMetrics { Vector masked_chars; size_t length { 0 }; + size_t visible_length { 0 }; + Optional bit_length { 0 }; size_t total_length() const { return length; } };