Support for "merged completions" (multiple completions on the same line)

in new pager. Support for using up-arrow to end pager navigation.
This commit is contained in:
ridiculousfish 2014-01-24 15:59:18 -08:00
parent 830ab99e6e
commit 0a1960865e
4 changed files with 84 additions and 35 deletions

View file

@ -98,7 +98,10 @@ enum selection_direction_t
/* logical directions */
direction_next,
direction_prev
direction_prev,
/* special value that means deselect */
direction_deselect
};
inline bool selection_direction_is_cardinal(selection_direction_t dir)

View file

@ -501,6 +501,9 @@ static comp_info_list_t process_completions_into_infos(const completion_list_t &
// Append the mangled description
comp_info->desc = comp.description;
mangle_1_completion_description(&comp_info->desc);
// Set the representative completion
comp_info->representative = comp;
}
return result;
}
@ -591,8 +594,6 @@ page_rendering_t render_completions(const completion_list_t &raw_completions, co
void pager_t::set_completions(const completion_list_t &raw_completions)
{
completions = raw_completions;
// Get completion infos out of it
completion_infos = process_completions_into_infos(raw_completions, prefix.c_str());
@ -1012,13 +1013,13 @@ pager_t::pager_t() : available_term_width(0), available_term_height(0), selected
bool pager_t::empty() const
{
return completions.empty();
return completion_infos.empty();
}
const completion_t *pager_t::select_next_completion_in_direction(selection_direction_t direction, const page_rendering_t &rendering)
{
/* Must have something to select */
if (completions.empty())
if (this->empty())
{
return NULL;
}
@ -1034,7 +1035,7 @@ const completion_t *pager_t::select_next_completion_in_direction(selection_direc
case direction_prev:
if (direction == direction_prev)
{
selected_completion_idx = completions.size() - 1;
selected_completion_idx = completion_infos.size() - 1;
}
else
{
@ -1046,6 +1047,7 @@ const completion_t *pager_t::select_next_completion_in_direction(selection_direc
case direction_north:
case direction_east:
case direction_west:
case direction_deselect:
default:
return NULL;
}
@ -1055,8 +1057,12 @@ const completion_t *pager_t::select_next_completion_in_direction(selection_direc
size_t new_selected_completion_idx = selected_completion_idx;
if (! selection_direction_is_cardinal(direction))
{
/* Next / previous, easy */
if (direction == direction_next)
/* Next, previous, or deselect, all easy */
if (direction == direction_deselect)
{
new_selected_completion_idx = PAGER_SELECTION_NONE;
}
else if (direction == direction_next)
{
new_selected_completion_idx = selected_completion_idx + 1;
if (new_selected_completion_idx >= completion_infos.size())
@ -1082,9 +1088,9 @@ const completion_t *pager_t::select_next_completion_in_direction(selection_direc
}
else
{
/* Cardinal directions. We have a completion index; we wish to compute its row and column. Completions are rendered column first, i.e. we go south before we go west. So if we have N rows, and our selected index is N + 2, then our row is 2 (mod by N) and our column is 1 (divide by N) */
size_t current_row = selected_completion_idx % rendering.rows;
size_t current_col = selected_completion_idx / rendering.rows;
/* Cardinal directions. We have a completion index; we wish to compute its row and column. */
size_t current_row = this->get_selected_row(rendering);
size_t current_col = this->get_selected_column(rendering);
switch (direction)
{
@ -1171,8 +1177,7 @@ const completion_t *pager_t::select_next_completion_in_direction(selection_direc
if (visible_row_count > 0 && selected_completion_idx != PAGER_SELECTION_NONE) //paranoia
{
size_t row_containing_selection = selected_completion_idx % rendering.rows;
size_t row_containing_selection = this->get_selected_row(rendering);
/* Ensure our suggested row start is not past the selected row */
if (suggested_row_start > row_containing_selection)
@ -1215,34 +1220,40 @@ size_t pager_t::visual_selected_completion_index(size_t rows, size_t cols) const
if (result != PAGER_SELECTION_NONE)
{
/* If the selected completion is beyond the last selection, go left by columns until it's within it. This is how we implement "column memory." */
while (result >= completions.size() && result >= rows)
while (result >= completion_infos.size() && result >= rows)
{
result -= rows;
}
}
assert(result == PAGER_SELECTION_NONE || result < completions.size());
assert(result == PAGER_SELECTION_NONE || result < completion_infos.size());
return result;
}
void pager_t::set_selected_completion_index(size_t completion_idx)
{
selected_completion_idx = completion_idx;
}
const completion_t *pager_t::selected_completion(const page_rendering_t &rendering) const
{
const completion_t * result = NULL;
size_t idx = visual_selected_completion_index(rendering.rows, rendering.cols);
if (idx != PAGER_SELECTION_NONE)
{
result = &completions.at(idx);
result = &completion_infos.at(idx).representative;
}
return result;
}
/* Completions are rendered column first, i.e. we go south before we go west. So if we have N rows, and our selected index is N + 2, then our row is 2 (mod by N) and our column is 1 (divide by N) */
size_t pager_t::get_selected_row(const page_rendering_t &rendering) const
{
return selected_completion_idx == PAGER_SELECTION_NONE ? PAGER_SELECTION_NONE : selected_completion_idx % rendering.rows;
}
size_t pager_t::get_selected_column(const page_rendering_t &rendering) const
{
return selected_completion_idx == PAGER_SELECTION_NONE ? PAGER_SELECTION_NONE : selected_completion_idx / rendering.rows;
}
void pager_t::clear()
{
completions.clear();
completion_infos.clear();
prefix.clear();
selected_completion_idx = PAGER_SELECTION_NONE;

16
pager.h
View file

@ -39,8 +39,6 @@ class pager_t
int available_term_width;
int available_term_height;
completion_list_t completions;
size_t selected_completion_idx;
size_t suggested_row_start;
@ -60,19 +58,22 @@ class pager_t
/** The description */
wcstring desc;
/** The representative completion */
completion_t representative;
/** On-screen width of the completion string */
int comp_width;
/** On-screen width of the description information */
int desc_width;
/** Preffered total width */
/** Preferred total width */
int pref_width;
/** Minimum acceptable width */
int min_width;
comp_t() : comp(), desc(), comp_width(0), desc_width(0), pref_width(0), min_width(0)
comp_t() : comp(), desc(), representative(L""), comp_width(0), desc_width(0), pref_width(0), min_width(0)
{
}
};
@ -103,15 +104,16 @@ class pager_t
/* Sets the terminal width and height */
void set_term_size(int w, int h);
/* Sets the index of the selected completion */
void set_selected_completion_index(size_t completion_idx);
/* Changes the selected completion in the given direction according to the layout of the given rendering. Returns the newly selected completion if it changed, NULL if nothing was selected or it did not change. */
const completion_t *select_next_completion_in_direction(selection_direction_t direction, const page_rendering_t &rendering);
/* Returns the currently selected completion for the given rendering */
const completion_t *selected_completion(const page_rendering_t &rendering) const;
/* Indicates the row and column for the given rendering. Returns -1 if no selection. */
size_t get_selected_row(const page_rendering_t &rendering) const;
size_t get_selected_column(const page_rendering_t &rendering) const;
/* Produces a rendering of the completions, at the given term size */
page_rendering_t render() const;

View file

@ -1548,17 +1548,27 @@ static void clear_pager()
static void select_completion_in_direction(enum selection_direction_t dir, const wcstring &cycle_command_line, size_t cycle_cursor_pos)
{
const completion_t *next_comp = data->pager.select_next_completion_in_direction(dir, data->current_page_rendering);
if (next_comp != NULL)
if (next_comp != NULL || dir == direction_deselect)
{
/* Update the cursor and command line */
size_t cursor_pos = cycle_cursor_pos;
const wcstring new_cmd_line = completion_apply_to_command_line(next_comp->completion, next_comp->flags, cycle_command_line, &cursor_pos, false);
wcstring new_cmd_line;
if (dir == direction_deselect)
{
new_cmd_line = cycle_command_line;
}
else
{
new_cmd_line = completion_apply_to_command_line(next_comp->completion, next_comp->flags, cycle_command_line, &cursor_pos, false);
}
reader_set_buffer_maintaining_pager(new_cmd_line, cursor_pos);
/* Since we just inserted a completion, don't immediately do a new autosuggestion */
data->suppress_autosuggestion = true;
/* Trigger repaint (see #765) */
reader_repaint();
reader_repaint_needed();
}
}
@ -3653,10 +3663,33 @@ const wchar_t *reader_readline(void)
case R_UP_LINE:
case R_DOWN_LINE:
{
/* If we are already navigating the pager, or if we pressed down with non-empty pager contents, begin navigation */
if (is_navigating_pager_contents() || (c == R_DOWN_LINE && ! data->pager.empty()))
if (is_navigating_pager_contents())
{
select_completion_in_direction(c == R_UP_LINE ? direction_north : direction_south, cycle_command_line, cycle_cursor_pos);
/* We are already navigating pager contents. */
selection_direction_t direction;
if (c == R_DOWN_LINE)
{
/* Down arrow is always south */
direction = direction_south;
}
else if (data->pager.get_selected_row(data->current_page_rendering) == 0 && data->pager.get_selected_column(data->current_page_rendering) == 0)
{
/* Up arrow, but we are in the first column and first row. End navigation */
direction = direction_deselect;
}
else
{
/* Up arrow, go north */
direction = direction_north;
}
/* Now do the selection */
select_completion_in_direction(direction, cycle_command_line, cycle_cursor_pos);
}
else if (c == R_DOWN_LINE && ! data->pager.empty())
{
/* We pressed down with a non-empty pager contents, begin navigation */
select_completion_in_direction(direction_south, cycle_command_line, cycle_cursor_pos);
}
else
{