2016-05-01 12:31:25 +08:00
|
|
|
// Functions for reading a character of input from stdin.
|
2005-09-20 21:26:39 +08:00
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <wchar.h>
|
2016-11-27 08:35:48 +08:00
|
|
|
#include <wctype.h>
|
2006-01-19 20:22:07 +08:00
|
|
|
#if HAVE_TERM_H
|
2018-02-04 16:59:37 +08:00
|
|
|
#include <curses.h>
|
2005-09-20 21:26:39 +08:00
|
|
|
#include <term.h>
|
2006-01-19 20:22:07 +08:00
|
|
|
#elif HAVE_NCURSES_TERM_H
|
|
|
|
#include <ncurses/term.h>
|
|
|
|
#endif
|
2018-02-04 16:59:37 +08:00
|
|
|
#include <termios.h>
|
2016-11-27 08:35:48 +08:00
|
|
|
|
2016-04-21 14:00:54 +08:00
|
|
|
#include <algorithm>
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
2016-05-01 12:31:25 +08:00
|
|
|
#include <vector>
|
2005-09-20 21:26:39 +08:00
|
|
|
|
|
|
|
#include "common.h"
|
|
|
|
#include "env.h"
|
2005-11-25 22:18:26 +08:00
|
|
|
#include "event.h"
|
2016-05-01 12:31:25 +08:00
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
|
|
|
#include "input.h"
|
|
|
|
#include "input_common.h"
|
2015-07-25 23:14:25 +08:00
|
|
|
#include "io.h"
|
2016-05-01 12:31:25 +08:00
|
|
|
#include "parser.h"
|
|
|
|
#include "proc.h"
|
|
|
|
#include "reader.h"
|
|
|
|
#include "signal.h" // IWYU pragma: keep
|
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
2007-09-26 00:14:47 +08:00
|
|
|
|
2014-01-22 17:00:44 +08:00
|
|
|
#define MAX_INPUT_FUNCTION_ARGS 20
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Struct representing a keybinding. Returned by input_get_mappings.
|
|
|
|
struct input_mapping_t {
|
|
|
|
/// Character sequence which generates this event.
|
|
|
|
wcstring seq;
|
|
|
|
/// Commands that should be evaluated by this mapping.
|
|
|
|
wcstring_list_t commands;
|
|
|
|
/// We wish to preserve the user-specified order. This is just an incrementing value.
|
2014-02-13 04:49:32 +08:00
|
|
|
unsigned int specification_order;
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Mode in which this command should be evaluated.
|
|
|
|
wcstring mode;
|
|
|
|
/// New mode that should be switched to after command evaluation.
|
|
|
|
wcstring sets_mode;
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2018-02-19 10:39:03 +08:00
|
|
|
input_mapping_t(wcstring s, std::vector<wcstring> c, wcstring m,
|
|
|
|
wcstring sm)
|
|
|
|
: seq(std::move(s)), commands(std::move(c)), mode(std::move(m)), sets_mode(std::move(sm)) {
|
2016-11-03 12:54:57 +08:00
|
|
|
static unsigned int s_last_input_map_spec_order = 0;
|
|
|
|
specification_order = ++s_last_input_map_spec_order;
|
2014-02-13 04:49:32 +08:00
|
|
|
}
|
2012-01-01 07:57:30 +08:00
|
|
|
};
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// A struct representing the mapping from a terminfo key name to a terminfo character sequence.
|
|
|
|
struct terminfo_mapping_t {
|
|
|
|
const wchar_t *name; // name of key
|
|
|
|
const char *seq; // character sequence generated on keypress
|
2012-02-08 15:15:32 +08:00
|
|
|
};
|
2007-09-26 00:14:47 +08:00
|
|
|
|
2018-01-30 11:15:16 +08:00
|
|
|
static constexpr size_t input_function_count = R_END_INPUT_FUNCTIONS - R_BEGIN_INPUT_FUNCTIONS;
|
|
|
|
|
|
|
|
/// 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;
|
|
|
|
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"},
|
2018-01-31 01:39:04 +08:00
|
|
|
{R_PAGER_TOGGLE_SEARCH, L"pager-toggle-search"},
|
2018-01-30 11:15:16 +08:00
|
|
|
{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_AND, L"and"},
|
|
|
|
{R_CANCEL, L"cancel"}};
|
|
|
|
|
|
|
|
static_assert(sizeof(input_function_metadata) / sizeof(input_function_metadata[0]) ==
|
|
|
|
input_function_count,
|
|
|
|
"input_function_metadata size mismatch with input_common. Did you forget to update "
|
|
|
|
"input_function_metadata?");
|
2016-05-01 12:31:25 +08:00
|
|
|
|
|
|
|
wcstring describe_char(wint_t c) {
|
2018-01-30 11:15:16 +08:00
|
|
|
if (c >= R_BEGIN_INPUT_FUNCTIONS && c < R_END_INPUT_FUNCTIONS) {
|
|
|
|
size_t idx = c - R_BEGIN_INPUT_FUNCTIONS;
|
|
|
|
return format_string(L"%02x (%ls)", c, input_function_metadata[idx].name);
|
2014-01-22 08:08:35 +08:00
|
|
|
}
|
|
|
|
return format_string(L"%02x", c);
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Mappings for the current input mode.
|
2012-02-08 15:15:32 +08:00
|
|
|
static std::vector<input_mapping_t> mapping_list;
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Terminfo map list.
|
2012-02-08 15:15:32 +08:00
|
|
|
static std::vector<terminfo_mapping_t> terminfo_mappings;
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
#define TERMINFO_ADD(key) \
|
|
|
|
{ (L## #key) + 4, key }
|
2012-02-08 15:15:32 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// List of all terminfo mappings.
|
2012-02-08 15:15:32 +08:00
|
|
|
static std::vector<terminfo_mapping_t> mappings;
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2017-07-16 12:21:24 +08:00
|
|
|
/// Set to true when the input subsystem has been initialized.
|
2017-02-16 12:09:26 +08:00
|
|
|
bool input_initialized = false;
|
2006-03-10 21:38:09 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Initialize terminfo.
|
2017-02-16 12:09:26 +08:00
|
|
|
static void init_input_terminfo();
|
2012-02-08 15:15:32 +08:00
|
|
|
|
2014-03-31 04:13:35 +08:00
|
|
|
static wchar_t input_function_args[MAX_INPUT_FUNCTION_ARGS];
|
|
|
|
static bool input_function_status;
|
|
|
|
static int input_function_args_index = 0;
|
2014-01-22 17:00:44 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Return the current bind mode.
|
|
|
|
wcstring input_get_bind_mode() {
|
2017-08-28 15:25:41 +08:00
|
|
|
auto mode = env_get(FISH_BIND_MODE_VAR);
|
|
|
|
return mode ? mode->as_string() : DEFAULT_BIND_MODE;
|
2013-12-31 08:52:41 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Set the current bind mode.
|
|
|
|
void input_set_bind_mode(const wcstring &bm) {
|
2016-09-05 06:54:25 +08:00
|
|
|
// Only set this if it differs to not execute variable handlers all the time.
|
2017-02-01 06:49:56 +08:00
|
|
|
// modes may not be empty - empty is a sentinel value meaning to not change the mode
|
2017-02-08 13:52:35 +08:00
|
|
|
assert(!bm.empty());
|
2016-09-05 06:54:25 +08:00
|
|
|
if (input_get_bind_mode() != bm.c_str()) {
|
2017-08-06 06:08:39 +08:00
|
|
|
env_set_one(FISH_BIND_MODE_VAR, ENV_GLOBAL, bm);
|
2016-09-05 06:54:25 +08:00
|
|
|
}
|
2013-12-31 08:52:41 +08:00
|
|
|
}
|
2006-07-24 04:52:03 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Returns the arity of a given input function.
|
2016-10-23 11:32:25 +08:00
|
|
|
static int input_function_arity(int function) {
|
|
|
|
return (function == R_FORWARD_JUMP || function == R_BACKWARD_JUMP) ? 1 : 0;
|
2014-01-22 17:00:44 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Sets the return status of the most recently executed input function.
|
|
|
|
void input_function_set_status(bool status) { input_function_status = status; }
|
2014-01-23 17:23:04 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Helper function to compare the lengths of sequences.
|
|
|
|
static bool length_is_greater_than(const input_mapping_t &m1, const input_mapping_t &m2) {
|
2014-02-13 04:49:32 +08:00
|
|
|
return m1.seq.size() > m2.seq.size();
|
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
static bool specification_order_is_less_than(const input_mapping_t &m1, const input_mapping_t &m2) {
|
2014-02-13 04:49:32 +08:00
|
|
|
return m1.specification_order < m2.specification_order;
|
|
|
|
}
|
2006-07-24 04:52:03 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Inserts an input mapping at the correct position. We sort them in descending order by length, so
|
|
|
|
/// that we test longer sequences first.
|
|
|
|
static void input_mapping_insert_sorted(const input_mapping_t &new_mapping) {
|
|
|
|
std::vector<input_mapping_t>::iterator loc = std::lower_bound(
|
|
|
|
mapping_list.begin(), mapping_list.end(), new_mapping, length_is_greater_than);
|
2014-02-13 04:49:32 +08:00
|
|
|
mapping_list.insert(loc, new_mapping);
|
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Adds an input mapping.
|
|
|
|
void input_mapping_add(const wchar_t *sequence, const wchar_t *const *commands, size_t commands_len,
|
|
|
|
const wchar_t *mode, const wchar_t *sets_mode) {
|
|
|
|
CHECK(sequence, );
|
|
|
|
CHECK(commands, );
|
|
|
|
CHECK(mode, );
|
|
|
|
CHECK(sets_mode, );
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2017-03-14 11:19:08 +08:00
|
|
|
// debug( 0, L"Add mapping from %ls to %ls in mode %ls", escape_string(sequence,
|
|
|
|
// ESCAPE_ALL).c_str(),
|
|
|
|
// escape_string(command, ESCAPE_ALL).c_str(), mode);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
// Remove existing mappings with this sequence.
|
2014-03-31 04:13:35 +08:00
|
|
|
const wcstring_list_t commands_vector(commands, commands + commands_len);
|
2014-01-01 07:11:32 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
for (size_t i = 0; i < mapping_list.size(); i++) {
|
2012-11-19 08:30:30 +08:00
|
|
|
input_mapping_t &m = mapping_list.at(i);
|
2016-05-01 12:31:25 +08:00
|
|
|
if (m.seq == sequence && m.mode == mode) {
|
2014-01-01 07:11:32 +08:00
|
|
|
m.commands = commands_vector;
|
2014-01-15 18:39:19 +08:00
|
|
|
m.sets_mode = sets_mode;
|
2012-11-19 08:30:30 +08:00
|
|
|
return;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
// Add a new mapping, using the next order.
|
2014-03-31 04:13:35 +08:00
|
|
|
const input_mapping_t new_mapping = input_mapping_t(sequence, commands_vector, mode, sets_mode);
|
|
|
|
input_mapping_insert_sorted(new_mapping);
|
2014-01-01 07:11:32 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
void input_mapping_add(const wchar_t *sequence, const wchar_t *command, const wchar_t *mode,
|
|
|
|
const wchar_t *sets_mode) {
|
2014-01-15 18:39:19 +08:00
|
|
|
input_mapping_add(sequence, &command, 1, mode, sets_mode);
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Handle interruptions to key reading by reaping finshed jobs and propagating the interrupt to the
|
|
|
|
/// reader.
|
|
|
|
static int interrupt_handler() {
|
|
|
|
// Fire any pending events.
|
2012-11-19 08:30:30 +08:00
|
|
|
event_fire(NULL);
|
2016-05-01 12:31:25 +08:00
|
|
|
// Reap stray processes, including printing exit status messages.
|
|
|
|
if (job_reap(1)) reader_repaint_needed();
|
|
|
|
// Tell the reader an event occured.
|
|
|
|
if (reader_reading_interrupted()) {
|
2016-12-20 08:11:36 +08:00
|
|
|
return shell_modes.c_cc[VINTR];
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
return R_NULL;
|
2007-09-26 00:14:47 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2017-02-16 12:09:26 +08:00
|
|
|
/// Set up arrays used by readch to detect escape sequences for special keys and perform related
|
|
|
|
/// initializations for our input subsystem.
|
|
|
|
void init_input() {
|
2012-11-19 08:30:30 +08:00
|
|
|
input_common_init(&interrupt_handler);
|
2017-02-16 12:09:26 +08:00
|
|
|
init_input_terminfo();
|
2012-03-06 02:44:08 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
// If we have no keybindings, add a few simple defaults.
|
|
|
|
if (mapping_list.empty()) {
|
2012-11-19 08:30:30 +08:00
|
|
|
input_mapping_add(L"", L"self-insert");
|
|
|
|
input_mapping_add(L"\n", L"execute");
|
2015-12-01 07:44:51 +08:00
|
|
|
input_mapping_add(L"\r", L"execute");
|
2012-11-19 08:30:30 +08:00
|
|
|
input_mapping_add(L"\t", L"complete");
|
|
|
|
input_mapping_add(L"\x3", L"commandline \"\"");
|
|
|
|
input_mapping_add(L"\x4", L"exit");
|
|
|
|
input_mapping_add(L"\x5", L"bind");
|
|
|
|
}
|
|
|
|
|
2017-02-16 12:09:26 +08:00
|
|
|
input_initialized = true;
|
2007-09-26 00:14:47 +08:00
|
|
|
}
|
2005-10-02 21:40:46 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
void input_destroy() {
|
2017-02-16 12:09:26 +08:00
|
|
|
if (!input_initialized) return;
|
|
|
|
input_initialized = false;
|
2012-11-19 08:30:30 +08:00
|
|
|
input_common_destroy();
|
2005-10-02 21:40:46 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
void input_function_push_arg(wchar_t arg) {
|
2014-01-22 17:00:44 +08:00
|
|
|
input_function_args[input_function_args_index++] = arg;
|
|
|
|
}
|
2014-01-01 07:11:32 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
wchar_t input_function_pop_arg() { return input_function_args[--input_function_args_index]; }
|
2014-01-22 17:00:44 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
void input_function_push_args(int code) {
|
2014-01-22 17:00:44 +08:00
|
|
|
int arity = input_function_arity(code);
|
2016-02-03 10:13:40 +08:00
|
|
|
std::vector<wchar_t> skipped;
|
2015-09-04 03:12:56 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
for (int i = 0; i < arity; i++) {
|
2015-09-04 03:12:56 +08:00
|
|
|
wchar_t arg;
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
// Skip and queue up any function codes. See issue #2357.
|
2018-01-30 03:52:55 +08:00
|
|
|
while ((arg = input_common_readch(0)) >= R_BEGIN_INPUT_FUNCTIONS &&
|
|
|
|
arg < R_END_INPUT_FUNCTIONS) {
|
2016-02-03 10:13:40 +08:00
|
|
|
skipped.push_back(arg);
|
2015-09-04 03:12:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
input_function_push_arg(arg);
|
2014-01-22 17:00:44 +08:00
|
|
|
}
|
2015-09-04 03:12:56 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
// Push the function codes back into the input stream.
|
2016-02-03 10:13:40 +08:00
|
|
|
size_t idx = skipped.size();
|
2016-05-01 12:31:25 +08:00
|
|
|
while (idx--) {
|
2016-02-03 10:13:40 +08:00
|
|
|
input_common_next_ch(skipped.at(idx));
|
|
|
|
}
|
2014-01-22 17:00:44 +08:00
|
|
|
}
|
2014-01-01 07:11:32 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Perform the action of the specified binding. allow_commands controls whether fish commands
|
|
|
|
/// should be executed, or should be deferred until later.
|
|
|
|
static void input_mapping_execute(const input_mapping_t &m, bool allow_commands) {
|
|
|
|
// has_functions: there are functions that need to be put on the input queue
|
|
|
|
// has_commands: there are shell commands that need to be evaluated
|
2015-04-17 21:49:52 +08:00
|
|
|
bool has_commands = false, has_functions = false;
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
for (wcstring_list_t::const_iterator it = m.commands.begin(), end = m.commands.end(); it != end;
|
|
|
|
++it) {
|
2015-08-17 09:45:42 +08:00
|
|
|
if (input_function_get_code(*it) != INPUT_CODE_NONE)
|
2015-04-17 21:49:52 +08:00
|
|
|
has_functions = true;
|
|
|
|
else
|
|
|
|
has_commands = true;
|
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
// !has_functions && !has_commands: only set bind mode
|
|
|
|
if (!has_commands && !has_functions) {
|
2017-02-01 06:49:56 +08:00
|
|
|
if (!m.sets_mode.empty()) input_set_bind_mode(m.sets_mode);
|
2015-04-17 21:49:52 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
if (has_commands && !allow_commands) {
|
|
|
|
// We don't want to run commands yet. Put the characters back and return R_NULL.
|
|
|
|
for (wcstring::const_reverse_iterator it = m.seq.rbegin(), end = m.seq.rend(); it != end;
|
|
|
|
++it) {
|
2015-04-17 21:49:52 +08:00
|
|
|
input_common_next_ch(*it);
|
2014-03-31 04:13:35 +08:00
|
|
|
}
|
2015-04-17 21:49:52 +08:00
|
|
|
input_common_next_ch(R_NULL);
|
2016-05-01 12:31:25 +08:00
|
|
|
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) {
|
2015-04-17 21:49:52 +08:00
|
|
|
wchar_t code = input_function_get_code(*it);
|
|
|
|
input_function_push_args(code);
|
2015-04-06 02:07:17 +08:00
|
|
|
input_common_next_ch(code);
|
2014-03-31 04:13:35 +08:00
|
|
|
}
|
2016-05-01 12:31:25 +08:00
|
|
|
} else if (has_commands && !has_functions) {
|
|
|
|
// Execute all commands.
|
|
|
|
//
|
|
|
|
// FIXME(snnw): if commands add stuff to input queue (e.g. commandline -f execute), we won't
|
|
|
|
// see that until all other commands have also been run.
|
2015-04-17 21:49:52 +08:00
|
|
|
int last_status = proc_get_last_status();
|
2016-05-01 12:31:25 +08:00
|
|
|
for (wcstring_list_t::const_iterator it = m.commands.begin(), end = m.commands.end();
|
|
|
|
it != end; ++it) {
|
2015-04-17 21:49:52 +08:00
|
|
|
parser_t::principal_parser().eval(it->c_str(), io_chain_t(), TOP);
|
Fix `commandline` behavior in bind functions
When a key is bound to a fish function, if that function invokes
`commandline`, it gets a stale copy of the commandline. This is because
any keys passed to `self-insert` (the default) don't actually get added
to the commandline until a special character is processed, such as the
R_NULL that gets returned after running a binding for a fish command.
To fix this, don't allow fish commands to be run for bindings if we're
processing more than one key. When a key wants to invoke a fish command,
instead we push the invocation sequence back onto the input, followed by
an R_NULL, and return. This causes the input loop to break out and
update the commandline. When it starts up again, it will re-process the
keys and invoke the fish command.
This is primarily an issue with pasting text that includes bound keys in
it. Typed text is slow enough that fish will update the commandline
between each character.
---
I don't know of any way to write a test for this, but the issue can be
reproduced as follows:
> bind _ 'commandline -i _'
This binds _ to a command that inserts _. Typing the following works:
> echo wat_is_it
But if you copy that line and paste it instead of typing it, the end
result looks like
> _echo wat_isit
With this fix in place, the pasted output correctly matches the typed
output.
2014-08-23 05:02:29 +08:00
|
|
|
}
|
2015-04-17 21:49:52 +08:00
|
|
|
proc_set_last_status(last_status);
|
|
|
|
input_common_next_ch(R_NULL);
|
2016-05-01 12:31:25 +08:00
|
|
|
} else {
|
|
|
|
// Invalid binding, mixed commands and functions. We would need to execute these one by
|
|
|
|
// one.
|
2015-04-17 21:49:52 +08:00
|
|
|
input_common_next_ch(R_NULL);
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2017-02-01 06:49:56 +08:00
|
|
|
// Empty bind mode indicates to not reset the mode (#2871)
|
|
|
|
if (!m.sets_mode.empty()) input_set_bind_mode(m.sets_mode);
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// Try reading the specified function mapping.
|
|
|
|
static bool input_mapping_is_match(const input_mapping_t &m) {
|
2017-01-27 09:28:46 +08:00
|
|
|
wchar_t c = 0;
|
2012-11-19 08:30:30 +08:00
|
|
|
int j;
|
|
|
|
|
2017-03-31 21:21:29 +08:00
|
|
|
debug(4, L"trying to match mapping %ls", escape_string(m.seq.c_str(), ESCAPE_ALL).c_str());
|
2012-01-01 07:57:30 +08:00
|
|
|
const wchar_t *str = m.seq.c_str();
|
2016-05-01 12:31:25 +08:00
|
|
|
for (j = 0; str[j] != L'\0'; j++) {
|
2013-12-31 23:16:28 +08:00
|
|
|
bool timed = (j > 0 && iswcntrl(str[0]));
|
2013-12-31 21:53:29 +08:00
|
|
|
|
2012-06-03 06:43:18 +08:00
|
|
|
c = input_common_readch(timed);
|
2016-05-01 12:31:25 +08:00
|
|
|
if (str[j] != c) {
|
2012-06-03 06:43:18 +08:00
|
|
|
break;
|
2013-12-31 21:53:29 +08:00
|
|
|
}
|
2012-06-03 06:43:18 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
if (str[j] == L'\0') {
|
2017-03-14 11:19:08 +08:00
|
|
|
// debug(0, L"matched mapping %ls (%ls)\n", escape_string(m.seq.c_str(),
|
|
|
|
// ESCAPE_ALL).c_str(),
|
2016-05-01 12:31:25 +08:00
|
|
|
// m.command.c_str());
|
|
|
|
// We matched the entire sequence.
|
2014-01-01 07:11:32 +08:00
|
|
|
return true;
|
2016-05-05 06:19:47 +08:00
|
|
|
}
|
|
|
|
|
2016-07-12 11:31:30 +08:00
|
|
|
// Reinsert the chars we read to be read again since we didn't match the bind sequence (i.e.,
|
|
|
|
// the input mapping).
|
2016-05-05 06:19:47 +08:00
|
|
|
input_common_next_ch(c);
|
|
|
|
for (int k = j - 1; k >= 0; k--) {
|
|
|
|
input_common_next_ch(m.seq[k]);
|
2012-01-01 07:57:30 +08:00
|
|
|
}
|
2013-12-31 23:16:28 +08:00
|
|
|
|
2014-01-01 07:11:32 +08:00
|
|
|
return false;
|
2007-09-26 00:14:47 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
void input_queue_ch(wint_t ch) { input_common_queue_ch(ch); }
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
static void input_mapping_execute_matching_or_generic(bool allow_commands) {
|
2014-01-01 07:11:32 +08:00
|
|
|
const input_mapping_t *generic = NULL;
|
|
|
|
|
2014-03-31 04:13:35 +08:00
|
|
|
const wcstring bind_mode = input_get_bind_mode();
|
2014-01-15 19:04:52 +08:00
|
|
|
|
2016-10-10 05:36:08 +08:00
|
|
|
for (size_t i = 0; i < mapping_list.size(); i++) {
|
2014-01-01 07:11:32 +08:00
|
|
|
const input_mapping_t &m = mapping_list.at(i);
|
|
|
|
|
2017-03-14 11:19:08 +08:00
|
|
|
// debug(0, L"trying mapping (%ls,%ls,%ls)\n", escape_string(m.seq.c_str(),
|
|
|
|
// ESCAPE_ALL).c_str(),
|
2014-01-15 18:39:19 +08:00
|
|
|
// m.mode.c_str(), m.sets_mode.c_str());
|
2014-04-01 01:01:39 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
if (m.mode != bind_mode) {
|
|
|
|
// debug(0, L"skipping mapping because mode %ls != %ls\n", m.mode.c_str(),
|
|
|
|
// input_get_bind_mode().c_str());
|
2014-01-01 07:11:32 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
if (m.seq.length() == 0) {
|
2014-01-01 07:11:32 +08:00
|
|
|
generic = &m;
|
2016-05-01 12:31:25 +08:00
|
|
|
} else if (input_mapping_is_match(m)) {
|
Fix `commandline` behavior in bind functions
When a key is bound to a fish function, if that function invokes
`commandline`, it gets a stale copy of the commandline. This is because
any keys passed to `self-insert` (the default) don't actually get added
to the commandline until a special character is processed, such as the
R_NULL that gets returned after running a binding for a fish command.
To fix this, don't allow fish commands to be run for bindings if we're
processing more than one key. When a key wants to invoke a fish command,
instead we push the invocation sequence back onto the input, followed by
an R_NULL, and return. This causes the input loop to break out and
update the commandline. When it starts up again, it will re-process the
keys and invoke the fish command.
This is primarily an issue with pasting text that includes bound keys in
it. Typed text is slow enough that fish will update the commandline
between each character.
---
I don't know of any way to write a test for this, but the issue can be
reproduced as follows:
> bind _ 'commandline -i _'
This binds _ to a command that inserts _. Typing the following works:
> echo wat_is_it
But if you copy that line and paste it instead of typing it, the end
result looks like
> _echo wat_isit
With this fix in place, the pasted output correctly matches the typed
output.
2014-08-23 05:02:29 +08:00
|
|
|
input_mapping_execute(m, allow_commands);
|
2014-01-01 07:11:32 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
if (generic) {
|
Fix `commandline` behavior in bind functions
When a key is bound to a fish function, if that function invokes
`commandline`, it gets a stale copy of the commandline. This is because
any keys passed to `self-insert` (the default) don't actually get added
to the commandline until a special character is processed, such as the
R_NULL that gets returned after running a binding for a fish command.
To fix this, don't allow fish commands to be run for bindings if we're
processing more than one key. When a key wants to invoke a fish command,
instead we push the invocation sequence back onto the input, followed by
an R_NULL, and return. This causes the input loop to break out and
update the commandline. When it starts up again, it will re-process the
keys and invoke the fish command.
This is primarily an issue with pasting text that includes bound keys in
it. Typed text is slow enough that fish will update the commandline
between each character.
---
I don't know of any way to write a test for this, but the issue can be
reproduced as follows:
> bind _ 'commandline -i _'
This binds _ to a command that inserts _. Typing the following works:
> echo wat_is_it
But if you copy that line and paste it instead of typing it, the end
result looks like
> _echo wat_isit
With this fix in place, the pasted output correctly matches the typed
output.
2014-08-23 05:02:29 +08:00
|
|
|
input_mapping_execute(*generic, allow_commands);
|
2016-05-01 12:31:25 +08:00
|
|
|
} else {
|
2016-07-12 11:31:30 +08:00
|
|
|
debug(2, L"no generic found, ignoring char...");
|
2014-01-01 07:11:32 +08:00
|
|
|
wchar_t c = input_common_readch(0);
|
2016-07-12 11:31:30 +08:00
|
|
|
if (c == R_EOF) {
|
|
|
|
input_common_next_ch(c);
|
|
|
|
}
|
2014-01-01 07:11:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
/// 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() {
|
2015-07-20 15:10:58 +08:00
|
|
|
std::vector<wchar_t> functions_to_put_back;
|
|
|
|
wchar_t char_to_return;
|
2016-05-01 12:31:25 +08:00
|
|
|
for (;;) {
|
2015-07-20 15:10:58 +08:00
|
|
|
char_to_return = input_common_readch(0);
|
2018-01-30 03:52:55 +08:00
|
|
|
bool is_readline_function =
|
|
|
|
(char_to_return >= R_BEGIN_INPUT_FUNCTIONS && char_to_return < R_END_INPUT_FUNCTIONS);
|
2016-05-01 12:31:25 +08:00
|
|
|
// 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) {
|
2015-07-20 15:10:58 +08:00
|
|
|
break;
|
|
|
|
}
|
2016-05-01 12:31:25 +08:00
|
|
|
// This is a readline function; save it off for later re-enqueing and try again.
|
2015-07-20 15:10:58 +08:00
|
|
|
functions_to_put_back.push_back(char_to_return);
|
|
|
|
}
|
2016-05-01 12:31:25 +08:00
|
|
|
// Restore any readline functions, in reverse to preserve their original order.
|
2015-07-20 15:10:58 +08:00
|
|
|
size_t idx = functions_to_put_back.size();
|
2016-05-01 12:31:25 +08:00
|
|
|
while (idx--) {
|
2015-07-20 15:10:58 +08:00
|
|
|
input_common_next_ch(functions_to_put_back.at(idx));
|
|
|
|
}
|
|
|
|
return char_to_return;
|
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
wint_t input_readch(bool allow_commands) {
|
2012-11-19 08:30:30 +08:00
|
|
|
CHECK_BLOCK(R_NULL);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
// Clear the interrupted flag.
|
2013-01-21 05:23:27 +08:00
|
|
|
reader_reset_interrupted();
|
2016-05-01 12:31:25 +08:00
|
|
|
// Search for sequence in mapping tables.
|
|
|
|
while (1) {
|
2014-01-01 07:11:32 +08:00
|
|
|
wchar_t c = input_common_readch(0);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2018-01-30 03:52:55 +08:00
|
|
|
if (c >= R_BEGIN_INPUT_FUNCTIONS && c < R_END_INPUT_FUNCTIONS) {
|
2016-05-01 12:31:25 +08:00
|
|
|
switch (c) {
|
|
|
|
case R_EOF: // if it's closed, then just return
|
2014-04-01 01:01:39 +08:00
|
|
|
{
|
2014-05-26 06:22:21 +08:00
|
|
|
return R_EOF;
|
2014-04-01 01:01:39 +08:00
|
|
|
}
|
2016-05-01 12:31:25 +08:00
|
|
|
case R_SELF_INSERT: {
|
|
|
|
// Issue #1595: ensure we only insert characters, not readline functions. The
|
|
|
|
// common case is that this will be empty.
|
2015-07-20 15:10:58 +08:00
|
|
|
return input_read_characters_only();
|
2014-04-01 01:01:39 +08:00
|
|
|
}
|
2016-05-01 12:31:25 +08:00
|
|
|
case R_AND: {
|
|
|
|
if (input_function_status) {
|
2014-04-01 01:01:39 +08:00
|
|
|
return input_readch();
|
|
|
|
}
|
2016-11-03 05:22:34 +08:00
|
|
|
c = input_common_readch(0);
|
2018-01-30 03:52:55 +08:00
|
|
|
while (c >= R_BEGIN_INPUT_FUNCTIONS && c < R_END_INPUT_FUNCTIONS) {
|
2016-11-03 05:22:34 +08:00
|
|
|
c = input_common_readch(0);
|
2016-05-05 06:19:47 +08:00
|
|
|
}
|
|
|
|
input_common_next_ch(c);
|
|
|
|
return input_readch();
|
2014-04-01 01:01:39 +08:00
|
|
|
}
|
2016-05-01 12:31:25 +08:00
|
|
|
default: { return c; }
|
2014-04-01 01:01:39 +08:00
|
|
|
}
|
2016-05-01 12:31:25 +08:00
|
|
|
} else {
|
2015-04-06 02:07:17 +08:00
|
|
|
input_common_next_ch(c);
|
Fix `commandline` behavior in bind functions
When a key is bound to a fish function, if that function invokes
`commandline`, it gets a stale copy of the commandline. This is because
any keys passed to `self-insert` (the default) don't actually get added
to the commandline until a special character is processed, such as the
R_NULL that gets returned after running a binding for a fish command.
To fix this, don't allow fish commands to be run for bindings if we're
processing more than one key. When a key wants to invoke a fish command,
instead we push the invocation sequence back onto the input, followed by
an R_NULL, and return. This causes the input loop to break out and
update the commandline. When it starts up again, it will re-process the
keys and invoke the fish command.
This is primarily an issue with pasting text that includes bound keys in
it. Typed text is slow enough that fish will update the commandline
between each character.
---
I don't know of any way to write a test for this, but the issue can be
reproduced as follows:
> bind _ 'commandline -i _'
This binds _ to a command that inserts _. Typing the following works:
> echo wat_is_it
But if you copy that line and paste it instead of typing it, the end
result looks like
> _echo wat_isit
With this fix in place, the pasted output correctly matches the typed
output.
2014-08-23 05:02:29 +08:00
|
|
|
input_mapping_execute_matching_or_generic(allow_commands);
|
2016-05-01 12:31:25 +08:00
|
|
|
// Regarding allow_commands, we're in a loop, but if a fish command
|
Fix `commandline` behavior in bind functions
When a key is bound to a fish function, if that function invokes
`commandline`, it gets a stale copy of the commandline. This is because
any keys passed to `self-insert` (the default) don't actually get added
to the commandline until a special character is processed, such as the
R_NULL that gets returned after running a binding for a fish command.
To fix this, don't allow fish commands to be run for bindings if we're
processing more than one key. When a key wants to invoke a fish command,
instead we push the invocation sequence back onto the input, followed by
an R_NULL, and return. This causes the input loop to break out and
update the commandline. When it starts up again, it will re-process the
keys and invoke the fish command.
This is primarily an issue with pasting text that includes bound keys in
it. Typed text is slow enough that fish will update the commandline
between each character.
---
I don't know of any way to write a test for this, but the issue can be
reproduced as follows:
> bind _ 'commandline -i _'
This binds _ to a command that inserts _. Typing the following works:
> echo wat_is_it
But if you copy that line and paste it instead of typing it, the end
result looks like
> _echo wat_isit
With this fix in place, the pasted output correctly matches the typed
output.
2014-08-23 05:02:29 +08:00
|
|
|
// is executed, R_NULL is unread, so the next pass through the loop
|
|
|
|
// we'll break out and return it.
|
2012-07-07 07:25:10 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2007-09-26 00:14:47 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
std::vector<input_mapping_name_t> input_mapping_get_names() {
|
|
|
|
// Sort the mappings by the user specification order, so we can return them in the same order
|
|
|
|
// that the user specified them in.
|
2014-02-13 04:49:32 +08:00
|
|
|
std::vector<input_mapping_t> local_list = mapping_list;
|
|
|
|
std::sort(local_list.begin(), local_list.end(), specification_order_is_less_than);
|
2014-09-23 12:04:06 +08:00
|
|
|
std::vector<input_mapping_name_t> result;
|
2014-03-31 04:13:35 +08:00
|
|
|
result.reserve(local_list.size());
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
for (size_t i = 0; i < local_list.size(); i++) {
|
2014-02-13 04:49:32 +08:00
|
|
|
const input_mapping_t &m = local_list.at(i);
|
2014-09-23 12:04:06 +08:00
|
|
|
result.push_back((input_mapping_name_t){m.seq, m.mode});
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2014-03-31 04:13:35 +08:00
|
|
|
return result;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
bool input_mapping_erase(const wcstring &sequence, const wcstring &mode) {
|
2012-01-01 07:57:30 +08:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2012-11-19 08:30:30 +08:00
|
|
|
bool result = false;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2014-09-23 12:25:51 +08:00
|
|
|
for (std::vector<input_mapping_t>::iterator it = mapping_list.begin(), end = mapping_list.end();
|
2016-05-01 12:31:25 +08:00
|
|
|
it != end; ++it) {
|
|
|
|
if (sequence == it->seq && mode == it->mode) {
|
2014-09-23 12:04:06 +08:00
|
|
|
mapping_list.erase(it);
|
2012-11-19 08:30:30 +08:00
|
|
|
result = true;
|
|
|
|
break;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2012-11-19 08:30:30 +08:00
|
|
|
return result;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
bool input_mapping_get(const wcstring &sequence, const wcstring &mode, wcstring_list_t *out_cmds,
|
|
|
|
wcstring *out_sets_mode) {
|
2014-03-31 04:13:35 +08:00
|
|
|
bool result = false;
|
2016-05-01 12:31:25 +08:00
|
|
|
for (std::vector<input_mapping_t>::const_iterator it = mapping_list.begin(),
|
|
|
|
end = mapping_list.end();
|
|
|
|
it != end; ++it) {
|
|
|
|
if (sequence == it->seq && mode == it->mode) {
|
2014-09-23 12:04:06 +08:00
|
|
|
*out_cmds = it->commands;
|
|
|
|
*out_sets_mode = it->sets_mode;
|
2014-03-31 04:13:35 +08:00
|
|
|
result = true;
|
|
|
|
break;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2014-03-31 04:13:35 +08:00
|
|
|
return result;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
2007-09-29 05:32:27 +08:00
|
|
|
|
2017-02-16 12:09:26 +08:00
|
|
|
/// Add all terminfo mappings and cache other terminfo facts we care about.
|
|
|
|
static void init_input_terminfo() {
|
|
|
|
assert(curses_initialized);
|
|
|
|
if (!cur_term) return; // setupterm() failed so we can't referency any key definitions
|
2016-05-01 12:31:25 +08:00
|
|
|
const terminfo_mapping_t tinfos[] = {
|
2012-11-19 08:30:30 +08:00
|
|
|
TERMINFO_ADD(key_a1),
|
|
|
|
TERMINFO_ADD(key_a3),
|
|
|
|
TERMINFO_ADD(key_b2),
|
|
|
|
TERMINFO_ADD(key_backspace),
|
|
|
|
TERMINFO_ADD(key_beg),
|
|
|
|
TERMINFO_ADD(key_btab),
|
|
|
|
TERMINFO_ADD(key_c1),
|
|
|
|
TERMINFO_ADD(key_c3),
|
|
|
|
TERMINFO_ADD(key_cancel),
|
|
|
|
TERMINFO_ADD(key_catab),
|
|
|
|
TERMINFO_ADD(key_clear),
|
|
|
|
TERMINFO_ADD(key_close),
|
|
|
|
TERMINFO_ADD(key_command),
|
|
|
|
TERMINFO_ADD(key_copy),
|
|
|
|
TERMINFO_ADD(key_create),
|
|
|
|
TERMINFO_ADD(key_ctab),
|
|
|
|
TERMINFO_ADD(key_dc),
|
|
|
|
TERMINFO_ADD(key_dl),
|
|
|
|
TERMINFO_ADD(key_down),
|
|
|
|
TERMINFO_ADD(key_eic),
|
|
|
|
TERMINFO_ADD(key_end),
|
|
|
|
TERMINFO_ADD(key_enter),
|
|
|
|
TERMINFO_ADD(key_eol),
|
|
|
|
TERMINFO_ADD(key_eos),
|
|
|
|
TERMINFO_ADD(key_exit),
|
|
|
|
TERMINFO_ADD(key_f0),
|
|
|
|
TERMINFO_ADD(key_f1),
|
|
|
|
TERMINFO_ADD(key_f2),
|
|
|
|
TERMINFO_ADD(key_f3),
|
|
|
|
TERMINFO_ADD(key_f4),
|
|
|
|
TERMINFO_ADD(key_f5),
|
|
|
|
TERMINFO_ADD(key_f6),
|
|
|
|
TERMINFO_ADD(key_f7),
|
|
|
|
TERMINFO_ADD(key_f8),
|
|
|
|
TERMINFO_ADD(key_f9),
|
|
|
|
TERMINFO_ADD(key_f10),
|
|
|
|
TERMINFO_ADD(key_f11),
|
|
|
|
TERMINFO_ADD(key_f12),
|
|
|
|
TERMINFO_ADD(key_f13),
|
|
|
|
TERMINFO_ADD(key_f14),
|
|
|
|
TERMINFO_ADD(key_f15),
|
|
|
|
TERMINFO_ADD(key_f16),
|
|
|
|
TERMINFO_ADD(key_f17),
|
|
|
|
TERMINFO_ADD(key_f18),
|
|
|
|
TERMINFO_ADD(key_f19),
|
|
|
|
TERMINFO_ADD(key_f20),
|
2016-05-01 12:31:25 +08:00
|
|
|
#if 0
|
|
|
|
// I know of no keyboard with more than 20 function keys, so adding the rest here makes very
|
|
|
|
// little sense, since it will take up a lot of room in any listings (like tab completions),
|
|
|
|
// but with no benefit.
|
2012-11-19 08:30:30 +08:00
|
|
|
TERMINFO_ADD(key_f21),
|
|
|
|
TERMINFO_ADD(key_f22),
|
|
|
|
TERMINFO_ADD(key_f23),
|
|
|
|
TERMINFO_ADD(key_f24),
|
|
|
|
TERMINFO_ADD(key_f25),
|
|
|
|
TERMINFO_ADD(key_f26),
|
|
|
|
TERMINFO_ADD(key_f27),
|
|
|
|
TERMINFO_ADD(key_f28),
|
|
|
|
TERMINFO_ADD(key_f29),
|
|
|
|
TERMINFO_ADD(key_f30),
|
|
|
|
TERMINFO_ADD(key_f31),
|
|
|
|
TERMINFO_ADD(key_f32),
|
|
|
|
TERMINFO_ADD(key_f33),
|
|
|
|
TERMINFO_ADD(key_f34),
|
|
|
|
TERMINFO_ADD(key_f35),
|
|
|
|
TERMINFO_ADD(key_f36),
|
|
|
|
TERMINFO_ADD(key_f37),
|
|
|
|
TERMINFO_ADD(key_f38),
|
|
|
|
TERMINFO_ADD(key_f39),
|
|
|
|
TERMINFO_ADD(key_f40),
|
|
|
|
TERMINFO_ADD(key_f41),
|
|
|
|
TERMINFO_ADD(key_f42),
|
|
|
|
TERMINFO_ADD(key_f43),
|
|
|
|
TERMINFO_ADD(key_f44),
|
|
|
|
TERMINFO_ADD(key_f45),
|
|
|
|
TERMINFO_ADD(key_f46),
|
|
|
|
TERMINFO_ADD(key_f47),
|
|
|
|
TERMINFO_ADD(key_f48),
|
|
|
|
TERMINFO_ADD(key_f49),
|
|
|
|
TERMINFO_ADD(key_f50),
|
|
|
|
TERMINFO_ADD(key_f51),
|
|
|
|
TERMINFO_ADD(key_f52),
|
|
|
|
TERMINFO_ADD(key_f53),
|
|
|
|
TERMINFO_ADD(key_f54),
|
|
|
|
TERMINFO_ADD(key_f55),
|
|
|
|
TERMINFO_ADD(key_f56),
|
|
|
|
TERMINFO_ADD(key_f57),
|
|
|
|
TERMINFO_ADD(key_f58),
|
|
|
|
TERMINFO_ADD(key_f59),
|
|
|
|
TERMINFO_ADD(key_f60),
|
|
|
|
TERMINFO_ADD(key_f61),
|
|
|
|
TERMINFO_ADD(key_f62),
|
2016-05-01 12:31:25 +08:00
|
|
|
TERMINFO_ADD(key_f63),
|
|
|
|
#endif
|
2012-11-19 08:30:30 +08:00
|
|
|
TERMINFO_ADD(key_find),
|
|
|
|
TERMINFO_ADD(key_help),
|
|
|
|
TERMINFO_ADD(key_home),
|
|
|
|
TERMINFO_ADD(key_ic),
|
|
|
|
TERMINFO_ADD(key_il),
|
|
|
|
TERMINFO_ADD(key_left),
|
|
|
|
TERMINFO_ADD(key_ll),
|
|
|
|
TERMINFO_ADD(key_mark),
|
|
|
|
TERMINFO_ADD(key_message),
|
|
|
|
TERMINFO_ADD(key_move),
|
|
|
|
TERMINFO_ADD(key_next),
|
|
|
|
TERMINFO_ADD(key_npage),
|
|
|
|
TERMINFO_ADD(key_open),
|
|
|
|
TERMINFO_ADD(key_options),
|
|
|
|
TERMINFO_ADD(key_ppage),
|
|
|
|
TERMINFO_ADD(key_previous),
|
|
|
|
TERMINFO_ADD(key_print),
|
|
|
|
TERMINFO_ADD(key_redo),
|
|
|
|
TERMINFO_ADD(key_reference),
|
|
|
|
TERMINFO_ADD(key_refresh),
|
|
|
|
TERMINFO_ADD(key_replace),
|
|
|
|
TERMINFO_ADD(key_restart),
|
|
|
|
TERMINFO_ADD(key_resume),
|
|
|
|
TERMINFO_ADD(key_right),
|
|
|
|
TERMINFO_ADD(key_save),
|
|
|
|
TERMINFO_ADD(key_sbeg),
|
|
|
|
TERMINFO_ADD(key_scancel),
|
|
|
|
TERMINFO_ADD(key_scommand),
|
|
|
|
TERMINFO_ADD(key_scopy),
|
|
|
|
TERMINFO_ADD(key_screate),
|
|
|
|
TERMINFO_ADD(key_sdc),
|
|
|
|
TERMINFO_ADD(key_sdl),
|
|
|
|
TERMINFO_ADD(key_select),
|
|
|
|
TERMINFO_ADD(key_send),
|
|
|
|
TERMINFO_ADD(key_seol),
|
|
|
|
TERMINFO_ADD(key_sexit),
|
|
|
|
TERMINFO_ADD(key_sf),
|
|
|
|
TERMINFO_ADD(key_sfind),
|
|
|
|
TERMINFO_ADD(key_shelp),
|
|
|
|
TERMINFO_ADD(key_shome),
|
|
|
|
TERMINFO_ADD(key_sic),
|
|
|
|
TERMINFO_ADD(key_sleft),
|
|
|
|
TERMINFO_ADD(key_smessage),
|
|
|
|
TERMINFO_ADD(key_smove),
|
|
|
|
TERMINFO_ADD(key_snext),
|
|
|
|
TERMINFO_ADD(key_soptions),
|
|
|
|
TERMINFO_ADD(key_sprevious),
|
|
|
|
TERMINFO_ADD(key_sprint),
|
|
|
|
TERMINFO_ADD(key_sr),
|
|
|
|
TERMINFO_ADD(key_sredo),
|
|
|
|
TERMINFO_ADD(key_sreplace),
|
|
|
|
TERMINFO_ADD(key_sright),
|
|
|
|
TERMINFO_ADD(key_srsume),
|
|
|
|
TERMINFO_ADD(key_ssave),
|
|
|
|
TERMINFO_ADD(key_ssuspend),
|
|
|
|
TERMINFO_ADD(key_stab),
|
|
|
|
TERMINFO_ADD(key_sundo),
|
|
|
|
TERMINFO_ADD(key_suspend),
|
|
|
|
TERMINFO_ADD(key_undo),
|
|
|
|
TERMINFO_ADD(key_up)
|
2012-07-18 03:47:01 +08:00
|
|
|
};
|
|
|
|
const size_t count = sizeof tinfos / sizeof *tinfos;
|
|
|
|
terminfo_mappings.reserve(terminfo_mappings.size() + count);
|
|
|
|
terminfo_mappings.insert(terminfo_mappings.end(), tinfos, tinfos + count);
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
bool input_terminfo_get_sequence(const wchar_t *name, wcstring *out_seq) {
|
2012-02-08 15:17:20 +08:00
|
|
|
ASSERT_IS_MAIN_THREAD();
|
2017-02-16 12:09:26 +08:00
|
|
|
assert(input_initialized);
|
|
|
|
CHECK(name, 0);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
const char *res = 0;
|
|
|
|
int err = ENOENT;
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
2012-11-19 08:30:30 +08:00
|
|
|
const terminfo_mapping_t &m = terminfo_mappings.at(i);
|
2016-05-01 12:31:25 +08:00
|
|
|
if (!wcscmp(name, m.name)) {
|
2012-11-19 08:30:30 +08:00
|
|
|
res = m.seq;
|
|
|
|
err = EILSEQ;
|
|
|
|
break;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
if (!res) {
|
2012-11-19 08:30:30 +08:00
|
|
|
errno = err;
|
2013-04-16 04:07:17 +08:00
|
|
|
return false;
|
2013-05-05 17:33:17 +08:00
|
|
|
}
|
|
|
|
|
2013-04-16 04:07:17 +08:00
|
|
|
*out_seq = format_string(L"%s", res);
|
|
|
|
return true;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
bool input_terminfo_get_name(const wcstring &seq, wcstring *out_name) {
|
2017-02-16 12:09:26 +08:00
|
|
|
assert(input_initialized);
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
2012-11-19 08:30:30 +08:00
|
|
|
terminfo_mapping_t &m = terminfo_mappings.at(i);
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
if (!m.seq) {
|
2012-11-19 08:30:30 +08:00
|
|
|
continue;
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
const wcstring map_buf = format_string(L"%s", m.seq);
|
|
|
|
if (map_buf == seq) {
|
2014-07-08 01:45:26 +08:00
|
|
|
out_name->assign(m.name);
|
2012-01-01 07:57:30 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2012-11-19 08:30:30 +08:00
|
|
|
return false;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
wcstring_list_t input_terminfo_get_names(bool skip_null) {
|
2017-02-16 12:09:26 +08:00
|
|
|
assert(input_initialized);
|
2012-02-08 09:06:45 +08:00
|
|
|
wcstring_list_t result;
|
2012-02-08 15:15:32 +08:00
|
|
|
result.reserve(terminfo_mappings.size());
|
2012-11-18 18:23:22 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
for (size_t i = 0; i < terminfo_mappings.size(); i++) {
|
2012-11-19 08:30:30 +08:00
|
|
|
terminfo_mapping_t &m = terminfo_mappings.at(i);
|
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
if (skip_null && !m.seq) {
|
2012-11-19 08:30:30 +08:00
|
|
|
continue;
|
|
|
|
}
|
2012-02-08 15:15:32 +08:00
|
|
|
result.push_back(wcstring(m.name));
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-02-08 09:06:45 +08:00
|
|
|
return result;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|
|
|
|
|
2018-01-30 11:15:16 +08:00
|
|
|
wcstring_list_t input_function_get_names() {
|
|
|
|
wcstring_list_t result;
|
|
|
|
result.reserve(input_function_count);
|
|
|
|
for (const auto &md : input_function_metadata) {
|
|
|
|
result.push_back(md.name);
|
|
|
|
}
|
|
|
|
return result;
|
2007-09-26 00:14:47 +08:00
|
|
|
}
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-01 12:31:25 +08:00
|
|
|
wchar_t input_function_get_code(const wcstring &name) {
|
2018-01-30 11:15:16 +08:00
|
|
|
for (const auto &md : input_function_metadata) {
|
|
|
|
if (name == md.name) {
|
|
|
|
return md.code;
|
2012-11-19 08:30:30 +08:00
|
|
|
}
|
2012-11-18 18:23:22 +08:00
|
|
|
}
|
2015-08-17 09:45:42 +08:00
|
|
|
return INPUT_CODE_NONE;
|
2005-09-20 21:26:39 +08:00
|
|
|
}
|