diff --git a/Libraries/LibLine/Editor.cpp b/Libraries/LibLine/Editor.cpp index bd30a751d0..71eeb1efbc 100644 --- a/Libraries/LibLine/Editor.cpp +++ b/Libraries/LibLine/Editor.cpp @@ -445,7 +445,7 @@ String Editor::get_line(const String& prompt) // reverse tab can count as regular tab here m_times_tab_pressed++; - int token_start = m_cursor - 1 - m_last_shown_suggestion_display_length; + int token_start = m_cursor; // ask for completions only on the first tab // and scan for the largest common prefix to display @@ -523,7 +523,8 @@ String Editor::get_line(const String& prompt) m_refresh_needed = true; } m_last_shown_suggestion = m_suggestions[m_next_suggestion_index]; - m_last_shown_suggestion.token_start_index = token_start - m_next_suggestion_invariant_offset - m_last_shown_suggestion.trailing_trivia.length(); + m_last_shown_suggestion.token_start_index = token_start - m_next_suggestion_invariant_offset - m_next_suggestion_static_offset; + dbg() << "Last shown suggestion token start index: " << m_last_shown_suggestion.token_start_index << " Token Start " << token_start << " invariant offset " << m_next_suggestion_invariant_offset; m_last_shown_suggestion_display_length = m_last_shown_suggestion.text.length(); m_last_shown_suggestion_was_complete = true; if (m_times_tab_pressed == 1) { @@ -537,7 +538,8 @@ String Editor::get_line(const String& prompt) m_times_tab_pressed = 0; // add in the trivia of the last selected suggestion insert(m_last_shown_suggestion.trailing_trivia); - m_last_shown_suggestion_display_length += m_last_shown_suggestion.trailing_trivia.length(); + m_last_shown_suggestion_display_length = 0; + readjust_anchored_styles(m_last_shown_suggestion.token_start_index, ModificationKind::ForcedOverlapRemoval); stylize({ m_last_shown_suggestion.token_start_index, m_cursor, Span::Mode::CodepointOriented }, m_last_shown_suggestion.style); } } else { @@ -659,6 +661,8 @@ String Editor::get_line(const String& prompt) if (m_times_tab_pressed) { // Apply the style of the last suggestion + dbg() << "Last shown suggestion token start index: " << m_last_shown_suggestion.token_start_index << " invariant offset " << m_next_suggestion_invariant_offset << " static offset " << m_next_suggestion_static_offset; + readjust_anchored_styles(m_last_shown_suggestion.token_start_index, ModificationKind::ForcedOverlapRemoval); stylize({ m_last_shown_suggestion.token_start_index, m_cursor, Span::Mode::CodepointOriented }, m_last_shown_suggestion.style); // we probably have some suggestions drawn // let's clean them up @@ -1415,9 +1419,16 @@ void Editor::readjust_anchored_styles(size_t hint_index, ModificationKind modifi }; Vector anchors_to_relocate; auto index_shift = modification == ModificationKind::Insertion ? 1 : -1; + auto forced_removal = modification == ModificationKind::ForcedOverlapRemoval; for (auto& start_entry : m_anchored_spans_starting) { for (auto& end_entry : start_entry.value) { + if (forced_removal) { + if (start_entry.key <= hint_index && end_entry.key >= hint_index) { + // remove any overlapping regions + continue; + } + } if (start_entry.key >= hint_index) { if (start_entry.key == hint_index && end_entry.key == hint_index + 1 && modification == ModificationKind::Removal) { // remove the anchor, as all its text was wiped @@ -1443,5 +1454,4 @@ void Editor::readjust_anchored_styles(size_t hint_index, ModificationKind modifi stylize(relocation.new_span, relocation.style); } } - } diff --git a/Libraries/LibLine/Editor.h b/Libraries/LibLine/Editor.h index cb8d91c584..9ececd76ad 100644 --- a/Libraries/LibLine/Editor.h +++ b/Libraries/LibLine/Editor.h @@ -169,9 +169,17 @@ public: void stylize(const Span&, const Style&); void strip_styles(bool strip_anchored = false); - void suggest(size_t invariant_offset = 0, size_t index = 0) const + // Invariant Offset is an offset into the suggested data, hinting the editor what parts of the suggestion will not change + // Static Offset is an offset into the token, signifying where the suggestions start + // e.g. + // foobar, on_tab_complete returns "barx", "bary", "barz" + // ^ ^ + // +-|- 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) const { - m_next_suggestion_index = index; + m_next_suggestion_index = 0; + m_next_suggestion_static_offset = static_offset; m_next_suggestion_invariant_offset = invariant_offset; } @@ -207,6 +215,7 @@ private: enum class ModificationKind { Insertion, Removal, + ForcedOverlapRemoval, }; void readjust_anchored_styles(size_t hint_index, ModificationKind); @@ -322,6 +331,7 @@ private: 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 }; size_t m_last_displayed_suggestion_index { 0 }; diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index db8978fc6e..9315e7128e 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -1540,6 +1540,7 @@ Vector Shell::complete(const Line::Editor& editor) } String path; + String original_token = token; ssize_t last_slash = token.length() - 1; while (last_slash >= 0 && token[last_slash] != '/') @@ -1563,7 +1564,8 @@ Vector Shell::complete(const Line::Editor& editor) // e. in `cd /foo/bar', 'bar' is the invariant // since we are not suggesting anything starting with // `/foo/', but rather just `bar...' - editor.suggest(escape_token(token).length(), 0); + auto token_length = escape_token(token).length(); + editor.suggest(token_length, original_token.length() - token_length); // only suggest dot-files if path starts with a dot Core::DirIterator files(path,