Bladeren bron

LibLine: Don't overwrite stuff when moving origin around

This fixes an issue (mainly) with multiline prompts, where a multiline
prompt would overwrite the lines before it when libline tries to display
it.
To reproduce, set `PROMPT="a\nb\nc> "` in the shell, then press return
a few times.
AnotherTest 4 jaren geleden
bovenliggende
commit
1c4a425bff
3 gewijzigde bestanden met toevoegingen van 56 en 18 verwijderingen
  1. 54 14
      Libraries/LibLine/Editor.cpp
  2. 1 4
      Libraries/LibLine/Editor.h
  3. 1 0
      Libraries/LibLine/StringMetrics.h

+ 54 - 14
Libraries/LibLine/Editor.cpp

@@ -545,9 +545,16 @@ auto Editor::get_line(const String& prompt) -> Result<String, Editor::Error>
 
 
     set_prompt(prompt);
     set_prompt(prompt);
     reset();
     reset();
-    set_origin();
     strip_styles(true);
     strip_styles(true);
 
 
+    auto prompt_lines = max(current_prompt_metrics().line_lengths.size(), 1ul) - 1;
+    for (size_t i = 0; i < prompt_lines; ++i)
+        putc('\n', stderr);
+
+    VT::move_relative(-prompt_lines, 0);
+
+    set_origin();
+
     m_history_cursor = m_history.size();
     m_history_cursor = m_history.size();
 
 
     refresh_display();
     refresh_display();
@@ -856,8 +863,18 @@ void Editor::handle_read_event()
             }
             }
             reverse_tab = false;
             reverse_tab = false;
 
 
-            auto completion_mode = m_times_tab_pressed == 1 ? SuggestionManager::CompletePrefix : m_times_tab_pressed == 2 ? SuggestionManager::ShowSuggestions
-                                                                                                                           : SuggestionManager::CycleSuggestions;
+            SuggestionManager::CompletionMode completion_mode;
+            switch (m_times_tab_pressed) {
+            case 1:
+                completion_mode = SuggestionManager::CompletePrefix;
+                break;
+            case 2:
+                completion_mode = SuggestionManager::ShowSuggestions;
+                break;
+            default:
+                completion_mode = SuggestionManager::CycleSuggestions;
+                break;
+            }
 
 
             auto completion_result = m_suggestion_manager.attempt_completion(completion_mode, token_start);
             auto completion_result = m_suggestion_manager.attempt_completion(completion_mode, token_start);
 
 
@@ -1016,13 +1033,9 @@ void Editor::cleanup()
     if (new_lines < shown_lines)
     if (new_lines < shown_lines)
         m_extra_forward_lines = max(shown_lines - new_lines, m_extra_forward_lines);
         m_extra_forward_lines = max(shown_lines - new_lines, m_extra_forward_lines);
 
 
-    VT::move_relative(-m_extra_forward_lines, m_pending_chars.size() - m_chars_inserted_in_the_middle);
-    auto current_line = cursor_line();
-
-    // There's a newline at the top, don't clear that line.
-    if (current_prompt_metrics().line_lengths.first() == 0)
-        --current_line;
-    VT::clear_lines(current_line - 1, num_lines() - current_line + m_extra_forward_lines);
+    reposition_cursor(true);
+    auto current_line = num_lines() - 1;
+    VT::clear_lines(current_line, m_extra_forward_lines);
     m_extra_forward_lines = 0;
     m_extra_forward_lines = 0;
     reposition_cursor();
     reposition_cursor();
 };
 };
@@ -1047,13 +1060,21 @@ void Editor::refresh_display()
     }
     }
     // We might be at the last line, and have more than one line;
     // We might be at the last line, and have more than one line;
     // Refreshing the display will cause the terminal to scroll,
     // Refreshing the display will cause the terminal to scroll,
-    // so note that fact and bring origin up.
+    // so note that fact and bring origin up, making sure to
+    // reserve the space for however many lines we move it up.
     auto current_num_lines = num_lines();
     auto current_num_lines = num_lines();
-    if (m_origin_row + current_num_lines > m_num_lines + 1) {
-        if (current_num_lines > m_num_lines)
+    if (m_origin_row + current_num_lines > m_num_lines) {
+        if (current_num_lines > m_num_lines) {
+            for (size_t i = 0; i < m_num_lines; ++i)
+                putc('\n', stderr);
             m_origin_row = 0;
             m_origin_row = 0;
-        else
+        } else {
+            auto old_origin_row = m_origin_row;
             m_origin_row = m_num_lines - current_num_lines + 1;
             m_origin_row = m_num_lines - current_num_lines + 1;
+            for (size_t i = 0; i < old_origin_row - m_origin_row; ++i)
+                putc('\n', stderr);
+        }
+        fflush(stderr);
     }
     }
     // Do not call hook on pure cursor movement.
     // Do not call hook on pure cursor movement.
     if (m_cached_prompt_valid && !m_refresh_needed && m_pending_chars.size() == 0) {
     if (m_cached_prompt_valid && !m_refresh_needed && m_pending_chars.size() == 0) {
@@ -1172,8 +1193,16 @@ void Editor::reposition_cursor(bool to_end)
     auto line = cursor_line() - 1;
     auto line = cursor_line() - 1;
     auto column = offset_in_line();
     auto column = offset_in_line();
 
 
+    ASSERT(column + m_origin_column <= m_num_columns);
     VT::move_absolute(line + m_origin_row, column + m_origin_column);
     VT::move_absolute(line + m_origin_row, column + m_origin_column);
 
 
+    if (line + m_origin_row > m_num_lines) {
+        for (size_t i = m_num_lines; i < line + m_origin_row; ++i)
+            fputc('\n', stderr);
+        m_origin_row -= line + m_origin_row - m_num_lines;
+        VT::move_relative(0, column + m_origin_column);
+    }
+
     m_cursor = saved_cursor;
     m_cursor = saved_cursor;
 }
 }
 
 
@@ -1622,4 +1651,15 @@ size_t StringMetrics::lines_with_addition(const StringMetrics& offset, size_t co
 
 
     return lines;
     return lines;
 }
 }
+
+size_t StringMetrics::offset_with_addition(const StringMetrics& offset, size_t column_width) const
+{
+    if (offset.line_lengths.size() > 1)
+        return offset.line_lengths.first() % column_width;
+
+    auto last = line_lengths.last();
+    last += offset.line_lengths.first();
+    return last % column_width;
+}
+
 }
 }

+ 1 - 4
Libraries/LibLine/Editor.h

@@ -349,10 +349,7 @@ private:
         if (cursor > m_cursor)
         if (cursor > m_cursor)
             cursor = m_cursor;
             cursor = m_cursor;
         auto buffer_metrics = actual_rendered_string_metrics(buffer_view().substring_view(0, cursor));
         auto buffer_metrics = actual_rendered_string_metrics(buffer_view().substring_view(0, cursor));
-        if (buffer_metrics.line_lengths.size() > 1)
-            return buffer_metrics.line_lengths.last() % m_num_columns;
-
-        return (buffer_metrics.line_lengths.last() + current_prompt_metrics().line_lengths.last()) % m_num_columns;
+        return current_prompt_metrics().offset_with_addition(buffer_metrics, m_num_columns);
     }
     }
 
 
     void set_origin()
     void set_origin()

+ 1 - 0
Libraries/LibLine/StringMetrics.h

@@ -37,6 +37,7 @@ struct StringMetrics {
     size_t max_line_length { 0 };
     size_t max_line_length { 0 };
 
 
     size_t lines_with_addition(const StringMetrics& offset, size_t column_width) const;
     size_t lines_with_addition(const StringMetrics& offset, size_t column_width) const;
+    size_t offset_with_addition(const StringMetrics& offset, size_t column_width) const;
     void reset()
     void reset()
     {
     {
         line_lengths.clear();
         line_lengths.clear();