mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Better input handling; add support multiple binding commands
This commit is contained in:
parent
f408bc4808
commit
dc90cd6bc4
4 changed files with 143 additions and 131 deletions
39
builtin.cpp
39
builtin.cpp
|
@ -413,27 +413,38 @@ static void builtin_bind_list(const wchar_t *bind_mode)
|
|||
{
|
||||
wcstring seq = lst.at(i);
|
||||
|
||||
wcstring ecmd;
|
||||
std::vector<wcstring> ecmds;
|
||||
wcstring mode;
|
||||
wcstring new_mode;
|
||||
|
||||
input_mapping_get(seq, ecmd, mode);
|
||||
input_mapping_get(seq, ecmds, mode, new_mode);
|
||||
|
||||
if(bind_mode != NULL && wcscmp(mode.c_str(), bind_mode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ecmd = escape_string(ecmd, 1);
|
||||
|
||||
wcstring tname;
|
||||
if (input_terminfo_get_name(seq, tname))
|
||||
{
|
||||
append_format(stdout_buffer, L"bind -k %ls %ls (in mode `%ls')\n", tname.c_str(), ecmd.c_str(), mode.c_str());
|
||||
append_format(stdout_buffer, L"bind -k %ls -M %ls -m %ls", tname.c_str(), mode.c_str(), new_mode.c_str());
|
||||
for(int i = 0; i < ecmds.size(); i++)
|
||||
{
|
||||
wcstring ecmd = ecmds.at(i);
|
||||
append_format(stdout_buffer, L" %ls", escape(ecmd.c_str(), 1));
|
||||
}
|
||||
append_format(stdout_buffer, L"\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
const wcstring eseq = escape_string(seq, 1);
|
||||
append_format(stdout_buffer, L"bind %ls %ls (in mode `%ls')\n", eseq.c_str(), ecmd.c_str(), mode.c_str());
|
||||
append_format(stdout_buffer, L"bind -k %ls -M %ls -m %ls", eseq.c_str(), mode.c_str(), new_mode.c_str());
|
||||
for(int i = 0; i < ecmds.size(); i++)
|
||||
{
|
||||
wcstring ecmd = ecmds.at(i);
|
||||
append_format(stdout_buffer, L" %ls", escape(ecmd.c_str(), 1));
|
||||
}
|
||||
append_format(stdout_buffer, L"\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -474,7 +485,8 @@ static void builtin_bind_function_names()
|
|||
/**
|
||||
Add specified key binding.
|
||||
*/
|
||||
static int builtin_bind_add(const wchar_t *seq, const wchar_t *cmd, const wchar_t *mode, const wchar_t *new_mode, int terminfo)
|
||||
static int builtin_bind_add(const wchar_t *seq, const wchar_t **cmds, size_t cmds_len,
|
||||
const wchar_t *mode, const wchar_t *new_mode, int terminfo)
|
||||
{
|
||||
|
||||
if (terminfo)
|
||||
|
@ -482,7 +494,7 @@ static int builtin_bind_add(const wchar_t *seq, const wchar_t *cmd, const wchar_
|
|||
wcstring seq2;
|
||||
if (input_terminfo_get_sequence(seq, &seq2))
|
||||
{
|
||||
input_mapping_add(seq2.c_str(), cmd, mode, new_mode);
|
||||
input_mapping_add(seq2.c_str(), cmds, cmds_len, mode, new_mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -515,7 +527,7 @@ static int builtin_bind_add(const wchar_t *seq, const wchar_t *cmd, const wchar_
|
|||
}
|
||||
else
|
||||
{
|
||||
input_mapping_add(seq, cmd, mode, new_mode);
|
||||
input_mapping_add(seq, cmds, cmds_len, mode, new_mode);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -718,18 +730,19 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
|
|||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
case 1:
|
||||
{
|
||||
builtin_bind_add(argv[woptind], argv[woptind+1], bind_mode, new_bind_mode, use_terminfo);
|
||||
res = STATUS_BUILTIN_ERROR;
|
||||
append_format(stderr_buffer, _(L"%ls: Expected zero or at least two parameters, got %d"), argv[0], argc-woptind);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
res = STATUS_BUILTIN_ERROR;
|
||||
append_format(stderr_buffer, _(L"%ls: Expected zero or two parameters, got %d"), argv[0], argc-woptind);
|
||||
builtin_bind_add(argv[woptind], (const wchar_t **)argv + (woptind + 1), argc - (woptind + 1), bind_mode, new_bind_mode, use_terminfo);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
205
input.cpp
205
input.cpp
|
@ -64,16 +64,17 @@
|
|||
/**
|
||||
Struct representing a keybinding. Returned by input_get_mappings.
|
||||
*/
|
||||
|
||||
struct input_mapping_t
|
||||
{
|
||||
wcstring seq; /**< Character sequence which generates this event */
|
||||
wcstring command; /**< command that should be evaluated by this mapping */
|
||||
std::vector<wcstring> commands; /**< commands that should be evaluated by this mapping */
|
||||
wcstring mode; /**< mode in which this command should be evaluated */
|
||||
wcstring new_mode; /** new mode that should be switched to after command evaluation */
|
||||
|
||||
input_mapping_t(const wcstring &s, const wcstring &c,
|
||||
input_mapping_t(const wcstring &s, const std::vector<wcstring> &c,
|
||||
const wcstring &m = DEFAULT_BIND_MODE,
|
||||
const wcstring &nm = DEFAULT_BIND_MODE) : seq(s), command(c), mode(m), new_mode(nm) {}
|
||||
const wcstring &nm = DEFAULT_BIND_MODE) : seq(s), commands(c), mode(m), new_mode(nm) {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -285,27 +286,35 @@ bool input_set_bind_mode(const wchar_t *bm)
|
|||
Returns the function description for the given function code.
|
||||
*/
|
||||
|
||||
void input_mapping_add(const wchar_t *sequence, const wchar_t *command,
|
||||
void input_mapping_add(const wchar_t *sequence, const wchar_t **commands, size_t commands_len,
|
||||
const wchar_t *mode, const wchar_t *new_mode)
|
||||
{
|
||||
CHECK(sequence,);
|
||||
CHECK(command,);
|
||||
CHECK(commands,);
|
||||
CHECK(mode,);
|
||||
CHECK(new_mode,);
|
||||
|
||||
// debug( 0, L"Add mapping from %ls to %ls in mode %ls", escape(sequence, 1), escape(command, 1 ), mode);
|
||||
|
||||
std::vector<wcstring> commands_vector(commands, commands + commands_len);
|
||||
|
||||
for (size_t i=0; i<mapping_list.size(); i++)
|
||||
{
|
||||
input_mapping_t &m = mapping_list.at(i);
|
||||
if (m.seq == sequence && m.mode == mode)
|
||||
{
|
||||
m.command = command;
|
||||
m.commands = commands_vector;
|
||||
m.new_mode = new_mode;
|
||||
return;
|
||||
}
|
||||
}
|
||||
mapping_list.push_back(input_mapping_t(sequence, command, mode, new_mode));
|
||||
mapping_list.push_back(input_mapping_t(sequence, commands_vector, mode, new_mode));
|
||||
}
|
||||
|
||||
void input_mapping_add(const wchar_t *sequence, const wchar_t *command,
|
||||
const wchar_t *mode, const wchar_t *new_mode)
|
||||
{
|
||||
input_mapping_add(sequence, &command, 1, mode, new_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -444,55 +453,37 @@ void input_destroy()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Perform the action of the specified binding
|
||||
*/
|
||||
static wint_t input_exec_binding(const input_mapping_t &m, const wcstring &seq)
|
||||
static void input_mapping_execute(const input_mapping_t &m)
|
||||
{
|
||||
wchar_t code = input_function_get_code(m.command);
|
||||
for(int i = m.commands.size() - 1; i >= 0; i--)
|
||||
{
|
||||
wcstring command = m.commands.at(i);
|
||||
wchar_t code = input_function_get_code(command);
|
||||
if (code != (wchar_t)-1)
|
||||
{
|
||||
switch (code)
|
||||
{
|
||||
|
||||
case R_SELF_INSERT:
|
||||
{
|
||||
return seq[0];
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
input_unreadch(code);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
This key sequence is bound to a command, which
|
||||
is sent to the parser for evaluation.
|
||||
*/
|
||||
int last_status = proc_get_last_status();
|
||||
|
||||
parser_t::principal_parser().eval(m.command.c_str(), io_chain_t(), TOP);
|
||||
parser_t::principal_parser().eval(command.c_str(), io_chain_t(), TOP);
|
||||
|
||||
proc_set_last_status(last_status);
|
||||
|
||||
/*
|
||||
We still need to return something to the caller, R_NULL
|
||||
tells the reader that no key press needs to be handled,
|
||||
and no repaint is needed.
|
||||
|
||||
Bindings that produce output should emit a R_REPAINT
|
||||
function by calling 'commandline -f repaint' to tell
|
||||
fish that a repaint is in order.
|
||||
*/
|
||||
|
||||
return R_NULL;
|
||||
|
||||
input_unreadch(R_NULL);
|
||||
}
|
||||
}
|
||||
|
||||
input_set_bind_mode(m.new_mode.c_str());
|
||||
}
|
||||
|
||||
|
||||
|
@ -500,22 +491,12 @@ static wint_t input_exec_binding(const input_mapping_t &m, const wcstring &seq)
|
|||
/**
|
||||
Try reading the specified function mapping
|
||||
*/
|
||||
static wint_t input_try_mapping(const input_mapping_t &m)
|
||||
static bool input_mapping_is_match(const input_mapping_t &m)
|
||||
{
|
||||
wint_t c=0;
|
||||
wint_t c = 0;
|
||||
int j;
|
||||
|
||||
/*
|
||||
Check if the actual function code of this mapping is on the stack
|
||||
*/
|
||||
c = input_common_readch(0);
|
||||
if (c == input_function_get_code(m.command))
|
||||
{
|
||||
return input_exec_binding(m, m.seq);
|
||||
}
|
||||
input_unreadch(c);
|
||||
|
||||
//debug(0, L"trying mapping %ls (%ls)\n", escape(m.seq.c_str(), 1), m.command.c_str());
|
||||
//debug(0, L"trying mapping %ls\n", escape(m.seq.c_str(), 1));
|
||||
const wchar_t *str = m.seq.c_str();
|
||||
for (j=0; str[j] != L'\0'; j++)
|
||||
{
|
||||
|
@ -532,7 +513,7 @@ static wint_t input_try_mapping(const input_mapping_t &m)
|
|||
{
|
||||
//debug(0, L"matched mapping %ls (%ls)\n", escape(m.seq.c_str(), 1), m.command.c_str());
|
||||
/* We matched the entire sequence */
|
||||
return input_exec_binding(m, m.seq);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -547,7 +528,7 @@ static wint_t input_try_mapping(const input_mapping_t &m)
|
|||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
@ -556,6 +537,48 @@ void input_unreadch(wint_t ch)
|
|||
input_common_unreadch(ch);
|
||||
}
|
||||
|
||||
static void input_mapping_execute_matching_or_generic()
|
||||
{
|
||||
const input_mapping_t *generic = NULL;
|
||||
|
||||
for (int i = 0; i < mapping_list.size(); i++)
|
||||
{
|
||||
const input_mapping_t &m = mapping_list.at(i);
|
||||
|
||||
//debug(0, L"trying mapping (%ls,%ls,%ls)\n", escape(m.seq.c_str(), 1),
|
||||
// m.mode.c_str(), m.new_mode.c_str());
|
||||
|
||||
if(wcscmp(m.mode.c_str(), input_get_bind_mode()))
|
||||
{
|
||||
//debug(0, L"skipping mapping because mode %ls != %ls\n", m.mode.c_str(), input_get_bind_mode());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m.seq.length() == 0)
|
||||
{
|
||||
generic = &m;
|
||||
}
|
||||
else if(input_mapping_is_match(m))
|
||||
{
|
||||
input_mapping_execute(m);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (generic)
|
||||
{
|
||||
input_mapping_execute(*generic);
|
||||
}
|
||||
else
|
||||
{
|
||||
//debug(0, L"no generic found, ignoring...");
|
||||
wchar_t c = input_common_readch(0);
|
||||
if (c == R_EOF)
|
||||
input_common_unreadch(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
wint_t input_readch()
|
||||
{
|
||||
size_t i;
|
||||
|
@ -573,62 +596,31 @@ wint_t input_readch()
|
|||
|
||||
while (1)
|
||||
{
|
||||
const input_mapping_t *generic = 0;
|
||||
for (i=0; i<mapping_list.size(); i++)
|
||||
{
|
||||
const input_mapping_t &m = mapping_list.at(i);
|
||||
|
||||
//debug(0, L"trying mapping (%ls,%ls,%ls,%ls)\n", escape(m.seq.c_str(), 1),
|
||||
// m.command.c_str(), m.mode.c_str(), m.new_mode.c_str());
|
||||
|
||||
if(wcscmp(m.mode.c_str(), input_get_bind_mode()))
|
||||
{
|
||||
//debug(0, L"skipping mapping because mode %ls != %ls\n", m.mode.c_str(), input_get_bind_mode());
|
||||
continue;
|
||||
}
|
||||
|
||||
wint_t res = input_try_mapping(m);
|
||||
if (res)
|
||||
{
|
||||
input_set_bind_mode(m.new_mode.c_str());
|
||||
return res;
|
||||
}
|
||||
|
||||
if (m.seq.length() == 0)
|
||||
{
|
||||
generic = &m;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
No matching exact mapping, try to find generic mapping.
|
||||
*/
|
||||
|
||||
if (generic)
|
||||
{
|
||||
wchar_t arr[2]=
|
||||
{
|
||||
0,
|
||||
0
|
||||
}
|
||||
;
|
||||
arr[0] = input_common_readch(0);
|
||||
|
||||
return input_exec_binding(*generic, arr);
|
||||
}
|
||||
|
||||
/*
|
||||
No action to take on specified character, ignore it
|
||||
and move to next one.
|
||||
*/
|
||||
wchar_t c = input_common_readch(0);
|
||||
|
||||
/* If it's closed, then just return */
|
||||
if (c == R_EOF)
|
||||
if (c >= R_MIN && c <= R_MAX)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case R_EOF: /* If it's closed, then just return */
|
||||
{
|
||||
return WEOF;
|
||||
}
|
||||
case R_SELF_INSERT:
|
||||
{
|
||||
return input_common_readch(0);
|
||||
}
|
||||
default:
|
||||
{
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
input_unreadch(c);
|
||||
input_mapping_execute_matching_or_generic();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -669,7 +661,7 @@ bool input_mapping_erase(const wchar_t *sequence, const wchar_t *mode)
|
|||
return result;
|
||||
}
|
||||
|
||||
bool input_mapping_get(const wcstring &sequence, wcstring &cmd, wcstring &mode)
|
||||
bool input_mapping_get(const wcstring &sequence, std::vector<wcstring> &cmds, wcstring &mode, wcstring &new_mode)
|
||||
{
|
||||
size_t i, sz = mapping_list.size();
|
||||
|
||||
|
@ -678,8 +670,9 @@ bool input_mapping_get(const wcstring &sequence, wcstring &cmd, wcstring &mode)
|
|||
const input_mapping_t &m = mapping_list.at(i);
|
||||
if (sequence == m.seq)
|
||||
{
|
||||
cmd = m.command;
|
||||
cmds = m.commands;
|
||||
mode = m.mode;
|
||||
new_mode = m.new_mode;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
10
input.h
10
input.h
|
@ -60,10 +60,13 @@ enum
|
|||
R_UP_LINE,
|
||||
R_DOWN_LINE,
|
||||
R_SUPPRESS_AUTOSUGGESTION,
|
||||
R_ACCEPT_AUTOSUGGESTION
|
||||
R_ACCEPT_AUTOSUGGESTION,
|
||||
}
|
||||
;
|
||||
|
||||
#define R_MIN R_NULL
|
||||
#define R_MAX R_ACCEPT_AUTOSUGGESTION
|
||||
|
||||
/**
|
||||
Initialize the terminal by calling setupterm, and set up arrays
|
||||
used by readch to detect escape sequences for special keys.
|
||||
|
@ -110,6 +113,9 @@ void input_mapping_add(const wchar_t *sequence, const wchar_t *command,
|
|||
const wchar_t *mode = DEFAULT_BIND_MODE,
|
||||
const wchar_t *new_mode = DEFAULT_BIND_MODE);
|
||||
|
||||
void input_mapping_add(const wchar_t *sequence, const wchar_t **commands, size_t commands_len,
|
||||
const wchar_t *mode = DEFAULT_BIND_MODE, const wchar_t *new_mode = DEFAULT_BIND_MODE);
|
||||
|
||||
/**
|
||||
Insert all mapping names into the specified wcstring_list_t
|
||||
*/
|
||||
|
@ -123,7 +129,7 @@ bool input_mapping_erase(const wchar_t *sequence, const wchar_t *mode = DEFAULT_
|
|||
/**
|
||||
Gets the command bound to the specified key sequence. Returns true if it exists, false if not.
|
||||
*/
|
||||
bool input_mapping_get(const wcstring &sequence, wcstring &cmd, wcstring &mode);
|
||||
bool input_mapping_get(const wcstring &sequence, std::vector<wcstring> &cmds, wcstring &mode, wcstring &new_mode);
|
||||
|
||||
/**
|
||||
Return the current bind mode
|
||||
|
|
|
@ -17,7 +17,7 @@ function fish_vi_key_bindings -d "vi-like key bindings for fish"
|
|||
bind -k left backward-char
|
||||
bind \n execute
|
||||
bind -m insert i force-repaint
|
||||
bind -m insert a forward-char
|
||||
bind -m insert a forward-char force-repaint
|
||||
|
||||
bind \x24 end-of-line
|
||||
bind \x5e beginning-of-line
|
||||
|
|
Loading…
Reference in a new issue