mirror of
https://github.com/fish-shell/fish-shell
synced 2025-01-13 13:39:02 +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
|
||||
long_options[] =
|
||||
{
|
||||
{
|
||||
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"key-names", no_argument, 0, 'K'
|
||||
}
|
||||
,
|
||||
{
|
||||
0, 0, 0, 0
|
||||
}
|
||||
}
|
||||
;
|
||||
{ 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"key-names", no_argument, 0, 'K' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
while (1)
|
||||
{
|
||||
|
|
|
@ -104,6 +104,7 @@ static const wchar_t * const name_arr[] =
|
|||
L"yank",
|
||||
L"yank-pop",
|
||||
L"complete",
|
||||
L"complete-and-search",
|
||||
L"beginning-of-history",
|
||||
L"end-of-history",
|
||||
L"backward-kill-line",
|
||||
|
@ -201,6 +202,7 @@ static const wchar_t code_arr[] =
|
|||
R_YANK,
|
||||
R_YANK_POP,
|
||||
R_COMPLETE,
|
||||
R_COMPLETE_AND_SEARCH,
|
||||
R_BEGINNING_OF_HISTORY,
|
||||
R_END_OF_HISTORY,
|
||||
R_BACKWARD_KILL_LINE,
|
||||
|
|
1
input.h
1
input.h
|
@ -33,6 +33,7 @@ enum
|
|||
R_YANK,
|
||||
R_YANK_POP,
|
||||
R_COMPLETE,
|
||||
R_COMPLETE_AND_SEARCH,
|
||||
R_BEGINNING_OF_HISTORY,
|
||||
R_END_OF_HISTORY,
|
||||
R_BACKWARD_KILL_LINE,
|
||||
|
|
|
@ -251,7 +251,6 @@ wchar_t input_common_readch(int timed)
|
|||
case 0:
|
||||
return 0;
|
||||
default:
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
160
pager.cpp
160
pager.cpp
|
@ -3,6 +3,7 @@
|
|||
#include "pager.h"
|
||||
#include "highlight.h"
|
||||
#include "input_common.h"
|
||||
#include "wutil.h"
|
||||
#include <vector>
|
||||
#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 */
|
||||
#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)
|
||||
{
|
||||
if (numer == 0)
|
||||
return 0;
|
||||
|
||||
assert(denom > 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);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
// 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
|
||||
if (prefix == L"-")
|
||||
join_completions(&completion_infos);
|
||||
join_completions(&unfiltered_completion_infos);
|
||||
|
||||
// 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)
|
||||
|
@ -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 */
|
||||
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)
|
||||
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" */
|
||||
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())
|
||||
{
|
||||
|
@ -561,11 +626,17 @@ bool pager_t::completion_try_print(size_t cols, const wcstring &prefix, const co
|
|||
if (search_field_shown)
|
||||
{
|
||||
/* Add the search field */
|
||||
wcstring spaces(8, L' ');
|
||||
spaces.insert(spaces.begin(), 1, L'h');
|
||||
wcstring search_field_text = search_field_line.text;
|
||||
/* 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);
|
||||
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;
|
||||
rendering.term_width = this->available_term_width;
|
||||
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--)
|
||||
{
|
||||
/* 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);
|
||||
|
||||
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 */
|
||||
continue;
|
||||
|
@ -613,13 +684,17 @@ page_rendering_t pager_t::render() const
|
|||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rendering;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -631,13 +706,13 @@ pager_t::pager_t() : available_term_width(0), available_term_height(0), selected
|
|||
|
||||
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)
|
||||
{
|
||||
/* Must have something to select */
|
||||
if (this->empty())
|
||||
if (this->completion_infos.empty())
|
||||
{
|
||||
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
|
||||
{
|
||||
/* No completions -> no selection */
|
||||
if (completion_infos.empty())
|
||||
{
|
||||
return PAGER_SELECTION_NONE;
|
||||
}
|
||||
|
||||
size_t result = selected_completion_idx;
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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());
|
||||
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 * 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) */
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void pager_t::clear()
|
||||
{
|
||||
unfiltered_completion_infos.clear();
|
||||
completion_infos.clear();
|
||||
prefix.clear();
|
||||
selected_completion_idx = PAGER_SELECTION_NONE;
|
||||
fully_disclosed = 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 */
|
||||
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 "screen.h"
|
||||
#include "reader.h"
|
||||
|
||||
/* Represents rendering from the pager */
|
||||
class page_rendering_t
|
||||
|
@ -20,6 +21,9 @@ class page_rendering_t
|
|||
|
||||
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" */
|
||||
page_rendering_t();
|
||||
};
|
||||
|
@ -47,7 +51,6 @@ class pager_t
|
|||
|
||||
/* Whether we show the search field */
|
||||
bool search_field_shown;
|
||||
wcstring search_field_string;
|
||||
|
||||
/* 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;
|
||||
|
@ -84,21 +87,33 @@ class pager_t
|
|||
|
||||
private:
|
||||
typedef std::vector<comp_t> comp_info_list_t;
|
||||
|
||||
/* The filtered list of 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;
|
||||
|
||||
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;
|
||||
|
||||
void recalc_min_widths(comp_info_list_t * lst) 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;
|
||||
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:
|
||||
|
||||
/* The text of the search field */
|
||||
editable_line_t search_field_line;
|
||||
|
||||
/* Sets the set of completions */
|
||||
void set_completions(const completion_list_t &comp);
|
||||
|
||||
|
@ -130,6 +145,21 @@ class pager_t
|
|||
/* Clears all completions and the prefix */
|
||||
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 */
|
||||
pager_t();
|
||||
};
|
||||
|
|
262
reader.cpp
262
reader.cpp
|
@ -210,9 +210,6 @@ public:
|
|||
/** 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 */
|
||||
bool allow_autosuggestion;
|
||||
|
||||
|
@ -254,14 +251,26 @@ public:
|
|||
/** The current position in search_prev */
|
||||
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 */
|
||||
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 */
|
||||
void command_line_changed(void);
|
||||
void command_line_changed(const editable_line_t *el);
|
||||
|
||||
/** Expand abbreviations at the current cursor position, minus backtrack_amt. */
|
||||
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 */
|
||||
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[i] is the classification (according to the enum in
|
||||
|
@ -343,13 +356,13 @@ public:
|
|||
|
||||
/** Constructor */
|
||||
reader_data_t() :
|
||||
is_navigating_pager(0),
|
||||
allow_autosuggestion(0),
|
||||
suppress_autosuggestion(0),
|
||||
expand_abbreviations(0),
|
||||
history(0),
|
||||
token_history_pos(0),
|
||||
search_pos(0),
|
||||
cycle_cursor_pos(0),
|
||||
complete_func(0),
|
||||
highlight_function(0),
|
||||
test_func(0),
|
||||
|
@ -531,12 +544,12 @@ wcstring combine_command_and_autosuggestion(const wcstring &cmdline, const wcstr
|
|||
|
||||
static void reader_repaint()
|
||||
{
|
||||
editable_line_t *el = &data->command_line;
|
||||
editable_line_t *cmd_line = &data->command_line;
|
||||
// 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
|
||||
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();
|
||||
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.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,
|
||||
data->left_prompt_buff,
|
||||
data->right_prompt_buff,
|
||||
full_line,
|
||||
el->size(),
|
||||
cmd_line->size(),
|
||||
&colors[0],
|
||||
&indents[0],
|
||||
el->position,
|
||||
data->current_page_rendering);
|
||||
cursor_position,
|
||||
data->current_page_rendering,
|
||||
focused_on_pager);
|
||||
|
||||
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);
|
||||
data->command_line_changed();
|
||||
data->command_line_changed(el);
|
||||
|
||||
reader_super_highlight_me_plenty();
|
||||
reader_repaint();
|
||||
|
@ -650,9 +667,11 @@ void reader_pop_current_filename()
|
|||
|
||||
|
||||
/** 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();
|
||||
if (el == &this->command_line)
|
||||
{
|
||||
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. */
|
||||
|
@ -663,6 +682,11 @@ void reader_data_t::command_line_changed()
|
|||
|
||||
/* Update the gen 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'. */
|
||||
|
@ -752,7 +776,7 @@ bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack)
|
|||
|
||||
el->text.swap(new_cmdline);
|
||||
el->position = new_buff_pos;
|
||||
data->command_line_changed();
|
||||
data->command_line_changed(el);
|
||||
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
|
||||
screen using syntax highlighting, etc.
|
||||
|
@ -1044,7 +1126,7 @@ static void remove_backward()
|
|||
el->text.erase(el->position, 1);
|
||||
}
|
||||
while (width == 0 && el->position > 0);
|
||||
data->command_line_changed();
|
||||
data->command_line_changed(el);
|
||||
data->suppress_autosuggestion = true;
|
||||
|
||||
reader_super_highlight_me_plenty();
|
||||
|
@ -1060,16 +1142,15 @@ static void remove_backward()
|
|||
Optionally also expand abbreviations.
|
||||
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();
|
||||
if (len == 0)
|
||||
return false;
|
||||
|
||||
editable_line_t *el = data->active_edit_line();
|
||||
el->insert_string(str);
|
||||
|
||||
data->command_line_changed();
|
||||
data->command_line_changed(el);
|
||||
|
||||
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
|
||||
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_changed();
|
||||
data->command_line_changed(&data->command_line);
|
||||
reader_super_highlight_me_plenty();
|
||||
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 */
|
||||
static void clear_pager()
|
||||
{
|
||||
|
@ -1901,6 +1977,7 @@ static bool handle_completions(const std::vector<completion_t> &comp)
|
|||
|
||||
/* Invalidate our rendering */
|
||||
data->current_page_rendering = page_rendering_t();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1913,9 +1990,10 @@ static bool handle_completions(const std::vector<completion_t> &comp)
|
|||
run_pager(prefix, is_quoted, surviving_completions);
|
||||
|
||||
s_reset(&data->screen, screen_reset_abandon_line);
|
||||
|
||||
}
|
||||
reader_repaint();
|
||||
|
||||
reader_repaint_needed();
|
||||
|
||||
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->position = pos;
|
||||
data->command_line_changed();
|
||||
data->command_line_changed(el);
|
||||
reader_super_highlight_me_plenty();
|
||||
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. */
|
||||
size_t command_line_len = b.size();
|
||||
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 */
|
||||
if (pos > command_line_len)
|
||||
|
@ -2580,7 +2658,7 @@ void reader_push(const wchar_t *name)
|
|||
|
||||
data=n;
|
||||
|
||||
data->command_line_changed();
|
||||
data->command_line_changed(&data->command_line);
|
||||
|
||||
if (data->next == 0)
|
||||
{
|
||||
|
@ -2931,7 +3009,7 @@ static int read_i(void)
|
|||
wcstring command = tmp;
|
||||
data->command_line.position=0;
|
||||
data->command_line.text.clear();
|
||||
data->command_line_changed();
|
||||
data->command_line_changed(&data->command_line);
|
||||
reader_run_command(parser, command);
|
||||
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. */
|
||||
bool coalescing_repaints = false;
|
||||
|
||||
/* The cycle index in our completion list */
|
||||
size_t completion_cycle_idx = (size_t)(-1);
|
||||
|
||||
/* The command line before completion */
|
||||
wcstring cycle_command_line;
|
||||
size_t cycle_cursor_pos = 0;
|
||||
data->cycle_command_line.clear();
|
||||
data->cycle_cursor_pos = 0;
|
||||
|
||||
data->search_buff.clear();
|
||||
data->search_mode = NO_SEARCH;
|
||||
|
@ -3095,7 +3170,7 @@ const wchar_t *reader_readline(void)
|
|||
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)
|
||||
yank_len=0;
|
||||
|
||||
/* We clear pager contents for most events, except for a few */
|
||||
switch (c)
|
||||
/* Clear the pager if necessary */
|
||||
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();
|
||||
break;
|
||||
}
|
||||
|
||||
//fprintf(stderr, "\n\nchar: %ls\n\n", describe_char(c).c_str());
|
||||
|
||||
switch (c)
|
||||
{
|
||||
|
||||
/* go to beginning of line*/
|
||||
case R_BEGINNING_OF_LINE:
|
||||
{
|
||||
|
@ -3213,6 +3276,7 @@ const wchar_t *reader_readline(void)
|
|||
|
||||
/* complete */
|
||||
case R_COMPLETE:
|
||||
case R_COMPLETE_AND_SEARCH:
|
||||
{
|
||||
|
||||
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 */
|
||||
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. */
|
||||
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
|
||||
{
|
||||
|
@ -3273,13 +3337,19 @@ const wchar_t *reader_readline(void)
|
|||
prioritize_completions(comp);
|
||||
|
||||
/* Record our cycle_command_line */
|
||||
cycle_command_line = el->text;
|
||||
cycle_cursor_pos = el->position;
|
||||
data->cycle_command_line = el->text;
|
||||
data->cycle_cursor_pos = el->position;
|
||||
|
||||
comp_empty = handle_completions(comp);
|
||||
|
||||
/* Start the cycle at the beginning */
|
||||
completion_cycle_idx = (size_t)(-1);
|
||||
/* Show the search field if requested and if we printed a list of completions */
|
||||
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;
|
||||
|
@ -3375,7 +3445,7 @@ const wchar_t *reader_readline(void)
|
|||
case R_YANK:
|
||||
{
|
||||
yank_str = kill_yank();
|
||||
insert_string(yank_str);
|
||||
insert_string(data->active_edit_line(), yank_str);
|
||||
yank_len = wcslen(yank_str);
|
||||
break;
|
||||
}
|
||||
|
@ -3389,7 +3459,7 @@ const wchar_t *reader_readline(void)
|
|||
remove_backward();
|
||||
|
||||
yank_str = kill_yank_rotate();
|
||||
insert_string(yank_str);
|
||||
insert_string(data->active_edit_line(), yank_str);
|
||||
yank_len = wcslen(yank_str);
|
||||
}
|
||||
break;
|
||||
|
@ -3454,14 +3524,14 @@ const wchar_t *reader_readline(void)
|
|||
data->autosuggestion.clear();
|
||||
|
||||
/* 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) */
|
||||
if (is_backslashed(el->text, el->position))
|
||||
{
|
||||
if (el->position >= el->size() || iswspace(el->text.at(el->position)))
|
||||
{
|
||||
insert_char('\n');
|
||||
insert_char(el, '\n');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -3502,7 +3572,7 @@ const wchar_t *reader_readline(void)
|
|||
*/
|
||||
case PARSER_TEST_INCOMPLETE:
|
||||
{
|
||||
insert_char('\n');
|
||||
insert_char(el, '\n');
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -3611,9 +3681,9 @@ const wchar_t *reader_readline(void)
|
|||
case R_BACKWARD_CHAR:
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
@ -3627,9 +3697,9 @@ const wchar_t *reader_readline(void)
|
|||
case R_FORWARD_CHAR:
|
||||
{
|
||||
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())
|
||||
{
|
||||
|
@ -3705,7 +3775,7 @@ const wchar_t *reader_readline(void)
|
|||
case R_UP_LINE:
|
||||
case R_DOWN_LINE:
|
||||
{
|
||||
if (is_navigating_pager_contents())
|
||||
if (data->is_navigating_pager_contents())
|
||||
{
|
||||
/* We are already navigating pager contents. */
|
||||
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 */
|
||||
direction = direction_deselect;
|
||||
|
||||
/* Also hide the search field */
|
||||
data->pager.search_field_line.clear();
|
||||
data->pager.set_search_field_shown(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3726,12 +3800,12 @@ const wchar_t *reader_readline(void)
|
|||
}
|
||||
|
||||
/* 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())
|
||||
{
|
||||
/* 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
|
||||
{
|
||||
|
@ -3888,7 +3962,7 @@ const wchar_t *reader_readline(void)
|
|||
data->command_line.text.at(pos) = chr;
|
||||
capitalized_first = capitalized_first || make_uppercase;
|
||||
}
|
||||
data->command_line_changed();
|
||||
data->command_line_changed(el);
|
||||
reader_super_highlight_me_plenty();
|
||||
reader_repaint_needed();
|
||||
break;
|
||||
|
@ -3899,19 +3973,20 @@ const wchar_t *reader_readline(void)
|
|||
{
|
||||
if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127)))
|
||||
{
|
||||
|
||||
if (is_navigating_pager_contents())
|
||||
bool should_expand_abbreviations = false;
|
||||
if (data->is_navigating_pager_contents())
|
||||
{
|
||||
|
||||
data->pager.set_search_field_shown(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Expand abbreviations after space */
|
||||
bool should_expand_abbreviations = (c == L' ');
|
||||
should_expand_abbreviations = (c == L' ');
|
||||
}
|
||||
|
||||
/* Regular character */
|
||||
insert_char(c, should_expand_abbreviations);
|
||||
}
|
||||
insert_char(data->active_edit_line(), c, should_expand_abbreviations);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3987,6 +4062,35 @@ int reader_has_pager_contents()
|
|||
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
|
||||
|
|
9
reader.h
9
reader.h
|
@ -49,6 +49,12 @@ class editable_line_t
|
|||
return text.empty();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
text.clear();
|
||||
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. */
|
||||
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
|
||||
|
|
17
screen.cpp
17
screen.cpp
|
@ -1237,7 +1237,8 @@ void s_write(screen_t *s,
|
|||
const highlight_spec_t *colors,
|
||||
const int *indent,
|
||||
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;
|
||||
|
||||
|
@ -1306,25 +1307,27 @@ void s_write(screen_t *s,
|
|||
{
|
||||
int color = colors[i];
|
||||
|
||||
if (i == cursor_pos)
|
||||
if (! cursor_position_is_within_pager && i == cursor_pos)
|
||||
{
|
||||
color = 0;
|
||||
}
|
||||
|
||||
if (i == cursor_pos)
|
||||
{
|
||||
cursor_arr = s->desired.cursor;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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) */
|
||||
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 indent the indent to use for the command line
|
||||
\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,
|
||||
const wcstring &left_prompt,
|
||||
|
@ -227,7 +229,8 @@ void s_write(screen_t *s,
|
|||
const highlight_spec_t *colors,
|
||||
const int *indent,
|
||||
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
|
||||
|
|
|
@ -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
|
||||
bind \ep '__fish_paginate'
|
||||
|
||||
# shift-tab does a tab complete followed by a search
|
||||
bind --key btab complete-and-search
|
||||
|
||||
# term-specific special bindings
|
||||
switch "$TERM"
|
||||
case 'rxvt*'
|
||||
|
@ -122,3 +125,4 @@ function fish_default_key_bindings -d "Default (Emacs-like) key bindings for fis
|
|||
bind \eOd backward-word
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue