mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-14 05:53:59 +00:00
Increased support for completion search field. Use btab (shift-tab) to
complete-and-search.
This commit is contained in:
parent
ce4c145f1c
commit
5be3606236
11 changed files with 425 additions and 167 deletions
37
builtin.cpp
37
builtin.cpp
|
@ -571,35 +571,14 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
|
||||||
static const struct woption
|
static const struct woption
|
||||||
long_options[] =
|
long_options[] =
|
||||||
{
|
{
|
||||||
{
|
{ L"all", no_argument, 0, 'a' },
|
||||||
L"all", no_argument, 0, 'a'
|
{ L"erase", no_argument, 0, 'e' },
|
||||||
}
|
{ L"function-names", no_argument, 0, 'f' },
|
||||||
,
|
{ L"help", no_argument, 0, 'h' },
|
||||||
{
|
{ L"key", no_argument, 0, 'k' },
|
||||||
L"erase", no_argument, 0, 'e'
|
{ L"key-names", no_argument, 0, 'K' },
|
||||||
}
|
{ 0, 0, 0, 0 }
|
||||||
,
|
};
|
||||||
{
|
|
||||||
L"function-names", no_argument, 0, 'f'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"help", no_argument, 0, 'h'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"key", no_argument, 0, 'k'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
L"key-names", no_argument, 0, 'K'
|
|
||||||
}
|
|
||||||
,
|
|
||||||
{
|
|
||||||
0, 0, 0, 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -104,6 +104,7 @@ static const wchar_t * const name_arr[] =
|
||||||
L"yank",
|
L"yank",
|
||||||
L"yank-pop",
|
L"yank-pop",
|
||||||
L"complete",
|
L"complete",
|
||||||
|
L"complete-and-search",
|
||||||
L"beginning-of-history",
|
L"beginning-of-history",
|
||||||
L"end-of-history",
|
L"end-of-history",
|
||||||
L"backward-kill-line",
|
L"backward-kill-line",
|
||||||
|
@ -201,6 +202,7 @@ static const wchar_t code_arr[] =
|
||||||
R_YANK,
|
R_YANK,
|
||||||
R_YANK_POP,
|
R_YANK_POP,
|
||||||
R_COMPLETE,
|
R_COMPLETE,
|
||||||
|
R_COMPLETE_AND_SEARCH,
|
||||||
R_BEGINNING_OF_HISTORY,
|
R_BEGINNING_OF_HISTORY,
|
||||||
R_END_OF_HISTORY,
|
R_END_OF_HISTORY,
|
||||||
R_BACKWARD_KILL_LINE,
|
R_BACKWARD_KILL_LINE,
|
||||||
|
|
1
input.h
1
input.h
|
@ -33,6 +33,7 @@ enum
|
||||||
R_YANK,
|
R_YANK,
|
||||||
R_YANK_POP,
|
R_YANK_POP,
|
||||||
R_COMPLETE,
|
R_COMPLETE,
|
||||||
|
R_COMPLETE_AND_SEARCH,
|
||||||
R_BEGINNING_OF_HISTORY,
|
R_BEGINNING_OF_HISTORY,
|
||||||
R_END_OF_HISTORY,
|
R_END_OF_HISTORY,
|
||||||
R_BACKWARD_KILL_LINE,
|
R_BACKWARD_KILL_LINE,
|
||||||
|
|
|
@ -251,7 +251,6 @@ wchar_t input_common_readch(int timed)
|
||||||
case 0:
|
case 0:
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
160
pager.cpp
160
pager.cpp
|
@ -3,6 +3,7 @@
|
||||||
#include "pager.h"
|
#include "pager.h"
|
||||||
#include "highlight.h"
|
#include "highlight.h"
|
||||||
#include "input_common.h"
|
#include "input_common.h"
|
||||||
|
#include "wutil.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
@ -18,9 +19,19 @@ typedef std::vector<comp_t> comp_info_list_t;
|
||||||
/** 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
|
||||||
|
|
||||||
/* Returns numer / denom, rounding up */
|
/** Width of the search field */
|
||||||
|
#define PAGER_SEARCH_FIELD_WIDTH 12
|
||||||
|
|
||||||
|
/** Text we use for the search field */
|
||||||
|
#define SEARCH_FIELD_PROMPT _(L"search: ")
|
||||||
|
|
||||||
|
/* Returns numer / denom, rounding up. As a "courtesy" 0/0 is 0. */
|
||||||
static size_t divide_round_up(size_t numer, size_t denom)
|
static size_t divide_round_up(size_t numer, size_t denom)
|
||||||
{
|
{
|
||||||
|
if (numer == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
assert(denom > 0);
|
||||||
return numer / denom + (numer % denom ? 1 : 0);
|
return numer / denom + (numer % denom ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,17 +347,66 @@ void pager_t::measure_completion_infos(comp_info_list_t *infos, const wcstring &
|
||||||
recalc_min_widths(infos);
|
recalc_min_widths(infos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Indicates if the given completion info passes any filtering we have */
|
||||||
|
bool pager_t::completion_info_passes_filter(const comp_t &info) const
|
||||||
|
{
|
||||||
|
/* If we have no filter, everything passes */
|
||||||
|
if (! search_field_shown || this->search_field_line.empty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const wcstring &needle = this->search_field_line.text;
|
||||||
|
|
||||||
|
/* We do substring matching */
|
||||||
|
const fuzzy_match_type_t limit = fuzzy_match_substring;
|
||||||
|
|
||||||
|
/* Match against the description */
|
||||||
|
if (string_fuzzy_match_string(needle, info.desc, limit).type != fuzzy_match_none)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Match against the completion strings */
|
||||||
|
for (size_t i=0; i < info.comp.size(); i++)
|
||||||
|
{
|
||||||
|
if (string_fuzzy_match_string(needle, info.comp.at(i), limit).type != fuzzy_match_none)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No match */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update completion_infos from unfiltered_completion_infos, to reflect the filter */
|
||||||
|
void pager_t::refilter_completions()
|
||||||
|
{
|
||||||
|
this->completion_infos.clear();
|
||||||
|
for (size_t i=0; i < this->unfiltered_completion_infos.size(); i++)
|
||||||
|
{
|
||||||
|
const comp_t &info = this->unfiltered_completion_infos.at(i);
|
||||||
|
if (this->completion_info_passes_filter(info))
|
||||||
|
{
|
||||||
|
this->completion_infos.push_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
note_selection_changed();
|
||||||
|
}
|
||||||
|
|
||||||
void pager_t::set_completions(const completion_list_t &raw_completions)
|
void pager_t::set_completions(const completion_list_t &raw_completions)
|
||||||
{
|
{
|
||||||
// Get completion infos out of it
|
// Get completion infos out of it
|
||||||
completion_infos = process_completions_into_infos(raw_completions, prefix.c_str());
|
unfiltered_completion_infos = process_completions_into_infos(raw_completions, prefix.c_str());
|
||||||
|
|
||||||
// Maybe join them
|
// Maybe join them
|
||||||
if (prefix == L"-")
|
if (prefix == L"-")
|
||||||
join_completions(&completion_infos);
|
join_completions(&unfiltered_completion_infos);
|
||||||
|
|
||||||
// Compute their various widths
|
// Compute their various widths
|
||||||
measure_completion_infos(&completion_infos, prefix);
|
measure_completion_infos(&unfiltered_completion_infos, prefix);
|
||||||
|
|
||||||
|
// Refilter them
|
||||||
|
this->refilter_completions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pager_t::set_prefix(const wcstring &pref)
|
void pager_t::set_prefix(const wcstring &pref)
|
||||||
|
@ -390,7 +450,7 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||||
|
|
||||||
/* Compute the effective term width and term height, accounting for disclosure */
|
/* Compute the effective term width and term height, accounting for disclosure */
|
||||||
int term_width = this->available_term_width;
|
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
|
int term_height = this->available_term_height - 1 - (search_field_shown ? 1 : 0); // we always subtract 1 to make room for a comment row
|
||||||
if (! this->fully_disclosed)
|
if (! this->fully_disclosed)
|
||||||
term_height = mini(term_height, PAGER_UNDISCLOSED_MAX_ROWS);
|
term_height = mini(term_height, PAGER_UNDISCLOSED_MAX_ROWS);
|
||||||
|
|
||||||
|
@ -551,6 +611,11 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||||
/* We have a scrollable interface. 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" */
|
/* We have a scrollable interface. 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);
|
progress_text = format_string(L"rows %lu to %lu of %lu", start_row + 1, stop_row, row_count);
|
||||||
}
|
}
|
||||||
|
else if (completion_infos.empty() && ! unfiltered_completion_infos.empty())
|
||||||
|
{
|
||||||
|
/* Everything is filtered */
|
||||||
|
progress_text = L"(no matches)";
|
||||||
|
}
|
||||||
|
|
||||||
if (! progress_text.empty())
|
if (! progress_text.empty())
|
||||||
{
|
{
|
||||||
|
@ -561,11 +626,17 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
||||||
if (search_field_shown)
|
if (search_field_shown)
|
||||||
{
|
{
|
||||||
/* Add the search field */
|
/* Add the search field */
|
||||||
wcstring spaces(8, L' ');
|
wcstring search_field_text = search_field_line.text;
|
||||||
spaces.insert(spaces.begin(), 1, L'h');
|
/* Append spaces to make it at least the required width */
|
||||||
|
if (search_field_text.size() < PAGER_SEARCH_FIELD_WIDTH)
|
||||||
|
{
|
||||||
|
search_field_text.append(PAGER_SEARCH_FIELD_WIDTH - search_field_text.size(), L' ');
|
||||||
|
}
|
||||||
line_t *search_field = &rendering->screen_data.insert_line_at_index(0);
|
line_t *search_field = &rendering->screen_data.insert_line_at_index(0);
|
||||||
int search_field_written = print_max(L"filter: ", highlight_spec_normal, term_width, false, search_field);
|
|
||||||
search_field_written += print_max(spaces, highlight_modifier_force_underline, term_width - search_field_written, false, search_field);
|
/* We limit the width to term_width - 1 */
|
||||||
|
int search_field_written = print_max(SEARCH_FIELD_PROMPT, highlight_spec_normal, term_width - 1, false, search_field);
|
||||||
|
search_field_written += print_max(search_field_text, highlight_modifier_force_underline, term_width - search_field_written - 1, false, search_field);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -585,9 +656,9 @@ page_rendering_t pager_t::render() const
|
||||||
page_rendering_t rendering;
|
page_rendering_t rendering;
|
||||||
rendering.term_width = this->available_term_width;
|
rendering.term_width = this->available_term_width;
|
||||||
rendering.term_height = this->available_term_height;
|
rendering.term_height = this->available_term_height;
|
||||||
|
rendering.search_field_shown = this->search_field_shown;
|
||||||
|
rendering.search_field_line = this->search_field_line;
|
||||||
|
|
||||||
if (! this->empty())
|
|
||||||
{
|
|
||||||
for (int cols = PAGER_MAX_COLS; cols > 0; cols--)
|
for (int cols = PAGER_MAX_COLS; cols > 0; cols--)
|
||||||
{
|
{
|
||||||
/* Initially empty rendering */
|
/* Initially empty rendering */
|
||||||
|
@ -598,7 +669,7 @@ page_rendering_t pager_t::render() const
|
||||||
size_t min_cols_required_for_rows = divide_round_up(completion_infos.size(), min_rows_required_for_cols);
|
size_t min_cols_required_for_rows = divide_round_up(completion_infos.size(), min_rows_required_for_cols);
|
||||||
|
|
||||||
assert(min_cols_required_for_rows <= cols);
|
assert(min_cols_required_for_rows <= cols);
|
||||||
if (min_cols_required_for_rows < cols)
|
if (cols > 1 && min_cols_required_for_rows < cols)
|
||||||
{
|
{
|
||||||
/* Next iteration will be better, so skip this one */
|
/* Next iteration will be better, so skip this one */
|
||||||
continue;
|
continue;
|
||||||
|
@ -613,13 +684,17 @@ page_rendering_t pager_t::render() const
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return rendering;
|
return rendering;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pager_t::update_rendering(page_rendering_t *rendering) const
|
void pager_t::update_rendering(page_rendering_t *rendering) const
|
||||||
{
|
{
|
||||||
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))
|
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->search_field_shown != this->search_field_shown ||
|
||||||
|
rendering->search_field_line.text != this->search_field_line.text ||
|
||||||
|
rendering->search_field_line.position != this->search_field_line.position)
|
||||||
{
|
{
|
||||||
*rendering = this->render();
|
*rendering = this->render();
|
||||||
}
|
}
|
||||||
|
@ -631,13 +706,13 @@ pager_t::pager_t() : available_term_width(0), available_term_height(0), selected
|
||||||
|
|
||||||
bool pager_t::empty() const
|
bool pager_t::empty() const
|
||||||
{
|
{
|
||||||
return completion_infos.empty();
|
return unfiltered_completion_infos.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
const completion_t *pager_t::select_next_completion_in_direction(selection_direction_t direction, const page_rendering_t &rendering)
|
const completion_t *pager_t::select_next_completion_in_direction(selection_direction_t direction, const page_rendering_t &rendering)
|
||||||
{
|
{
|
||||||
/* Must have something to select */
|
/* Must have something to select */
|
||||||
if (this->empty())
|
if (this->completion_infos.empty())
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -834,6 +909,12 @@ const completion_t *pager_t::select_next_completion_in_direction(selection_direc
|
||||||
|
|
||||||
size_t pager_t::visual_selected_completion_index(size_t rows, size_t cols) const
|
size_t pager_t::visual_selected_completion_index(size_t rows, size_t cols) const
|
||||||
{
|
{
|
||||||
|
/* No completions -> no selection */
|
||||||
|
if (completion_infos.empty())
|
||||||
|
{
|
||||||
|
return PAGER_SELECTION_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
size_t result = selected_completion_idx;
|
size_t result = selected_completion_idx;
|
||||||
if (result != PAGER_SELECTION_NONE)
|
if (result != PAGER_SELECTION_NONE)
|
||||||
{
|
{
|
||||||
|
@ -842,11 +923,21 @@ size_t pager_t::visual_selected_completion_index(size_t rows, size_t cols) const
|
||||||
{
|
{
|
||||||
result -= rows;
|
result -= rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If we are still beyond the last selection, clamp it */
|
||||||
|
if (result >= completion_infos.size())
|
||||||
|
result = completion_infos.size() - 1;
|
||||||
}
|
}
|
||||||
assert(result == PAGER_SELECTION_NONE || result < completion_infos.size());
|
assert(result == PAGER_SELECTION_NONE || result < completion_infos.size());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* It's possible we have no visual selection but are still navigating the contents, e.g. every completion is filtered */
|
||||||
|
bool pager_t::is_navigating_contents() const
|
||||||
|
{
|
||||||
|
return selected_completion_idx != PAGER_SELECTION_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
const completion_t *pager_t::selected_completion(const page_rendering_t &rendering) const
|
const completion_t *pager_t::selected_completion(const page_rendering_t &rendering) const
|
||||||
{
|
{
|
||||||
const completion_t * result = NULL;
|
const completion_t * result = NULL;
|
||||||
|
@ -861,25 +952,58 @@ const completion_t *pager_t::selected_completion(const page_rendering_t &renderi
|
||||||
/* Get the selected 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) */
|
/* Get the selected 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 pager_t::get_selected_row(const page_rendering_t &rendering) const
|
size_t pager_t::get_selected_row(const page_rendering_t &rendering) const
|
||||||
{
|
{
|
||||||
|
if (rendering.rows == 0)
|
||||||
|
return PAGER_SELECTION_NONE;
|
||||||
|
|
||||||
return selected_completion_idx == PAGER_SELECTION_NONE ? PAGER_SELECTION_NONE : selected_completion_idx % rendering.rows;
|
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
|
size_t pager_t::get_selected_column(const page_rendering_t &rendering) const
|
||||||
{
|
{
|
||||||
|
if (rendering.rows == 0)
|
||||||
|
return PAGER_SELECTION_NONE;
|
||||||
|
|
||||||
return selected_completion_idx == PAGER_SELECTION_NONE ? PAGER_SELECTION_NONE : selected_completion_idx / rendering.rows;
|
return selected_completion_idx == PAGER_SELECTION_NONE ? PAGER_SELECTION_NONE : selected_completion_idx / rendering.rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pager_t::clear()
|
void pager_t::clear()
|
||||||
{
|
{
|
||||||
|
unfiltered_completion_infos.clear();
|
||||||
completion_infos.clear();
|
completion_infos.clear();
|
||||||
prefix.clear();
|
prefix.clear();
|
||||||
selected_completion_idx = PAGER_SELECTION_NONE;
|
selected_completion_idx = PAGER_SELECTION_NONE;
|
||||||
fully_disclosed = false;
|
fully_disclosed = false;
|
||||||
search_field_shown = false;
|
search_field_shown = false;
|
||||||
search_field_string.clear();
|
search_field_line.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void pager_t::set_search_field_shown(bool flag)
|
||||||
|
{
|
||||||
|
this->search_field_shown = flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pager_t::is_search_field_shown() const
|
||||||
|
{
|
||||||
|
return this->search_field_shown;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pager_t::cursor_position() const
|
||||||
|
{
|
||||||
|
size_t result = wcslen(SEARCH_FIELD_PROMPT) + this->search_field_line.position;
|
||||||
|
/* Clamp it to the right edge */
|
||||||
|
if (available_term_width > 0 && result + 1 > available_term_width)
|
||||||
|
{
|
||||||
|
result = available_term_width - 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pager_t::note_selection_changed()
|
||||||
|
{
|
||||||
|
reader_selected_completion_changed(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Constructor */
|
/* Constructor */
|
||||||
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)
|
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), search_field_shown(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
32
pager.h
32
pager.h
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "complete.h"
|
#include "complete.h"
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
|
#include "reader.h"
|
||||||
|
|
||||||
/* Represents rendering from the pager */
|
/* Represents rendering from the pager */
|
||||||
class page_rendering_t
|
class page_rendering_t
|
||||||
|
@ -20,6 +21,9 @@ class page_rendering_t
|
||||||
|
|
||||||
size_t remaining_to_disclose;
|
size_t remaining_to_disclose;
|
||||||
|
|
||||||
|
bool search_field_shown;
|
||||||
|
editable_line_t search_field_line;
|
||||||
|
|
||||||
/* Returns a rendering with invalid data, useful to indicate "no rendering" */
|
/* Returns a rendering with invalid data, useful to indicate "no rendering" */
|
||||||
page_rendering_t();
|
page_rendering_t();
|
||||||
};
|
};
|
||||||
|
@ -47,7 +51,6 @@ class pager_t
|
||||||
|
|
||||||
/* Whether we show the search field */
|
/* Whether we show the search field */
|
||||||
bool search_field_shown;
|
bool search_field_shown;
|
||||||
wcstring search_field_string;
|
|
||||||
|
|
||||||
/* Returns the index of the completion that should draw selected, using the given number of columns */
|
/* 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;
|
size_t visual_selected_completion_index(size_t rows, size_t cols) const;
|
||||||
|
@ -84,21 +87,33 @@ class pager_t
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::vector<comp_t> comp_info_list_t;
|
typedef std::vector<comp_t> comp_info_list_t;
|
||||||
|
|
||||||
|
/* The filtered list of completion infos */
|
||||||
comp_info_list_t completion_infos;
|
comp_info_list_t completion_infos;
|
||||||
|
|
||||||
|
/* The unfiltered list. Note there's a lot of duplication here. */
|
||||||
|
comp_info_list_t unfiltered_completion_infos;
|
||||||
|
|
||||||
wcstring prefix;
|
wcstring prefix;
|
||||||
|
|
||||||
|
void note_selection_changed();
|
||||||
|
|
||||||
bool 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;
|
bool 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;
|
||||||
|
|
||||||
void recalc_min_widths(comp_info_list_t * lst) const;
|
void recalc_min_widths(comp_info_list_t * lst) const;
|
||||||
void measure_completion_infos(std::vector<comp_t> *infos, const wcstring &prefix) const;
|
void measure_completion_infos(std::vector<comp_t> *infos, const wcstring &prefix) 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, const wcstring &prefix, const comp_info_list_t &lst, page_rendering_t *rendering) const;
|
void completion_print(size_t cols, int *width_per_column, size_t row_start, size_t row_stop, const wcstring &prefix, const comp_info_list_t &lst, page_rendering_t *rendering) const;
|
||||||
line_t completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, size_t column, int width, bool secondary, bool selected, page_rendering_t *rendering) const;
|
line_t completion_print_item(const wcstring &prefix, const comp_t *c, size_t row, size_t column, int width, bool secondary, bool selected, page_rendering_t *rendering) const;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/* The text of the search field */
|
||||||
|
editable_line_t search_field_line;
|
||||||
|
|
||||||
/* Sets the set of completions */
|
/* Sets the set of completions */
|
||||||
void set_completions(const completion_list_t &comp);
|
void set_completions(const completion_list_t &comp);
|
||||||
|
|
||||||
|
@ -130,6 +145,21 @@ class pager_t
|
||||||
/* Clears all completions and the prefix */
|
/* Clears all completions and the prefix */
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
|
/* Updates the completions list per the filter */
|
||||||
|
void refilter_completions();
|
||||||
|
|
||||||
|
/* Sets whether the search field is shown */
|
||||||
|
void set_search_field_shown(bool flag);
|
||||||
|
|
||||||
|
/* Gets whether the search field shown */
|
||||||
|
bool is_search_field_shown() const;
|
||||||
|
|
||||||
|
/* Indicates if we are navigating our contents */
|
||||||
|
bool is_navigating_contents() const;
|
||||||
|
|
||||||
|
/* Position of the cursor */
|
||||||
|
size_t cursor_position() const;
|
||||||
|
|
||||||
/* Constructor */
|
/* Constructor */
|
||||||
pager_t();
|
pager_t();
|
||||||
};
|
};
|
||||||
|
|
262
reader.cpp
262
reader.cpp
|
@ -210,9 +210,6 @@ public:
|
||||||
/** Current page rendering */
|
/** Current page rendering */
|
||||||
page_rendering_t current_page_rendering;
|
page_rendering_t current_page_rendering;
|
||||||
|
|
||||||
/** Whether we are navigating the pager */
|
|
||||||
bool is_navigating_pager;
|
|
||||||
|
|
||||||
/** Whether autosuggesting is allowed at all */
|
/** Whether autosuggesting is allowed at all */
|
||||||
bool allow_autosuggestion;
|
bool allow_autosuggestion;
|
||||||
|
|
||||||
|
@ -254,14 +251,26 @@ public:
|
||||||
/** The current position in search_prev */
|
/** The current position in search_prev */
|
||||||
size_t search_pos;
|
size_t search_pos;
|
||||||
|
|
||||||
|
bool is_navigating_pager_contents() const
|
||||||
|
{
|
||||||
|
return this->pager.is_navigating_contents();
|
||||||
|
}
|
||||||
|
|
||||||
/* The line that is currently being edited. Typically the command line, but may be the search field */
|
/* The line that is currently being edited. Typically the command line, but may be the search field */
|
||||||
editable_line_t *active_edit_line()
|
editable_line_t *active_edit_line()
|
||||||
{
|
{
|
||||||
return &command_line;
|
if (this->is_navigating_pager_contents() && this->pager.is_search_field_shown())
|
||||||
|
{
|
||||||
|
return &this->pager.search_field_line;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return &this->command_line;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Do what we need to do whenever our command line changes */
|
/** Do what we need to do whenever our command line changes */
|
||||||
void command_line_changed(void);
|
void command_line_changed(const editable_line_t *el);
|
||||||
|
|
||||||
/** Expand abbreviations at the current cursor position, minus backtrack_amt. */
|
/** Expand abbreviations at the current cursor position, minus backtrack_amt. */
|
||||||
bool expand_abbreviation_as_necessary(size_t cursor_backtrack);
|
bool expand_abbreviation_as_necessary(size_t cursor_backtrack);
|
||||||
|
@ -279,6 +288,10 @@ public:
|
||||||
/** The output of the last evaluation of the right prompt command */
|
/** The output of the last evaluation of the right prompt command */
|
||||||
wcstring right_prompt_buff;
|
wcstring right_prompt_buff;
|
||||||
|
|
||||||
|
/* Completion support */
|
||||||
|
wcstring cycle_command_line;
|
||||||
|
size_t cycle_cursor_pos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Color is the syntax highlighting for buff. The format is that
|
Color is the syntax highlighting for buff. The format is that
|
||||||
color[i] is the classification (according to the enum in
|
color[i] is the classification (according to the enum in
|
||||||
|
@ -343,13 +356,13 @@ public:
|
||||||
|
|
||||||
/** Constructor */
|
/** Constructor */
|
||||||
reader_data_t() :
|
reader_data_t() :
|
||||||
is_navigating_pager(0),
|
|
||||||
allow_autosuggestion(0),
|
allow_autosuggestion(0),
|
||||||
suppress_autosuggestion(0),
|
suppress_autosuggestion(0),
|
||||||
expand_abbreviations(0),
|
expand_abbreviations(0),
|
||||||
history(0),
|
history(0),
|
||||||
token_history_pos(0),
|
token_history_pos(0),
|
||||||
search_pos(0),
|
search_pos(0),
|
||||||
|
cycle_cursor_pos(0),
|
||||||
complete_func(0),
|
complete_func(0),
|
||||||
highlight_function(0),
|
highlight_function(0),
|
||||||
test_func(0),
|
test_func(0),
|
||||||
|
@ -531,12 +544,12 @@ wcstring combine_command_and_autosuggestion(const wcstring &cmdline, const wcstr
|
||||||
|
|
||||||
static void reader_repaint()
|
static void reader_repaint()
|
||||||
{
|
{
|
||||||
editable_line_t *el = &data->command_line;
|
editable_line_t *cmd_line = &data->command_line;
|
||||||
// Update the indentation
|
// Update the indentation
|
||||||
data->indents = parse_util_compute_indents(el->text);
|
data->indents = parse_util_compute_indents(cmd_line->text);
|
||||||
|
|
||||||
// Combine the command and autosuggestion into one string
|
// Combine the command and autosuggestion into one string
|
||||||
wcstring full_line = combine_command_and_autosuggestion(el->text, data->autosuggestion);
|
wcstring full_line = combine_command_and_autosuggestion(cmd_line->text, data->autosuggestion);
|
||||||
|
|
||||||
size_t len = full_line.size();
|
size_t len = full_line.size();
|
||||||
if (len < 1)
|
if (len < 1)
|
||||||
|
@ -553,15 +566,19 @@ static void reader_repaint()
|
||||||
data->pager.set_term_size(maxi(1, common_get_width()), maxi(1, common_get_height() - 1));
|
data->pager.set_term_size(maxi(1, common_get_width()), maxi(1, common_get_height() - 1));
|
||||||
data->pager.update_rendering(&data->current_page_rendering);
|
data->pager.update_rendering(&data->current_page_rendering);
|
||||||
|
|
||||||
|
bool focused_on_pager = data->active_edit_line() == &data->pager.search_field_line;
|
||||||
|
size_t cursor_position = focused_on_pager ? data->pager.cursor_position() : cmd_line->position;
|
||||||
|
|
||||||
s_write(&data->screen,
|
s_write(&data->screen,
|
||||||
data->left_prompt_buff,
|
data->left_prompt_buff,
|
||||||
data->right_prompt_buff,
|
data->right_prompt_buff,
|
||||||
full_line,
|
full_line,
|
||||||
el->size(),
|
cmd_line->size(),
|
||||||
&colors[0],
|
&colors[0],
|
||||||
&indents[0],
|
&indents[0],
|
||||||
el->position,
|
cursor_position,
|
||||||
data->current_page_rendering);
|
data->current_page_rendering,
|
||||||
|
focused_on_pager);
|
||||||
|
|
||||||
data->repaint_needed = false;
|
data->repaint_needed = false;
|
||||||
}
|
}
|
||||||
|
@ -609,7 +626,7 @@ static void reader_kill(editable_line_t *el, size_t begin_idx, size_t length, in
|
||||||
}
|
}
|
||||||
|
|
||||||
el->text.erase(begin_idx, length);
|
el->text.erase(begin_idx, length);
|
||||||
data->command_line_changed();
|
data->command_line_changed(el);
|
||||||
|
|
||||||
reader_super_highlight_me_plenty();
|
reader_super_highlight_me_plenty();
|
||||||
reader_repaint();
|
reader_repaint();
|
||||||
|
@ -650,9 +667,11 @@ void reader_pop_current_filename()
|
||||||
|
|
||||||
|
|
||||||
/** Make sure buffers are large enough to hold the current string length */
|
/** Make sure buffers are large enough to hold the current string length */
|
||||||
void reader_data_t::command_line_changed()
|
void reader_data_t::command_line_changed(const editable_line_t *el)
|
||||||
{
|
{
|
||||||
ASSERT_IS_MAIN_THREAD();
|
ASSERT_IS_MAIN_THREAD();
|
||||||
|
if (el == &this->command_line)
|
||||||
|
{
|
||||||
size_t len = this->command_line.size();
|
size_t len = this->command_line.size();
|
||||||
|
|
||||||
/* When we grow colors, propagate the last color (if any), under the assumption that usually it will be correct. If it is, it avoids a repaint. */
|
/* When we grow colors, propagate the last color (if any), under the assumption that usually it will be correct. If it is, it avoids a repaint. */
|
||||||
|
@ -664,6 +683,11 @@ void reader_data_t::command_line_changed()
|
||||||
/* Update the gen count */
|
/* Update the gen count */
|
||||||
s_generation_count++;
|
s_generation_count++;
|
||||||
}
|
}
|
||||||
|
else if (el == &this->pager.search_field_line)
|
||||||
|
{
|
||||||
|
this->pager.refilter_completions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Expand abbreviations at the given cursor position. Does NOT inspect 'data'. */
|
/* Expand abbreviations at the given cursor position. Does NOT inspect 'data'. */
|
||||||
bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, wcstring *output)
|
bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, wcstring *output)
|
||||||
|
@ -752,7 +776,7 @@ bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack)
|
||||||
|
|
||||||
el->text.swap(new_cmdline);
|
el->text.swap(new_cmdline);
|
||||||
el->position = new_buff_pos;
|
el->position = new_buff_pos;
|
||||||
data->command_line_changed();
|
data->command_line_changed(el);
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1024,6 +1048,64 @@ void reader_react_to_color_change()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Indicates if the given command char ends paging */
|
||||||
|
static bool command_ends_paging(wchar_t c, bool focused_on_search_field)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
/* These commands always end paging */
|
||||||
|
case R_HISTORY_SEARCH_BACKWARD:
|
||||||
|
case R_HISTORY_SEARCH_FORWARD:
|
||||||
|
case R_BEGINNING_OF_HISTORY:
|
||||||
|
case R_END_OF_HISTORY:
|
||||||
|
case R_HISTORY_TOKEN_SEARCH_BACKWARD:
|
||||||
|
case R_HISTORY_TOKEN_SEARCH_FORWARD:
|
||||||
|
case R_EXECUTE:
|
||||||
|
case R_ACCEPT_AUTOSUGGESTION:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
/* These commands never do */
|
||||||
|
case R_COMPLETE:
|
||||||
|
case R_COMPLETE_AND_SEARCH:
|
||||||
|
case R_BACKWARD_CHAR:
|
||||||
|
case R_FORWARD_CHAR:
|
||||||
|
case R_UP_LINE:
|
||||||
|
case R_DOWN_LINE:
|
||||||
|
case R_NULL:
|
||||||
|
case R_REPAINT:
|
||||||
|
case R_SUPPRESS_AUTOSUGGESTION:
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* These commands operate on the search field if that's where the focus is */
|
||||||
|
case R_BEGINNING_OF_LINE:
|
||||||
|
case R_END_OF_LINE:
|
||||||
|
case R_FORWARD_WORD:
|
||||||
|
case R_BACKWARD_WORD:
|
||||||
|
case R_DELETE_CHAR:
|
||||||
|
case R_BACKWARD_DELETE_CHAR:
|
||||||
|
case R_KILL_LINE:
|
||||||
|
case R_YANK:
|
||||||
|
case R_YANK_POP:
|
||||||
|
case R_BACKWARD_KILL_LINE:
|
||||||
|
case R_KILL_WHOLE_LINE:
|
||||||
|
case R_KILL_WORD:
|
||||||
|
case R_BACKWARD_KILL_WORD:
|
||||||
|
case R_BACKWARD_KILL_PATH_COMPONENT:
|
||||||
|
case R_SELF_INSERT:
|
||||||
|
case R_TRANSPOSE_CHARS:
|
||||||
|
case R_TRANSPOSE_WORDS:
|
||||||
|
case R_UPCASE_WORD:
|
||||||
|
case R_DOWNCASE_WORD:
|
||||||
|
case R_CAPITALIZE_WORD:
|
||||||
|
case R_VI_ARG_DIGIT:
|
||||||
|
case R_VI_DELETE_TO:
|
||||||
|
case R_BEGINNING_OF_BUFFER:
|
||||||
|
case R_END_OF_BUFFER:
|
||||||
|
return ! focused_on_search_field;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Remove the previous character in the character buffer and on the
|
Remove the previous character in the character buffer and on the
|
||||||
screen using syntax highlighting, etc.
|
screen using syntax highlighting, etc.
|
||||||
|
@ -1044,7 +1126,7 @@ static void remove_backward()
|
||||||
el->text.erase(el->position, 1);
|
el->text.erase(el->position, 1);
|
||||||
}
|
}
|
||||||
while (width == 0 && el->position > 0);
|
while (width == 0 && el->position > 0);
|
||||||
data->command_line_changed();
|
data->command_line_changed(el);
|
||||||
data->suppress_autosuggestion = true;
|
data->suppress_autosuggestion = true;
|
||||||
|
|
||||||
reader_super_highlight_me_plenty();
|
reader_super_highlight_me_plenty();
|
||||||
|
@ -1060,16 +1142,15 @@ static void remove_backward()
|
||||||
Optionally also expand abbreviations.
|
Optionally also expand abbreviations.
|
||||||
Returns true if the string changed.
|
Returns true if the string changed.
|
||||||
*/
|
*/
|
||||||
static bool insert_string(const wcstring &str, bool should_expand_abbreviations = false)
|
static bool insert_string(editable_line_t *el, const wcstring &str, bool should_expand_abbreviations = false)
|
||||||
{
|
{
|
||||||
size_t len = str.size();
|
size_t len = str.size();
|
||||||
if (len == 0)
|
if (len == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
editable_line_t *el = data->active_edit_line();
|
|
||||||
el->insert_string(str);
|
el->insert_string(str);
|
||||||
|
|
||||||
data->command_line_changed();
|
data->command_line_changed(el);
|
||||||
|
|
||||||
if (el == &data->command_line)
|
if (el == &data->command_line)
|
||||||
{
|
{
|
||||||
|
@ -1091,9 +1172,9 @@ static bool insert_string(const wcstring &str, bool should_expand_abbreviations
|
||||||
Insert the character into the command line buffer and print it to
|
Insert the character into the command line buffer and print it to
|
||||||
the screen using syntax highlighting, etc.
|
the screen using syntax highlighting, etc.
|
||||||
*/
|
*/
|
||||||
static bool insert_char(wchar_t c, bool should_expand_abbreviations = false)
|
static bool insert_char(editable_line_t *el, wchar_t c, bool should_expand_abbreviations = false)
|
||||||
{
|
{
|
||||||
return insert_string(wcstring(1, c), should_expand_abbreviations);
|
return insert_string(el, wcstring(1, c), should_expand_abbreviations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1538,17 +1619,12 @@ static void accept_autosuggestion(bool full)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data->command_line.position = data->command_line.size();
|
data->command_line.position = data->command_line.size();
|
||||||
data->command_line_changed();
|
data->command_line_changed(&data->command_line);
|
||||||
reader_super_highlight_me_plenty();
|
reader_super_highlight_me_plenty();
|
||||||
reader_repaint();
|
reader_repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_navigating_pager_contents()
|
|
||||||
{
|
|
||||||
return data && data->pager.selected_completion(data->current_page_rendering) != NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure we have no pager contents */
|
/* Ensure we have no pager contents */
|
||||||
static void clear_pager()
|
static void clear_pager()
|
||||||
{
|
{
|
||||||
|
@ -1901,6 +1977,7 @@ static bool handle_completions(const std::vector<completion_t> &comp)
|
||||||
|
|
||||||
/* Invalidate our rendering */
|
/* Invalidate our rendering */
|
||||||
data->current_page_rendering = page_rendering_t();
|
data->current_page_rendering = page_rendering_t();
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1913,9 +1990,10 @@ static bool handle_completions(const std::vector<completion_t> &comp)
|
||||||
run_pager(prefix, is_quoted, surviving_completions);
|
run_pager(prefix, is_quoted, surviving_completions);
|
||||||
|
|
||||||
s_reset(&data->screen, screen_reset_abandon_line);
|
s_reset(&data->screen, screen_reset_abandon_line);
|
||||||
|
|
||||||
}
|
}
|
||||||
reader_repaint();
|
|
||||||
|
reader_repaint_needed();
|
||||||
|
|
||||||
success = false;
|
success = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2143,7 +2221,7 @@ static void set_command_line_and_position(editable_line_t *el, const wcstring &n
|
||||||
{
|
{
|
||||||
el->text = new_str;
|
el->text = new_str;
|
||||||
el->position = pos;
|
el->position = pos;
|
||||||
data->command_line_changed();
|
data->command_line_changed(el);
|
||||||
reader_super_highlight_me_plenty();
|
reader_super_highlight_me_plenty();
|
||||||
reader_repaint();
|
reader_repaint();
|
||||||
}
|
}
|
||||||
|
@ -2420,7 +2498,7 @@ static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos)
|
||||||
/* Callers like to pass us pointers into ourselves, so be careful! I don't know if we can use operator= with a pointer to our interior, so use an intermediate. */
|
/* Callers like to pass us pointers into ourselves, so be careful! I don't know if we can use operator= with a pointer to our interior, so use an intermediate. */
|
||||||
size_t command_line_len = b.size();
|
size_t command_line_len = b.size();
|
||||||
data->command_line.text = b;
|
data->command_line.text = b;
|
||||||
data->command_line_changed();
|
data->command_line_changed(&data->command_line);
|
||||||
|
|
||||||
/* Don't set a position past the command line length */
|
/* Don't set a position past the command line length */
|
||||||
if (pos > command_line_len)
|
if (pos > command_line_len)
|
||||||
|
@ -2580,7 +2658,7 @@ void reader_push(const wchar_t *name)
|
||||||
|
|
||||||
data=n;
|
data=n;
|
||||||
|
|
||||||
data->command_line_changed();
|
data->command_line_changed(&data->command_line);
|
||||||
|
|
||||||
if (data->next == 0)
|
if (data->next == 0)
|
||||||
{
|
{
|
||||||
|
@ -2931,7 +3009,7 @@ static int read_i(void)
|
||||||
wcstring command = tmp;
|
wcstring command = tmp;
|
||||||
data->command_line.position=0;
|
data->command_line.position=0;
|
||||||
data->command_line.text.clear();
|
data->command_line.text.clear();
|
||||||
data->command_line_changed();
|
data->command_line_changed(&data->command_line);
|
||||||
reader_run_command(parser, command);
|
reader_run_command(parser, command);
|
||||||
if (data->end_loop)
|
if (data->end_loop)
|
||||||
{
|
{
|
||||||
|
@ -3023,12 +3101,9 @@ const wchar_t *reader_readline(void)
|
||||||
/* Coalesce redundant repaints. When we get a repaint, we set this to true, and skip repaints until we get something else. */
|
/* Coalesce redundant repaints. When we get a repaint, we set this to true, and skip repaints until we get something else. */
|
||||||
bool coalescing_repaints = false;
|
bool coalescing_repaints = false;
|
||||||
|
|
||||||
/* The cycle index in our completion list */
|
|
||||||
size_t completion_cycle_idx = (size_t)(-1);
|
|
||||||
|
|
||||||
/* The command line before completion */
|
/* The command line before completion */
|
||||||
wcstring cycle_command_line;
|
data->cycle_command_line.clear();
|
||||||
size_t cycle_cursor_pos = 0;
|
data->cycle_cursor_pos = 0;
|
||||||
|
|
||||||
data->search_buff.clear();
|
data->search_buff.clear();
|
||||||
data->search_mode = NO_SEARCH;
|
data->search_mode = NO_SEARCH;
|
||||||
|
@ -3095,7 +3170,7 @@ const wchar_t *reader_readline(void)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
insert_string(arr);
|
insert_string(&data->command_line, arr);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3111,29 +3186,17 @@ const wchar_t *reader_readline(void)
|
||||||
if (last_char != R_YANK && last_char != R_YANK_POP)
|
if (last_char != R_YANK && last_char != R_YANK_POP)
|
||||||
yank_len=0;
|
yank_len=0;
|
||||||
|
|
||||||
/* We clear pager contents for most events, except for a few */
|
/* Clear the pager if necessary */
|
||||||
switch (c)
|
bool focused_on_search_field = (data->active_edit_line() == &data->pager.search_field_line);
|
||||||
|
if (command_ends_paging(c, focused_on_search_field))
|
||||||
{
|
{
|
||||||
case R_COMPLETE:
|
|
||||||
case R_BACKWARD_CHAR:
|
|
||||||
case R_FORWARD_CHAR:
|
|
||||||
case R_UP_LINE:
|
|
||||||
case R_DOWN_LINE:
|
|
||||||
case R_NULL:
|
|
||||||
case R_REPAINT:
|
|
||||||
case R_SUPPRESS_AUTOSUGGESTION:
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
clear_pager();
|
clear_pager();
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//fprintf(stderr, "\n\nchar: %ls\n\n", describe_char(c).c_str());
|
//fprintf(stderr, "\n\nchar: %ls\n\n", describe_char(c).c_str());
|
||||||
|
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
|
|
||||||
/* go to beginning of line*/
|
/* go to beginning of line*/
|
||||||
case R_BEGINNING_OF_LINE:
|
case R_BEGINNING_OF_LINE:
|
||||||
{
|
{
|
||||||
|
@ -3213,6 +3276,7 @@ const wchar_t *reader_readline(void)
|
||||||
|
|
||||||
/* complete */
|
/* complete */
|
||||||
case R_COMPLETE:
|
case R_COMPLETE:
|
||||||
|
case R_COMPLETE_AND_SEARCH:
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!data->complete_func)
|
if (!data->complete_func)
|
||||||
|
@ -3220,10 +3284,10 @@ const wchar_t *reader_readline(void)
|
||||||
|
|
||||||
/* Use the command line only; it doesn't make sense to complete in any other line */
|
/* Use the command line only; it doesn't make sense to complete in any other line */
|
||||||
editable_line_t *el = &data->command_line;
|
editable_line_t *el = &data->command_line;
|
||||||
if (is_navigating_pager_contents() || (! comp_empty && last_char == R_COMPLETE))
|
if (data->is_navigating_pager_contents() || (! comp_empty && last_char == R_COMPLETE))
|
||||||
{
|
{
|
||||||
/* The user typed R_COMPLETE more than once in a row. Cycle through our available completions. */
|
/* The user typed R_COMPLETE more than once in a row. Cycle through our available completions. */
|
||||||
select_completion_in_direction(direction_next, cycle_command_line, cycle_cursor_pos);
|
select_completion_in_direction(c == R_COMPLETE ? direction_next : direction_prev, data->cycle_command_line, data->cycle_cursor_pos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3273,13 +3337,19 @@ const wchar_t *reader_readline(void)
|
||||||
prioritize_completions(comp);
|
prioritize_completions(comp);
|
||||||
|
|
||||||
/* Record our cycle_command_line */
|
/* Record our cycle_command_line */
|
||||||
cycle_command_line = el->text;
|
data->cycle_command_line = el->text;
|
||||||
cycle_cursor_pos = el->position;
|
data->cycle_cursor_pos = el->position;
|
||||||
|
|
||||||
comp_empty = handle_completions(comp);
|
comp_empty = handle_completions(comp);
|
||||||
|
|
||||||
/* Start the cycle at the beginning */
|
/* Show the search field if requested and if we printed a list of completions */
|
||||||
completion_cycle_idx = (size_t)(-1);
|
if (c == R_COMPLETE_AND_SEARCH && ! comp_empty && ! data->pager.empty())
|
||||||
|
{
|
||||||
|
data->pager.set_search_field_shown(true);
|
||||||
|
select_completion_in_direction(direction_next, data->cycle_command_line, data->cycle_cursor_pos);
|
||||||
|
reader_repaint_needed();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -3375,7 +3445,7 @@ const wchar_t *reader_readline(void)
|
||||||
case R_YANK:
|
case R_YANK:
|
||||||
{
|
{
|
||||||
yank_str = kill_yank();
|
yank_str = kill_yank();
|
||||||
insert_string(yank_str);
|
insert_string(data->active_edit_line(), yank_str);
|
||||||
yank_len = wcslen(yank_str);
|
yank_len = wcslen(yank_str);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -3389,7 +3459,7 @@ const wchar_t *reader_readline(void)
|
||||||
remove_backward();
|
remove_backward();
|
||||||
|
|
||||||
yank_str = kill_yank_rotate();
|
yank_str = kill_yank_rotate();
|
||||||
insert_string(yank_str);
|
insert_string(data->active_edit_line(), yank_str);
|
||||||
yank_len = wcslen(yank_str);
|
yank_len = wcslen(yank_str);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -3454,14 +3524,14 @@ const wchar_t *reader_readline(void)
|
||||||
data->autosuggestion.clear();
|
data->autosuggestion.clear();
|
||||||
|
|
||||||
/* We only execute the command line */
|
/* We only execute the command line */
|
||||||
const editable_line_t *el = &data->command_line;
|
editable_line_t *el = &data->command_line;
|
||||||
|
|
||||||
/* Allow backslash-escaped newlines, but only if the following character is whitespace, or we're at the end of the text (see issue #163) */
|
/* Allow backslash-escaped newlines, but only if the following character is whitespace, or we're at the end of the text (see issue #163) */
|
||||||
if (is_backslashed(el->text, el->position))
|
if (is_backslashed(el->text, el->position))
|
||||||
{
|
{
|
||||||
if (el->position >= el->size() || iswspace(el->text.at(el->position)))
|
if (el->position >= el->size() || iswspace(el->text.at(el->position)))
|
||||||
{
|
{
|
||||||
insert_char('\n');
|
insert_char(el, '\n');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3502,7 +3572,7 @@ const wchar_t *reader_readline(void)
|
||||||
*/
|
*/
|
||||||
case PARSER_TEST_INCOMPLETE:
|
case PARSER_TEST_INCOMPLETE:
|
||||||
{
|
{
|
||||||
insert_char('\n');
|
insert_char(el, '\n');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3611,9 +3681,9 @@ const wchar_t *reader_readline(void)
|
||||||
case R_BACKWARD_CHAR:
|
case R_BACKWARD_CHAR:
|
||||||
{
|
{
|
||||||
editable_line_t *el = data->active_edit_line();
|
editable_line_t *el = data->active_edit_line();
|
||||||
if (is_navigating_pager_contents())
|
if (data->is_navigating_pager_contents() && ! data->pager.is_search_field_shown())
|
||||||
{
|
{
|
||||||
select_completion_in_direction(direction_west, cycle_command_line, cycle_cursor_pos);
|
select_completion_in_direction(direction_west, data->cycle_command_line, data->cycle_cursor_pos);
|
||||||
}
|
}
|
||||||
else if (el->position > 0)
|
else if (el->position > 0)
|
||||||
{
|
{
|
||||||
|
@ -3627,9 +3697,9 @@ const wchar_t *reader_readline(void)
|
||||||
case R_FORWARD_CHAR:
|
case R_FORWARD_CHAR:
|
||||||
{
|
{
|
||||||
editable_line_t *el = data->active_edit_line();
|
editable_line_t *el = data->active_edit_line();
|
||||||
if (is_navigating_pager_contents())
|
if (data->is_navigating_pager_contents() && ! data->pager.is_search_field_shown())
|
||||||
{
|
{
|
||||||
select_completion_in_direction(direction_east, cycle_command_line, cycle_cursor_pos);
|
select_completion_in_direction(direction_east, data->cycle_command_line, data->cycle_cursor_pos);
|
||||||
}
|
}
|
||||||
else if (el->position < el->size())
|
else if (el->position < el->size())
|
||||||
{
|
{
|
||||||
|
@ -3705,7 +3775,7 @@ const wchar_t *reader_readline(void)
|
||||||
case R_UP_LINE:
|
case R_UP_LINE:
|
||||||
case R_DOWN_LINE:
|
case R_DOWN_LINE:
|
||||||
{
|
{
|
||||||
if (is_navigating_pager_contents())
|
if (data->is_navigating_pager_contents())
|
||||||
{
|
{
|
||||||
/* We are already navigating pager contents. */
|
/* We are already navigating pager contents. */
|
||||||
selection_direction_t direction;
|
selection_direction_t direction;
|
||||||
|
@ -3718,6 +3788,10 @@ const wchar_t *reader_readline(void)
|
||||||
{
|
{
|
||||||
/* Up arrow, but we are in the first column and first row. End navigation */
|
/* Up arrow, but we are in the first column and first row. End navigation */
|
||||||
direction = direction_deselect;
|
direction = direction_deselect;
|
||||||
|
|
||||||
|
/* Also hide the search field */
|
||||||
|
data->pager.search_field_line.clear();
|
||||||
|
data->pager.set_search_field_shown(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3726,12 +3800,12 @@ const wchar_t *reader_readline(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now do the selection */
|
/* Now do the selection */
|
||||||
select_completion_in_direction(direction, cycle_command_line, cycle_cursor_pos);
|
select_completion_in_direction(direction, data->cycle_command_line, data->cycle_cursor_pos);
|
||||||
}
|
}
|
||||||
else if (c == R_DOWN_LINE && ! data->pager.empty())
|
else if (c == R_DOWN_LINE && ! data->pager.empty())
|
||||||
{
|
{
|
||||||
/* We pressed down with a non-empty pager contents, begin navigation */
|
/* We pressed down with a non-empty pager contents, begin navigation */
|
||||||
select_completion_in_direction(direction_south, cycle_command_line, cycle_cursor_pos);
|
select_completion_in_direction(direction_south, data->cycle_command_line, data->cycle_cursor_pos);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3888,7 +3962,7 @@ const wchar_t *reader_readline(void)
|
||||||
data->command_line.text.at(pos) = chr;
|
data->command_line.text.at(pos) = chr;
|
||||||
capitalized_first = capitalized_first || make_uppercase;
|
capitalized_first = capitalized_first || make_uppercase;
|
||||||
}
|
}
|
||||||
data->command_line_changed();
|
data->command_line_changed(el);
|
||||||
reader_super_highlight_me_plenty();
|
reader_super_highlight_me_plenty();
|
||||||
reader_repaint_needed();
|
reader_repaint_needed();
|
||||||
break;
|
break;
|
||||||
|
@ -3899,19 +3973,20 @@ const wchar_t *reader_readline(void)
|
||||||
{
|
{
|
||||||
if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127)))
|
if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127)))
|
||||||
{
|
{
|
||||||
|
bool should_expand_abbreviations = false;
|
||||||
if (is_navigating_pager_contents())
|
if (data->is_navigating_pager_contents())
|
||||||
{
|
{
|
||||||
|
data->pager.set_search_field_shown(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Expand abbreviations after space */
|
/* Expand abbreviations after space */
|
||||||
bool should_expand_abbreviations = (c == L' ');
|
should_expand_abbreviations = (c == L' ');
|
||||||
|
}
|
||||||
|
|
||||||
/* Regular character */
|
/* Regular character */
|
||||||
insert_char(c, should_expand_abbreviations);
|
insert_char(data->active_edit_line(), c, should_expand_abbreviations);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3987,6 +4062,35 @@ int reader_has_pager_contents()
|
||||||
return ! data->current_page_rendering.screen_data.empty();
|
return ! data->current_page_rendering.screen_data.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reader_selected_completion_changed(pager_t *pager)
|
||||||
|
{
|
||||||
|
/* Only interested in the top level pager */
|
||||||
|
if (data == NULL || pager != &data->pager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const completion_t *completion = pager->selected_completion(data->current_page_rendering);
|
||||||
|
|
||||||
|
/* Update the cursor and command line */
|
||||||
|
size_t cursor_pos = data->cycle_cursor_pos;
|
||||||
|
wcstring new_cmd_line;
|
||||||
|
|
||||||
|
if (completion == NULL)
|
||||||
|
{
|
||||||
|
new_cmd_line = data->cycle_command_line;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
new_cmd_line = completion_apply_to_command_line(completion->completion, completion->flags, data->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_needed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Read non-interactively. Read input from stdin without displaying
|
Read non-interactively. Read input from stdin without displaying
|
||||||
|
|
9
reader.h
9
reader.h
|
@ -49,6 +49,12 @@ class editable_line_t
|
||||||
return text.empty();
|
return text.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
text.clear();
|
||||||
|
position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
editable_line_t() : text(), position(0)
|
editable_line_t() : text(), position(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -295,5 +301,8 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso
|
||||||
/* Apply a completion string. Exposed for testing only. */
|
/* Apply a completion string. Exposed for testing only. */
|
||||||
wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flags_t flags, const wcstring &command_line, size_t *inout_cursor_pos, bool append_only);
|
wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flags_t flags, const wcstring &command_line, size_t *inout_cursor_pos, bool append_only);
|
||||||
|
|
||||||
|
/* Called by pager */
|
||||||
|
class pager_t;
|
||||||
|
void reader_selected_completion_changed(pager_t *pager);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
17
screen.cpp
17
screen.cpp
|
@ -1237,7 +1237,8 @@ void s_write(screen_t *s,
|
||||||
const highlight_spec_t *colors,
|
const highlight_spec_t *colors,
|
||||||
const int *indent,
|
const int *indent,
|
||||||
size_t cursor_pos,
|
size_t cursor_pos,
|
||||||
const page_rendering_t &pager)
|
const page_rendering_t &pager,
|
||||||
|
bool cursor_position_is_within_pager)
|
||||||
{
|
{
|
||||||
screen_data_t::cursor_t cursor_arr;
|
screen_data_t::cursor_t cursor_arr;
|
||||||
|
|
||||||
|
@ -1306,25 +1307,27 @@ void s_write(screen_t *s,
|
||||||
{
|
{
|
||||||
int color = colors[i];
|
int color = colors[i];
|
||||||
|
|
||||||
if (i == cursor_pos)
|
if (! cursor_position_is_within_pager && i == cursor_pos)
|
||||||
{
|
{
|
||||||
color = 0;
|
color = 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (i == cursor_pos)
|
|
||||||
{
|
|
||||||
cursor_arr = s->desired.cursor;
|
cursor_arr = s->desired.cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
s_desired_append_char(s, effective_commandline.at(i), color, indent[i], first_line_prompt_space);
|
s_desired_append_char(s, effective_commandline.at(i), color, indent[i], first_line_prompt_space);
|
||||||
}
|
}
|
||||||
if (i == cursor_pos)
|
if (! cursor_position_is_within_pager && i == cursor_pos)
|
||||||
{
|
{
|
||||||
cursor_arr = s->desired.cursor;
|
cursor_arr = s->desired.cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
s->desired.cursor = cursor_arr;
|
s->desired.cursor = cursor_arr;
|
||||||
|
|
||||||
|
if (cursor_position_is_within_pager)
|
||||||
|
{
|
||||||
|
s->desired.cursor.x = (int)cursor_pos;
|
||||||
|
s->desired.cursor.y = (int)s->desired.line_count();
|
||||||
|
}
|
||||||
|
|
||||||
/* Append pager_data (none if empty) */
|
/* Append pager_data (none if empty) */
|
||||||
s->desired.append_lines(pager.screen_data);
|
s->desired.append_lines(pager.screen_data);
|
||||||
|
|
||||||
|
|
5
screen.h
5
screen.h
|
@ -218,6 +218,8 @@ public:
|
||||||
\param colors the colors to use for the comand line
|
\param colors the colors to use for the comand line
|
||||||
\param indent the indent to use for the command line
|
\param indent the indent to use for the command line
|
||||||
\param cursor_pos where the cursor is
|
\param cursor_pos where the cursor is
|
||||||
|
\param pager_data any pager data, to append to the screen
|
||||||
|
\param position_is_within_pager whether the position is within the pager line (first line)
|
||||||
*/
|
*/
|
||||||
void s_write(screen_t *s,
|
void s_write(screen_t *s,
|
||||||
const wcstring &left_prompt,
|
const wcstring &left_prompt,
|
||||||
|
@ -227,7 +229,8 @@ void s_write(screen_t *s,
|
||||||
const highlight_spec_t *colors,
|
const highlight_spec_t *colors,
|
||||||
const int *indent,
|
const int *indent,
|
||||||
size_t cursor_pos,
|
size_t cursor_pos,
|
||||||
const page_rendering_t &pager_data);
|
const page_rendering_t &pager_data,
|
||||||
|
bool position_is_within_pager);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
This function resets the screen buffers internal knowledge about
|
This function resets the screen buffers internal knowledge about
|
||||||
|
|
|
@ -114,6 +114,9 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis
|
||||||
# This will make sure the output of the current command is paged using the less pager when you press Meta-p
|
# This will make sure the output of the current command is paged using the less pager when you press Meta-p
|
||||||
bind \ep '__fish_paginate'
|
bind \ep '__fish_paginate'
|
||||||
|
|
||||||
|
# shift-tab does a tab complete followed by a search
|
||||||
|
bind --key btab complete-and-search
|
||||||
|
|
||||||
# term-specific special bindings
|
# term-specific special bindings
|
||||||
switch "$TERM"
|
switch "$TERM"
|
||||||
case 'rxvt*'
|
case 'rxvt*'
|
||||||
|
@ -122,3 +125,4 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis
|
||||||
bind \eOd backward-word
|
bind \eOd backward-word
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue