mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-04-19 14:51:13 -03:00
Merge branch 'input_cleanup'
This merges a bunch of changes that clean up how the reader loop and input works. Prior to this fix, we abused wchar_t by cramming readline functions into "private" regions. Readline functions then were further abused with meta-readline functions like R_NULL or R_TIMEOUT. This fix introduces a new type char_event_type_t which wraps up the "meta" character types. A char event may be a null (try again), timeout, readline, or real input character. These are all distinct values. The reader loop is then refactored to handle these cases separately.
This commit is contained in:
@@ -325,11 +325,10 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv)
|
||||
}
|
||||
|
||||
for (i = w.woptind; i < argc; i++) {
|
||||
wchar_t c = input_function_get_code(argv[i]);
|
||||
if (c != INPUT_CODE_NONE) {
|
||||
if (auto mc = input_function_get_code(argv[i])) {
|
||||
// input_unreadch inserts the specified keypress or readline function at the back of
|
||||
// the queue of unused keypresses.
|
||||
input_queue_ch(c);
|
||||
input_queue_ch(*mc);
|
||||
} else {
|
||||
streams.err.append_format(_(L"%ls: Unknown input function '%ls'"), cmd, argv[i]);
|
||||
builtin_print_help(parser, streams, cmd, streams.err);
|
||||
|
||||
@@ -2382,8 +2382,7 @@ char **make_null_terminated_array(const std::vector<std::string> &lst) {
|
||||
// TODO: Actually implement the replacement as documented above.
|
||||
bool fish_reserved_codepoint(wchar_t c) {
|
||||
return (c >= RESERVED_CHAR_BASE && c < RESERVED_CHAR_END) ||
|
||||
(c >= ENCODE_DIRECT_BASE && c < ENCODE_DIRECT_END) ||
|
||||
(c >= INPUT_COMMON_BASE && c < INPUT_COMMON_END);
|
||||
(c >= ENCODE_DIRECT_BASE && c < ENCODE_DIRECT_END);
|
||||
}
|
||||
|
||||
/// Reopen stdin, stdout and/or stderr on /dev/null. This is invoked when we find that our tty has
|
||||
|
||||
@@ -87,8 +87,6 @@ typedef std::vector<wcstring> wcstring_list_t;
|
||||
// on Mac OS X. See http://www.unicode.org/faq/private_use.html.
|
||||
#define ENCODE_DIRECT_BASE (wchar_t)0xF600
|
||||
#define ENCODE_DIRECT_END (ENCODE_DIRECT_BASE + 256)
|
||||
#define INPUT_COMMON_BASE (wchar_t)0xF700
|
||||
#define INPUT_COMMON_END (INPUT_COMMON_BASE + 64)
|
||||
|
||||
// NAME_MAX is not defined on Solaris
|
||||
#if !defined(NAME_MAX)
|
||||
|
||||
@@ -204,13 +204,13 @@ static void process_input(bool continuous_mode) {
|
||||
|
||||
std::fwprintf(stderr, L"Press a key\n\n");
|
||||
while (keep_running) {
|
||||
wchar_t wc;
|
||||
char_event_t evt{0};
|
||||
if (reader_test_and_clear_interrupted()) {
|
||||
wc = shell_modes.c_cc[VINTR];
|
||||
evt = char_event_t{shell_modes.c_cc[VINTR]};
|
||||
} else {
|
||||
wc = input_common_readch(true);
|
||||
evt = input_common_readch_timed(true);
|
||||
}
|
||||
if (wc == R_TIMEOUT || wc == R_EOF) {
|
||||
if (!evt.is_char()) {
|
||||
output_bind_command(bind_chars);
|
||||
if (first_char_seen && !continuous_mode) {
|
||||
return;
|
||||
@@ -218,6 +218,7 @@ static void process_input(bool continuous_mode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wchar_t wc = evt.get_char();
|
||||
prev_tstamp = output_elapsed_time(prev_tstamp, first_char_seen);
|
||||
add_char_to_bind_command(wc, bind_chars);
|
||||
output_info_about_char(wc);
|
||||
|
||||
@@ -2996,9 +2996,11 @@ static void test_input() {
|
||||
}
|
||||
|
||||
// Now test.
|
||||
wint_t c = input_readch();
|
||||
if (c != R_DOWN_LINE) {
|
||||
err(L"Expected to read char R_DOWN_LINE, but instead got %ls\n", describe_char(c).c_str());
|
||||
auto evt = input_readch();
|
||||
if (!evt.is_readline()) {
|
||||
err(L"Event is not a readline");
|
||||
} else if (evt.get_readline() != readline_cmd_t::down_line) {
|
||||
err(L"Expected to read char down_line");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
313
src/input.cpp
313
src/input.cpp
@@ -51,6 +51,9 @@ struct input_mapping_t {
|
||||
static unsigned int s_last_input_map_spec_order = 0;
|
||||
specification_order = ++s_last_input_map_spec_order;
|
||||
}
|
||||
|
||||
/// \return true if this is a generic mapping, i.e. acts as a fallback.
|
||||
bool is_generic() const { return seq.empty(); }
|
||||
};
|
||||
|
||||
/// A struct representing the mapping from a terminfo key name to a terminfo character sequence.
|
||||
@@ -64,68 +67,69 @@ static constexpr size_t input_function_count = R_END_INPUT_FUNCTIONS - R_BEGIN_I
|
||||
/// Input function metadata. This list should be kept in sync with the key code list in
|
||||
/// input_common.h.
|
||||
struct input_function_metadata_t {
|
||||
wchar_t code;
|
||||
readline_cmd_t code;
|
||||
const wchar_t *name;
|
||||
};
|
||||
|
||||
static const input_function_metadata_t input_function_metadata[] = {
|
||||
{R_BEGINNING_OF_LINE, L"beginning-of-line"},
|
||||
{R_END_OF_LINE, L"end-of-line"},
|
||||
{R_FORWARD_CHAR, L"forward-char"},
|
||||
{R_BACKWARD_CHAR, L"backward-char"},
|
||||
{R_FORWARD_WORD, L"forward-word"},
|
||||
{R_BACKWARD_WORD, L"backward-word"},
|
||||
{R_FORWARD_BIGWORD, L"forward-bigword"},
|
||||
{R_BACKWARD_BIGWORD, L"backward-bigword"},
|
||||
{R_HISTORY_SEARCH_BACKWARD, L"history-search-backward"},
|
||||
{R_HISTORY_SEARCH_FORWARD, L"history-search-forward"},
|
||||
{R_DELETE_CHAR, L"delete-char"},
|
||||
{R_BACKWARD_DELETE_CHAR, L"backward-delete-char"},
|
||||
{R_KILL_LINE, L"kill-line"},
|
||||
{R_YANK, L"yank"},
|
||||
{R_YANK_POP, L"yank-pop"},
|
||||
{R_COMPLETE, L"complete"},
|
||||
{R_COMPLETE_AND_SEARCH, L"complete-and-search"},
|
||||
{R_PAGER_TOGGLE_SEARCH, L"pager-toggle-search"},
|
||||
{R_BEGINNING_OF_HISTORY, L"beginning-of-history"},
|
||||
{R_END_OF_HISTORY, L"end-of-history"},
|
||||
{R_BACKWARD_KILL_LINE, L"backward-kill-line"},
|
||||
{R_KILL_WHOLE_LINE, L"kill-whole-line"},
|
||||
{R_KILL_WORD, L"kill-word"},
|
||||
{R_KILL_BIGWORD, L"kill-bigword"},
|
||||
{R_BACKWARD_KILL_WORD, L"backward-kill-word"},
|
||||
{R_BACKWARD_KILL_PATH_COMPONENT, L"backward-kill-path-component"},
|
||||
{R_BACKWARD_KILL_BIGWORD, L"backward-kill-bigword"},
|
||||
{R_HISTORY_TOKEN_SEARCH_BACKWARD, L"history-token-search-backward"},
|
||||
{R_HISTORY_TOKEN_SEARCH_FORWARD, L"history-token-search-forward"},
|
||||
{R_SELF_INSERT, L"self-insert"},
|
||||
{R_TRANSPOSE_CHARS, L"transpose-chars"},
|
||||
{R_TRANSPOSE_WORDS, L"transpose-words"},
|
||||
{R_UPCASE_WORD, L"upcase-word"},
|
||||
{R_DOWNCASE_WORD, L"downcase-word"},
|
||||
{R_CAPITALIZE_WORD, L"capitalize-word"},
|
||||
{R_VI_ARG_DIGIT, L"vi-arg-digit"},
|
||||
{R_VI_DELETE_TO, L"vi-delete-to"},
|
||||
{R_EXECUTE, L"execute"},
|
||||
{R_BEGINNING_OF_BUFFER, L"beginning-of-buffer"},
|
||||
{R_END_OF_BUFFER, L"end-of-buffer"},
|
||||
{R_REPAINT, L"repaint"},
|
||||
{R_FORCE_REPAINT, L"force-repaint"},
|
||||
{R_UP_LINE, L"up-line"},
|
||||
{R_DOWN_LINE, L"down-line"},
|
||||
{R_SUPPRESS_AUTOSUGGESTION, L"suppress-autosuggestion"},
|
||||
{R_ACCEPT_AUTOSUGGESTION, L"accept-autosuggestion"},
|
||||
{R_BEGIN_SELECTION, L"begin-selection"},
|
||||
{R_SWAP_SELECTION_START_STOP, L"swap-selection-start-stop"},
|
||||
{R_END_SELECTION, L"end-selection"},
|
||||
{R_KILL_SELECTION, L"kill-selection"},
|
||||
{R_FORWARD_JUMP, L"forward-jump"},
|
||||
{R_BACKWARD_JUMP, L"backward-jump"},
|
||||
{R_FORWARD_JUMP_TILL, L"forward-jump-till"},
|
||||
{R_BACKWARD_JUMP_TILL, L"backward-jump-till"},
|
||||
{R_REPEAT_JUMP, L"repeat-jump"},
|
||||
{R_REVERSE_REPEAT_JUMP, L"repeat-jump-reverse"},
|
||||
{R_AND, L"and"},
|
||||
{R_CANCEL, L"cancel"}};
|
||||
{readline_cmd_t::beginning_of_line, L"beginning-of-line"},
|
||||
{readline_cmd_t::end_of_line, L"end-of-line"},
|
||||
{readline_cmd_t::forward_char, L"forward-char"},
|
||||
{readline_cmd_t::backward_char, L"backward-char"},
|
||||
{readline_cmd_t::forward_word, L"forward-word"},
|
||||
{readline_cmd_t::backward_word, L"backward-word"},
|
||||
{readline_cmd_t::forward_bigword, L"forward-bigword"},
|
||||
{readline_cmd_t::backward_bigword, L"backward-bigword"},
|
||||
{readline_cmd_t::history_search_backward, L"history-search-backward"},
|
||||
{readline_cmd_t::history_search_forward, L"history-search-forward"},
|
||||
{readline_cmd_t::delete_char, L"delete-char"},
|
||||
{readline_cmd_t::backward_delete_char, L"backward-delete-char"},
|
||||
{readline_cmd_t::kill_line, L"kill-line"},
|
||||
{readline_cmd_t::yank, L"yank"},
|
||||
{readline_cmd_t::yank_POP, L"yank-pop"},
|
||||
{readline_cmd_t::complete, L"complete"},
|
||||
{readline_cmd_t::complete_AND_SEARCH, L"complete-and-search"},
|
||||
{readline_cmd_t::pager_toggle_search, L"pager-toggle-search"},
|
||||
{readline_cmd_t::beginning_of_history, L"beginning-of-history"},
|
||||
{readline_cmd_t::end_of_history, L"end-of-history"},
|
||||
{readline_cmd_t::backward_kill_line, L"backward-kill-line"},
|
||||
{readline_cmd_t::kill_whole_line, L"kill-whole-line"},
|
||||
{readline_cmd_t::kill_word, L"kill-word"},
|
||||
{readline_cmd_t::kill_bigword, L"kill-bigword"},
|
||||
{readline_cmd_t::backward_kill_word, L"backward-kill-word"},
|
||||
{readline_cmd_t::backward_kill_path_component, L"backward-kill-path-component"},
|
||||
{readline_cmd_t::backward_kill_bigword, L"backward-kill-bigword"},
|
||||
{readline_cmd_t::history_token_search_backward, L"history-token-search-backward"},
|
||||
{readline_cmd_t::history_token_search_forward, L"history-token-search-forward"},
|
||||
{readline_cmd_t::self_insert, L"self-insert"},
|
||||
{readline_cmd_t::transpose_chars, L"transpose-chars"},
|
||||
{readline_cmd_t::transpose_words, L"transpose-words"},
|
||||
{readline_cmd_t::upcase_word, L"upcase-word"},
|
||||
{readline_cmd_t::downcase_word, L"downcase-word"},
|
||||
{readline_cmd_t::capitalize_word, L"capitalize-word"},
|
||||
{readline_cmd_t::vi_arg_digit, L"vi-arg-digit"},
|
||||
{readline_cmd_t::vi_delete_to, L"vi-delete-to"},
|
||||
{readline_cmd_t::execute, L"execute"},
|
||||
{readline_cmd_t::beginning_of_buffer, L"beginning-of-buffer"},
|
||||
{readline_cmd_t::end_of_buffer, L"end-of-buffer"},
|
||||
{readline_cmd_t::repaint, L"repaint"},
|
||||
{readline_cmd_t::force_repaint, L"force-repaint"},
|
||||
{readline_cmd_t::up_line, L"up-line"},
|
||||
{readline_cmd_t::down_line, L"down-line"},
|
||||
{readline_cmd_t::suppress_autosuggestion, L"suppress-autosuggestion"},
|
||||
{readline_cmd_t::accept_autosuggestion, L"accept-autosuggestion"},
|
||||
{readline_cmd_t::begin_selection, L"begin-selection"},
|
||||
{readline_cmd_t::swap_selection_start_stop, L"swap-selection-start-stop"},
|
||||
{readline_cmd_t::end_selection, L"end-selection"},
|
||||
{readline_cmd_t::kill_selection, L"kill-selection"},
|
||||
{readline_cmd_t::forward_jump, L"forward-jump"},
|
||||
{readline_cmd_t::backward_jump, L"backward-jump"},
|
||||
{readline_cmd_t::forward_jump_till, L"forward-jump-till"},
|
||||
{readline_cmd_t::backward_jump_till, L"backward-jump-till"},
|
||||
{readline_cmd_t::repeat_jump, L"repeat-jump"},
|
||||
{readline_cmd_t::reverse_repeat_jump, L"repeat-jump-reverse"},
|
||||
{readline_cmd_t::func_and, L"and"},
|
||||
{readline_cmd_t::cancel, L"cancel"}};
|
||||
|
||||
static_assert(sizeof(input_function_metadata) / sizeof(input_function_metadata[0]) ==
|
||||
input_function_count,
|
||||
@@ -179,12 +183,12 @@ void input_set_bind_mode(const wcstring &bm) {
|
||||
}
|
||||
|
||||
/// Returns the arity of a given input function.
|
||||
static int input_function_arity(int function) {
|
||||
static int input_function_arity(readline_cmd_t function) {
|
||||
switch (function) {
|
||||
case R_FORWARD_JUMP:
|
||||
case R_BACKWARD_JUMP:
|
||||
case R_FORWARD_JUMP_TILL:
|
||||
case R_BACKWARD_JUMP_TILL:
|
||||
case readline_cmd_t::forward_jump:
|
||||
case readline_cmd_t::backward_jump:
|
||||
case readline_cmd_t::forward_jump_till:
|
||||
case readline_cmd_t::backward_jump_till:
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
@@ -246,17 +250,21 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *command, const wc
|
||||
|
||||
/// Handle interruptions to key reading by reaping finshed jobs and propagating the interrupt to the
|
||||
/// reader.
|
||||
static int interrupt_handler() {
|
||||
static maybe_t<char_event_t> interrupt_handler() {
|
||||
// Fire any pending events.
|
||||
event_fire_delayed();
|
||||
// Reap stray processes, including printing exit status messages.
|
||||
if (job_reap(true)) reader_repaint_needed();
|
||||
// Tell the reader an event occured.
|
||||
if (reader_reading_interrupted()) {
|
||||
return shell_modes.c_cc[VINTR];
|
||||
auto vintr = shell_modes.c_cc[VINTR];
|
||||
if (vintr == 0) {
|
||||
return none();
|
||||
}
|
||||
return char_event_t{vintr};
|
||||
}
|
||||
|
||||
return R_NULL;
|
||||
return char_event_t{char_event_type_t::check_exit};
|
||||
}
|
||||
|
||||
static std::atomic<bool> input_initialized{false};
|
||||
@@ -295,19 +303,21 @@ void input_function_push_arg(wchar_t arg) {
|
||||
|
||||
wchar_t input_function_pop_arg() { return input_function_args[--input_function_args_index]; }
|
||||
|
||||
void input_function_push_args(int code) {
|
||||
void input_function_push_args(readline_cmd_t code) {
|
||||
int arity = input_function_arity(code);
|
||||
std::vector<wchar_t> skipped;
|
||||
std::vector<char_event_t> skipped;
|
||||
|
||||
for (int i = 0; i < arity; i++) {
|
||||
wchar_t arg;
|
||||
|
||||
// Skip and queue up any function codes. See issue #2357.
|
||||
while ((arg = input_common_readch(0)) >= R_BEGIN_INPUT_FUNCTIONS &&
|
||||
arg < R_END_INPUT_FUNCTIONS) {
|
||||
skipped.push_back(arg);
|
||||
wchar_t arg{};
|
||||
for (;;) {
|
||||
auto evt = input_common_readch();
|
||||
if (evt.is_char()) {
|
||||
arg = evt.get_char();
|
||||
break;
|
||||
}
|
||||
skipped.push_back(evt);
|
||||
}
|
||||
|
||||
input_function_push_arg(arg);
|
||||
}
|
||||
|
||||
@@ -326,7 +336,7 @@ static void input_mapping_execute(const input_mapping_t &m, bool allow_commands)
|
||||
bool has_commands = false, has_functions = false;
|
||||
|
||||
for (const wcstring &cmd : m.commands) {
|
||||
if (input_function_get_code(cmd) != INPUT_CODE_NONE)
|
||||
if (input_function_get_code(cmd))
|
||||
has_functions = true;
|
||||
else
|
||||
has_commands = true;
|
||||
@@ -339,19 +349,19 @@ static void input_mapping_execute(const input_mapping_t &m, bool allow_commands)
|
||||
}
|
||||
|
||||
if (has_commands && !allow_commands) {
|
||||
// We don't want to run commands yet. Put the characters back and return R_NULL.
|
||||
// We don't want to run commands yet. Put the characters back and return check_exit.
|
||||
for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end;
|
||||
++it) {
|
||||
input_common_next_ch(*it);
|
||||
}
|
||||
input_common_next_ch(R_NULL);
|
||||
input_common_next_ch(char_event_type_t::check_exit);
|
||||
return; // skip the input_set_bind_mode
|
||||
} else if (has_functions && !has_commands) {
|
||||
// Functions are added at the head of the input queue.
|
||||
for (wcstring_list_t::const_reverse_iterator it = m.commands.rbegin(),
|
||||
end = m.commands.rend();
|
||||
it != end; ++it) {
|
||||
wchar_t code = input_function_get_code(*it);
|
||||
readline_cmd_t code = input_function_get_code(*it).value();
|
||||
input_function_push_args(code);
|
||||
input_common_next_ch(code);
|
||||
}
|
||||
@@ -365,11 +375,11 @@ static void input_mapping_execute(const input_mapping_t &m, bool allow_commands)
|
||||
parser_t::principal_parser().eval(cmd, io_chain_t(), TOP);
|
||||
}
|
||||
proc_set_last_statuses(std::move(last_statuses));
|
||||
input_common_next_ch(R_NULL);
|
||||
input_common_next_ch(char_event_type_t::check_exit);
|
||||
} else {
|
||||
// Invalid binding, mixed commands and functions. We would need to execute these one by
|
||||
// one.
|
||||
input_common_next_ch(R_NULL);
|
||||
input_common_next_ch(char_event_type_t::check_exit);
|
||||
}
|
||||
|
||||
// Empty bind mode indicates to not reset the mode (#2871)
|
||||
@@ -384,15 +394,13 @@ static bool input_mapping_is_match(const input_mapping_t &m) {
|
||||
|
||||
bool timed = false;
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
wchar_t read = input_common_readch(timed);
|
||||
|
||||
if (read != str[i]) {
|
||||
// We didn't match the bind sequence/input mapping, (it timed out or they entered something else)
|
||||
// Undo consumption of the read characters since we didn't match the bind sequence and abort.
|
||||
input_common_next_ch(read);
|
||||
while (i--) {
|
||||
input_common_next_ch(str[i]);
|
||||
}
|
||||
auto evt = timed ? input_common_readch_timed() : input_common_readch();
|
||||
if (!evt.is_char() || evt.get_char() != str[i]) {
|
||||
// We didn't match the bind sequence/input mapping, (it timed out or they entered
|
||||
// something else) Undo consumption of the read characters since we didn't match the
|
||||
// bind sequence and abort.
|
||||
input_common_next_ch(evt);
|
||||
while (i--) input_common_next_ch(str[i]);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -404,115 +412,102 @@ static bool input_mapping_is_match(const input_mapping_t &m) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void input_queue_ch(wint_t ch) { input_common_queue_ch(ch); }
|
||||
void input_queue_ch(char_event_t ch) { input_common_queue_ch(ch); }
|
||||
|
||||
static void input_mapping_execute_matching_or_generic(bool allow_commands) {
|
||||
/// \return the first mapping that matches, walking first over the user's mapping list, then the
|
||||
/// preset list. \return null if nothing matches.
|
||||
static const input_mapping_t *find_mapping() {
|
||||
const input_mapping_t *generic = NULL;
|
||||
|
||||
const auto &vars = parser_t::principal_parser().vars();
|
||||
const wcstring bind_mode = input_get_bind_mode(vars);
|
||||
|
||||
for (auto& m : mapping_list) {
|
||||
if (m.mode != bind_mode) {
|
||||
continue;
|
||||
}
|
||||
const auto lists = {&mapping_list, &preset_mapping_list};
|
||||
for (const auto *listp : lists) {
|
||||
for (const auto &m : *listp) {
|
||||
if (m.mode != bind_mode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m.seq.length() == 0) {
|
||||
generic = &m;
|
||||
} else if (input_mapping_is_match(m)) {
|
||||
input_mapping_execute(m, allow_commands);
|
||||
return;
|
||||
if (m.is_generic()) {
|
||||
if (!generic) generic = &m;
|
||||
} else if (input_mapping_is_match(m)) {
|
||||
return &m;
|
||||
}
|
||||
}
|
||||
}
|
||||
return generic;
|
||||
}
|
||||
|
||||
// HACK: This is ugly duplication.
|
||||
for (auto& m : preset_mapping_list) {
|
||||
if (m.mode != bind_mode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m.seq.length() == 0) {
|
||||
// Only use this generic if the user list didn't have one.
|
||||
if (!generic) generic = &m;
|
||||
} else if (input_mapping_is_match(m)) {
|
||||
input_mapping_execute(m, allow_commands);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (generic) {
|
||||
input_mapping_execute(*generic, allow_commands);
|
||||
static void input_mapping_execute_matching_or_generic(bool allow_commands) {
|
||||
const input_mapping_t *mapping = find_mapping();
|
||||
if (mapping) {
|
||||
input_mapping_execute(*mapping, allow_commands);
|
||||
} else {
|
||||
debug(2, L"no generic found, ignoring char...");
|
||||
wchar_t c = input_common_readch(0);
|
||||
if (c == R_EOF) {
|
||||
input_common_next_ch(c);
|
||||
auto evt = input_common_readch();
|
||||
if (evt.is_eof()) {
|
||||
input_common_next_ch(evt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function. Picks through the queue of incoming characters until we get to one that's not a
|
||||
/// readline function.
|
||||
static wchar_t input_read_characters_only() {
|
||||
std::vector<wchar_t> functions_to_put_back;
|
||||
wchar_t char_to_return;
|
||||
static char_event_t input_read_characters_no_readline() {
|
||||
std::vector<char_event_t> saved_events;
|
||||
char_event_t evt_to_return{0};
|
||||
for (;;) {
|
||||
char_to_return = input_common_readch(0);
|
||||
bool is_readline_function =
|
||||
(char_to_return >= R_BEGIN_INPUT_FUNCTIONS && char_to_return < R_END_INPUT_FUNCTIONS);
|
||||
// R_NULL and R_EOF are more control characters than readline functions, so check specially
|
||||
// for those.
|
||||
if (!is_readline_function || char_to_return == R_NULL || char_to_return == R_EOF) {
|
||||
auto evt = input_common_readch();
|
||||
if (evt.is_readline()) {
|
||||
saved_events.push_back(evt);
|
||||
} else {
|
||||
evt_to_return = evt;
|
||||
break;
|
||||
}
|
||||
// This is a readline function; save it off for later re-enqueing and try again.
|
||||
functions_to_put_back.push_back(char_to_return);
|
||||
}
|
||||
// Restore any readline functions, in reverse to preserve their original order.
|
||||
size_t idx = functions_to_put_back.size();
|
||||
while (idx--) {
|
||||
input_common_next_ch(functions_to_put_back.at(idx));
|
||||
for (auto iter = saved_events.rbegin(); iter != saved_events.rend(); ++iter) {
|
||||
input_common_next_ch(*iter);
|
||||
}
|
||||
return char_to_return;
|
||||
return evt_to_return;
|
||||
}
|
||||
|
||||
wint_t input_readch(bool allow_commands) {
|
||||
char_event_t input_readch(bool allow_commands) {
|
||||
// Clear the interrupted flag.
|
||||
reader_reset_interrupted();
|
||||
// Search for sequence in mapping tables.
|
||||
while (true) {
|
||||
wchar_t c = input_common_readch(0);
|
||||
auto evt = input_common_readch();
|
||||
|
||||
if (c >= R_BEGIN_INPUT_FUNCTIONS && c < R_END_INPUT_FUNCTIONS) {
|
||||
switch (c) {
|
||||
case R_SELF_INSERT: {
|
||||
if (evt.is_readline()) {
|
||||
switch (evt.get_readline()) {
|
||||
case readline_cmd_t::self_insert: {
|
||||
// Issue #1595: ensure we only insert characters, not readline functions. The
|
||||
// common case is that this will be empty.
|
||||
return input_read_characters_only();
|
||||
return input_read_characters_no_readline();
|
||||
}
|
||||
case R_AND: {
|
||||
case readline_cmd_t::func_and: {
|
||||
if (input_function_status) {
|
||||
return input_readch();
|
||||
}
|
||||
c = input_common_readch(0);
|
||||
while (c >= R_BEGIN_INPUT_FUNCTIONS && c < R_END_INPUT_FUNCTIONS) {
|
||||
c = input_common_readch(0);
|
||||
}
|
||||
input_common_next_ch(c);
|
||||
do {
|
||||
evt = input_common_readch();
|
||||
} while (evt.is_readline());
|
||||
input_common_next_ch(evt);
|
||||
return input_readch();
|
||||
}
|
||||
default: { return c; }
|
||||
default: { return evt; }
|
||||
}
|
||||
} else if (c == R_EOF) {
|
||||
// If we have R_EOF, we need to immediately quit.
|
||||
} else if (evt.is_eof()) {
|
||||
// If we have EOF, we need to immediately quit.
|
||||
// There's no need to go through the input functions.
|
||||
return R_EOF;
|
||||
return evt;
|
||||
} else {
|
||||
input_common_next_ch(c);
|
||||
input_common_next_ch(evt);
|
||||
input_mapping_execute_matching_or_generic(allow_commands);
|
||||
// Regarding allow_commands, we're in a loop, but if a fish command
|
||||
// is executed, R_NULL is unread, so the next pass through the loop
|
||||
// we'll break out and return it.
|
||||
// Regarding allow_commands, we're in a loop, but if a fish command is executed,
|
||||
// check_exit is unread, so the next pass through the loop we'll break out and return
|
||||
// it.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -800,11 +795,11 @@ wcstring_list_t input_function_get_names() {
|
||||
return result;
|
||||
}
|
||||
|
||||
wchar_t input_function_get_code(const wcstring &name) {
|
||||
maybe_t<readline_cmd_t> input_function_get_code(const wcstring &name) {
|
||||
for (const auto &md : input_function_metadata) {
|
||||
if (name == md.name) {
|
||||
return md.code;
|
||||
}
|
||||
}
|
||||
return INPUT_CODE_NONE;
|
||||
return none();
|
||||
}
|
||||
|
||||
12
src/input.h
12
src/input.h
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "builtin_bind.h"
|
||||
#include "common.h"
|
||||
#include "input_common.h"
|
||||
|
||||
#define FISH_BIND_MODE_VAR L"fish_bind_mode"
|
||||
|
||||
@@ -30,13 +31,13 @@ void init_input();
|
||||
/// key press, and is returned as such.
|
||||
///
|
||||
/// The argument determines whether fish commands are allowed to be run as bindings. If false, when
|
||||
/// a character is encountered that would invoke a fish command, it is unread and R_NULL is
|
||||
/// returned.
|
||||
wint_t input_readch(bool allow_commands = true);
|
||||
/// a character is encountered that would invoke a fish command, it is unread and
|
||||
/// char_event_type_t::check_exit is returned.
|
||||
char_event_t input_readch(bool allow_commands = true);
|
||||
|
||||
/// Enqueue a character or a readline function to the queue of unread characters that input_readch
|
||||
/// will return before actually reading from fd 0.
|
||||
void input_queue_ch(wint_t ch);
|
||||
void input_queue_ch(char_event_t ch);
|
||||
|
||||
/// Add a key mapping from the specified sequence to the specified command.
|
||||
///
|
||||
@@ -94,8 +95,7 @@ bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name);
|
||||
wcstring_list_t input_terminfo_get_names(bool skip_null);
|
||||
|
||||
/// Returns the input function code for the given input function name.
|
||||
#define INPUT_CODE_NONE (wchar_t(-1))
|
||||
wchar_t input_function_get_code(const wcstring &name);
|
||||
maybe_t<readline_cmd_t> input_function_get_code(const wcstring &name);
|
||||
|
||||
/// Returns a list of all existing input function names.
|
||||
wcstring_list_t input_function_get_names(void);
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
#define WAIT_ON_ESCAPE_DEFAULT 30
|
||||
static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT;
|
||||
|
||||
/// Characters that have been read and returned by the sequence matching code.
|
||||
static std::deque<wchar_t> lookahead_list;
|
||||
/// Events which have been read and returned by the sequence matching code.
|
||||
static std::deque<char_event_t> lookahead_list;
|
||||
|
||||
// Queue of pairs of (function pointer, argument) to be invoked. Expected to be mostly empty.
|
||||
typedef std::list<std::function<void(void)>> callback_queue_t;
|
||||
@@ -43,26 +43,35 @@ static void input_flush_callbacks();
|
||||
|
||||
static bool has_lookahead() { return !lookahead_list.empty(); }
|
||||
|
||||
static wint_t lookahead_pop() {
|
||||
wint_t result = lookahead_list.front();
|
||||
static char_event_t lookahead_pop() {
|
||||
auto result = lookahead_list.front();
|
||||
lookahead_list.pop_front();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void lookahead_push_back(wint_t c) { lookahead_list.push_back(c); }
|
||||
/// \return the next lookahead char, or none if none. Discards timeouts.
|
||||
static maybe_t<char_event_t> lookahead_pop_evt() {
|
||||
while (has_lookahead()) {
|
||||
auto evt = lookahead_pop();
|
||||
if (! evt.is_timeout()) {
|
||||
return evt;
|
||||
}
|
||||
}
|
||||
return none();
|
||||
}
|
||||
|
||||
static void lookahead_push_front(wint_t c) { lookahead_list.push_front(c); }
|
||||
static void lookahead_push_back(char_event_t c) { lookahead_list.push_back(c); }
|
||||
|
||||
static wint_t lookahead_front() { return lookahead_list.front(); }
|
||||
static void lookahead_push_front(char_event_t c) { lookahead_list.push_front(c); }
|
||||
|
||||
/// Callback function for handling interrupts on reading.
|
||||
static int (*interrupt_handler)();
|
||||
static interrupt_func_t interrupt_handler;
|
||||
|
||||
void input_common_init(int (*ih)()) { interrupt_handler = ih; }
|
||||
void input_common_init(interrupt_func_t func) { interrupt_handler = func; }
|
||||
|
||||
/// Internal function used by input_common_readch to read one byte from fd 0. This function should
|
||||
/// only be called by input_common_readch().
|
||||
static wint_t readb() {
|
||||
static char_event_t readb() {
|
||||
// do_loop must be set on every path through the loop; leaving it uninitialized allows the
|
||||
// static analyzer to assist in catching mistakes.
|
||||
unsigned char arr[1];
|
||||
@@ -107,15 +116,17 @@ static wint_t readb() {
|
||||
if (res == -1) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
if (interrupt_handler) {
|
||||
int res = interrupt_handler();
|
||||
if (res) return res;
|
||||
if (has_lookahead()) return lookahead_pop();
|
||||
if (auto interrupt_evt = interrupt_handler()) {
|
||||
return *interrupt_evt;
|
||||
} else if (auto mc = lookahead_pop_evt()) {
|
||||
return *mc;
|
||||
}
|
||||
}
|
||||
|
||||
do_loop = true;
|
||||
} else {
|
||||
// The terminal has been closed. Save and exit.
|
||||
return R_EOF;
|
||||
// The terminal has been closed.
|
||||
return char_event_type_t::eof;
|
||||
}
|
||||
} else {
|
||||
// Assume we loop unless we see a character in stdin.
|
||||
@@ -133,15 +144,15 @@ static wint_t readb() {
|
||||
|
||||
if (ioport > 0 && FD_ISSET(ioport, &fdset)) {
|
||||
iothread_service_completion();
|
||||
if (has_lookahead()) {
|
||||
return lookahead_pop();
|
||||
if (auto mc = lookahead_pop_evt()) {
|
||||
return *mc;
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET(STDIN_FILENO, &fdset)) {
|
||||
if (read_blocked(0, arr, 1) != 1) {
|
||||
// The teminal has been closed. Save and exit.
|
||||
return R_EOF;
|
||||
// The teminal has been closed.
|
||||
return char_event_type_t::eof;
|
||||
}
|
||||
|
||||
// We read from stdin, so don't loop.
|
||||
@@ -173,63 +184,67 @@ void update_wait_on_escape_ms(const environment_t &vars) {
|
||||
}
|
||||
}
|
||||
|
||||
wchar_t input_common_readch(int timed) {
|
||||
if (!has_lookahead()) {
|
||||
if (timed) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0, &fds);
|
||||
struct timeval tm = {wait_on_escape_ms / 1000, 1000 * (wait_on_escape_ms % 1000)};
|
||||
int count = select(1, &fds, 0, 0, &tm);
|
||||
if (count <= 0) {
|
||||
return R_TIMEOUT;
|
||||
}
|
||||
char_event_t input_common_readch() {
|
||||
if (auto mc = lookahead_pop_evt()) {
|
||||
return *mc;
|
||||
}
|
||||
wchar_t res;
|
||||
mbstate_t state = {};
|
||||
while (1) {
|
||||
auto evt = readb();
|
||||
if (!evt.is_char()) {
|
||||
return evt;
|
||||
}
|
||||
|
||||
wchar_t res;
|
||||
mbstate_t state = {};
|
||||
|
||||
while (1) {
|
||||
wint_t b = readb();
|
||||
|
||||
if (b >= R_NULL && b < R_END_INPUT_FUNCTIONS) return b;
|
||||
|
||||
if (MB_CUR_MAX == 1) {
|
||||
// return (unsigned char)b; // single-byte locale, all values are legal
|
||||
return b; // single-byte locale, all values are legal
|
||||
}
|
||||
|
||||
char bb = b;
|
||||
size_t sz = std::mbrtowc(&res, &bb, 1, &state);
|
||||
|
||||
switch (sz) {
|
||||
case (size_t)(-1): {
|
||||
std::memset(&state, '\0', sizeof(state));
|
||||
debug(2, L"Illegal input");
|
||||
return R_NULL;
|
||||
}
|
||||
case (size_t)(-2): {
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
return 0;
|
||||
}
|
||||
default: { return res; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!timed) {
|
||||
while (has_lookahead() && lookahead_front() == R_TIMEOUT) lookahead_pop();
|
||||
if (!has_lookahead()) return input_common_readch(0);
|
||||
wint_t b = evt.get_char();
|
||||
if (MB_CUR_MAX == 1) {
|
||||
return b; // single-byte locale, all values are legal
|
||||
}
|
||||
|
||||
return lookahead_pop();
|
||||
char bb = b;
|
||||
size_t sz = std::mbrtowc(&res, &bb, 1, &state);
|
||||
|
||||
switch (sz) {
|
||||
case (size_t)(-1): {
|
||||
std::memset(&state, '\0', sizeof(state));
|
||||
debug(2, L"Illegal input");
|
||||
return char_event_type_t::check_exit;
|
||||
}
|
||||
case (size_t)(-2): {
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
return 0;
|
||||
}
|
||||
default: { return res; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void input_common_queue_ch(wint_t ch) { lookahead_push_back(ch); }
|
||||
char_event_t input_common_readch_timed(bool dequeue_timeouts) {
|
||||
char_event_t result{char_event_type_t::timeout};
|
||||
if (has_lookahead()) {
|
||||
result = lookahead_pop();
|
||||
} else {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(STDIN_FILENO, &fds);
|
||||
struct timeval tm = {wait_on_escape_ms / 1000, 1000 * (wait_on_escape_ms % 1000)};
|
||||
if (select(1, &fds, 0, 0, &tm) > 0) {
|
||||
result = input_common_readch();
|
||||
}
|
||||
}
|
||||
// If we got a timeout, either through dequeuing or creating, ensure it stays on the queue.
|
||||
if (result.is_timeout()) {
|
||||
if (!dequeue_timeouts) lookahead_push_front(char_event_type_t::timeout);
|
||||
return char_event_type_t::timeout;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void input_common_next_ch(wint_t ch) { lookahead_push_front(ch); }
|
||||
void input_common_queue_ch(char_event_t ch) { lookahead_push_back(ch); }
|
||||
|
||||
void input_common_next_ch(char_event_t ch) { lookahead_push_front(ch); }
|
||||
|
||||
void input_common_add_callback(std::function<void(void)> callback) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
|
||||
@@ -7,102 +7,168 @@
|
||||
#include <functional>
|
||||
|
||||
#include "common.h"
|
||||
#include "maybe.h"
|
||||
|
||||
enum {
|
||||
R_MIN = INPUT_COMMON_BASE,
|
||||
// R_NULL is sometimes returned by the input when a character was requested but none could be
|
||||
// delivered, or when an exception happened.
|
||||
R_NULL = R_MIN,
|
||||
R_EOF,
|
||||
|
||||
R_BEGINNING_OF_LINE,
|
||||
R_END_OF_LINE,
|
||||
R_FORWARD_CHAR,
|
||||
R_BACKWARD_CHAR,
|
||||
R_FORWARD_WORD,
|
||||
R_BACKWARD_WORD,
|
||||
R_FORWARD_BIGWORD,
|
||||
R_BACKWARD_BIGWORD,
|
||||
R_HISTORY_SEARCH_BACKWARD,
|
||||
R_HISTORY_SEARCH_FORWARD,
|
||||
R_DELETE_CHAR,
|
||||
R_BACKWARD_DELETE_CHAR,
|
||||
R_KILL_LINE,
|
||||
R_YANK,
|
||||
R_YANK_POP,
|
||||
R_COMPLETE,
|
||||
R_COMPLETE_AND_SEARCH,
|
||||
R_PAGER_TOGGLE_SEARCH,
|
||||
R_BEGINNING_OF_HISTORY,
|
||||
R_END_OF_HISTORY,
|
||||
R_BACKWARD_KILL_LINE,
|
||||
R_KILL_WHOLE_LINE,
|
||||
R_KILL_WORD,
|
||||
R_KILL_BIGWORD,
|
||||
R_BACKWARD_KILL_WORD,
|
||||
R_BACKWARD_KILL_PATH_COMPONENT,
|
||||
R_BACKWARD_KILL_BIGWORD,
|
||||
R_HISTORY_TOKEN_SEARCH_BACKWARD,
|
||||
R_HISTORY_TOKEN_SEARCH_FORWARD,
|
||||
R_SELF_INSERT,
|
||||
R_TRANSPOSE_CHARS,
|
||||
R_TRANSPOSE_WORDS,
|
||||
R_UPCASE_WORD,
|
||||
R_DOWNCASE_WORD,
|
||||
R_CAPITALIZE_WORD,
|
||||
R_VI_ARG_DIGIT,
|
||||
R_VI_DELETE_TO,
|
||||
R_EXECUTE,
|
||||
R_BEGINNING_OF_BUFFER,
|
||||
R_END_OF_BUFFER,
|
||||
R_REPAINT,
|
||||
R_FORCE_REPAINT,
|
||||
R_UP_LINE,
|
||||
R_DOWN_LINE,
|
||||
R_SUPPRESS_AUTOSUGGESTION,
|
||||
R_ACCEPT_AUTOSUGGESTION,
|
||||
R_BEGIN_SELECTION,
|
||||
R_SWAP_SELECTION_START_STOP,
|
||||
R_END_SELECTION,
|
||||
R_KILL_SELECTION,
|
||||
R_FORWARD_JUMP,
|
||||
R_BACKWARD_JUMP,
|
||||
R_FORWARD_JUMP_TILL,
|
||||
R_BACKWARD_JUMP_TILL,
|
||||
R_AND,
|
||||
R_CANCEL,
|
||||
R_REPEAT_JUMP,
|
||||
R_REVERSE_REPEAT_JUMP,
|
||||
|
||||
R_TIMEOUT, // we didn't get interactive input within wait_on_escape_ms
|
||||
|
||||
// The range of key codes for inputrc-style keyboard functions that are passed on to the caller
|
||||
// of input_read().
|
||||
R_BEGIN_INPUT_FUNCTIONS = R_BEGINNING_OF_LINE,
|
||||
R_END_INPUT_FUNCTIONS = R_REVERSE_REPEAT_JUMP + 1
|
||||
enum class readline_cmd_t {
|
||||
beginning_of_line,
|
||||
end_of_line,
|
||||
forward_char,
|
||||
backward_char,
|
||||
forward_word,
|
||||
backward_word,
|
||||
forward_bigword,
|
||||
backward_bigword,
|
||||
history_search_backward,
|
||||
history_search_forward,
|
||||
delete_char,
|
||||
backward_delete_char,
|
||||
kill_line,
|
||||
yank,
|
||||
yank_POP,
|
||||
complete,
|
||||
complete_AND_SEARCH,
|
||||
pager_toggle_search,
|
||||
beginning_of_history,
|
||||
end_of_history,
|
||||
backward_kill_line,
|
||||
kill_whole_line,
|
||||
kill_word,
|
||||
kill_bigword,
|
||||
backward_kill_word,
|
||||
backward_kill_path_component,
|
||||
backward_kill_bigword,
|
||||
history_token_search_backward,
|
||||
history_token_search_forward,
|
||||
self_insert,
|
||||
transpose_chars,
|
||||
transpose_words,
|
||||
upcase_word,
|
||||
downcase_word,
|
||||
capitalize_word,
|
||||
vi_arg_digit,
|
||||
vi_delete_to,
|
||||
execute,
|
||||
beginning_of_buffer,
|
||||
end_of_buffer,
|
||||
repaint,
|
||||
force_repaint,
|
||||
up_line,
|
||||
down_line,
|
||||
suppress_autosuggestion,
|
||||
accept_autosuggestion,
|
||||
begin_selection,
|
||||
swap_selection_start_stop,
|
||||
end_selection,
|
||||
kill_selection,
|
||||
forward_jump,
|
||||
backward_jump,
|
||||
forward_jump_till,
|
||||
backward_jump_till,
|
||||
func_and,
|
||||
cancel,
|
||||
repeat_jump,
|
||||
reverse_repeat_jump,
|
||||
};
|
||||
|
||||
/// Init the library.
|
||||
void input_common_init(int (*ih)());
|
||||
// The range of key codes for inputrc-style keyboard functions.
|
||||
enum {
|
||||
R_BEGIN_INPUT_FUNCTIONS = static_cast<int>(readline_cmd_t::beginning_of_line),
|
||||
R_END_INPUT_FUNCTIONS = static_cast<int>(readline_cmd_t::reverse_repeat_jump) + 1
|
||||
};
|
||||
|
||||
/// Represents an event on the character input stream.
|
||||
enum class char_event_type_t : uint8_t {
|
||||
/// A character was entered.
|
||||
charc,
|
||||
|
||||
/// A readline event.
|
||||
readline,
|
||||
|
||||
/// A timeout was hit.
|
||||
timeout,
|
||||
|
||||
/// end-of-file was reached.
|
||||
eof,
|
||||
|
||||
/// An event was handled internally, or an interrupt was received. Check to see if the reader
|
||||
/// loop should exit.
|
||||
check_exit,
|
||||
};
|
||||
|
||||
class char_event_t {
|
||||
union {
|
||||
/// Set if the type is charc.
|
||||
wchar_t c;
|
||||
|
||||
/// Set if the type is readline.
|
||||
readline_cmd_t rl;
|
||||
} v_{};
|
||||
|
||||
public:
|
||||
char_event_type_t type;
|
||||
|
||||
bool is_timeout() const { return type == char_event_type_t::timeout; }
|
||||
|
||||
bool is_char() const { return type == char_event_type_t::charc; }
|
||||
|
||||
bool is_eof() const { return type == char_event_type_t::eof; }
|
||||
|
||||
bool is_check_exit() const { return type == char_event_type_t::check_exit; }
|
||||
|
||||
bool is_readline() const { return type == char_event_type_t::readline; }
|
||||
|
||||
wchar_t get_char() const {
|
||||
assert(type == char_event_type_t::charc && "Not a char type");
|
||||
return v_.c;
|
||||
}
|
||||
|
||||
readline_cmd_t get_readline() const {
|
||||
assert(type == char_event_type_t::readline && "Not a readline type");
|
||||
return v_.rl;
|
||||
}
|
||||
|
||||
/* implicit */ char_event_t(wchar_t c) : type(char_event_type_t::charc) { v_.c = c; }
|
||||
|
||||
/* implicit */ char_event_t(readline_cmd_t rl) : type(char_event_type_t::readline) {
|
||||
v_.rl = rl;
|
||||
}
|
||||
|
||||
/* implicit */ char_event_t(char_event_type_t type) : type(type) {
|
||||
assert(type != char_event_type_t::charc && type != char_event_type_t::readline &&
|
||||
"Cannot create a char event with this constructor");
|
||||
}
|
||||
};
|
||||
|
||||
/// A type of function invoked on interrupt.
|
||||
/// \return the event which is to be returned to the reader loop, or none if VINTR is 0.
|
||||
using interrupt_func_t = maybe_t<char_event_t> (*)();
|
||||
|
||||
/// Init the library with an interrupt function.
|
||||
void input_common_init(interrupt_func_t func);
|
||||
|
||||
/// Adjust the escape timeout.
|
||||
class environment_t;
|
||||
void update_wait_on_escape_ms(const environment_t &vars);
|
||||
|
||||
/// Function used by input_readch to read bytes from stdin until enough bytes have been read to
|
||||
/// convert them to a wchar_t. Conversion is done using mbrtowc. If a character has previously been
|
||||
/// read and then 'unread' using \c input_common_unreadch, that character is returned. If timed is
|
||||
/// true, readch2 will wait at most WAIT_ON_ESCAPE milliseconds for a character to be available for
|
||||
/// reading before returning with the value R_EOF.
|
||||
wchar_t input_common_readch(int timed);
|
||||
/// read and then 'unread' using \c input_common_unreadch, that character is returned.
|
||||
/// This function never returns a timeout.
|
||||
char_event_t input_common_readch();
|
||||
|
||||
/// Like input_common_readch(), except it will wait at most WAIT_ON_ESCAPE milliseconds for a
|
||||
/// character to be available for reading.
|
||||
/// If \p dequeue_timeouts is set, remove any timeout from the queue; otherwise retain them.
|
||||
char_event_t input_common_readch_timed(bool dequeue_timeouts = false);
|
||||
|
||||
/// Enqueue a character or a readline function to the queue of unread characters that input_readch
|
||||
/// will return before actually reading from fd 0.
|
||||
void input_common_queue_ch(wint_t ch);
|
||||
void input_common_queue_ch(char_event_t ch);
|
||||
|
||||
/// Add a character or a readline function to the front of the queue of unread characters. This
|
||||
/// will be the first character returned by input_readch (unless this function is called more than
|
||||
/// once).
|
||||
void input_common_next_ch(wint_t ch);
|
||||
void input_common_next_ch(char_event_t ch);
|
||||
|
||||
/// Adds a callback to be invoked at the next turn of the "event loop." The callback function will
|
||||
/// be invoked and passed arg.
|
||||
|
||||
19
src/kill.cpp
19
src/kill.cpp
@@ -17,10 +17,11 @@
|
||||
typedef std::list<wcstring> kill_list_t;
|
||||
static kill_list_t kill_list;
|
||||
|
||||
void kill_add(const wcstring &str) {
|
||||
void kill_add(wcstring str) {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
if (str.empty()) return;
|
||||
kill_list.push_front(str);
|
||||
if (!str.empty()) {
|
||||
kill_list.push_front(std::move(str));
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove first match for specified string from circular list.
|
||||
@@ -35,19 +36,19 @@ void kill_replace(const wcstring &old, const wcstring &newv) {
|
||||
kill_add(newv);
|
||||
}
|
||||
|
||||
const wchar_t *kill_yank_rotate() {
|
||||
wcstring kill_yank_rotate() {
|
||||
ASSERT_IS_MAIN_THREAD();
|
||||
// Move the first element to the end.
|
||||
if (kill_list.empty()) {
|
||||
return NULL;
|
||||
return {};
|
||||
}
|
||||
kill_list.splice(kill_list.end(), kill_list, kill_list.begin());
|
||||
return kill_list.front().c_str();
|
||||
return kill_list.front();
|
||||
}
|
||||
|
||||
const wchar_t *kill_yank() {
|
||||
wcstring kill_yank() {
|
||||
if (kill_list.empty()) {
|
||||
return L"";
|
||||
return {};
|
||||
}
|
||||
return kill_list.front().c_str();
|
||||
return kill_list.front();
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
void kill_replace(const wcstring &old, const wcstring &newv);
|
||||
|
||||
/// Add a string to the top of the killring.
|
||||
void kill_add(const wcstring &str);
|
||||
void kill_add(wcstring str);
|
||||
|
||||
/// Rotate the killring.
|
||||
const wchar_t *kill_yank_rotate();
|
||||
wcstring kill_yank_rotate();
|
||||
|
||||
/// Paste from the killring.
|
||||
const wchar_t *kill_yank();
|
||||
wcstring kill_yank();
|
||||
|
||||
#endif
|
||||
|
||||
@@ -203,6 +203,12 @@ class maybe_t : private maybe_detail::conditionally_copyable_t<T> {
|
||||
}
|
||||
|
||||
bool operator!=(const maybe_t &rhs) const { return !(*this == rhs); }
|
||||
|
||||
bool operator==(const T &rhs) const { return this->has_value() && this->value() == rhs; }
|
||||
|
||||
bool operator!=(const T &rhs) const { return !(*this == rhs); }
|
||||
|
||||
~maybe_t() { reset(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
1803
src/reader.cpp
1803
src/reader.cpp
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user