Browse Source

LibLine: Correctly track the completion start and end

To achieve this, the API was tweaked a bit to allow for easier tracking
of completions.
This API change is non-disruptive to any application that does not use
anchored styles.
AnotherTest 5 years ago
parent
commit
0751592a18
3 changed files with 29 additions and 7 deletions
  1. 14 4
      Libraries/LibLine/Editor.cpp
  2. 12 2
      Libraries/LibLine/Editor.h
  3. 3 1
      Shell/Shell.cpp

+ 14 - 4
Libraries/LibLine/Editor.cpp

@@ -445,7 +445,7 @@ String Editor::get_line(const String& prompt)
                 // reverse tab can count as regular tab here
                 // reverse tab can count as regular tab here
                 m_times_tab_pressed++;
                 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
                 // ask for completions only on the first tab
                 // and scan for the largest common prefix to display
                 // 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_refresh_needed = true;
                     }
                     }
                     m_last_shown_suggestion = m_suggestions[m_next_suggestion_index];
                     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_display_length = m_last_shown_suggestion.text.length();
                     m_last_shown_suggestion_was_complete = true;
                     m_last_shown_suggestion_was_complete = true;
                     if (m_times_tab_pressed == 1) {
                     if (m_times_tab_pressed == 1) {
@@ -537,7 +538,8 @@ String Editor::get_line(const String& prompt)
                                 m_times_tab_pressed = 0;
                                 m_times_tab_pressed = 0;
                                 // add in the trivia of the last selected suggestion
                                 // add in the trivia of the last selected suggestion
                                 insert(m_last_shown_suggestion.trailing_trivia);
                                 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);
                                 stylize({ m_last_shown_suggestion.token_start_index, m_cursor, Span::Mode::CodepointOriented }, m_last_shown_suggestion.style);
                             }
                             }
                         } else {
                         } else {
@@ -659,6 +661,8 @@ String Editor::get_line(const String& prompt)
 
 
             if (m_times_tab_pressed) {
             if (m_times_tab_pressed) {
                 // Apply the style of the last suggestion
                 // 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);
                 stylize({ m_last_shown_suggestion.token_start_index, m_cursor, Span::Mode::CodepointOriented }, m_last_shown_suggestion.style);
                 // we probably have some suggestions drawn
                 // we probably have some suggestions drawn
                 // let's clean them up
                 // let's clean them up
@@ -1415,9 +1419,16 @@ void Editor::readjust_anchored_styles(size_t hint_index, ModificationKind modifi
     };
     };
     Vector<Anchor> anchors_to_relocate;
     Vector<Anchor> anchors_to_relocate;
     auto index_shift = modification == ModificationKind::Insertion ? 1 : -1;
     auto index_shift = modification == ModificationKind::Insertion ? 1 : -1;
+    auto forced_removal = modification == ModificationKind::ForcedOverlapRemoval;
 
 
     for (auto& start_entry : m_anchored_spans_starting) {
     for (auto& start_entry : m_anchored_spans_starting) {
         for (auto& end_entry : start_entry.value) {
         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) {
                 if (start_entry.key == hint_index && end_entry.key == hint_index + 1 && modification == ModificationKind::Removal) {
                 if (start_entry.key == hint_index && end_entry.key == hint_index + 1 && modification == ModificationKind::Removal) {
                     // remove the anchor, as all its text was wiped
                     // 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);
         stylize(relocation.new_span, relocation.style);
     }
     }
 }
 }
-
 }
 }

+ 12 - 2
Libraries/LibLine/Editor.h

@@ -169,9 +169,17 @@ public:
     void stylize(const Span&, const Style&);
     void stylize(const Span&, const Style&);
     void strip_styles(bool strip_anchored = false);
     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<suggestion initiated>, 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;
         m_next_suggestion_invariant_offset = invariant_offset;
     }
     }
 
 
@@ -207,6 +215,7 @@ private:
     enum class ModificationKind {
     enum class ModificationKind {
         Insertion,
         Insertion,
         Removal,
         Removal,
+        ForcedOverlapRemoval,
     };
     };
     void readjust_anchored_styles(size_t hint_index, ModificationKind);
     void readjust_anchored_styles(size_t hint_index, ModificationKind);
 
 
@@ -322,6 +331,7 @@ private:
     bool m_last_shown_suggestion_was_complete { false };
     bool m_last_shown_suggestion_was_complete { false };
     mutable size_t m_next_suggestion_index { 0 };
     mutable size_t m_next_suggestion_index { 0 };
     mutable size_t m_next_suggestion_invariant_offset { 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_largest_common_suggestion_prefix_length { 0 };
     size_t m_last_displayed_suggestion_index { 0 };
     size_t m_last_displayed_suggestion_index { 0 };
 
 

+ 3 - 1
Shell/Shell.cpp

@@ -1540,6 +1540,7 @@ Vector<Line::CompletionSuggestion> Shell::complete(const Line::Editor& editor)
     }
     }
 
 
     String path;
     String path;
+    String original_token = token;
 
 
     ssize_t last_slash = token.length() - 1;
     ssize_t last_slash = token.length() - 1;
     while (last_slash >= 0 && token[last_slash] != '/')
     while (last_slash >= 0 && token[last_slash] != '/')
@@ -1563,7 +1564,8 @@ Vector<Line::CompletionSuggestion> Shell::complete(const Line::Editor& editor)
     // e. in `cd /foo/bar', 'bar' is the invariant
     // e. in `cd /foo/bar', 'bar' is the invariant
     //      since we are not suggesting anything starting with
     //      since we are not suggesting anything starting with
     //      `/foo/', but rather just `bar...'
     //      `/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
     // only suggest dot-files if path starts with a dot
     Core::DirIterator files(path,
     Core::DirIterator files(path,