From 118590325a0fe289e3e23b975b60c60d0a10c04e Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Mon, 28 Feb 2022 17:28:47 +0330 Subject: LibLine+Userland: Make suggestion offsets per-suggestion This allows the user to modify different parts of the input with different suggestions. --- Userland/Libraries/LibLine/Editor.cpp | 7 +- Userland/Libraries/LibLine/Editor.h | 2 +- Userland/Libraries/LibLine/SuggestionManager.cpp | 20 +++--- Userland/Libraries/LibLine/SuggestionManager.h | 10 +-- Userland/Shell/Shell.cpp | 82 ++++++++++++++++-------- Userland/Utilities/js.cpp | 9 ++- 6 files changed, 79 insertions(+), 51 deletions(-) (limited to 'Userland') diff --git a/Userland/Libraries/LibLine/Editor.cpp b/Userland/Libraries/LibLine/Editor.cpp index 3a5ce1f420..6e33d22941 100644 --- a/Userland/Libraries/LibLine/Editor.cpp +++ b/Userland/Libraries/LibLine/Editor.cpp @@ -489,7 +489,7 @@ void Editor::stylize(Span const& span, Style const& style) ending_map.set(start, style); } -void Editor::suggest(size_t invariant_offset, size_t static_offset, Span::Mode offset_mode) const +void Editor::transform_suggestion_offsets(size_t& invariant_offset, size_t& static_offset, Span::Mode offset_mode) const { auto internal_static_offset = static_offset; auto internal_invariant_offset = invariant_offset; @@ -501,7 +501,8 @@ void Editor::suggest(size_t invariant_offset, size_t static_offset, Span::Mode o internal_static_offset = offsets.start; internal_invariant_offset = offsets.end - offsets.start; } - m_suggestion_manager.set_suggestion_variants(internal_static_offset, internal_invariant_offset, 0); + invariant_offset = internal_invariant_offset; + static_offset = internal_static_offset; } void Editor::initialize() @@ -1141,7 +1142,6 @@ 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. - suggest(0, 0, Span::CodepointOriented); m_times_tab_pressed = 0; m_suggestion_manager.reset(); m_suggestion_display->finish(); @@ -1180,7 +1180,6 @@ void Editor::cleanup_suggestions() m_refresh_needed = true; } m_suggestion_manager.reset(); - suggest(0, 0, Span::CodepointOriented); m_suggestion_display->finish(); } m_times_tab_pressed = 0; // Safe to say if we get here, the user didn't press TAB diff --git a/Userland/Libraries/LibLine/Editor.h b/Userland/Libraries/LibLine/Editor.h index 4f9cf5746b..446585fe3d 100644 --- a/Userland/Libraries/LibLine/Editor.h +++ b/Userland/Libraries/LibLine/Editor.h @@ -221,7 +221,7 @@ public: // +-|- static offset: the suggestions start here // +- invariant offset: the suggestions do not change up to here // - void suggest(size_t invariant_offset = 0, size_t static_offset = 0, Span::Mode offset_mode = Span::ByteOriented) const; + void transform_suggestion_offsets(size_t& invariant_offset, size_t& static_offset, Span::Mode offset_mode = Span::ByteOriented) const; const struct termios& termios() const { return m_termios; } const struct termios& default_termios() const { return m_default_termios; } diff --git a/Userland/Libraries/LibLine/SuggestionManager.cpp b/Userland/Libraries/LibLine/SuggestionManager.cpp index fc40862bbd..80cf37b457 100644 --- a/Userland/Libraries/LibLine/SuggestionManager.cpp +++ b/Userland/Libraries/LibLine/SuggestionManager.cpp @@ -84,10 +84,12 @@ CompletionSuggestion const& SuggestionManager::suggest() void SuggestionManager::set_current_suggestion_initiation_index(size_t index) { + auto& suggestion = m_suggestions[m_next_suggestion_index]; + if (m_last_shown_suggestion_display_length) - m_last_shown_suggestion.start_index = index - m_next_suggestion_static_offset - m_last_shown_suggestion_display_length; + m_last_shown_suggestion.start_index = index - suggestion.static_offset - m_last_shown_suggestion_display_length; else - m_last_shown_suggestion.start_index = index - m_next_suggestion_static_offset - m_next_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_was_complete = true; @@ -98,7 +100,9 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion CompletionAttemptResult result { mode }; if (m_next_suggestion_index < m_suggestions.size()) { - auto can_complete = m_next_suggestion_invariant_offset <= m_largest_common_suggestion_prefix_length; + auto& next_suggestion = m_suggestions[m_next_suggestion_index]; + + auto can_complete = next_suggestion.invariant_offset <= m_largest_common_suggestion_prefix_length; ssize_t actual_offset; size_t shown_length = m_last_shown_suggestion_display_length; switch (mode) { @@ -106,7 +110,7 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion actual_offset = 0; break; case ShowSuggestions: - actual_offset = 0 - m_largest_common_suggestion_prefix_length + m_next_suggestion_invariant_offset; + actual_offset = 0 - m_largest_common_suggestion_prefix_length + next_suggestion.invariant_offset; if (can_complete) shown_length = m_largest_common_suggestion_prefix_length + m_last_shown_suggestion.trivia_view.length(); break; @@ -114,11 +118,11 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion if (m_last_shown_suggestion_display_length == 0) actual_offset = 0; else - actual_offset = 0 - m_last_shown_suggestion_display_length + m_next_suggestion_invariant_offset; + actual_offset = 0 - m_last_shown_suggestion_display_length + next_suggestion.invariant_offset; break; } - result.offset_region_to_remove = { m_next_suggestion_invariant_offset, shown_length }; + result.offset_region_to_remove = { next_suggestion.invariant_offset, shown_length }; result.new_cursor_offset = actual_offset; auto& suggestion = suggest(); @@ -127,7 +131,7 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion if (mode == CompletePrefix) { // Only auto-complete *if possible*. if (can_complete) { - result.insert.append(suggestion.text_view.substring_view(m_next_suggestion_invariant_offset, m_largest_common_suggestion_prefix_length - m_next_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; // Do not increment the suggestion index, as the first tab should only be a *peek*. if (m_suggestions.size() == 1) { @@ -147,7 +151,7 @@ SuggestionManager::CompletionAttemptResult SuggestionManager::attempt_completion m_last_shown_suggestion_was_complete = false; m_last_shown_suggestion = String::empty(); } else { - result.insert.append(suggestion.text_view.substring_view(m_next_suggestion_invariant_offset, suggestion.text_view.length() - m_next_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. result.insert.append(suggestion.trivia_view); m_last_shown_suggestion_display_length += suggestion.trivia_view.length(); diff --git a/Userland/Libraries/LibLine/SuggestionManager.h b/Userland/Libraries/LibLine/SuggestionManager.h index d1ad97bcf2..b3282cf7f9 100644 --- a/Userland/Libraries/LibLine/SuggestionManager.h +++ b/Userland/Libraries/LibLine/SuggestionManager.h @@ -53,6 +53,8 @@ public: Style style; size_t start_index { 0 }; size_t input_offset { 0 }; + size_t static_offset { 0 }; + size_t invariant_offset { 0 }; Utf32View text_view; Utf32View trivia_view; @@ -102,12 +104,6 @@ public: void next(); void previous(); - void set_suggestion_variants(size_t static_offset, size_t invariant_offset, size_t suggestion_index) const - { - m_next_suggestion_index = suggestion_index; - m_next_suggestion_static_offset = static_offset; - m_next_suggestion_invariant_offset = invariant_offset; - } CompletionSuggestion const& suggest(); CompletionSuggestion const& current_suggestion() const { return m_last_shown_suggestion; } @@ -131,8 +127,6 @@ private: size_t m_last_shown_suggestion_display_length { 0 }; bool m_last_shown_suggestion_was_complete { false }; mutable size_t m_next_suggestion_index { 0 }; - mutable size_t m_next_suggestion_invariant_offset { 0 }; - mutable size_t m_next_suggestion_static_offset { 0 }; size_t m_largest_common_suggestion_prefix_length { 0 }; mutable size_t m_last_displayed_suggestion_index { 0 }; size_t m_selected_suggestion_index { 0 }; diff --git a/Userland/Shell/Shell.cpp b/Userland/Shell/Shell.cpp index 9bf307e508..30295eb6b2 100644 --- a/Userland/Shell/Shell.cpp +++ b/Userland/Shell/Shell.cpp @@ -1416,8 +1416,10 @@ Vector Shell::complete_path(StringView base, // since we are not suggesting anything starting with // `/foo/', but rather just `bar...' auto token_length = escape_token(token).length(); + size_t static_offset = last_slash + 1; + auto invariant_offset = token_length; if (m_editor) - m_editor->suggest(token_length, last_slash + 1); + m_editor->transform_suggestion_offsets(invariant_offset, static_offset); // only suggest dot-files if path starts with a dot Core::DirIterator files(path, @@ -1440,6 +1442,8 @@ Vector Shell::complete_path(StringView base, suggestions.append({ escape_token(file), " " }); } suggestions.last().input_offset = token_length; + suggestions.last().invariant_offset = invariant_offset; + suggestions.last().static_offset = static_offset; } } } @@ -1465,8 +1469,10 @@ Vector Shell::complete_program_name(StringView name, String completion = *match; auto token_length = escape_token(name).length(); + auto invariant_offset = token_length; + size_t static_offset = 0; if (m_editor) - m_editor->suggest(token_length, 0); + m_editor->transform_suggestion_offsets(invariant_offset, static_offset); // Now that we have a program name starting with our token, we look at // other program names starting with our token and cut off any mismatching @@ -1475,16 +1481,17 @@ Vector Shell::complete_program_name(StringView name, Vector suggestions; int index = match - cached_path.data(); - for (int i = index - 1; i >= 0 && cached_path[i].starts_with(name); --i) { + for (int i = index - 1; i >= 0 && cached_path[i].starts_with(name); --i) suggestions.append({ cached_path[i], " " }); - suggestions.last().input_offset = token_length; - } - for (size_t i = index + 1; i < cached_path.size() && cached_path[i].starts_with(name); ++i) { + for (size_t i = index + 1; i < cached_path.size() && cached_path[i].starts_with(name); ++i) suggestions.append({ cached_path[i], " " }); - suggestions.last().input_offset = token_length; - } suggestions.append({ cached_path[index], " " }); - suggestions.last().input_offset = token_length; + + for (auto& entry : suggestions) { + entry.input_offset = token_length; + entry.invariant_offset = invariant_offset; + entry.static_offset = static_offset; + } return suggestions; } @@ -1494,8 +1501,10 @@ Vector Shell::complete_variable(StringView name, siz Vector suggestions; auto pattern = offset ? name.substring_view(0, offset) : ""; + auto invariant_offset = offset; + size_t static_offset = 0; if (m_editor) - m_editor->suggest(offset); + m_editor->transform_suggestion_offsets(invariant_offset, static_offset); // Look at local variables. for (auto& frame : m_local_frames) { @@ -1516,10 +1525,15 @@ Vector Shell::complete_variable(StringView name, siz if (suggestions.contains_slow(name)) continue; suggestions.append(move(name)); - suggestions.last().input_offset = offset; } } + for (auto& entry : suggestions) { + entry.input_offset = offset; + entry.invariant_offset = invariant_offset; + entry.static_offset = static_offset; + } + return suggestions; } @@ -1528,8 +1542,10 @@ Vector Shell::complete_user(StringView name, size_t Vector suggestions; auto pattern = offset ? name.substring_view(0, offset) : ""; + auto invariant_offset = offset; + size_t static_offset = 0; if (m_editor) - m_editor->suggest(offset); + m_editor->transform_suggestion_offsets(invariant_offset, static_offset); Core::DirIterator di("/home", Core::DirIterator::SkipParentAndBaseDir); @@ -1540,7 +1556,10 @@ Vector Shell::complete_user(StringView name, size_t String name = di.next_path(); if (name.starts_with(pattern)) { suggestions.append(name); - suggestions.last().input_offset = offset; + auto& suggestion = suggestions.last(); + suggestion.input_offset = offset; + suggestion.invariant_offset = invariant_offset; + suggestion.static_offset = static_offset; } } @@ -1553,8 +1572,10 @@ Vector Shell::complete_option(StringView program_nam while (start < option.length() && option[start] == '-' && start < 2) ++start; auto option_pattern = offset > start ? option.substring_view(start, offset - start) : ""; + auto invariant_offset = offset; + size_t static_offset = 0; if (m_editor) - m_editor->suggest(offset); + m_editor->transform_suggestion_offsets(invariant_offset, static_offset); Vector suggestions; @@ -1579,13 +1600,18 @@ Vector Shell::complete_option(StringView program_nam return builder.to_string(); }; #define __ENUMERATE_SHELL_OPTION(name, d_, descr_) \ - if (#name##sv.starts_with(option_pattern)) { \ - suggestions.append(maybe_negate(#name)); \ - suggestions.last().input_offset = offset; \ - } + if (#name##sv.starts_with(option_pattern)) \ + suggestions.append(maybe_negate(#name)); ENUMERATE_SHELL_OPTIONS(); #undef __ENUMERATE_SHELL_OPTION + + for (auto& entry : suggestions) { + entry.input_offset = offset; + entry.invariant_offset = invariant_offset; + entry.static_offset = static_offset; + } + return suggestions; } } @@ -1596,18 +1622,24 @@ Vector Shell::complete_immediate_function_name(Strin { Vector suggestions; -#define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(fn_name) \ - if (auto name_view = #fn_name##sv; name_view.starts_with(name)) { \ - suggestions.append({ name_view, " " }); \ - suggestions.last().input_offset = offset; \ - } + auto invariant_offset = offset; + size_t static_offset = 0; + if (m_editor) + m_editor->transform_suggestion_offsets(invariant_offset, static_offset); + +#define __ENUMERATE_SHELL_IMMEDIATE_FUNCTION(fn_name) \ + if (auto name_view = #fn_name##sv; name_view.starts_with(name)) \ + suggestions.append({ name_view, " " }); ENUMERATE_SHELL_IMMEDIATE_FUNCTIONS(); #undef __ENUMERATE_SHELL_IMMEDIATE_FUNCTION - if (m_editor) - m_editor->suggest(offset); + for (auto& entry : suggestions) { + entry.input_offset = offset; + entry.invariant_offset = invariant_offset; + entry.static_offset = static_offset; + } return suggestions; } diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index fe570e4a50..6672cd01fa 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -1580,6 +1580,7 @@ ErrorOr serenity_main(Main::Arguments arguments) Line::CompletionSuggestion completion { key, Line::CompletionSuggestion::ForSearch }; if (!results.contains_slow(completion)) { // hide duplicates results.append(String(key)); + results.last().invariant_offset = property_pattern.length(); } } } @@ -1605,8 +1606,6 @@ ErrorOr serenity_main(Main::Arguments arguments) auto const* object = MUST(variable.to_object(interpreter->global_object())); auto const& shape = object->shape(); list_all_properties(shape, property_name); - if (results.size()) - editor.suggest(property_name.length()); break; } case CompleteVariable: { @@ -1614,12 +1613,12 @@ ErrorOr serenity_main(Main::Arguments arguments) list_all_properties(variable.shape(), variable_name); for (String& name : global_environment.declarative_record().bindings()) { - if (name.starts_with(variable_name)) + if (name.starts_with(variable_name)) { results.empend(name); + results.last().invariant_offset = variable_name.length(); + } } - if (results.size()) - editor.suggest(variable_name.length()); break; } default: -- cgit v1.2.3