aboutsummaryrefslogtreecommitdiffhomepage
path: root/reader.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2014-03-29 14:19:45 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2014-03-29 14:19:45 -0700
commitd4fafeb6d66e415e85c67700e5a370765c09bb93 (patch)
tree7e208d860649e8651180b281ec715a96169a9dbe /reader.cpp
parent844b01cb6be2ab3a3f7070112c94604b43710835 (diff)
parent31bf50b2d495222925371556169f61c1c5a81ed7 (diff)
Merge branch 'master' into 1218_rebase
Conflicts: builtin.cpp builtin_commandline.cpp highlight.cpp input.cpp input.h reader.cpp screen.cpp screen.h
Diffstat (limited to 'reader.cpp')
-rw-r--r--reader.cpp1215
1 files changed, 706 insertions, 509 deletions
diff --git a/reader.cpp b/reader.cpp
index e02cfc54..243d50ab 100644
--- a/reader.cpp
+++ b/reader.cpp
@@ -100,6 +100,7 @@ commence.
#include "parse_util.h"
#include "parser_keywords.h"
#include "parse_tree.h"
+#include "pager.h"
/**
Maximum length of prefix string when printing completion
@@ -180,7 +181,13 @@ static volatile unsigned int s_generation_count;
/* This pthreads generation count is set when an autosuggestion background thread starts up, so it can easily check if the work it is doing is no longer useful. */
static pthread_key_t generation_count_key;
-static void set_command_line_and_position(const wcstring &new_str, size_t pos);
+static void set_command_line_and_position(editable_line_t *el, const wcstring &new_str, size_t pos);
+
+void editable_line_t::insert_string(const wcstring &str)
+{
+ this->text.insert(this->position, str);
+ this->position += str.size();
+}
/**
A struct describing the state of the interactive reader. These
@@ -192,11 +199,17 @@ class reader_data_t
public:
/** String containing the whole current commandline */
- wcstring command_line;
+ editable_line_t command_line;
/** String containing the autosuggestion */
wcstring autosuggestion;
+ /** Current pager */
+ pager_t pager;
+
+ /** Current page rendering */
+ page_rendering_t current_page_rendering;
+
/** Whether autosuggesting is allowed at all */
bool allow_autosuggestion;
@@ -238,21 +251,33 @@ public:
/** The current position in search_prev */
size_t search_pos;
- /** Length of the command */
- size_t command_length() const
+ 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.size();
+ 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);
+
+ /** Do what we need to do whenever our pager selection */
+ void pager_selection_changed();
/** Expand abbreviations at the current cursor position, minus backtrack_amt. */
bool expand_abbreviation_as_necessary(size_t cursor_backtrack);
- /** The current position of the cursor in buff. */
- size_t buff_pos;
-
/** Indicates whether a selection is currently active */
bool sel_active;
@@ -278,6 +303,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
@@ -348,11 +377,11 @@ public:
history(0),
token_history_pos(0),
search_pos(0),
- buff_pos(0),
sel_active(0),
sel_begin_pos(0),
sel_start_pos(0),
sel_stop_pos(0),
+ cycle_cursor_pos(0),
complete_func(0),
highlight_function(0),
test_func(0),
@@ -367,6 +396,9 @@ public:
}
};
+/* Sets the command line contents, without clearing the pager */
+static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos);
+
/**
The current interactive reading context
*/
@@ -413,7 +445,7 @@ static struct termios terminal_mode_on_startup;
static struct termios terminal_mode_for_executing_programs;
-static void reader_super_highlight_me_plenty(size_t pos);
+static void reader_super_highlight_me_plenty(int highlight_pos_adjust = 0, bool no_io = false);
/**
Variable to keep track of forced exits - see \c reader_exit_forced();
@@ -450,10 +482,10 @@ static void term_donate()
/**
Update the cursor position
*/
-static void update_buff_pos(size_t buff_pos)
+static void update_buff_pos(editable_line_t *el, size_t buff_pos)
{
- data->buff_pos = buff_pos;
- if(data->sel_active)
+ el->position = buff_pos;
+ if (el == &data->command_line && data->sel_active)
{
if(data->sel_begin_pos <= buff_pos)
{
@@ -553,11 +585,12 @@ wcstring combine_command_and_autosuggestion(const wcstring &cmdline, const wcstr
*/
static void reader_repaint()
{
+ editable_line_t *cmd_line = &data->command_line;
// Update the indentation
- data->indents = parse_util_compute_indents(data->command_line);
+ data->indents = parse_util_compute_indents(cmd_line->text);
// Combine the command and autosuggestion into one string
- wcstring full_line = combine_command_and_autosuggestion(data->command_line, data->autosuggestion);
+ wcstring full_line = combine_command_and_autosuggestion(cmd_line->text, data->autosuggestion);
size_t len = full_line.size();
if (len < 1)
@@ -566,46 +599,46 @@ static void reader_repaint()
std::vector<highlight_spec_t> colors = data->colors;
colors.resize(len, highlight_spec_autosuggestion);
- if(data->sel_active)
+ if (data->sel_active)
{
- highlight_spec_t selection_color = highlight_make_background(highlight_spec_selection);
- for(int i = data->sel_start_pos; i <= std::min(len - 1, data->sel_stop_pos); i++)
- {
- colors[i] = selection_color;
- }
+ highlight_spec_t selection_color = highlight_make_background(highlight_spec_selection);
+ for(size_t i = data->sel_start_pos; i <= std::min(len - 1, data->sel_stop_pos); i++)
+ {
+ colors[i] = selection_color;
+ }
}
std::vector<int> indents = data->indents;
indents.resize(len);
+ // Re-render our completions page if necessary
+ // We set the term size to 1 less than the true term height. This means we will always show the (bottom) line of the prompt.
+ data->pager.set_term_size(maxi(1, common_get_width()), maxi(1, common_get_height() - 1));
+ data->pager.update_rendering(&data->current_page_rendering);
+
+ 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,
- data->command_length(),
+ cmd_line->size(),
&colors[0],
&indents[0],
- data->buff_pos,
data->sel_start_pos,
- data->sel_stop_pos);
+ data->sel_stop_pos,
+ cursor_position,
+ data->current_page_rendering,
+ focused_on_pager);
data->repaint_needed = false;
}
-
-static void reader_repaint_without_autosuggestion()
-{
- // Swap in an empty autosuggestion, repaint, then swap it out
- wcstring saved_autosuggestion;
- data->autosuggestion.swap(saved_autosuggestion);
- reader_repaint();
- data->autosuggestion.swap(saved_autosuggestion);
-}
-
/** Internal helper function for handling killing parts of text. */
-static void reader_kill(size_t begin_idx, size_t length, int mode, int newv)
+static void reader_kill(editable_line_t *el, size_t begin_idx, size_t length, int mode, int newv)
{
- const wchar_t *begin = data->command_line.c_str() + begin_idx;
+ const wchar_t *begin = el->text.c_str() + begin_idx;
if (newv)
{
data->kill_item = wcstring(begin, length);
@@ -628,19 +661,18 @@ static void reader_kill(size_t begin_idx, size_t length, int mode, int newv)
kill_replace(old, data->kill_item);
}
- if (data->buff_pos > begin_idx)
+ if (el->position > begin_idx)
{
/* Move the buff position back by the number of characters we deleted, but don't go past buff_pos */
- size_t backtrack = mini(data->buff_pos - begin_idx, length);
- update_buff_pos(data->buff_pos - backtrack);
+ size_t backtrack = mini(el->position - begin_idx, length);
+ update_buff_pos(el, el->position - backtrack);
}
- data->command_line.erase(begin_idx, length);
- data->command_line_changed();
+ el->text.erase(begin_idx, length);
+ data->command_line_changed(el);
- reader_super_highlight_me_plenty(data->buff_pos);
+ reader_super_highlight_me_plenty();
reader_repaint();
-
}
@@ -678,10 +710,12 @@ 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();
- size_t len = command_length();
+ 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. */
highlight_spec_t last_color = colors.empty() ? highlight_spec_t() : colors.back();
@@ -692,6 +726,39 @@ 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();
+ this->pager_selection_changed();
+ }
+}
+
+void reader_data_t::pager_selection_changed()
+{
+ ASSERT_IS_MAIN_THREAD();
+
+ const completion_t *completion = this->pager.selected_completion(this->current_page_rendering);
+
+ /* Update the cursor and command line */
+ size_t cursor_pos = this->cycle_cursor_pos;
+ wcstring new_cmd_line;
+
+ if (completion == NULL)
+ {
+ new_cmd_line = this->cycle_command_line;
+ }
+ else
+ {
+ new_cmd_line = completion_apply_to_command_line(completion->completion, completion->flags, this->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 */
+ this->suppress_autosuggestion = true;
+
+ /* Trigger repaint (see #765) */
+ reader_repaint_needed();
+}
/* 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)
@@ -767,19 +834,21 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso
bool reader_data_t::expand_abbreviation_as_necessary(size_t cursor_backtrack)
{
bool result = false;
- if (this->expand_abbreviations)
+ editable_line_t *el = data->active_edit_line();
+ if (this->expand_abbreviations && el == &data->command_line)
{
/* Try expanding abbreviations */
wcstring new_cmdline;
- size_t cursor_pos = this->buff_pos - mini(this->buff_pos, cursor_backtrack);
- if (reader_expand_abbreviation_in_command(this->command_line, cursor_pos, &new_cmdline))
+ size_t cursor_pos = el->position - mini(el->position, cursor_backtrack);
+ if (reader_expand_abbreviation_in_command(el->text, cursor_pos, &new_cmdline))
{
/* We expanded an abbreviation! The cursor moves by the difference in the command line lengths. */
- size_t new_buff_pos = this->buff_pos + new_cmdline.size() - this->command_line.size();
+ size_t new_buff_pos = el->position + new_cmdline.size() - el->text.size();
+
- this->command_line.swap(new_cmdline);
- update_buff_pos(new_buff_pos);
- data->command_line_changed();
+ el->text.swap(new_cmdline);
+ update_buff_pos(el, new_buff_pos);
+ data->command_line_changed(el);
result = true;
}
}
@@ -1051,29 +1120,92 @@ 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_ACCEPT_AUTOSUGGESTION:
+ case R_CANCEL:
+ 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;
+
+ /* R_EXECUTE does end paging, but only executes if it was not paging. So it's handled specially */
+ case R_EXECUTE:
+ 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.
*/
static void remove_backward()
{
+ editable_line_t *el = data->active_edit_line();
- if (data->buff_pos <= 0)
+ if (el->position <= 0)
return;
/* Fake composed character sequences by continuing to delete until we delete a character of width at least 1. */
int width;
do
{
- update_buff_pos(data->buff_pos - 1);
- width = fish_wcwidth(data->command_line.at(data->buff_pos));
- data->command_line.erase(data->buff_pos, 1);
+ update_buff_pos(el, el->position - 1);
+ width = fish_wcwidth(el->text.at(el->position));
+ el->text.erase(el->position, 1);
}
- while (width == 0 && data->buff_pos > 0);
- data->command_line_changed();
+ while (width == 0 && el->position > 0);
+ data->command_line_changed(el);
data->suppress_autosuggestion = true;
- reader_super_highlight_me_plenty(data->buff_pos);
+ reader_super_highlight_me_plenty();
reader_repaint();
@@ -1086,23 +1218,27 @@ 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;
- data->command_line.insert(data->buff_pos, str);
- update_buff_pos(data->buff_pos + len);
- data->command_line_changed();
- data->suppress_autosuggestion = false;
-
- if (should_expand_abbreviations)
- data->expand_abbreviation_as_necessary(1);
-
- /* Syntax highlight. Note we must have that buff_pos > 0 because we just added something nonzero to its length */
- assert(data->buff_pos > 0);
- reader_super_highlight_me_plenty(data->buff_pos-1);
+ el->insert_string(str);
+ update_buff_pos(el, el->position);
+ data->command_line_changed(el);
+
+ if (el == &data->command_line)
+ {
+ data->suppress_autosuggestion = false;
+
+ if (should_expand_abbreviations)
+ data->expand_abbreviation_as_necessary(1);
+
+ /* Syntax highlight. Note we must have that buff_pos > 0 because we just added something nonzero to its length */
+ assert(el->position > 0);
+ reader_super_highlight_me_plenty(-1);
+ }
reader_repaint();
return true;
@@ -1112,9 +1248,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);
}
@@ -1243,150 +1379,15 @@ wcstring completion_apply_to_command_line(const wcstring &val_str, complete_flag
*/
static void completion_insert(const wchar_t *val, complete_flags_t flags)
{
- size_t cursor = data->buff_pos;
- wcstring new_command_line = completion_apply_to_command_line(val, flags, data->command_line, &cursor, false /* not append only */);
- reader_set_buffer(new_command_line, cursor);
+ editable_line_t *el = data->active_edit_line();
+ size_t cursor = el->position;
+ wcstring new_command_line = completion_apply_to_command_line(val, flags, el->text, &cursor, false /* not append only */);
+ reader_set_buffer_maintaining_pager(new_command_line, cursor);
/* Since we just inserted a completion, don't immediately do a new autosuggestion */
data->suppress_autosuggestion = true;
}
-/* Return an escaped path to fish_pager */
-static wcstring escaped_fish_pager_path(void)
-{
- wcstring result;
- const env_var_t bin_dir = env_get_string(L"__fish_bin_dir");
- if (bin_dir.missing_or_empty())
- {
- /* This isn't good, hope our normal command stuff can find it */
- result = L"fish_pager";
- }
- else
- {
- result = escape_string(bin_dir + L"/fish_pager", ESCAPE_ALL);
- }
- return result;
-}
-
-/**
- Run the fish_pager command to display the completion list. If the
- fish_pager outputs any text, it is inserted into the input
- backbuffer.
-
- \param prefix the string to display before every completion.
- \param is_quoted should be set if the argument is quoted. This will change the display style.
- \param comp the list of completions to display
-*/
-static void run_pager(const wcstring &prefix, int is_quoted, const std::vector<completion_t> &comp)
-{
- wcstring msg;
- wcstring prefix_esc;
- char *foo;
-
- shared_ptr<io_buffer_t> in_buff(io_buffer_t::create(true, 3));
- shared_ptr<io_buffer_t> out_buff(io_buffer_t::create(false, 4));
-
- // The above may fail e.g. if we have too many open fds
- if (in_buff.get() == NULL || out_buff.get() == NULL)
- return;
-
- wchar_t *escaped_separator;
-
- if (prefix.empty())
- {
- prefix_esc = L"\"\"";
- }
- else
- {
- prefix_esc = escape_string(prefix, 1);
- }
-
-
- const wcstring pager_path = escaped_fish_pager_path();
- const wcstring cmd = format_string(L"%ls -c 3 -r 4 %ls -p %ls",
- // L"valgrind --track-fds=yes --log-file=pager.txt --leak-check=full ./%ls %d %ls",
- pager_path.c_str(),
- is_quoted?L"-q":L"",
- prefix_esc.c_str());
-
- escaped_separator = escape(COMPLETE_SEP_STR, 1);
-
- for (size_t i=0; i< comp.size(); i++)
- {
- long base_len=-1;
- const completion_t &el = comp.at(i);
-
- wcstring completion_text;
- wcstring description_text;
-
- // Note that an empty completion is perfectly sensible here, e.g. tab-completing 'foo' with a file called 'foo' and another called 'foobar'
- if ((el.flags & COMPLETE_REPLACES_TOKEN) && match_type_shares_prefix(el.match.type))
- {
- // Compute base_len if we have not yet
- if (base_len == -1)
- {
- const wchar_t *begin, *buff = data->command_line.c_str();
-
- parse_util_token_extent(buff, data->buff_pos, &begin, 0, 0, 0);
- base_len = data->buff_pos - (begin-buff);
- }
-
- completion_text = escape_string(el.completion.c_str() + base_len, ESCAPE_ALL | ESCAPE_NO_QUOTED);
- }
- else
- {
- completion_text = escape_string(el.completion, ESCAPE_ALL | ESCAPE_NO_QUOTED);
- }
-
-
- if (! el.description.empty())
- {
- description_text = escape_string(el.description, true);
- }
-
- /* It's possible (even common) to have an empty completion with no description. An example would be completing 'foo' with extant files 'foo' and 'foobar'. But fish_pager ignores blank lines. So if our completion text is empty, always include a description, even if it's empty.
- */
- msg.reserve(msg.size() + completion_text.size() + description_text.size() + 2);
- msg.append(completion_text);
- if (! description_text.empty() || completion_text.empty())
- {
- msg.append(escaped_separator);
- msg.append(description_text);
- }
- msg.push_back(L'\n');
- }
-
- free(escaped_separator);
-
- foo = wcs2str(msg.c_str());
- in_buff->out_buffer_append(foo, strlen(foo));
- free(foo);
-
- term_donate();
- parser_t &parser = parser_t::principal_parser();
- io_chain_t io_chain;
- io_chain.push_back(out_buff);
- io_chain.push_back(in_buff);
- parser.eval(cmd, io_chain, TOP);
- term_steal();
-
- out_buff->read();
-
- const char zero = 0;
- out_buff->out_buffer_append(&zero, 1);
-
- const char *out_data = out_buff->out_buffer_ptr();
- if (out_data)
- {
- const wcstring str = str2wcstring(out_data);
- size_t idx = str.size();
- while (idx--)
- {
- input_unreadch(str.at(idx));
- }
- }
-}
-
struct autosuggestion_context_t
{
wcstring search_string;
@@ -1496,17 +1497,19 @@ static int threaded_autosuggest(autosuggestion_context_t *ctx)
static bool can_autosuggest(void)
{
/* We autosuggest if suppress_autosuggestion is not set, if we're not doing a history search, and our command line contains a non-whitespace character. */
+ const editable_line_t *el = data->active_edit_line();
const wchar_t *whitespace = L" \t\r\n\v";
return ! data->suppress_autosuggestion &&
data->history_search.is_at_end() &&
- data->command_line.find_first_not_of(whitespace) != wcstring::npos;
+ el == &data->command_line &&
+ el->text.find_first_not_of(whitespace) != wcstring::npos;
}
static void autosuggest_completed(autosuggestion_context_t *ctx, int result)
{
if (result &&
can_autosuggest() &&
- ctx->search_string == data->command_line &&
+ ctx->search_string == data->command_line.text &&
string_prefixes_string_case_insensitive(ctx->search_string, ctx->autosuggestion))
{
/* Autosuggestion is active and the search term has not changed, so we're good to go */
@@ -1524,7 +1527,8 @@ static void update_autosuggestion(void)
data->autosuggestion.clear();
if (data->allow_autosuggestion && ! data->suppress_autosuggestion && ! data->command_line.empty() && data->history_search.is_at_end())
{
- autosuggestion_context_t *ctx = new autosuggestion_context_t(data->history, data->command_line, data->buff_pos);
+ const editable_line_t *el = data->active_edit_line();
+ autosuggestion_context_t *ctx = new autosuggestion_context_t(data->history, el->text, el->position);
iothread_perform(threaded_autosuggest, autosuggest_completed, ctx);
}
}
@@ -1538,7 +1542,7 @@ static void accept_autosuggestion(bool full)
if (full)
{
/* Just take the whole thing */
- data->command_line = data->autosuggestion;
+ data->command_line.text = data->autosuggestion;
}
else
{
@@ -1549,16 +1553,38 @@ static void accept_autosuggestion(bool full)
wchar_t wc = data->autosuggestion.at(idx);
if (! state.consume_char(wc))
break;
- data->command_line.push_back(wc);
+ data->command_line.text.push_back(wc);
}
}
- update_buff_pos(data->command_line.size());
- data->command_line_changed();
- reader_super_highlight_me_plenty(data->buff_pos);
+ update_buff_pos(&data->command_line, data->command_line.size());
+ data->command_line_changed(&data->command_line);
+ reader_super_highlight_me_plenty();
reader_repaint();
}
}
+/* Ensure we have no pager contents */
+static void clear_pager()
+{
+ if (data)
+ {
+ data->pager.clear();
+ data->current_page_rendering = page_rendering_t();
+ reader_repaint_needed();
+ }
+}
+
+static void select_completion_in_direction(enum selection_direction_t dir)
+{
+ assert(data != NULL);
+ /* Note: this will probably trigger reader_selected_completion_changed, which will cause us to update stuff */
+ bool selection_changed = data->pager.select_next_completion_in_direction(dir, data->current_page_rendering);
+ if (selection_changed)
+ {
+ data->pager_selection_changed();
+ }
+}
+
/**
Flash the screen. This function only changed the color of the
current line, since the flash_screen sequnce is rather painful to
@@ -1568,7 +1594,8 @@ static void reader_flash()
{
struct timespec pollint;
- for (size_t i=0; i<data->buff_pos; i++)
+ editable_line_t *el = &data->command_line;
+ for (size_t i=0; i<el->position; i++)
{
data->colors.at(i) = highlight_spec_search_match<<16;
}
@@ -1579,7 +1606,7 @@ static void reader_flash()
pollint.tv_nsec = 100 * 1000000;
nanosleep(&pollint, NULL);
- reader_super_highlight_me_plenty(data->buff_pos);
+ reader_super_highlight_me_plenty();
reader_repaint();
}
@@ -1677,40 +1704,6 @@ static void prioritize_completions(std::vector<completion_t> &comp)
sort(comp.begin(), comp.end(), compare_completions_by_match_type);
}
-/* Given a list of completions, get the completion at an index past *inout_idx, and then increment it. inout_idx should be initialized to (size_t)(-1) for the first call. */
-static const completion_t *cycle_competions(const std::vector<completion_t> &comp, const wcstring &command_line, size_t *inout_idx)
-{
- const size_t size = comp.size();
- if (size == 0)
- return NULL;
-
- // note start_idx will be set to -1 initially, so that when it gets incremented we start at 0
- const size_t start_idx = *inout_idx;
- size_t idx = start_idx;
-
- const completion_t *result = NULL;
- size_t remaining = comp.size();
- while (remaining--)
- {
- /* Bump the index */
- idx = (idx + 1) % size;
-
- /* Get the completion */
- const completion_t &c = comp.at(idx);
-
- /* Try this completion */
- if (!(c.flags & COMPLETE_REPLACES_TOKEN) || reader_can_replace(command_line, c.flags))
- {
- /* Success */
- result = &c;
- break;
- }
- }
-
- *inout_idx = idx;
- return result;
-}
-
/**
Handle the list of completions. This means the following:
@@ -1727,18 +1720,20 @@ static const completion_t *cycle_competions(const std::vector<completion_t> &com
completions.
\param comp the list of completion strings
+ \param continue_after_prefix_insertion If we have a shared prefix, whether to print the list of completions after inserting it.
Return true if we inserted text into the command line, false if we did not.
*/
-static bool handle_completions(const std::vector<completion_t> &comp)
+static bool handle_completions(const std::vector<completion_t> &comp, bool continue_after_prefix_insertion)
{
bool done = false;
bool success = false;
- const wchar_t *begin, *end, *buff = data->command_line.c_str();
+ const editable_line_t *el = &data->command_line;
+ const wchar_t *begin, *end, *buff = el->text.c_str();
- parse_util_token_extent(buff, data->buff_pos, &begin, 0, 0, 0);
- end = buff+data->buff_pos;
+ parse_util_token_extent(buff, el->position, &begin, 0, 0, 0);
+ end = buff+el->position;
const wcstring tok(begin, end - begin);
@@ -1867,15 +1862,16 @@ static bool handle_completions(const std::vector<completion_t> &comp)
completion_insert(common_prefix.c_str(), flags);
success = true;
}
- else
+
+ if (continue_after_prefix_insertion || common_prefix.empty())
{
- /* We didn't get a common prefix. Print the list. */
+ /* We didn't get a common prefix, or we want to print the list anyways. */
size_t len, prefix_start = 0;
wcstring prefix;
- parse_util_get_parameter_info(data->command_line, data->buff_pos, NULL, &prefix_start, NULL);
+ parse_util_get_parameter_info(el->text, el->position, NULL, &prefix_start, NULL);
- assert(data->buff_pos >= prefix_start);
- len = data->buff_pos - prefix_start;
+ assert(el->position >= prefix_start);
+ len = el->position - prefix_start;
if (match_type_requires_full_replacement(best_match_type))
{
@@ -1884,32 +1880,30 @@ static bool handle_completions(const std::vector<completion_t> &comp)
}
else if (len <= PREFIX_MAX_LEN)
{
- prefix.append(data->command_line, prefix_start, len);
+ prefix.append(el->text, prefix_start, len);
}
else
{
// append just the end of the string
prefix = wcstring(&ellipsis_char, 1);
- prefix.append(data->command_line, prefix_start + len - PREFIX_MAX_LEN, PREFIX_MAX_LEN);
+ prefix.append(el->text, prefix_start + len - PREFIX_MAX_LEN, PREFIX_MAX_LEN);
}
- {
- int is_quoted;
-
wchar_t quote;
- parse_util_get_parameter_info(data->command_line, data->buff_pos, &quote, NULL, NULL);
- is_quoted = (quote != L'\0');
+ parse_util_get_parameter_info(el->text, el->position, &quote, NULL, NULL);
- /* Clear the autosuggestion from the old commandline before abandoning it (see #561) */
- if (! data->autosuggestion.empty())
- reader_repaint_without_autosuggestion();
+ /* Update the pager data */
+ data->pager.set_prefix(prefix);
+ data->pager.set_completions(surviving_completions);
- write_loop(1, "\n", 1);
+ /* Invalidate our rendering */
+ data->current_page_rendering = page_rendering_t();
+
+ /* Modify the command line to reflect the new pager */
+ data->pager_selection_changed();
+
+ reader_repaint_needed();
- run_pager(prefix, is_quoted, surviving_completions);
- }
- s_reset(&data->screen, screen_reset_abandon_line);
- reader_repaint();
success = false;
}
}
@@ -2118,13 +2112,13 @@ void reader_sanity_check()
/* Note: 'data' is non-null if we are interactive, except in the testing environment */
if (get_is_interactive() && data != NULL)
{
- if (!(data->buff_pos <= data->command_length()))
+ if (data->command_line.position > data->command_line.size())
sanity_lose();
- if (data->colors.size() != data->command_length())
+ if (data->colors.size() != data->command_line.size())
sanity_lose();
- if (data->indents.size() != data->command_length())
+ if (data->indents.size() != data->command_line.size())
sanity_lose();
}
@@ -2133,13 +2127,13 @@ void reader_sanity_check()
/**
Set the specified string as the current buffer.
*/
-static void set_command_line_and_position(const wcstring &new_str, size_t pos)
+static void set_command_line_and_position(editable_line_t *el, const wcstring &new_str, size_t pos)
{
- data->command_line = new_str;
- data->command_line_changed();
- update_buff_pos(pos);
- reader_super_highlight_me_plenty(data->buff_pos);
- reader_repaint();
+ el->text = new_str;
+ update_buff_pos(el, pos);
+ data->command_line_changed(el);
+ reader_super_highlight_me_plenty();
+ reader_repaint_needed();
}
static void reader_replace_current_token(const wchar_t *new_token)
@@ -2149,8 +2143,9 @@ static void reader_replace_current_token(const wchar_t *new_token)
size_t new_pos;
/* Find current token */
- const wchar_t *buff = data->command_line.c_str();
- parse_util_token_extent(buff, data->buff_pos, &begin, &end, 0, 0);
+ editable_line_t *el = data->active_edit_line();
+ const wchar_t *buff = el->text.c_str();
+ parse_util_token_extent(buff, el->position, &begin, &end, 0, 0);
if (!begin || !end)
return;
@@ -2161,7 +2156,7 @@ static void reader_replace_current_token(const wchar_t *new_token)
new_buff.append(end);
new_pos = (begin-buff) + wcslen(new_token);
- set_command_line_and_position(new_buff, new_pos);
+ set_command_line_and_position(el, new_buff, new_pos);
}
@@ -2170,9 +2165,10 @@ static void reader_replace_current_token(const wchar_t *new_token)
*/
static void reset_token_history()
{
+ const editable_line_t *el = data->active_edit_line();
const wchar_t *begin, *end;
- const wchar_t *buff = data->command_line.c_str();
- parse_util_token_extent((wchar_t *)buff, data->buff_pos, &begin, &end, 0, 0);
+ const wchar_t *buff = el->text.c_str();
+ parse_util_token_extent((wchar_t *)buff, el->position, &begin, &end, 0, 0);
data->search_buff.clear();
if (begin)
@@ -2233,7 +2229,7 @@ static void handle_token_history(int forward, int reset)
}
reader_replace_current_token(str);
- reader_super_highlight_me_plenty(data->buff_pos);
+ reader_super_highlight_me_plenty();
reader_repaint();
}
else
@@ -2314,7 +2310,7 @@ static void handle_token_history(int forward, int reset)
if (str)
{
reader_replace_current_token(str);
- reader_super_highlight_me_plenty(data->buff_pos);
+ reader_super_highlight_me_plenty();
reader_repaint();
data->search_pos = data->search_prev.size();
data->search_prev.push_back(str);
@@ -2342,19 +2338,19 @@ enum move_word_dir_t
MOVE_DIR_RIGHT
};
-static void move_word(bool move_right, bool erase, enum move_word_style_t style, bool newv)
+static void move_word(editable_line_t *el, bool move_right, bool erase, enum move_word_style_t style, bool newv)
{
/* Return if we are already at the edge */
- const size_t boundary = move_right ? data->command_length() : 0;
- if (data->buff_pos == boundary)
+ const size_t boundary = move_right ? el->size() : 0;
+ if (el->position == boundary)
return;
/* When moving left, a value of 1 means the character at index 0. */
move_word_state_machine_t state(style);
- const wchar_t * const command_line = data->command_line.c_str();
- const size_t start_buff_pos = data->buff_pos;
+ const wchar_t * const command_line = el->text.c_str();
+ const size_t start_buff_pos = el->position;
- size_t buff_pos = data->buff_pos;
+ size_t buff_pos = el->position;
while (buff_pos != boundary)
{
size_t idx = (move_right ? buff_pos : buff_pos - 1);
@@ -2368,24 +2364,27 @@ static void move_word(bool move_right, bool erase, enum move_word_style_t style,
if (buff_pos == start_buff_pos)
buff_pos = (move_right ? buff_pos + 1 : buff_pos - 1);
- /* If we are moving left, buff_pos-1 is the index of the first character we do not delete (possibly -1). If we are moving right, then buff_pos is that index - possibly data->command_length(). */
+ /* If we are moving left, buff_pos-1 is the index of the first character we do not delete (possibly -1). If we are moving right, then buff_pos is that index - possibly el->size(). */
if (erase)
{
/* Don't autosuggest after a kill */
+ if (el == &data->command_line)
+ {
data->suppress_autosuggestion = true;
+ }
if (move_right)
{
- reader_kill(start_buff_pos, buff_pos - start_buff_pos, KILL_APPEND, newv);
+ reader_kill(el, start_buff_pos, buff_pos - start_buff_pos, KILL_APPEND, newv);
}
else
{
- reader_kill(buff_pos, start_buff_pos - buff_pos, KILL_PREPEND, newv);
+ reader_kill(el, buff_pos, start_buff_pos - buff_pos, KILL_PREPEND, newv);
}
}
else
{
- update_buff_pos(buff_pos);
+ update_buff_pos(el, buff_pos);
reader_repaint();
}
@@ -2394,7 +2393,7 @@ static void move_word(bool move_right, bool erase, enum move_word_style_t style,
const wchar_t *reader_get_buffer(void)
{
ASSERT_IS_MAIN_THREAD();
- return data?data->command_line.c_str():NULL;
+ return data ? data->command_line.text.c_str() : NULL;
}
history_t *reader_get_history(void)
@@ -2403,40 +2402,49 @@ history_t *reader_get_history(void)
return data ? data->history : NULL;
}
-void reader_set_buffer(const wcstring &b, size_t pos)
+/* Sets the command line contents, without clearing the pager */
+static void reader_set_buffer_maintaining_pager(const wcstring &b, size_t pos)
{
- if (!data)
- return;
-
/* 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 = b;
- data->command_line_changed();
+ data->command_line.text = b;
+ data->command_line_changed(&data->command_line);
/* Don't set a position past the command line length */
if (pos > command_line_len)
pos = command_line_len;
- update_buff_pos(pos);
+ update_buff_pos(&data->command_line, pos);
+ /* Clear history search and pager contents */
data->search_mode = NO_SEARCH;
data->search_buff.clear();
data->history_search.go_to_end();
- reader_super_highlight_me_plenty(data->buff_pos);
+ reader_super_highlight_me_plenty();
reader_repaint_needed();
}
+/* Sets the command line contents, clearing the pager */
+void reader_set_buffer(const wcstring &b, size_t pos)
+{
+ if (!data)
+ return;
+
+ clear_pager();
+ reader_set_buffer_maintaining_pager(b, pos);
+}
+
size_t reader_get_cursor_pos()
{
if (!data)
return (size_t)(-1);
- return data->buff_pos;
+ return data->command_line.position;
}
-bool reader_get_selection(size_t &start, size_t &len)
+bool reader_get_selection(size_t *start, size_t *len)
{
if (!data)
{
@@ -2444,14 +2452,16 @@ bool reader_get_selection(size_t &start, size_t &len)
}
else
{
- if(!data->sel_active)
- return false;
- else
- {
- start = data->sel_start_pos;
- len = std::min(data->sel_stop_pos - data->sel_start_pos + 1, data->command_length());
- return true;
- }
+ if (! data->sel_active)
+ {
+ return false;
+ }
+ else
+ {
+ *start = data->sel_start_pos;
+ *len = std::min(data->sel_stop_pos - data->sel_start_pos + 1, data->command_line.size());
+ return true;
+ }
}
}
@@ -2580,7 +2590,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)
{
@@ -2716,7 +2726,7 @@ public:
{
}
- int threaded_highlight()
+ int perform_highlight()
{
if (generation_count != s_generation_count)
{
@@ -2736,8 +2746,9 @@ static void highlight_search(void)
{
if (! data->search_buff.empty() && ! data->history_search.is_at_end())
{
+ const editable_line_t *el = &data->command_line;
const wcstring &needle = data->search_buff;
- size_t match_pos = data->command_line.find(needle);
+ size_t match_pos = el->text.find(needle);
if (match_pos != wcstring::npos)
{
size_t end = match_pos + needle.size();
@@ -2752,10 +2763,10 @@ static void highlight_search(void)
static void highlight_complete(background_highlight_context_t *ctx, int result)
{
ASSERT_IS_MAIN_THREAD();
- if (ctx->string_to_highlight == data->command_line)
+ if (ctx->string_to_highlight == data->command_line.text)
{
/* The data hasn't changed, so swap in our colors. The colors may not have changed, so do nothing if they have not. */
- assert(ctx->colors.size() == data->command_length());
+ assert(ctx->colors.size() == data->command_line.size());
if (data->colors != ctx->colors)
{
data->colors.swap(ctx->colors);
@@ -2771,7 +2782,7 @@ static void highlight_complete(background_highlight_context_t *ctx, int result)
static int threaded_highlight(background_highlight_context_t *ctx)
{
- return ctx->threaded_highlight();
+ return ctx->perform_highlight();
}
@@ -2781,19 +2792,36 @@ static int threaded_highlight(background_highlight_context_t *ctx)
to avoid repaint issues on terminals where e.g. syntax highlighting
maykes characters under the sursor unreadable.
- \param match_highlight_pos the position to use for bracket matching. This need not be the same as the surrent cursor position
+ \param match_highlight_pos_adjust the adjustment to the position to use for bracket matching. This is added to the current cursor position and may be negative.
\param error if non-null, any possible errors in the buffer are further descibed by the strings inserted into the specified arraylist
+ \param no_io if true, do a highlight that does not perform I/O, synchronously. If false, perform an asynchronous highlight in the background, which may perform disk I/O.
*/
-static void reader_super_highlight_me_plenty(size_t match_highlight_pos)
+static void reader_super_highlight_me_plenty(int match_highlight_pos_adjust, bool no_io)
{
+ const editable_line_t *el = &data->command_line;
+ long match_highlight_pos = (long)el->position + match_highlight_pos_adjust;
+ assert(match_highlight_pos >= 0);
+
reader_sanity_check();
- background_highlight_context_t *ctx = new background_highlight_context_t(data->command_line, match_highlight_pos, data->highlight_function);
+ highlight_function_t highlight_func = no_io ? highlight_shell_no_io : data->highlight_function;
+ background_highlight_context_t *ctx = new background_highlight_context_t(el->text, match_highlight_pos, highlight_func);
+ if (no_io)
+ {
+ // Highlighting without IO, we just do it
+ // Note that highlight_complete deletes ctx.
+ int result = ctx->perform_highlight();
+ highlight_complete(ctx, result);
+ }
+ else
+ {
+ // Highlighting including I/O proceeds in the background
iothread_perform(threaded_highlight, highlight_complete, ctx);
+ }
highlight_search();
/* Here's a hack. Check to see if our autosuggestion still applies; if so, don't recompute it. Since the autosuggestion computation is asynchronous, this avoids "flashing" as you type into the autosuggestion. */
- const wcstring &cmd = data->command_line, &suggest = data->autosuggestion;
+ const wcstring &cmd = el->text, &suggest = data->autosuggestion;
if (can_autosuggest() && ! suggest.empty() && string_prefixes_string_case_insensitive(cmd, suggest))
{
/* The autosuggestion is still reasonable, so do nothing */
@@ -2877,6 +2905,21 @@ static void handle_end_loop()
}
}
+static bool selection_is_at_top()
+{
+ const pager_t *pager = &data->pager;
+ size_t row = pager->get_selected_row(data->current_page_rendering);
+ if (row != 0 && row != PAGER_SELECTION_NONE)
+ return false;
+
+ size_t col = pager->get_selected_column(data->current_page_rendering);
+ if (col != 0 && col != PAGER_SELECTION_NONE)
+ return false;
+
+ return true;
+}
+
+
/**
Read interactively. Read input from stdin while providing editing
facilities.
@@ -2923,10 +2966,10 @@ static int read_i(void)
}
else if (tmp)
{
- wcstring command = tmp;
- update_buff_pos(0);
- data->command_line.clear();
- data->command_line_changed();
+ const wcstring command = tmp;
+ update_buff_pos(&data->command_line, 0);
+ data->command_line.text.clear();
+ data->command_line_changed(&data->command_line);
reader_run_command(parser, command);
if (data->end_loop)
{
@@ -3018,19 +3061,16 @@ 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;
exec_prompt();
- reader_super_highlight_me_plenty(data->buff_pos);
+ reader_super_highlight_me_plenty();
s_reset(&data->screen, screen_reset_abandon_line);
reader_repaint();
@@ -3090,7 +3130,7 @@ const wchar_t *reader_readline(void)
break;
}
- insert_string(arr);
+ insert_string(&data->command_line, arr);
}
}
@@ -3106,31 +3146,47 @@ const wchar_t *reader_readline(void)
if (last_char != R_YANK && last_char != R_YANK_POP)
yank_len=0;
- const wchar_t *buff = data->command_line.c_str();
- switch (c)
+ /* Restore the text */
+ if (c == R_CANCEL && data->is_navigating_pager_contents())
{
+ set_command_line_and_position(&data->command_line, data->cycle_command_line, data->cycle_cursor_pos);
+ }
+
+
+ /* 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))
+ {
+ clear_pager();
+ }
+
+ //fprintf(stderr, "\n\nchar: %ls\n\n", describe_char(c).c_str());
+ switch (c)
+ {
/* go to beginning of line*/
case R_BEGINNING_OF_LINE:
{
- while ((data->buff_pos>0) &&
- (buff[data->buff_pos-1] != L'\n'))
+ editable_line_t *el = data->active_edit_line();
+ while (el->position > 0 && el->text.at(el->position - 1) != L'\n')
{
- update_buff_pos(data->buff_pos - 1);
+ update_buff_pos(el, el->position - 1);
}
- reader_repaint();
+ reader_repaint_needed();
break;
}
case R_END_OF_LINE:
{
- if (data->buff_pos < data->command_length())
+ editable_line_t *el = data->active_edit_line();
+ if (el->position < el->size())
{
- while (buff[data->buff_pos] &&
- buff[data->buff_pos] != L'\n')
+ const wchar_t *buff = el->text.c_str();
+ while (buff[el->position] &&
+ buff[el->position] != L'\n')
{
- update_buff_pos(data->buff_pos + 1);
+ update_buff_pos(el, el->position + 1);
}
}
else
@@ -3138,31 +3194,35 @@ const wchar_t *reader_readline(void)
accept_autosuggestion(true);
}
- reader_repaint();
+ reader_repaint_needed();
break;
}
case R_BEGINNING_OF_BUFFER:
{
- update_buff_pos(0);
-
- reader_repaint();
+ update_buff_pos(&data->command_line, 0);
+ reader_repaint_needed();
break;
}
/* go to EOL*/
case R_END_OF_BUFFER:
{
- update_buff_pos(data->command_length());
+ update_buff_pos(&data->command_line, data->command_line.size());
- reader_repaint();
+ reader_repaint_needed();
break;
}
case R_NULL:
{
- reader_repaint_if_needed();
+ break;
+ }
+
+ case R_CANCEL:
+ {
+ // The only thing we can cancel right now is paging, which we handled up above
break;
}
@@ -3189,26 +3249,25 @@ const wchar_t *reader_readline(void)
/* complete */
case R_COMPLETE:
+ case R_COMPLETE_AND_SEARCH:
{
if (!data->complete_func)
break;
- if (! comp_empty && last_char == R_COMPLETE)
+ /* Use the command line only; it doesn't make sense to complete in any other line */
+ editable_line_t *el = &data->command_line;
+ 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 */
- const completion_t *next_comp = cycle_competions(comp, cycle_command_line, &completion_cycle_idx);
- if (next_comp != NULL)
+ /* The user typed R_COMPLETE more than once in a row. If we are not yet fully disclosed, then become so; otherwise cycle through our available completions. */
+ if (data->current_page_rendering.remaining_to_disclose > 0)
{
- size_t cursor_pos = cycle_cursor_pos;
- const wcstring new_cmd_line = completion_apply_to_command_line(next_comp->completion, next_comp->flags, cycle_command_line, &cursor_pos, false);
- reader_set_buffer(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_if_needed();
+ data->pager.set_fully_disclosed(true);
+ reader_repaint_needed();
+ }
+ else
+ {
+ select_completion_in_direction(c == R_COMPLETE ? direction_next : direction_prev);
}
}
else
@@ -3216,24 +3275,24 @@ const wchar_t *reader_readline(void)
/* Either the user hit tab only once, or we had no visible completion list. */
/* Remove a trailing backslash. This may trigger an extra repaint, but this is rare. */
- if (is_backslashed(data->command_line, data->buff_pos))
+ if (is_backslashed(el->text, el->position))
{
remove_backward();
}
/* Get the string; we have to do this after removing any trailing backslash */
- const wchar_t * const buff = data->command_line.c_str();
+ const wchar_t * const buff = el->text.c_str();
/* Clear the completion list */
comp.clear();
/* Figure out the extent of the command substitution surrounding the cursor. This is because we only look at the current command substitution to form completions - stuff happening outside of it is not interesting. */
const wchar_t *cmdsub_begin, *cmdsub_end;
- parse_util_cmdsubst_extent(buff, data->buff_pos, &cmdsub_begin, &cmdsub_end);
+ parse_util_cmdsubst_extent(buff, el->position, &cmdsub_begin, &cmdsub_end);
/* Figure out the extent of the token within the command substitution. Note we pass cmdsub_begin here, not buff */
const wchar_t *token_begin, *token_end;
- parse_util_token_extent(cmdsub_begin, data->buff_pos - (cmdsub_begin-buff), &token_begin, &token_end, 0, 0);
+ parse_util_token_extent(cmdsub_begin, el->position - (cmdsub_begin-buff), &token_begin, &token_end, 0, 0);
/* Hack: the token may extend past the end of the command substitution, e.g. in (echo foo) the last token is 'foo)'. Don't let that happen. */
if (token_end > cmdsub_end) token_end = cmdsub_end;
@@ -3242,9 +3301,9 @@ const wchar_t *reader_readline(void)
size_t end_of_token_offset = token_end - buff;
/* Move the cursor to the end */
- if (data->buff_pos != end_of_token_offset)
+ if (el->position != end_of_token_offset)
{
- update_buff_pos(end_of_token_offset);
+ update_buff_pos(el, end_of_token_offset);
reader_repaint();
}
@@ -3259,16 +3318,20 @@ const wchar_t *reader_readline(void)
prioritize_completions(comp);
/* Record our cycle_command_line */
- cycle_command_line = data->command_line;
- cycle_cursor_pos = data->buff_pos;
+ data->cycle_command_line = el->text;
+ data->cycle_cursor_pos = el->position;
- comp_empty = handle_completions(comp);
+ bool continue_after_prefix_insertion = (c == R_COMPLETE_AND_SEARCH);
+ comp_empty = handle_completions(comp, continue_after_prefix_insertion);
- /* 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);
+ reader_repaint_needed();
+ }
- /* Repaint */
- reader_repaint_if_needed();
}
break;
@@ -3277,8 +3340,9 @@ const wchar_t *reader_readline(void)
/* kill */
case R_KILL_LINE:
{
- const wchar_t *buff = data->command_line.c_str();
- const wchar_t *begin = &buff[data->buff_pos];
+ editable_line_t *el = data->active_edit_line();
+ const wchar_t *buff = el->text.c_str();
+ const wchar_t *begin = &buff[el->position];
const wchar_t *end = begin;
while (*end && *end != L'\n')
@@ -3290,7 +3354,7 @@ const wchar_t *reader_readline(void)
size_t len = end-begin;
if (len)
{
- reader_kill(begin - buff, len, KILL_APPEND, last_char!=R_KILL_LINE);
+ reader_kill(el, begin - buff, len, KILL_APPEND, last_char!=R_KILL_LINE);
}
break;
@@ -3298,10 +3362,11 @@ const wchar_t *reader_readline(void)
case R_BACKWARD_KILL_LINE:
{
- if (data->buff_pos > 0)
+ editable_line_t *el = data->active_edit_line();
+ if (el->position > 0)
{
- const wchar_t *buff = data->command_line.c_str();
- const wchar_t *end = &buff[data->buff_pos];
+ const wchar_t *buff = el->text.c_str();
+ const wchar_t *end = &buff[el->position];
const wchar_t *begin = end;
/* Make sure we delete at least one character (see #580) */
@@ -3319,7 +3384,7 @@ const wchar_t *reader_readline(void)
size_t len = maxi<size_t>(end-begin, 1);
begin = end - len;
- reader_kill(begin - buff, len, KILL_PREPEND, last_char!=R_BACKWARD_KILL_LINE);
+ reader_kill(el, begin - buff, len, KILL_PREPEND, last_char!=R_BACKWARD_KILL_LINE);
}
break;
@@ -3327,8 +3392,9 @@ const wchar_t *reader_readline(void)
case R_KILL_WHOLE_LINE:
{
- const wchar_t *buff = data->command_line.c_str();
- const wchar_t *end = &buff[data->buff_pos];
+ editable_line_t *el = data->active_edit_line();
+ const wchar_t *buff = el->text.c_str();
+ const wchar_t *end = &buff[el->position];
const wchar_t *begin = end;
size_t len;
@@ -3351,7 +3417,7 @@ const wchar_t *reader_readline(void)
if (len)
{
- reader_kill(begin - buff, len, KILL_APPEND, last_char!=R_KILL_WHOLE_LINE);
+ reader_kill(el, begin - buff, len, KILL_APPEND, last_char!=R_KILL_WHOLE_LINE);
}
break;
@@ -3361,7 +3427,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;
}
@@ -3375,7 +3441,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;
@@ -3399,9 +3465,8 @@ const wchar_t *reader_readline(void)
reader_replace_current_token(data->search_buff.c_str());
}
data->search_buff.clear();
- reader_super_highlight_me_plenty(data->buff_pos);
- reader_repaint();
-
+ reader_super_highlight_me_plenty();
+ reader_repaint_needed();
}
break;
@@ -3421,9 +3486,10 @@ const wchar_t *reader_readline(void)
Remove the current character in the character buffer and on the
screen using syntax highlighting, etc.
*/
- if (data->buff_pos < data->command_length())
+ editable_line_t *el = data->active_edit_line();
+ if (el->position < el->size())
{
- update_buff_pos(data->buff_pos + 1);
+ update_buff_pos(el, el->position + 1);
remove_backward();
}
break;
@@ -3439,18 +3505,31 @@ const wchar_t *reader_readline(void)
/* Delete any autosuggestion */
data->autosuggestion.clear();
+ /* If the user hits return while navigating the pager, it only clears the pager */
+ if (data->is_navigating_pager_contents())
+ {
+ clear_pager();
+ break;
+ }
+
+ /* The user may have hit return with pager contents, but while not navigating them. Clear the pager in that event. */
+ clear_pager();
+
+ /* We only execute the 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(data->command_line, data->buff_pos))
+ if (is_backslashed(el->text, el->position))
{
- if (data->buff_pos >= data->command_length() || iswspace(data->command_line.at(data->buff_pos)))
+ if (el->position >= el->size() || iswspace(el->text.at(el->position)))
{
- insert_char('\n');
+ insert_char(el, '\n');
break;
}
}
/* See if this command is valid */
- int command_test_result = data->test_func(data->command_line.c_str());
+ int command_test_result = data->test_func(el->text.c_str());
if (command_test_result == 0 || command_test_result == PARSER_TEST_INCOMPLETE)
{
/* This command is valid, but an abbreviation may make it invalid. If so, we will have to test again. */
@@ -3458,8 +3537,11 @@ const wchar_t *reader_readline(void)
if (abbreviation_expanded)
{
/* It's our reponsibility to rehighlight and repaint. But everything we do below triggers a repaint. */
- reader_super_highlight_me_plenty(data->buff_pos);
- command_test_result = data->test_func(data->command_line.c_str());
+ command_test_result = data->test_func(el->text.c_str());
+
+ /* If the command is OK, then we're going to execute it. We still want to do syntax highlighting, but a synchronous variant that performs no I/O, so as not to block the user */
+ bool skip_io = (command_test_result == 0);
+ reader_super_highlight_me_plenty(0, skip_io);
}
}
@@ -3469,12 +3551,13 @@ const wchar_t *reader_readline(void)
case 0:
{
/* Finished command, execute it. Don't add items that start with a leading space. */
- if (data->history != NULL && ! data->command_line.empty() && data->command_line.at(0) != L' ')
+ const editable_line_t *el = &data->command_line;
+ if (data->history != NULL && ! el->empty() && el->text.at(0) != L' ')
{
- data->history->add_with_file_detection(data->command_line);
+ data->history->add_with_file_detection(el->text);
}
finished=1;
- update_buff_pos(data->command_length());
+ update_buff_pos(&data->command_line, data->command_line.size());
reader_repaint();
break;
}
@@ -3484,7 +3567,7 @@ const wchar_t *reader_readline(void)
*/
case PARSER_TEST_INCOMPLETE:
{
- insert_char('\n');
+ insert_char(el, '\n');
break;
}
@@ -3496,7 +3579,7 @@ const wchar_t *reader_readline(void)
default:
{
s_reset(&data->screen, screen_reset_abandon_line);
- reader_repaint();
+ reader_repaint_needed();
break;
}
@@ -3512,7 +3595,6 @@ const wchar_t *reader_readline(void)
case R_HISTORY_TOKEN_SEARCH_FORWARD:
{
int reset = 0;
-
if (data->search_mode == NO_SEARCH)
{
reset = 1;
@@ -3526,7 +3608,8 @@ const wchar_t *reader_readline(void)
data->search_mode = TOKEN_SEARCH;
}
- data->search_buff.append(data->command_line);
+ const editable_line_t *el = &data->command_line;
+ data->search_buff.append(el->text);
data->history_search = history_search_t(*data->history, data->search_buff, HISTORY_SEARCH_TYPE_CONTAINS);
/* Skip the autosuggestion as history unless it was truncated */
@@ -3539,7 +3622,6 @@ const wchar_t *reader_readline(void)
switch (data->search_mode)
{
-
case LINE_SEARCH:
{
if ((c == R_HISTORY_SEARCH_BACKWARD) ||
@@ -3565,7 +3647,7 @@ const wchar_t *reader_readline(void)
{
new_text = data->history_search.current_string();
}
- set_command_line_and_position(new_text, new_text.size());
+ set_command_line_and_position(&data->command_line, new_text, new_text.size());
break;
}
@@ -3593,10 +3675,15 @@ const wchar_t *reader_readline(void)
/* Move left*/
case R_BACKWARD_CHAR:
{
- if (data->buff_pos > 0)
+ editable_line_t *el = data->active_edit_line();
+ if (data->is_navigating_pager_contents())
{
- update_buff_pos(data->buff_pos - 1);
- reader_repaint();
+ select_completion_in_direction(direction_west);
+ }
+ else if (el->position > 0)
+ {
+ update_buff_pos(el, el->position-1);
+ reader_repaint_needed();
}
break;
}
@@ -3604,10 +3691,15 @@ const wchar_t *reader_readline(void)
/* Move right*/
case R_FORWARD_CHAR:
{
- if (data->buff_pos < data->command_length())
+ editable_line_t *el = data->active_edit_line();
+ if (data->is_navigating_pager_contents())
{
- update_buff_pos(data->buff_pos + 1);
- reader_repaint();
+ select_completion_in_direction(direction_east);
+ }
+ else if (el->position < el->size())
+ {
+ update_buff_pos(el, el->position + 1);
+ reader_repaint_needed();
}
else
{
@@ -3622,30 +3714,31 @@ const wchar_t *reader_readline(void)
{
move_word_style_t style = (c == R_BACKWARD_KILL_PATH_COMPONENT ? move_word_style_path_components : move_word_style_punctuation);
bool newv = (last_char != R_BACKWARD_KILL_WORD && last_char != R_BACKWARD_KILL_PATH_COMPONENT);
- move_word(MOVE_DIR_LEFT, true /* erase */, style, newv);
+ move_word(data->active_edit_line(), MOVE_DIR_LEFT, true /* erase */, style, newv);
break;
}
/* kill one word right */
case R_KILL_WORD:
{
- move_word(MOVE_DIR_RIGHT, true /* erase */, move_word_style_punctuation, last_char!=R_KILL_WORD);
+ move_word(data->active_edit_line(), MOVE_DIR_RIGHT, true /* erase */, move_word_style_punctuation, last_char!=R_KILL_WORD);
break;
}
/* move one word left*/
case R_BACKWARD_WORD:
{
- move_word(MOVE_DIR_LEFT, false /* do not erase */, move_word_style_punctuation, false);
+ move_word(data->active_edit_line(), MOVE_DIR_LEFT, false /* do not erase */, move_word_style_punctuation, false);
break;
}
/* move one word right*/
case R_FORWARD_WORD:
{
- if (data->buff_pos < data->command_length())
+ editable_line_t *el = data->active_edit_line();
+ if (el->position < el->size())
{
- move_word(MOVE_DIR_RIGHT, false /* do not erase */, move_word_style_punctuation, false);
+ move_word(el, MOVE_DIR_RIGHT, false /* do not erase */, move_word_style_punctuation, false);
}
else
{
@@ -3656,12 +3749,13 @@ const wchar_t *reader_readline(void)
case R_BEGINNING_OF_HISTORY:
{
- data->history_search = history_search_t(*data->history, data->command_line, HISTORY_SEARCH_TYPE_PREFIX);
+ const editable_line_t *el = &data->command_line;
+ data->history_search = history_search_t(*data->history, el->text, HISTORY_SEARCH_TYPE_PREFIX);
data->history_search.go_to_beginning();
if (! data->history_search.is_at_end())
{
wcstring new_text = data->history_search.current_string();
- set_command_line_and_position(new_text, new_text.size());
+ set_command_line_and_position(&data->command_line, new_text, new_text.size());
}
break;
@@ -3676,41 +3770,74 @@ const wchar_t *reader_readline(void)
case R_UP_LINE:
case R_DOWN_LINE:
{
- int line_old = parse_util_get_line_from_offset(data->command_line, data->buff_pos);
- int line_new;
-
- if (c == R_UP_LINE)
- line_new = line_old-1;
+ if (data->is_navigating_pager_contents())
+ {
+ /* We are already navigating pager contents. */
+ selection_direction_t direction;
+ if (c == R_DOWN_LINE)
+ {
+ /* Down arrow is always south */
+ direction = direction_south;
+ }
+ else if (selection_is_at_top())
+ {
+ /* Up arrow, but we are in the first column and first row. End navigation */
+ direction = direction_deselect;
+ }
+ else
+ {
+ /* Up arrow, go north */
+ direction = direction_north;
+ }
+
+ /* Now do the selection */
+ select_completion_in_direction(direction);
+ }
+ 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);
+ }
else
- line_new = line_old+1;
-
- int line_count = parse_util_lineno(data->command_line.c_str(), data->command_length())-1;
-
- if (line_new >= 0 && line_new <= line_count)
{
- size_t base_pos_new;
- size_t base_pos_old;
-
- int indent_old;
- int indent_new;
- size_t line_offset_old;
- size_t total_offset_new;
-
- base_pos_new = parse_util_get_offset_from_line(data->command_line, line_new);
-
- base_pos_old = parse_util_get_offset_from_line(data->command_line, line_old);
-
- assert(base_pos_new != (size_t)(-1) && base_pos_old != (size_t)(-1));
- indent_old = data->indents.at(base_pos_old);
- indent_new = data->indents.at(base_pos_new);
-
- line_offset_old = data->buff_pos - parse_util_get_offset_from_line(data->command_line, line_old);
- total_offset_new = parse_util_get_offset(data->command_line, line_new, line_offset_old - 4*(indent_new-indent_old));
-
- update_buff_pos(total_offset_new);
- reader_repaint();
+ /* Not navigating the pager contents */
+ editable_line_t *el = data->active_edit_line();
+ int line_old = parse_util_get_line_from_offset(el->text, el->position);
+ int line_new;
+
+ if (c == R_UP_LINE)
+ line_new = line_old-1;
+ else
+ line_new = line_old+1;
+
+ int line_count = parse_util_lineno(el->text.c_str(), el->size())-1;
+
+ if (line_new >= 0 && line_new <= line_count)
+ {
+ size_t base_pos_new;
+ size_t base_pos_old;
+
+ int indent_old;
+ int indent_new;
+ size_t line_offset_old;
+ size_t total_offset_new;
+
+ base_pos_new = parse_util_get_offset_from_line(el->text, line_new);
+
+ base_pos_old = parse_util_get_offset_from_line(el->text, line_old);
+
+ assert(base_pos_new != (size_t)(-1) && base_pos_old != (size_t)(-1));
+ indent_old = data->indents.at(base_pos_old);
+ indent_new = data->indents.at(base_pos_new);
+
+ line_offset_old = el->position - parse_util_get_offset_from_line(el->text, line_old);
+ total_offset_new = parse_util_get_offset(el->text, line_new, line_offset_old - 4*(indent_new-indent_old));
+ update_buff_pos(el, total_offset_new);
+ el->position = total_offset_new;
+ reader_repaint_needed();
+ }
}
-
+
break;
}
@@ -3718,7 +3845,7 @@ const wchar_t *reader_readline(void)
{
data->suppress_autosuggestion = true;
data->autosuggestion.clear();
- reader_repaint();
+ reader_repaint_needed();
break;
}
@@ -3730,44 +3857,46 @@ const wchar_t *reader_readline(void)
case R_TRANSPOSE_CHARS:
{
- if (data->command_length() < 2)
+ editable_line_t *el = data->active_edit_line();
+ if (el->size() < 2)
{
break;
}
/* If the cursor is at the end, transpose the last two characters of the line */
- if (data->buff_pos == data->command_length())
+ if (el->position == el->size())
{
- update_buff_pos(data->buff_pos - 1);
+ update_buff_pos(el, el->position - 1);
}
/*
Drag the character before the cursor forward over the character at the cursor, moving
the cursor forward as well.
*/
- if (data->buff_pos > 0)
+ if (el->position > 0)
{
- wcstring local_cmd = data->command_line;
- std::swap(local_cmd.at(data->buff_pos), local_cmd.at(data->buff_pos-1));
- set_command_line_and_position(local_cmd, data->buff_pos + 1);
+ wcstring local_cmd = el->text;
+ std::swap(local_cmd.at(el->position), local_cmd.at(el->position-1));
+ set_command_line_and_position(el, local_cmd, el->position + 1);
}
break;
}
case R_TRANSPOSE_WORDS:
{
- size_t len = data->command_length();
- const wchar_t *buff = data->command_line.c_str();
+ editable_line_t *el = data->active_edit_line();
+ size_t len = el->size();
+ const wchar_t *buff = el->text.c_str();
const wchar_t *tok_begin, *tok_end, *prev_begin, *prev_end;
/* If we are not in a token, look for one ahead */
- size_t buff_pos = data->buff_pos;
+ size_t buff_pos = el->position;
while (buff_pos != len && !iswalnum(buff[buff_pos]))
buff_pos++;
- update_buff_pos(buff_pos);
+ update_buff_pos(el, buff_pos);
- parse_util_token_extent(buff, data->buff_pos, &tok_begin, &tok_end, &prev_begin, &prev_end);
+ parse_util_token_extent(buff, el->position, &tok_begin, &tok_end, &prev_begin, &prev_end);
/* In case we didn't find a token at or after the cursor... */
if (tok_begin == &buff[len])
@@ -3792,7 +3921,7 @@ const wchar_t *reader_readline(void)
new_buff.append(prev);
new_buff.append(trail);
/* Put cursor right after the second token */
- set_command_line_and_position(new_buff, tok_end - buff);
+ set_command_line_and_position(el, new_buff, tok_end - buff);
}
break;
}
@@ -3801,15 +3930,16 @@ const wchar_t *reader_readline(void)
case R_DOWNCASE_WORD:
case R_CAPITALIZE_WORD:
{
+ editable_line_t *el = data->active_edit_line();
// For capitalize_word, whether we've capitalized a character so far
bool capitalized_first = false;
// We apply the operation from the current location to the end of the word
- size_t pos = data->buff_pos;
- move_word(MOVE_DIR_RIGHT, false, move_word_style_punctuation, false);
- for (; pos < data->buff_pos; pos++)
+ size_t pos = el->position;
+ move_word(el, MOVE_DIR_RIGHT, false, move_word_style_punctuation, false);
+ for (; pos < el->position; pos++)
{
- wchar_t chr = data->command_line.at(pos);
+ wchar_t chr = el->text.at(pos);
// We always change the case; this decides whether we go uppercase (true) or lowercase (false)
bool make_uppercase;
@@ -3824,29 +3954,29 @@ const wchar_t *reader_readline(void)
else
chr = towlower(chr);
- data->command_line.at(pos) = chr;
+ data->command_line.text.at(pos) = chr;
capitalized_first = capitalized_first || make_uppercase;
}
- data->command_line_changed();
- reader_super_highlight_me_plenty(data->buff_pos);
- reader_repaint();
+ data->command_line_changed(el);
+ reader_super_highlight_me_plenty();
+ reader_repaint_needed();
break;
}
case R_BEGIN_SELECTION:
{
data->sel_active = true;
- data->sel_begin_pos = data->buff_pos;
- data->sel_start_pos = data->buff_pos;
- data->sel_stop_pos = data->buff_pos;
+ data->sel_begin_pos = data->command_line.position;
+ data->sel_start_pos = data->command_line.position;
+ data->sel_stop_pos = data->command_line.position;
break;
}
case R_END_SELECTION:
{
data->sel_active = false;
- data->sel_start_pos = data->buff_pos;
- data->sel_stop_pos = data->buff_pos;
+ data->sel_start_pos = data->command_line.position;
+ data->sel_stop_pos = data->command_line.position;
break;
}
@@ -3854,50 +3984,51 @@ const wchar_t *reader_readline(void)
{
bool newv = (last_char != R_KILL_SELECTION);
size_t start, len;
- if(reader_get_selection(start, len))
+ if (reader_get_selection(&start, &len))
{
- reader_kill(start, len, KILL_APPEND, newv);
+ reader_kill(&data->command_line, start, len, KILL_APPEND, newv);
}
break;
}
case R_FORWARD_JUMP:
{
+ editable_line_t *el = data->active_edit_line();
wchar_t target = input_function_pop_arg();
- size_t len = data->command_length();
bool status = false;
- for(int i = data->buff_pos + 1; i < len; i++)
+ for (size_t i = el->position + 1; i < el->size(); i++)
{
- if(buff[i] == target)
+ if (el->at(i) == target)
{
- update_buff_pos(i);
+ update_buff_pos(el, i);
status = true;
break;
}
}
input_function_set_status(status);
- reader_repaint();
+ reader_repaint_needed();
break;
}
case R_BACKWARD_JUMP:
{
+ editable_line_t *el = data->active_edit_line();
wchar_t target = input_function_pop_arg();
- size_t len = data->command_length();
bool status = false;
- for(int i = data->buff_pos - 1; i >= 0; i--)
+ size_t tmp_pos = el->position;
+ while (tmp_pos--)
{
- if(buff[i] == target)
+ if (el->at(tmp_pos) == target)
{
- update_buff_pos(i);
+ update_buff_pos(el, tmp_pos);
status = true;
break;
}
}
input_function_set_status(status);
- reader_repaint();
+ reader_repaint_needed();
break;
}
@@ -3906,11 +4037,28 @@ const wchar_t *reader_readline(void)
{
if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127)))
{
+ 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);
+ editable_line_t *el = data->active_edit_line();
+ insert_char(data->active_edit_line(), c, should_expand_abbreviations);
+
+ /* End paging upon inserting into the normal command line */
+ if (el == &data->command_line)
+ {
+ clear_pager();
+ }
+
+
}
else
{
@@ -3939,10 +4087,20 @@ const wchar_t *reader_readline(void)
}
last_char = c;
+
+ reader_repaint_if_needed();
}
writestr(L"\n");
+ /* Ensure we have no pager contents when we exit */
+ if (! data->pager.empty())
+ {
+ /* Clear to end of screen to erase the pager contents. TODO: this may fail if eos doesn't exist, in which case we should emit newlines */
+ screen_force_clear_to_end();
+ data->pager.clear();
+ }
+
if (!reader_exit_forced())
{
if (tcsetattr(0,TCSANOW,&old_modes)) /* return to previous mode */
@@ -3953,7 +4111,7 @@ const wchar_t *reader_readline(void)
set_color(rgb_color_t::reset(), rgb_color_t::reset());
}
- return finished ? data->command_line.c_str() : 0;
+ return finished ? data->command_line.text.c_str() : NULL;
}
int reader_search_mode()
@@ -3966,6 +4124,45 @@ int reader_search_mode()
return !!data->search_mode;
}
+int reader_has_pager_contents()
+{
+ if (!data)
+ {
+ return -1;
+ }
+
+ 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