mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 04:43:10 +00:00
Merge branch 'cleanup_pager'
Fixes those ugly compiler warnings in the pager
This commit is contained in:
commit
41d4058156
4 changed files with 240 additions and 128 deletions
|
@ -1627,6 +1627,106 @@ static void test_pager_navigation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct pager_layout_testcase_t {
|
||||||
|
size_t width;
|
||||||
|
const wchar_t *expected;
|
||||||
|
|
||||||
|
// Run ourselves as a test case
|
||||||
|
// Set our data on the pager, and then check the rendering
|
||||||
|
// We should have one line, and it should have our expected text
|
||||||
|
void run(pager_t &pager) const {
|
||||||
|
pager.set_term_size(this->width, 24);
|
||||||
|
page_rendering_t rendering = pager.render();
|
||||||
|
const screen_data_t &sd = rendering.screen_data;
|
||||||
|
do_test(sd.line_count() == 1);
|
||||||
|
if (sd.line_count() > 0) {
|
||||||
|
wcstring expected = this->expected;
|
||||||
|
|
||||||
|
// hack: handle the case where ellipsis is not L'\x2026'
|
||||||
|
if (ellipsis_char != L'\x2026') {
|
||||||
|
std::replace(expected.begin(), expected.end(), L'\x2026', ellipsis_char);
|
||||||
|
}
|
||||||
|
|
||||||
|
wcstring text = sd.line(0).to_string();
|
||||||
|
if (text != expected) {
|
||||||
|
fprintf(stderr, "width %lu got <%ls>, expected <%ls>\n", this->width, text.c_str(), expected.c_str());
|
||||||
|
}
|
||||||
|
do_test(text == expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void test_pager_layout() {
|
||||||
|
// These tests are woefully incomplete
|
||||||
|
// They only test the truncation logic for a single completion
|
||||||
|
say(L"Testing pager layout");
|
||||||
|
pager_t pager;
|
||||||
|
|
||||||
|
// These test cases have equal completions and descriptions
|
||||||
|
const completion_t c1(L"abcdefghij", L"1234567890");
|
||||||
|
pager.set_completions(completion_list_t(1, c1));
|
||||||
|
const pager_layout_testcase_t testcases1[] = {
|
||||||
|
{26, L"abcdefghij (1234567890)"},
|
||||||
|
{25, L"abcdefghij (1234567890)"},
|
||||||
|
{24, L"abcdefghij (1234567890)"},
|
||||||
|
{23, L"abcdefghij (12345678…)"},
|
||||||
|
{22, L"abcdefghij (1234567…)"},
|
||||||
|
{21, L"abcdefghij (123456…)"},
|
||||||
|
{20, L"abcdefghij (12345…)"},
|
||||||
|
{19, L"abcdefghij (1234…)"},
|
||||||
|
{18, L"abcdefgh… (1234…)"},
|
||||||
|
{17, L"abcdefg… (1234…)"},
|
||||||
|
{16, L"abcdefg… (123…)"},
|
||||||
|
{0, NULL} // sentinel terminator
|
||||||
|
};
|
||||||
|
for (size_t i=0; testcases1[i].expected != NULL; i++) {
|
||||||
|
testcases1[i].run(pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These test cases have heavyweight completions
|
||||||
|
const completion_t c2(L"abcdefghijklmnopqrs", L"1");
|
||||||
|
pager.set_completions(completion_list_t(1, c2));
|
||||||
|
const pager_layout_testcase_t testcases2[] = {
|
||||||
|
{26, L"abcdefghijklmnopqrs (1)"},
|
||||||
|
{25, L"abcdefghijklmnopqrs (1)"},
|
||||||
|
{24, L"abcdefghijklmnopqrs (1)"},
|
||||||
|
{23, L"abcdefghijklmnopq… (1)"},
|
||||||
|
{22, L"abcdefghijklmnop… (1)"},
|
||||||
|
{21, L"abcdefghijklmno… (1)"},
|
||||||
|
{20, L"abcdefghijklmn… (1)"},
|
||||||
|
{19, L"abcdefghijklm… (1)"},
|
||||||
|
{18, L"abcdefghijkl… (1)"},
|
||||||
|
{17, L"abcdefghijk… (1)"},
|
||||||
|
{16, L"abcdefghij… (1)"},
|
||||||
|
{0, NULL} // sentinel terminator
|
||||||
|
};
|
||||||
|
for (size_t i=0; testcases2[i].expected != NULL; i++) {
|
||||||
|
testcases2[i].run(pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These test cases have no descriptions
|
||||||
|
const completion_t c3(L"abcdefghijklmnopqrst", L"");
|
||||||
|
pager.set_completions(completion_list_t(1, c3));
|
||||||
|
const pager_layout_testcase_t testcases3[] = {
|
||||||
|
{26, L"abcdefghijklmnopqrst"},
|
||||||
|
{25, L"abcdefghijklmnopqrst"},
|
||||||
|
{24, L"abcdefghijklmnopqrst"},
|
||||||
|
{23, L"abcdefghijklmnopqrst"},
|
||||||
|
{22, L"abcdefghijklmnopqrst"},
|
||||||
|
{21, L"abcdefghijklmnopqrst"},
|
||||||
|
{20, L"abcdefghijklmnopqrst"},
|
||||||
|
{19, L"abcdefghijklmnopqr…"},
|
||||||
|
{18, L"abcdefghijklmnopq…"},
|
||||||
|
{17, L"abcdefghijklmnop…"},
|
||||||
|
{16, L"abcdefghijklmno…"},
|
||||||
|
{0, NULL} // sentinel terminator
|
||||||
|
};
|
||||||
|
for (size_t i=0; testcases3[i].expected != NULL; i++) {
|
||||||
|
testcases3[i].run(pager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
enum word_motion_t { word_motion_left, word_motion_right };
|
enum word_motion_t { word_motion_left, word_motion_right };
|
||||||
static void test_1_word_motion(word_motion_t motion, move_word_style_t style,
|
static void test_1_word_motion(word_motion_t motion, move_word_style_t style,
|
||||||
const wcstring &test) {
|
const wcstring &test) {
|
||||||
|
@ -4123,6 +4223,7 @@ int main(int argc, char **argv) {
|
||||||
if (should_test_function("test")) test_test();
|
if (should_test_function("test")) test_test();
|
||||||
if (should_test_function("path")) test_path();
|
if (should_test_function("path")) test_path();
|
||||||
if (should_test_function("pager_navigation")) test_pager_navigation();
|
if (should_test_function("pager_navigation")) test_pager_navigation();
|
||||||
|
if (should_test_function("pager_layout")) test_pager_layout();
|
||||||
if (should_test_function("word_motion")) test_word_motion();
|
if (should_test_function("word_motion")) test_word_motion();
|
||||||
if (should_test_function("is_potential_path")) test_is_potential_path();
|
if (should_test_function("is_potential_path")) test_is_potential_path();
|
||||||
if (should_test_function("colors")) test_colors();
|
if (should_test_function("colors")) test_colors();
|
||||||
|
|
230
src/pager.cpp
230
src/pager.cpp
|
@ -6,6 +6,7 @@
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
#include <wctype.h>
|
#include <wctype.h>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <numeric>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
@ -25,6 +26,9 @@ typedef std::vector<comp_t> comp_info_list_t;
|
||||||
/// The minimum width (in characters) the terminal must to show completions at all.
|
/// The minimum width (in characters) the terminal must to show completions at all.
|
||||||
#define PAGER_MIN_WIDTH 16
|
#define PAGER_MIN_WIDTH 16
|
||||||
|
|
||||||
|
/// Minimum height to show completions
|
||||||
|
#define PAGER_MIN_HEIGHT 4
|
||||||
|
|
||||||
/// The maximum number of columns of completion to attempt to fit onto the screen.
|
/// The maximum number of columns of completion to attempt to fit onto the screen.
|
||||||
#define PAGER_MAX_COLS 6
|
#define PAGER_MAX_COLS 6
|
||||||
|
|
||||||
|
@ -43,17 +47,6 @@ static size_t divide_round_up(size_t numer, size_t denom) {
|
||||||
return numer / denom + (has_rem ? 1 : 0);
|
return numer / denom + (has_rem ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function calculates the minimum width for each completion entry in the specified
|
|
||||||
/// array_list. This width depends on the terminal size, so this function should be called when the
|
|
||||||
/// terminal changes size.
|
|
||||||
void pager_t::recalc_min_widths(comp_info_list_t *lst) const {
|
|
||||||
for (size_t i = 0; i < lst->size(); i++) {
|
|
||||||
comp_t *c = &lst->at(i);
|
|
||||||
c->min_width = mini(c->desc_width, maxi((size_t)0, available_term_width / 3 - 2)) +
|
|
||||||
mini(c->desc_width, maxi((size_t)0, available_term_width / 5 - 4)) + 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Print the specified string, but use at most the specified amount of space. If the whole string
|
/// Print the specified string, but use at most the specified amount of space. If the whole string
|
||||||
/// can't be fitted, ellipsize it.
|
/// can't be fitted, ellipsize it.
|
||||||
///
|
///
|
||||||
|
@ -61,47 +54,68 @@ void pager_t::recalc_min_widths(comp_info_list_t *lst) const {
|
||||||
/// \param color the color to apply to every printed character
|
/// \param color the color to apply to every printed character
|
||||||
/// \param max the maximum space that may be used for printing
|
/// \param max the maximum space that may be used for printing
|
||||||
/// \param has_more if this flag is true, this is not the entire string, and the string should be
|
/// \param has_more if this flag is true, this is not the entire string, and the string should be
|
||||||
/// ellisiszed even if the string fits but takes up the whole space.
|
/// ellipsized even if the string fits but takes up the whole space.
|
||||||
static int print_max(const wcstring &str, highlight_spec_t color, int max, bool has_more,
|
static size_t print_max(const wcstring &str, highlight_spec_t color, size_t max, bool has_more,
|
||||||
line_t *line) {
|
line_t *line) {
|
||||||
int written = 0;
|
size_t remaining = max;
|
||||||
for (size_t i = 0; i < str.size(); i++) {
|
for (size_t i = 0; i < str.size(); i++) {
|
||||||
wchar_t c = str.at(i);
|
wchar_t c = str.at(i);
|
||||||
|
int iwidth_c = fish_wcwidth(c);
|
||||||
|
if (iwidth_c < 0) {
|
||||||
|
// skip non-printable characters
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
size_t width_c = size_t(iwidth_c);
|
||||||
|
|
||||||
if (written + fish_wcwidth(c) > max) break;
|
if (width_c > remaining) break;
|
||||||
if ((written + fish_wcwidth(c) == max) && (has_more || i + 1 < str.size())) {
|
|
||||||
|
if ((width_c == remaining) && (has_more || i + 1 < str.size())) {
|
||||||
line->append(ellipsis_char, color);
|
line->append(ellipsis_char, color);
|
||||||
written += fish_wcwidth(ellipsis_char);
|
int ellipsis_width = fish_wcwidth(ellipsis_char);
|
||||||
|
remaining -= std::min(remaining, size_t(ellipsis_width));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
line->append(c, color);
|
line->append(c, color);
|
||||||
written += fish_wcwidth(c);
|
assert(remaining >= width_c);
|
||||||
|
remaining -= width_c;
|
||||||
}
|
}
|
||||||
return written;
|
|
||||||
|
// return how much we consumed
|
||||||
|
assert(remaining <= max);
|
||||||
|
return max - remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print the specified item using at the specified amount of space.
|
/// Print the specified item using at the specified amount of space.
|
||||||
line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, size_t row,
|
line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, size_t row,
|
||||||
size_t column, int width, bool secondary, bool selected,
|
size_t column, size_t width, bool secondary, bool selected,
|
||||||
page_rendering_t *rendering) const {
|
page_rendering_t *rendering) const {
|
||||||
UNUSED(column);
|
UNUSED(column);
|
||||||
UNUSED(row);
|
UNUSED(row);
|
||||||
UNUSED(rendering);
|
UNUSED(rendering);
|
||||||
int comp_width, desc_width;
|
size_t comp_width, desc_width;
|
||||||
line_t line_data;
|
line_t line_data;
|
||||||
|
|
||||||
if (c->pref_width <= width) {
|
if (c->preferred_width() <= width) {
|
||||||
// The entry fits, we give it as much space as it wants.
|
// The entry fits, we give it as much space as it wants.
|
||||||
comp_width = c->comp_width;
|
comp_width = c->comp_width;
|
||||||
desc_width = c->desc_width;
|
desc_width = c->desc_width;
|
||||||
} else {
|
} else {
|
||||||
// The completion and description won't fit on the allocated space. Give a maximum of 2/3 of
|
// The completion and description won't fit on the allocated space. Give a maximum of 2/3 of
|
||||||
// the space to the completion, and whatever is left to the description.
|
// the space to the completion, and whatever is left to the description
|
||||||
int desc_all = c->desc_width ? c->desc_width + 4 : 0;
|
// This expression is an overflow-safe way of calculating (width-4)*2/3
|
||||||
|
size_t width_minus_spacer = width - std::min(width, size_t(4));
|
||||||
|
size_t two_thirds_width = (width_minus_spacer / 3) * 2 + ((width_minus_spacer % 3) * 2) / 3;
|
||||||
|
comp_width = std::min(c->comp_width, two_thirds_width);
|
||||||
|
|
||||||
comp_width = maxi(mini((int)c->comp_width, 2 * (width - 4) / 3), width - desc_all);
|
// If the description is short, give the completion the remaining space
|
||||||
desc_width = c->desc_width ? width - 4 - comp_width : 0;
|
size_t desc_punct_width = c->description_punctuated_width();
|
||||||
|
if (width > desc_punct_width) {
|
||||||
|
comp_width = std::max(comp_width, width - desc_punct_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The description gets what's left
|
||||||
|
assert(comp_width <= width);
|
||||||
}
|
}
|
||||||
|
|
||||||
int bg_color = secondary ? highlight_spec_pager_secondary : highlight_spec_normal;
|
int bg_color = secondary ? highlight_spec_pager_secondary : highlight_spec_normal;
|
||||||
|
@ -109,38 +123,48 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s
|
||||||
bg_color = highlight_spec_search_match;
|
bg_color = highlight_spec_search_match;
|
||||||
}
|
}
|
||||||
|
|
||||||
int written = 0;
|
// Print the completion part
|
||||||
|
size_t comp_remaining = comp_width;
|
||||||
for (size_t i = 0; i < c->comp.size(); i++) {
|
for (size_t i = 0; i < c->comp.size(); i++) {
|
||||||
const wcstring &comp = c->comp.at(i);
|
const wcstring &comp = c->comp.at(i);
|
||||||
|
if (i > 0) {
|
||||||
|
comp_remaining -= print_max(PAGER_SPACER_STRING, highlight_spec_normal, comp_remaining,
|
||||||
|
true /* has_more */, &line_data);
|
||||||
|
}
|
||||||
|
|
||||||
if (i != 0)
|
highlight_spec_t packed_color =
|
||||||
written += print_max(PAGER_SPACER_STRING, highlight_spec_normal, comp_width - written,
|
highlight_spec_pager_prefix | highlight_make_background(bg_color);
|
||||||
true /* has_more */, &line_data);
|
comp_remaining -=
|
||||||
|
print_max(prefix, packed_color, comp_remaining, !comp.empty(), &line_data);
|
||||||
int packed_color = highlight_spec_pager_prefix | highlight_make_background(bg_color);
|
|
||||||
written += print_max(prefix, packed_color, comp_width - written, !comp.empty(), &line_data);
|
|
||||||
|
|
||||||
packed_color = highlight_spec_pager_completion | highlight_make_background(bg_color);
|
packed_color = highlight_spec_pager_completion | highlight_make_background(bg_color);
|
||||||
written +=
|
comp_remaining -=
|
||||||
print_max(comp, packed_color, comp_width - written, i + 1 < c->comp.size(), &line_data);
|
print_max(comp, packed_color, comp_remaining, i + 1 < c->comp.size(), &line_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (desc_width) {
|
size_t desc_remaining = width - comp_width + comp_remaining;
|
||||||
int packed_color = highlight_spec_pager_description | highlight_make_background(bg_color);
|
if (c->desc_width > 0 && desc_remaining > 4) {
|
||||||
while (written < (width - desc_width - 2)) // the 2 here refers to the parenthesis below
|
highlight_spec_t desc_color =
|
||||||
{
|
highlight_spec_pager_description | highlight_make_background(bg_color);
|
||||||
written += print_max(L" ", packed_color, 1, false, &line_data);
|
highlight_spec_t punct_color =
|
||||||
|
highlight_spec_pager_completion | highlight_make_background(bg_color);
|
||||||
|
|
||||||
|
// always have at least two spaces to separate completion and description
|
||||||
|
desc_remaining -= print_max(L" ", punct_color, 2, false, &line_data);
|
||||||
|
|
||||||
|
// right-justify the description by adding spaces
|
||||||
|
// the 2 here refers to the parenthesis below
|
||||||
|
while (desc_remaining > c->desc_width + 2) {
|
||||||
|
desc_remaining -= print_max(L" ", punct_color, 1, false, &line_data);
|
||||||
}
|
}
|
||||||
// hack - this just works around the issue
|
|
||||||
print_max(L"(", highlight_spec_pager_completion | highlight_make_background(bg_color), 1,
|
assert(desc_remaining >= 2);
|
||||||
false, &line_data);
|
desc_remaining -= print_max(L"(", punct_color, 1, false, &line_data);
|
||||||
print_max(c->desc, packed_color, desc_width, false, &line_data);
|
desc_remaining -= print_max(c->desc, desc_color, desc_remaining - 1, false, &line_data);
|
||||||
print_max(L")", highlight_spec_pager_completion | highlight_make_background(bg_color), 1,
|
desc_remaining -= print_max(L")", punct_color, 1, false, &line_data);
|
||||||
false, &line_data);
|
|
||||||
} else {
|
} else {
|
||||||
while (written < width) {
|
// No description, or it won't fit. Just add spaces.
|
||||||
written += print_max(L" ", 0, 1, false, &line_data);
|
print_max(wcstring(desc_remaining, L' '), 0, desc_remaining, false, &line_data);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return line_data;
|
return line_data;
|
||||||
|
@ -150,12 +174,12 @@ line_t pager_t::completion_print_item(const wcstring &prefix, const comp_t *c, s
|
||||||
/// style.
|
/// style.
|
||||||
///
|
///
|
||||||
/// \param cols number of columns to print in
|
/// \param cols number of columns to print in
|
||||||
/// \param width_per_column An array specifying the width of each column
|
/// \param width_by_column An array specifying the width of each column
|
||||||
/// \param row_start The first row to print
|
/// \param row_start The first row to print
|
||||||
/// \param row_stop the row after the last row to print
|
/// \param row_stop the row after the last row to print
|
||||||
/// \param prefix The string to print before each completion
|
/// \param prefix The string to print before each completion
|
||||||
/// \param lst The list of completions to print
|
/// \param lst The list of completions to print
|
||||||
void pager_t::completion_print(size_t cols, int *width_per_column, size_t row_start,
|
void pager_t::completion_print(size_t cols, const size_t *width_by_column, size_t row_start,
|
||||||
size_t row_stop, const wcstring &prefix, const comp_info_list_t &lst,
|
size_t row_stop, const wcstring &prefix, const comp_info_list_t &lst,
|
||||||
page_rendering_t *rendering) const {
|
page_rendering_t *rendering) const {
|
||||||
// Teach the rendering about the rows it printed.
|
// Teach the rendering about the rows it printed.
|
||||||
|
@ -164,14 +188,12 @@ void pager_t::completion_print(size_t cols, int *width_per_column, size_t row_st
|
||||||
rendering->row_start = row_start;
|
rendering->row_start = row_start;
|
||||||
rendering->row_end = row_stop;
|
rendering->row_end = row_stop;
|
||||||
|
|
||||||
size_t rows = (lst.size() - 1) / cols + 1;
|
size_t rows = divide_round_up(lst.size(), cols);
|
||||||
|
|
||||||
size_t effective_selected_idx = this->visual_selected_completion_index(rows, cols);
|
size_t effective_selected_idx = this->visual_selected_completion_index(rows, cols);
|
||||||
|
|
||||||
for (size_t row = row_start; row < row_stop; row++) {
|
for (size_t row = row_start; row < row_stop; row++) {
|
||||||
for (size_t col = 0; col < cols; col++) {
|
for (size_t col = 0; col < cols; col++) {
|
||||||
int is_last = (col == (cols - 1));
|
|
||||||
|
|
||||||
if (lst.size() <= col * rows + row) continue;
|
if (lst.size() <= col * rows + row) continue;
|
||||||
|
|
||||||
size_t idx = col * rows + row;
|
size_t idx = col * rows + row;
|
||||||
|
@ -179,10 +201,8 @@ void pager_t::completion_print(size_t cols, int *width_per_column, size_t row_st
|
||||||
bool is_selected = (idx == effective_selected_idx);
|
bool is_selected = (idx == effective_selected_idx);
|
||||||
|
|
||||||
// Print this completion on its own "line".
|
// Print this completion on its own "line".
|
||||||
line_t line = completion_print_item(
|
line_t line = completion_print_item(prefix, el, row, col, width_by_column[col], row % 2,
|
||||||
prefix, el, row, col,
|
is_selected, rendering);
|
||||||
width_per_column[col] - (is_last ? 0 : PAGER_SPACER_STRING_WIDTH), row % 2,
|
|
||||||
is_selected, rendering);
|
|
||||||
|
|
||||||
// If there's more to come, append two spaces.
|
// If there's more to come, append two spaces.
|
||||||
if (col + 1 < cols) {
|
if (col + 1 < cols) {
|
||||||
|
@ -293,12 +313,7 @@ void pager_t::measure_completion_infos(comp_info_list_t *infos, const wcstring &
|
||||||
|
|
||||||
// Compute desc_width.
|
// Compute desc_width.
|
||||||
comp->desc_width = fish_wcswidth(comp->desc.c_str());
|
comp->desc_width = fish_wcswidth(comp->desc.c_str());
|
||||||
|
|
||||||
// Compute preferred width.
|
|
||||||
comp->pref_width = comp->comp_width + comp->desc_width + (comp->desc_width ? 4 : 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
recalc_min_widths(infos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Indicates if the given completion info passes any filtering we have.
|
// Indicates if the given completion info passes any filtering we have.
|
||||||
|
@ -359,23 +374,21 @@ void pager_t::set_term_size(int w, int h) {
|
||||||
assert(h > 0);
|
assert(h > 0);
|
||||||
available_term_width = w;
|
available_term_width = w;
|
||||||
available_term_height = h;
|
available_term_height = h;
|
||||||
recalc_min_widths(&completion_infos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to print the list of completions l with the prefix prefix using cols as the number of
|
/// Try to print the list of completions lst with the prefix prefix using cols as the number of
|
||||||
/// columns. Return true if the completion list was printed, false if the terminal is too narrow for
|
/// columns. Return true if the completion list was printed, false if the terminal is too narrow for
|
||||||
/// the specified number of columns. Always succeeds if cols is 1.
|
/// the specified number of columns. Always succeeds if cols is 1.
|
||||||
bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst,
|
bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const comp_info_list_t &lst,
|
||||||
page_rendering_t *rendering, size_t suggested_start_row) const {
|
page_rendering_t *rendering, size_t suggested_start_row) const {
|
||||||
|
assert(cols > 0);
|
||||||
// The calculated preferred width of each column.
|
// The calculated preferred width of each column.
|
||||||
int pref_width[PAGER_MAX_COLS] = {0};
|
size_t width_by_column[PAGER_MAX_COLS] = {0};
|
||||||
// The calculated minimum width of each column.
|
|
||||||
int min_width[PAGER_MAX_COLS] = {0};
|
|
||||||
// If the list can be printed with this width, width will contain the width of each column.
|
|
||||||
int *width = pref_width;
|
|
||||||
|
|
||||||
// Set to one if the list should be printed at this width.
|
// Skip completions on tiny terminals.
|
||||||
bool print = false;
|
if (this->available_term_width < PAGER_MIN_WIDTH ||
|
||||||
|
this->available_term_height < PAGER_MIN_HEIGHT)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Compute the effective term width and term height, accounting for disclosure.
|
// Compute the effective term width and term height, accounting for disclosure.
|
||||||
size_t term_width = this->available_term_width;
|
size_t term_width = this->available_term_width;
|
||||||
|
@ -398,52 +411,34 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||||
|
|
||||||
// If we have only one row remaining to disclose, then squelch the comment row. This prevents us
|
// If we have only one row remaining to disclose, then squelch the comment row. This prevents us
|
||||||
// from consuming a line to show "...and 1 more row".
|
// from consuming a line to show "...and 1 more row".
|
||||||
if (!this->fully_disclosed && rendering->remaining_to_disclose == 1) {
|
if (rendering->remaining_to_disclose == 1) {
|
||||||
term_height += 1;
|
term_height += 1;
|
||||||
rendering->remaining_to_disclose = 0;
|
rendering->remaining_to_disclose = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t pref_tot_width = 0;
|
|
||||||
size_t min_tot_width = 0;
|
|
||||||
|
|
||||||
// Skip completions on tiny terminals.
|
|
||||||
if (term_width < PAGER_MIN_WIDTH) return true;
|
|
||||||
|
|
||||||
// Calculate how wide the list would be.
|
// Calculate how wide the list would be.
|
||||||
for (size_t col = 0; col < cols; col++) {
|
for (size_t col = 0; col < cols; col++) {
|
||||||
for (size_t row = 0; row < row_count; row++) {
|
for (size_t row = 0; row < row_count; row++) {
|
||||||
int pref, min;
|
const size_t comp_idx = col * row_count + row;
|
||||||
const comp_t *c;
|
if (comp_idx >= lst.size()) continue;
|
||||||
if (lst.size() <= col * row_count + row) continue;
|
const comp_t &c = lst.at(comp_idx);
|
||||||
|
width_by_column[col] = std::max(width_by_column[col], c.preferred_width());
|
||||||
c = &lst.at(col * row_count + row);
|
|
||||||
pref = c->pref_width;
|
|
||||||
min = c->min_width;
|
|
||||||
|
|
||||||
if (col != cols - 1) {
|
|
||||||
pref += 2;
|
|
||||||
min += 2;
|
|
||||||
}
|
|
||||||
min_width[col] = maxi(min_width[col], min);
|
|
||||||
pref_width[col] = maxi(pref_width[col], pref);
|
|
||||||
}
|
}
|
||||||
min_tot_width += min_width[col];
|
|
||||||
pref_tot_width += pref_width[col];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool print;
|
||||||
|
assert(cols >= 1);
|
||||||
// Force fit if one column.
|
// Force fit if one column.
|
||||||
if (cols == 1) {
|
if (cols == 1) {
|
||||||
if (pref_tot_width > term_width) {
|
width_by_column[0] = std::min(width_by_column[0], term_width);
|
||||||
pref_width[0] = term_width;
|
|
||||||
}
|
|
||||||
width = pref_width;
|
|
||||||
print = true;
|
|
||||||
} else if (pref_tot_width <= term_width) {
|
|
||||||
// Terminal is wide enough. Print the list!
|
|
||||||
width = pref_width;
|
|
||||||
print = true;
|
print = true;
|
||||||
|
} else {
|
||||||
|
// Compute total preferred width, plus spacing
|
||||||
|
assert(cols > 0);
|
||||||
|
size_t total_width_needed = std::accumulate(width_by_column, width_by_column + cols, 0);
|
||||||
|
total_width_needed += (cols - 1) * PAGER_SPACER_STRING_WIDTH;
|
||||||
|
print = (total_width_needed <= term_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!print) {
|
if (!print) {
|
||||||
return false; // no need to continue
|
return false; // no need to continue
|
||||||
}
|
}
|
||||||
|
@ -467,18 +462,17 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||||
assert(stop_row >= start_row);
|
assert(stop_row >= start_row);
|
||||||
assert(stop_row <= row_count);
|
assert(stop_row <= row_count);
|
||||||
assert(stop_row - start_row <= term_height);
|
assert(stop_row - start_row <= term_height);
|
||||||
completion_print(cols, width, start_row, stop_row, prefix, lst, rendering);
|
completion_print(cols, width_by_column, start_row, stop_row, prefix, lst, rendering);
|
||||||
|
|
||||||
// Ellipsis helper string. Either empty or containing the ellipsis char.
|
// Ellipsis helper string. Either empty or containing the ellipsis char.
|
||||||
const wchar_t ellipsis_string[] = {ellipsis_char == L'\x2026' ? L'\x2026' : L'\0', L'\0'};
|
const wchar_t ellipsis_string[] = {ellipsis_char == L'\x2026' ? L'\x2026' : L'\0', L'\0'};
|
||||||
|
|
||||||
// Add the progress line. It's a "more to disclose" line if necessary, or a row listing if
|
// Add the progress line. It's a "more to disclose" line if necessary, or a row listing if
|
||||||
// it's scrollable; otherwise ignore it.
|
// it's scrollable; otherwise ignore it.
|
||||||
|
// We should never have one row remaining to disclose (else we would have just disclosed it)
|
||||||
wcstring progress_text;
|
wcstring progress_text;
|
||||||
if (rendering->remaining_to_disclose == 1) {
|
assert(rendering->remaining_to_disclose != 1);
|
||||||
// I don't expect this case to ever happen.
|
if (rendering->remaining_to_disclose > 1) {
|
||||||
progress_text = format_string(_(L"%lsand 1 more row"), ellipsis_string);
|
|
||||||
} else if (rendering->remaining_to_disclose > 1) {
|
|
||||||
progress_text = format_string(_(L"%lsand %lu more rows"), ellipsis_string,
|
progress_text = format_string(_(L"%lsand %lu more rows"), ellipsis_string,
|
||||||
(unsigned long)rendering->remaining_to_disclose);
|
(unsigned long)rendering->remaining_to_disclose);
|
||||||
} else if (start_row > 0 || stop_row < row_count) {
|
} else if (start_row > 0 || stop_row < row_count) {
|
||||||
|
@ -494,9 +488,9 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||||
|
|
||||||
if (!progress_text.empty()) {
|
if (!progress_text.empty()) {
|
||||||
line_t &line = rendering->screen_data.add_line();
|
line_t &line = rendering->screen_data.add_line();
|
||||||
print_max(progress_text, highlight_spec_pager_progress |
|
highlight_spec_t spec = highlight_spec_pager_progress |
|
||||||
highlight_make_background(highlight_spec_pager_progress),
|
highlight_make_background(highlight_spec_pager_progress);
|
||||||
term_width, true /* has_more */, &line);
|
print_max(progress_text, spec, term_width, true /* has_more */, &line);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (search_field_shown) {
|
if (search_field_shown) {
|
||||||
|
@ -509,10 +503,12 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||||
line_t *search_field = &rendering->screen_data.insert_line_at_index(0);
|
line_t *search_field = &rendering->screen_data.insert_line_at_index(0);
|
||||||
|
|
||||||
// We limit the width to term_width - 1.
|
// We limit the width to term_width - 1.
|
||||||
int search_field_written = print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal,
|
size_t search_field_remaining = term_width - 1;
|
||||||
term_width - 1, false, search_field);
|
search_field_remaining -= print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal,
|
||||||
print_max(search_field_text, highlight_modifier_force_underline,
|
search_field_remaining, false, search_field);
|
||||||
term_width - search_field_written - 1, false, search_field);
|
|
||||||
|
search_field_remaining -= print_max(search_field_text, highlight_modifier_force_underline,
|
||||||
|
search_field_remaining, false, search_field);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
29
src/pager.h
29
src/pager.h
|
@ -43,8 +43,6 @@ class page_rendering_t {
|
||||||
#define PAGER_UNDISCLOSED_MAX_ROWS 4
|
#define PAGER_UNDISCLOSED_MAX_ROWS 4
|
||||||
|
|
||||||
typedef std::vector<completion_t> completion_list_t;
|
typedef std::vector<completion_t> completion_list_t;
|
||||||
page_rendering_t render_completions(const completion_list_t &raw_completions,
|
|
||||||
const wcstring &prefix);
|
|
||||||
|
|
||||||
class pager_t {
|
class pager_t {
|
||||||
size_t available_term_width;
|
size_t available_term_width;
|
||||||
|
@ -76,19 +74,30 @@ class pager_t {
|
||||||
size_t comp_width;
|
size_t comp_width;
|
||||||
/// On-screen width of the description information.
|
/// On-screen width of the description information.
|
||||||
size_t desc_width;
|
size_t desc_width;
|
||||||
/// Preferred total width.
|
|
||||||
size_t pref_width;
|
|
||||||
/// Minimum acceptable width.
|
/// Minimum acceptable width.
|
||||||
size_t min_width;
|
//size_t min_width;
|
||||||
|
|
||||||
comp_t()
|
comp_t()
|
||||||
: comp(),
|
: comp(),
|
||||||
desc(),
|
desc(),
|
||||||
representative(L""),
|
representative(L""),
|
||||||
comp_width(0),
|
comp_width(0),
|
||||||
desc_width(0),
|
desc_width(0)
|
||||||
pref_width(0),
|
{}
|
||||||
min_width(0) {}
|
|
||||||
|
// Our text looks like this:
|
||||||
|
// completion (description)
|
||||||
|
// Two spaces separating, plus parens, yields 4 total extra space
|
||||||
|
// but only if we have a description of course
|
||||||
|
size_t description_punctuated_width() const {
|
||||||
|
return this->desc_width + (this->desc_width ? 4 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the preferred width, containing the sum of the
|
||||||
|
// width of the completion, separator, description
|
||||||
|
size_t preferred_width() const {
|
||||||
|
return this->comp_width + this->description_punctuated_width();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -110,11 +119,11 @@ class pager_t {
|
||||||
|
|
||||||
bool completion_info_passes_filter(const comp_t &info) const;
|
bool completion_info_passes_filter(const comp_t &info) const;
|
||||||
|
|
||||||
void completion_print(size_t cols, int *width_per_column, size_t row_start, size_t row_stop,
|
void completion_print(size_t cols, const size_t *width_per_column, size_t row_start, size_t row_stop,
|
||||||
const wcstring &prefix, const comp_info_list_t &lst,
|
const wcstring &prefix, const comp_info_list_t &lst,
|
||||||
page_rendering_t *rendering) const;
|
page_rendering_t *rendering) const;
|
||||||
line_t completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, size_t column,
|
line_t completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, size_t column,
|
||||||
int width, bool secondary, bool selected,
|
size_t width, bool secondary, bool selected,
|
||||||
page_rendering_t *rendering) const;
|
page_rendering_t *rendering) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -54,6 +54,10 @@ struct line_t {
|
||||||
text.insert(text.end(), line.text.begin(), line.text.end());
|
text.insert(text.end(), line.text.begin(), line.text.end());
|
||||||
colors.insert(colors.end(), line.colors.begin(), line.colors.end());
|
colors.insert(colors.end(), line.colors.begin(), line.colors.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wcstring to_string() const {
|
||||||
|
return wcstring(this->text.begin(), this->text.end());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A class representing screen contents.
|
/// A class representing screen contents.
|
||||||
|
@ -89,7 +93,9 @@ class screen_data_t {
|
||||||
|
|
||||||
line_t &line(size_t idx) { return line_datas.at(idx); }
|
line_t &line(size_t idx) { return line_datas.at(idx); }
|
||||||
|
|
||||||
size_t line_count(void) { return line_datas.size(); }
|
const line_t &line(size_t idx) const { return line_datas.at(idx); }
|
||||||
|
|
||||||
|
size_t line_count() const { return line_datas.size(); }
|
||||||
|
|
||||||
void append_lines(const screen_data_t &d) {
|
void append_lines(const screen_data_t &d) {
|
||||||
this->line_datas.insert(this->line_datas.end(), d.line_datas.begin(), d.line_datas.end());
|
this->line_datas.insert(this->line_datas.end(), d.line_datas.begin(), d.line_datas.end());
|
||||||
|
|
Loading…
Reference in a new issue