mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 19:27:44 +00:00
LibLine: Properly paginate suggestions in XtermSuggestionDisplay
This commit adds back suggestion pagination, and makes it 10x better. Also adds a "< page m of n >" indicator at the bottom if there are more suggestions than would fit in a page. It properly handles cycling forwards and backwards :^)
This commit is contained in:
parent
0446b7e347
commit
3c3edf5831
4 changed files with 95 additions and 4 deletions
|
@ -566,6 +566,7 @@ String Editor::get_line(const String& prompt)
|
||||||
suggest(0, 0, Span::CodepointOriented);
|
suggest(0, 0, Span::CodepointOriented);
|
||||||
m_times_tab_pressed = 0;
|
m_times_tab_pressed = 0;
|
||||||
m_suggestion_manager.reset();
|
m_suggestion_manager.reset();
|
||||||
|
m_suggestion_display->finish();
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -582,6 +583,7 @@ String Editor::get_line(const String& prompt)
|
||||||
}
|
}
|
||||||
m_suggestion_manager.reset();
|
m_suggestion_manager.reset();
|
||||||
suggest(0, 0, Span::CodepointOriented);
|
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
|
m_times_tab_pressed = 0; // Safe to say if we get here, the user didn't press TAB
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ public:
|
||||||
virtual ~SuggestionDisplay() { }
|
virtual ~SuggestionDisplay() { }
|
||||||
virtual void display(const SuggestionManager&) = 0;
|
virtual void display(const SuggestionManager&) = 0;
|
||||||
virtual bool cleanup() = 0;
|
virtual bool cleanup() = 0;
|
||||||
|
virtual void finish() = 0;
|
||||||
virtual void set_initial_prompt_lines(size_t) = 0;
|
virtual void set_initial_prompt_lines(size_t) = 0;
|
||||||
|
|
||||||
virtual void set_vt_size(size_t lines, size_t columns) = 0;
|
virtual void set_vt_size(size_t lines, size_t columns) = 0;
|
||||||
|
@ -67,6 +68,10 @@ public:
|
||||||
virtual ~XtermSuggestionDisplay() override { }
|
virtual ~XtermSuggestionDisplay() override { }
|
||||||
virtual void display(const SuggestionManager&) override;
|
virtual void display(const SuggestionManager&) override;
|
||||||
virtual bool cleanup() override;
|
virtual bool cleanup() override;
|
||||||
|
virtual void finish() override
|
||||||
|
{
|
||||||
|
m_pages.clear();
|
||||||
|
}
|
||||||
|
|
||||||
virtual void set_initial_prompt_lines(size_t lines) override
|
virtual void set_initial_prompt_lines(size_t lines) override
|
||||||
{
|
{
|
||||||
|
@ -77,14 +82,22 @@ public:
|
||||||
{
|
{
|
||||||
m_num_lines = lines;
|
m_num_lines = lines;
|
||||||
m_num_columns = columns;
|
m_num_columns = columns;
|
||||||
|
m_pages.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
size_t fit_to_page_boundary(size_t selection_index);
|
||||||
size_t m_lines_used_for_last_suggestions { 0 };
|
size_t m_lines_used_for_last_suggestions { 0 };
|
||||||
size_t m_num_lines { 0 };
|
size_t m_num_lines { 0 };
|
||||||
size_t m_num_columns { 0 };
|
size_t m_num_columns { 0 };
|
||||||
size_t m_prompt_lines_at_suggestion_initiation { 0 };
|
size_t m_prompt_lines_at_suggestion_initiation { 0 };
|
||||||
size_t m_prompt_length { 0 };
|
size_t m_prompt_length { 0 };
|
||||||
|
|
||||||
|
struct PageRange {
|
||||||
|
size_t start;
|
||||||
|
size_t end;
|
||||||
|
};
|
||||||
|
Vector<PageRange> m_pages;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,12 +83,13 @@ class SuggestionManager {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void set_suggestions(Vector<CompletionSuggestion>&& suggestions);
|
void set_suggestions(Vector<CompletionSuggestion>&& suggestions);
|
||||||
void set_current_suggestion_initiation_index(size_t index);
|
void set_current_suggestion_initiation_index(size_t start_index);
|
||||||
|
|
||||||
size_t count() const { return m_suggestions.size(); }
|
size_t count() const { return m_suggestions.size(); }
|
||||||
size_t display_length() const { return m_last_shown_suggestion_display_length; }
|
size_t display_length() const { return m_last_shown_suggestion_display_length; }
|
||||||
size_t index() const { return m_last_displayed_suggestion_index; }
|
size_t start_index() const { return m_last_displayed_suggestion_index; }
|
||||||
size_t next_index() const { return m_next_suggestion_index; }
|
size_t next_index() const { return m_next_suggestion_index; }
|
||||||
|
void set_start_index(size_t index) const { m_last_displayed_suggestion_index = index; }
|
||||||
|
|
||||||
size_t for_each_suggestion(Function<IterationDecision(const CompletionSuggestion&, size_t)>) const;
|
size_t for_each_suggestion(Function<IterationDecision(const CompletionSuggestion&, size_t)>) const;
|
||||||
|
|
||||||
|
@ -151,7 +152,7 @@ private:
|
||||||
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 };
|
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 };
|
mutable size_t m_last_displayed_suggestion_index { 0 };
|
||||||
size_t m_selected_suggestion_index { 0 };
|
size_t m_selected_suggestion_index { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,9 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/BinarySearch.h>
|
||||||
#include <AK/Function.h>
|
#include <AK/Function.h>
|
||||||
|
#include <AK/StringBuilder.h>
|
||||||
#include <LibLine/SuggestionDisplay.h>
|
#include <LibLine/SuggestionDisplay.h>
|
||||||
#include <LibLine/VT.h>
|
#include <LibLine/VT.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -43,7 +45,7 @@ void XtermSuggestionDisplay::display(const SuggestionManager& manager)
|
||||||
});
|
});
|
||||||
|
|
||||||
size_t num_printed = 0;
|
size_t num_printed = 0;
|
||||||
size_t lines_used { 1 };
|
size_t lines_used = 1;
|
||||||
|
|
||||||
VT::save_cursor();
|
VT::save_cursor();
|
||||||
VT::clear_lines(0, m_lines_used_for_last_suggestions);
|
VT::clear_lines(0, m_lines_used_for_last_suggestions);
|
||||||
|
@ -62,8 +64,45 @@ void XtermSuggestionDisplay::display(const SuggestionManager& manager)
|
||||||
lines_used += max_line_count;
|
lines_used += max_line_count;
|
||||||
longest_suggestion_length = 0;
|
longest_suggestion_length = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
VT::move_absolute(max_line_count + m_origin_x, 1);
|
VT::move_absolute(max_line_count + m_origin_x, 1);
|
||||||
|
|
||||||
|
if (m_pages.is_empty()) {
|
||||||
|
size_t num_printed = 0;
|
||||||
|
size_t lines_used = 1;
|
||||||
|
// cache the pages
|
||||||
|
manager.set_start_index(0);
|
||||||
|
size_t page_start = 0;
|
||||||
|
manager.for_each_suggestion([&](auto& suggestion, auto index) {
|
||||||
|
size_t next_column = num_printed + suggestion.text_view.length() + longest_suggestion_length + 2;
|
||||||
|
if (next_column > m_num_columns) {
|
||||||
|
auto lines = (suggestion.text_view.length() + m_num_columns - 1) / m_num_columns;
|
||||||
|
lines_used += lines;
|
||||||
|
num_printed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lines_used + m_prompt_lines_at_suggestion_initiation >= m_num_lines) {
|
||||||
|
m_pages.append({ page_start, index });
|
||||||
|
page_start = index;
|
||||||
|
lines_used = 1;
|
||||||
|
num_printed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spans_entire_line)
|
||||||
|
num_printed += m_num_columns;
|
||||||
|
else
|
||||||
|
num_printed += longest_suggestion_length + 2;
|
||||||
|
|
||||||
|
return IterationDecision::Continue;
|
||||||
|
});
|
||||||
|
// last page
|
||||||
|
m_pages.append({ page_start, manager.count() });
|
||||||
|
}
|
||||||
|
|
||||||
|
auto page_index = fit_to_page_boundary(manager.next_index());
|
||||||
|
|
||||||
|
manager.set_start_index(m_pages[page_index].start);
|
||||||
|
|
||||||
manager.for_each_suggestion([&](auto& suggestion, auto index) {
|
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;
|
||||||
|
|
||||||
|
@ -106,6 +145,23 @@ void XtermSuggestionDisplay::display(const SuggestionManager& manager)
|
||||||
if (m_origin_x + lines_used >= m_num_lines) {
|
if (m_origin_x + lines_used >= m_num_lines) {
|
||||||
m_origin_x = m_num_lines - lines_used;
|
m_origin_x = m_num_lines - lines_used;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_pages.size() > 1) {
|
||||||
|
auto left_arrow = page_index > 0 ? '<' : ' ';
|
||||||
|
auto right_arrow = page_index < m_pages.size() - 1 ? '>' : ' ';
|
||||||
|
auto string = String::format("%c page %d of %d %c", left_arrow, page_index + 1, m_pages.size(), right_arrow);
|
||||||
|
|
||||||
|
if (string.length() > m_num_columns - 1) {
|
||||||
|
// this would overflow into the next line, so just don't print an indicator
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VT::move_absolute(m_origin_x + lines_used, m_num_columns - string.length() - 1);
|
||||||
|
VT::apply_style({ Style::Background(Style::XtermColor::Green) });
|
||||||
|
fputs(string.characters(), stdout);
|
||||||
|
VT::apply_style(Style::reset_style());
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool XtermSuggestionDisplay::cleanup()
|
bool XtermSuggestionDisplay::cleanup()
|
||||||
|
@ -119,4 +175,23 @@ bool XtermSuggestionDisplay::cleanup()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t XtermSuggestionDisplay::fit_to_page_boundary(size_t selection_index)
|
||||||
|
{
|
||||||
|
ASSERT(m_pages.size() > 0);
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
auto* match = binary_search(
|
||||||
|
m_pages.data(), m_pages.size(), { selection_index, selection_index }, [](auto& a, auto& b) -> int {
|
||||||
|
if (a.start >= b.start && a.start < b.end)
|
||||||
|
return 0;
|
||||||
|
return a.start - b.start;
|
||||||
|
},
|
||||||
|
&index);
|
||||||
|
|
||||||
|
if (!match)
|
||||||
|
return m_pages.size() - 1;
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue