1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-24 18:27:42 +00:00

LibLine: Remove duplicate members in CompletionSuggestion

Previously, we stored two representations of the same string in
`CompletionSuggestion` object: one for the bytes and the other for the
code points corresponding to those bytes. To minimize duplication, this
patch combine both representations into a single UTF-8 string, which is
already supported by our new String class.

Following this update, we successfully reduce the size of each object
from 376 bytes to 256 bytes
This commit is contained in:
hanaa12G 2023-12-07 03:49:35 +07:00 committed by Ali Mohammad Pur
parent 89877b3f40
commit 19f137c1e6
7 changed files with 54 additions and 66 deletions

View file

@ -150,7 +150,7 @@ Vector<CodeComprehension::AutocompleteResultEntry> ShellComprehensionEngine::get
auto completions = const_cast<::Shell::AST::Node*>(document.node.ptr())->complete_for_editor(shell(), offset_in_file, hit_test).release_value_but_fixme_should_propagate_errors(); auto completions = const_cast<::Shell::AST::Node*>(document.node.ptr())->complete_for_editor(shell(), offset_in_file, hit_test).release_value_but_fixme_should_propagate_errors();
Vector<CodeComprehension::AutocompleteResultEntry> entries; Vector<CodeComprehension::AutocompleteResultEntry> entries;
for (auto& completion : completions) for (auto& completion : completions)
entries.append({ completion.text_string, completion.input_offset }); entries.append({ completion.text_string(), completion.input_offset });
return entries; return entries;
} }

View file

@ -1176,7 +1176,7 @@ ErrorOr<void> Editor::handle_read_event()
m_chars_touched_in_the_middle++; m_chars_touched_in_the_middle++;
for (auto& view : completion_result.insert) for (auto& view : completion_result.insert)
insert(view); insert(view.as_string());
auto stderr_stream = TRY(Core::File::standard_error()); auto stderr_stream = TRY(Core::File::standard_error());
TRY(reposition_cursor(*stderr_stream)); TRY(reposition_cursor(*stderr_stream));

View file

