diff --git a/highlight.cpp b/highlight.cpp index 31ea2c8e0..f2ae460c0 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -361,7 +361,10 @@ bool plain_statement_get_expanded_command(const wcstring &src, const parse_node_ rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) { rgb_color_t result = rgb_color_t::normal(); - + + /* If sloppy_background is set, then we look at the foreground color even if is_background is set */ + bool treat_as_background = is_background && ! (highlight & highlight_modifier_sloppy_background); + /* Get the primary variable */ size_t idx = highlight_get_primary(highlight); if (idx >= VAR_COUNT) @@ -377,9 +380,9 @@ rgb_color_t highlight_get_color(highlight_spec_t highlight, bool is_background) val_wstr = env_get_string(highlight_var[0]); if (! val_wstr.missing()) - result = parse_color(val_wstr, is_background); + result = parse_color(val_wstr, treat_as_background); - /* Handle modifiers. Just one for now */ + /* Handle modifiers. */ if (highlight & highlight_modifier_valid_path) { env_var_t val2_wstr = env_get_string(L"fish_color_valid_path"); diff --git a/highlight.h b/highlight.h index 9b191a78b..dfe3f9310 100644 --- a/highlight.h +++ b/highlight.h @@ -40,6 +40,7 @@ enum /* The following values are modifiers */ highlight_modifier_valid_path = 0x100, + highlight_modifier_sloppy_background = 0x200, //hackish, indicates that we should treat a foreground color as background, per certain historical behavior /* Very special value */ highlight_spec_invalid = 0xFFFF @@ -54,7 +55,7 @@ inline highlight_spec_t highlight_get_primary(highlight_spec_t val) inline highlight_spec_t highlight_make_background(highlight_spec_t val) { - assert(val >> 16 == 0); + assert(val >> 16 == 0); //should have nothing in upper bits, otherwise this is already a background return val << 16; } diff --git a/pager.cpp b/pager.cpp index b1fc92265..b08e8359e 100644 --- a/pager.cpp +++ b/pager.cpp @@ -142,8 +142,8 @@ void pager_t::recalc_min_widths(comp_info_list_t * lst) const { comp_t *c = &lst->at(i); - c->min_width = mini(c->desc_width, maxi(0, term_width/3 - 2)) + - mini(c->desc_width, maxi(0, term_width/5 - 4)) +4; + c->min_width = mini(c->desc_width, maxi(0, available_term_width/3 - 2)) + + mini(c->desc_width, maxi(0, available_term_width/5 - 4)) +4; } } @@ -613,8 +613,8 @@ void pager_t::set_term_size(int w, int h) { assert(w > 0); assert(h > 0); - term_width = w; - term_height = h; + available_term_width = w; + available_term_height = h; } /** @@ -652,8 +652,24 @@ int pager_t::completion_try_print(size_t cols, const wcstring &prefix, const com Set to one if the list should be printed at this width */ int print=0; + + /* Compute the effective term width and term height, accounting for disclosure */ + int term_width = this->available_term_width; + int term_height = this->available_term_height - 1; // we always subtract 1 to make room for a comment row + if (! this->fully_disclosed) + term_height = mini(term_height, PAGER_UNDISCLOSED_MAX_ROWS); size_t row_count = divide_round_up(lst.size(), cols); + + /* We have more to disclose if we are not fully disclosed and there's more rows than we have in our term height */ + if (! this->fully_disclosed && row_count > term_height) + { + rendering->remaining_to_disclose = row_count - term_height; + } + else + { + rendering->remaining_to_disclose = 0; + } int pref_tot_width=0; int min_tot_width = 0; @@ -781,6 +797,26 @@ int pager_t::completion_try_print(size_t cols, const wcstring &prefix, const com assert(stop_row <= row_count); assert(stop_row - start_row <= term_height); completion_print(cols, width, start_row, stop_row, prefix, lst, rendering); + + /* Add the progress line. It's a "more to disclose" line if necessary. */ + wcstring progress_text; + if (rendering->remaining_to_disclose == 1) + { + /* I don't expect this case to ever happen */ + progress_text = L"and 1 more row"; + } + else if (rendering->remaining_to_disclose > 1) + { + progress_text = format_string(L"and %lu more rows", (unsigned long)rendering->remaining_to_disclose); + } + else + { + /* The +1 here is because we are zero indexed, but want to present things as 1-indexed. We do not add 1 to stop_row or row_count because these are the "past the last value" */ + progress_text = format_string(L"rows %lu to %lu of %lu", start_row + 1, stop_row, row_count); + } + line_t &line = rendering->screen_data.add_line(); + print_max(progress_text.c_str(), highlight_spec_pager_progress | highlight_make_background(highlight_spec_pager_progress), term_width, true /* has_more */, &line); + return PAGER_DONE; res=PAGER_DONE; @@ -905,8 +941,8 @@ page_rendering_t pager_t::render() const column never fails. */ page_rendering_t rendering; - rendering.term_width = this->term_width; - rendering.term_height = this->term_height; + rendering.term_width = this->available_term_width; + rendering.term_height = this->available_term_height; if (! this->empty()) { @@ -926,10 +962,9 @@ page_rendering_t pager_t::render() const /* Next iteration will be better, so skip this one */ continue; } - rendering.cols = (size_t)cols; - rendering.rows = divide_round_up(completion_infos.size(), rendering.cols); + rendering.rows = min_rows_required_for_cols; rendering.selected_completion_idx = this->visual_selected_completion_index(rendering.rows, rendering.cols); bool done = false; @@ -962,13 +997,13 @@ page_rendering_t pager_t::render() const void pager_t::update_rendering(page_rendering_t *rendering) const { - if (rendering->term_width != this->term_width || rendering->term_height != this->term_height || rendering->selected_completion_idx != this->visual_selected_completion_index(rendering->rows, rendering->cols)) + if (rendering->term_width != this->available_term_width || rendering->term_height != this->available_term_height || rendering->selected_completion_idx != this->visual_selected_completion_index(rendering->rows, rendering->cols)) { *rendering = this->render(); } } -pager_t::pager_t() : term_width(0), term_height(0), selected_completion_idx(PAGER_SELECTION_NONE), suggested_row_start(0) +pager_t::pager_t() : available_term_width(0), available_term_height(0), selected_completion_idx(PAGER_SELECTION_NONE), suggested_row_start(0), fully_disclosed(false) { } @@ -1135,6 +1170,7 @@ const completion_t *pager_t::select_next_completion_in_direction(selection_direc { size_t row_containing_selection = selected_completion_idx % rendering.rows; + /* Ensure our suggested row start is not past the selected row */ if (suggested_row_start > row_containing_selection) { @@ -1144,7 +1180,20 @@ const completion_t *pager_t::select_next_completion_in_direction(selection_direc /* Ensure our suggested row start is not too early before it */ if (suggested_row_start + visible_row_count <= row_containing_selection) { - suggested_row_start = row_containing_selection - visible_row_count + 1; + /* The user moved south past the bottom completion */ + if (! fully_disclosed && rendering.remaining_to_disclose > 0) + { + /* Perform disclosure */ + fully_disclosed = true; + } + else + { + /* Scroll */ + suggested_row_start = row_containing_selection - visible_row_count + 1; + + /* Ensure fully_disclosed is set. I think we can hit this case if the user resizes the window - we don't want to drop back to the disclosed style */ + fully_disclosed = true; + } } } @@ -1194,8 +1243,9 @@ void pager_t::clear() completion_infos.clear(); prefix.clear(); selected_completion_idx = PAGER_SELECTION_NONE; + fully_disclosed = false; } -page_rendering_t::page_rendering_t() : term_width(-1), term_height(-1), rows(0), cols(0), row_start(0), row_end(0), selected_completion_idx(-1) +page_rendering_t::page_rendering_t() : term_width(-1), term_height(-1), rows(0), cols(0), row_start(0), row_end(0), selected_completion_idx(-1), remaining_to_disclose(0) { } diff --git a/pager.h b/pager.h index 9a3c022ca..df2d00d26 100644 --- a/pager.h +++ b/pager.h @@ -18,6 +18,8 @@ class page_rendering_t size_t selected_completion_idx; screen_data_t screen_data; + size_t remaining_to_disclose; + /* Returns a rendering with invalid data, useful to indicate "no rendering" */ page_rendering_t(); }; @@ -26,19 +28,25 @@ class page_rendering_t #define PAGER_SPACER_STRING L" " #define PAGER_SPACER_STRING_WIDTH 2 +/* How many rows we will show in the "initial" pager */ +#define PAGER_UNDISCLOSED_MAX_ROWS 4 + typedef std::vector completion_list_t; page_rendering_t render_completions(const completion_list_t &raw_completions, const wcstring &prefix); class pager_t { - int term_width; - int term_height; + int available_term_width; + int available_term_height; completion_list_t completions; size_t selected_completion_idx; size_t suggested_row_start; + /* Fully disclosed means that we show all completions */ + bool fully_disclosed; + /* Returns the index of the completion that should draw selected, using the given number of columns */ size_t visual_selected_completion_index(size_t rows, size_t cols) const; diff --git a/reader.cpp b/reader.cpp index fe06a386e..d1ad4bf4e 100644 --- a/reader.cpp +++ b/reader.cpp @@ -546,7 +546,8 @@ static void reader_repaint() indents.resize(len); // Re-render our completions page if necessary - data->pager.set_term_size(common_get_width(), 8 /* common_get_height() */); + // We set the term size to 1 less than the true term height. This means we will always show the (bottom) line of the prompt. + data->pager.set_term_size(maxi(1, common_get_width()), maxi(1, common_get_height() - 1)); data->pager.update_rendering(&data->current_page_rendering); s_write(&data->screen,