@ -4,61 +4,50 @@
* SPDX-License-Identifier: BSD-2-Clause * SPDX-License-Identifier: BSD-2-Clause
*/ */
#include <AK/Assertions.h>
#include <AK/Function.h> #include <AK/Function.h>
#include <LibLine/SuggestionManager.h> #include <LibLine/SuggestionManager.h>
namespace Line { namespace Line {
CompletionSuggestion::CompletionSuggestion(StringView completion, StringView trailing_trivia, StringView display_trivia, Style style) CompletionSuggestion::CompletionSuggestion(StringView completion, StringView trailing_trivia, StringView display_trivia, Style style)
: style(style) : text(MUST(String::from_utf8(completion)))
, text_string(completion) , trailing_trivia(MUST(String::from_utf8(trailing_trivia)))
, display_trivia_string(display_trivia) , display_trivia(MUST(String::from_utf8(display_trivia)))
, style(style)
, is_valid(true) , is_valid(true)
{ {
Utf8View text_u8 { completion };
Utf8View trivia_u8 { trailing_trivia };
Utf8View display_u8 { display_trivia };
for (auto cp : text_u8)
text.append(cp);
for (auto cp : trivia_u8)
this->trailing_trivia.append(cp);
for (auto cp : display_u8)
this->display_trivia.append(cp);
text_view = Utf32View { text.data(), text.size() };
trivia_view = Utf32View { this->trailing_trivia.data(), this->trailing_trivia.size() };
display_trivia_view = Utf32View { this->display_trivia.data(), this->display_trivia.size() };
} }
void SuggestionManager::set_suggestions(Vector<CompletionSuggestion>&& suggestions) void SuggestionManager::set_suggestions(Vector<CompletionSuggestion>&& suggestions)
{ {
m_suggestions = move(suggestions); auto code_point_at = [](Utf8View view, size_t index) {
size_t count = 0;
for (auto cp : view) {
if (count == index) {
return cp;
}
count++;
}
VERIFY_NOT_REACHED();
};
// Set the views and make sure we were not given invalid suggestions m_suggestions = move(suggestions);
for (auto& suggestion : m_suggestions) {
VERIFY(suggestion.is_valid);
suggestion.text_view = { suggestion.text.data(), suggestion.text.size() };
suggestion.trivia_view = { suggestion.trailing_trivia.data(), suggestion.trailing_trivia.size() };
suggestion.display_trivia_view = { suggestion.display_trivia.data(), suggestion.display_trivia.size() };
}
size_t common_suggestion_prefix { 0 }; size_t common_suggestion_prefix { 0 };
if (m_suggestions.size() == 1) { if (m_suggestions.size() == 1) {
m_largest_common_suggestion_prefix_length = m_suggestions[0].text_view.length(); m_largest_common_suggestion_prefix_length = m_suggestions[0].text_view().length();
} else if (m_suggestions.size()) { } else if (m_suggestions.size()) {
u32 last_valid_suggestion_code_point; u32 last_valid_suggestion_code_point;
for (;; ++common_suggestion_prefix) { for (;; ++common_suggestion_prefix) {
if (m_suggestions[0].text_view.length() <= common_suggestion_prefix) if (m_suggestions[0].text_view().length() <= common_suggestion_prefix)
goto no_more_commons; goto no_more_commons;
last_valid_suggestion_code_point = m_suggestions[0].text_view.code_points()[common_suggestion_prefix]; last_valid_suggestion_code_point = code_point_at(m_suggestions[0].text_view(), common_suggestion_prefix);
for (auto& suggestion : m_suggestions) { for (auto& suggestion : m_suggestions) {
if (suggestion.text_view.length() <= common_suggestion_prefix || suggestion.text_view.code_points()[common_suggestion_prefix] != last_valid_suggestion_code_point) { if (suggestion.text_view().length() <= common_suggestion_prefix || code_point_at(suggestion.text_view(), common_suggestion_prefix) != last_valid_suggestion_code_point) {
goto no_more_commons; goto no_more_commons;
} }
} }
@ -101,7 +90,7 @@ void SuggestionManager::set_current_suggestion_initiation_index(size_t index)
else else
m_last_shown_suggestion.start_index = index - suggestion.static_offset - suggestion.invariant_offset; m_last_shown_suggestion.start_index = index - suggestion.static_offset - suggestion.invariant_offset;
m_last_shown_suggestion_display_length = m_last_shown_suggestion.text_view.length(); m_last_shown_suggestion_display_length = m_last_shown_suggestion.text_view().length();
m_last_shown_suggestion_was_complete = true; m_last_shown_suggestion_was_complete = true;
} }
@ -131,7 +120,7 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion
case ShowSuggestions: case ShowSuggestions:
actual_offset = 0 - m_largest_common_suggestion_prefix_length + next_suggestion.invariant_offset; actual_offset = 0 - m_largest_common_suggestion_prefix_length + next_suggestion.invariant_offset;
if (can_complete && next_suggestion.allow_commit_without_listing) if (can_complete && next_suggestion.allow_commit_without_listing)
shown_length = m_largest_common_suggestion_prefix_length + m_last_shown_suggestion.trivia_view.length(); shown_length = m_largest_common_suggestion_prefix_length + m_last_shown_suggestion.trivia_view().length();
break; break;
default: default:
if (m_last_shown_suggestion_display_length == 0) if (m_last_shown_suggestion_display_length == 0)
@ -151,14 +140,14 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion
if (mode == CompletePrefix) { if (mode == CompletePrefix) {
// Only auto-complete *if possible*. // Only auto-complete *if possible*.
if (can_complete) { if (can_complete) {
result.insert.append(suggestion.text_view.substring_view(suggestion.invariant_offset, m_largest_common_suggestion_prefix_length - suggestion.invariant_offset)); result.insert.append(suggestion.text_view().substring_view(suggestion.invariant_offset, m_largest_common_suggestion_prefix_length - suggestion.invariant_offset));
m_last_shown_suggestion_display_length = m_largest_common_suggestion_prefix_length; m_last_shown_suggestion_display_length = m_largest_common_suggestion_prefix_length;
// Do not increment the suggestion index, as the first tab should only be a *peek*. // Do not increment the suggestion index, as the first tab should only be a *peek*.
if (m_suggestions.size() == 1) { if (m_suggestions.size() == 1) {
// If there's one suggestion, commit and forget. // If there's one suggestion, commit and forget.
result.new_completion_mode = DontComplete; result.new_completion_mode = DontComplete;
// Add in the trivia of the last selected suggestion. // Add in the trivia of the last selected suggestion.
result.insert.append(suggestion.trivia_view); result.insert.append(suggestion.trivia_view());
m_last_shown_suggestion_display_length = 0; m_last_shown_suggestion_display_length = 0;
result.style_to_apply = suggestion.style; result.style_to_apply = suggestion.style;
m_last_shown_suggestion_was_complete = true; m_last_shown_suggestion_was_complete = true;
@ -171,10 +160,10 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion
m_last_shown_suggestion_was_complete = false; m_last_shown_suggestion_was_complete = false;
m_last_shown_suggestion = DeprecatedString::empty(); m_last_shown_suggestion = DeprecatedString::empty();
} else { } else {
result.insert.append(suggestion.text_view.substring_view(suggestion.invariant_offset, suggestion.text_view.length() - suggestion.invariant_offset)); result.insert.append(suggestion.text_view().substring_view(suggestion.invariant_offset, suggestion.text_view().length() - suggestion.invariant_offset));
// Add in the trivia of the last selected suggestion. // Add in the trivia of the last selected suggestion.
result.insert.append(suggestion.trivia_view); result.insert.append(suggestion.trivia_view());
m_last_shown_suggestion_display_length += suggestion.trivia_view.length(); m_last_shown_suggestion_display_length += suggestion.trivia_view().length();
} }
} else { } else {
m_next_suggestion_index = 0; m_next_suggestion_index = 0;

View file

@ -8,14 +8,13 @@
#include <AK/DeprecatedString.h> #include <AK/DeprecatedString.h>
#include <AK/Forward.h> #include <AK/Forward.h>
#include <AK/String.h>
#include <AK/Utf32View.h> #include <AK/Utf32View.h>
#include <AK/Utf8View.h> #include <AK/Utf8View.h>
#include <LibLine/Style.h> #include <LibLine/Style.h>
namespace Line { namespace Line {
// FIXME: These objects are pretty heavy since they store two copies of text
// somehow get rid of one.
struct CompletionSuggestion { struct CompletionSuggestion {
private: private:
struct ForSearchTag { struct ForSearchTag {
@ -30,8 +29,8 @@ public:
{ {
} }
CompletionSuggestion(DeprecatedString const& completion, ForSearchTag) CompletionSuggestion(StringView completion, ForSearchTag)
: text_string(completion) : text(MUST(String::from_utf8(completion)))
{ {
} }
@ -44,12 +43,12 @@ public:
bool operator==(CompletionSuggestion const& suggestion) const bool operator==(CompletionSuggestion const& suggestion) const
{ {
return suggestion.text_string == text_string; return suggestion.text == text;
} }
Vector<u32> text; String text;
Vector<u32> trailing_trivia; String trailing_trivia;
Vector<u32> display_trivia; String display_trivia;
Style style; Style style;
size_t start_index { 0 }; size_t start_index { 0 };
size_t input_offset { 0 }; size_t input_offset { 0 };
@ -57,11 +56,11 @@ public:
size_t invariant_offset { 0 }; size_t invariant_offset { 0 };
bool allow_commit_without_listing { true }; bool allow_commit_without_listing { true };
Utf32View text_view; Utf8View text_view() const { return text.code_points(); }
Utf32View trivia_view; Utf8View trivia_view() const { return trailing_trivia.code_points(); }
Utf32View display_trivia_view; Utf8View display_trivia_view() const { return display_trivia.code_points(); }
DeprecatedString text_string; StringView text_string() const { return text.bytes_as_string_view(); }
DeprecatedString display_trivia_string; StringView display_trivia_string() const { return display_trivia.bytes_as_string_view(); }
bool is_valid { false }; bool is_valid { false };
}; };
@ -101,7 +100,7 @@ public:
// This bit of data will be removed, but restored if the suggestion is rejected. // This bit of data will be removed, but restored if the suggestion is rejected.
size_t static_offset_from_cursor { 0 }; size_t static_offset_from_cursor { 0 };
Vector<Utf32View> insert {}; Vector<Utf8View> insert {};
Optional<Style> style_to_apply {}; Optional<Style> style_to_apply {};

View file

@ -26,9 +26,9 @@ ErrorOr<void> XtermSuggestionDisplay::display(SuggestionManager const& manager)
manager.set_start_index(0); manager.set_start_index(0);
TRY(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;
})); }));
@ -65,9 +65,9 @@ ErrorOr<void> XtermSuggestionDisplay::display(SuggestionManager const& manager)
manager.set_start_index(0); manager.set_start_index(0);
size_t page_start = 0; size_t page_start = 0;
TRY(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;
lines_used += lines; lines_used += lines;
num_printed = 0; num_printed = 0;
} }
@ -94,10 +94,10 @@ ErrorOr<void> XtermSuggestionDisplay::display(SuggestionManager const& manager)
manager.set_start_index(m_pages[page_index].start); manager.set_start_index(m_pages[page_index].start);
TRY(manager.for_each_suggestion([&](auto& suggestion, auto index) -> ErrorOr<IterationDecision> { 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;
TRY(stderr_stream->write_until_depleted("\n"sv.bytes())); TRY(stderr_stream->write_until_depleted("\n"sv.bytes()));
num_printed = 0; num_printed = 0;
@ -115,10 +115,10 @@ ErrorOr<void> XtermSuggestionDisplay::display(SuggestionManager const& manager)
if (spans_entire_line) { if (spans_entire_line) {
num_printed += m_num_columns; num_printed += m_num_columns;
TRY(stderr_stream->write_until_depleted(suggestion.text_string.bytes())); TRY(stderr_stream->write_until_depleted(suggestion.text_string().bytes()));
TRY(stderr_stream->write_until_depleted(suggestion.display_trivia_string.bytes())); TRY(stderr_stream->write_until_depleted(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());
TRY(stderr_stream->write_until_depleted(DeprecatedString::formatted("{: <{}}", field, longest_suggestion_byte_length + 2).bytes())); TRY(stderr_stream->write_until_depleted(DeprecatedString::formatted("{: <{}}", field, longest_suggestion_byte_length + 2).bytes()));
num_printed += longest_suggestion_length + 2; num_printed += longest_suggestion_length + 2;
} }

View file

@ -349,7 +349,7 @@ ErrorOr<Vector<Line::CompletionSuggestion>> Node::complete_for_editor(Shell& she
auto set_results_trivia = [enclosure_type](Vector<Line::CompletionSuggestion>&& suggestions) { auto set_results_trivia = [enclosure_type](Vector<Line::CompletionSuggestion>&& suggestions) {
if (enclosure_type != StringLiteral::EnclosureType::None) { if (enclosure_type != StringLiteral::EnclosureType::None) {
for (auto& entry : suggestions) for (auto& entry : suggestions)
entry.trailing_trivia = { static_cast<u32>(enclosure_type == StringLiteral::EnclosureType::SingleQuotes ? '\'' : '"') }; entry.trailing_trivia = String::from_code_point(static_cast<u32>(enclosure_type == StringLiteral::EnclosureType::SingleQuotes ? '\'' : '"'));
} }
return suggestions; return suggestions;
}; };

View file

@ -1657,7 +1657,7 @@ Vector<Line::CompletionSuggestion> Shell::complete_path(StringView base, StringV
// The results of DirIterator are in the order they appear on-disk. // The results of DirIterator are in the order they appear on-disk.
// Instead, return suggestions in lexicographical order. // Instead, return suggestions in lexicographical order.
quick_sort(suggestions, [](auto& a, auto& b) { return a.text_string < b.text_string; }); quick_sort(suggestions, [](auto& a, auto& b) { return a.text_string() < b.text_string(); });
return suggestions; return suggestions;
} }