Better input handling; add support multiple binding commands

This commit is contained in:
Julian Aron Prenner 2014-01-01 00:11:32 +01:00
parent f408bc4808
commit dc90cd6bc4
4 changed files with 143 additions and 131 deletions

View file

@ -413,27 +413,38 @@ static void builtin_bind_list(const wchar_t *bind_mode)
{ {
wcstring seq = lst.at(i); wcstring seq = lst.at(i);
wcstring ecmd; std::vector<wcstring> ecmds;
wcstring mode; 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)) if(bind_mode != NULL && wcscmp(mode.c_str(), bind_mode))
{ {
continue; continue;
} }
ecmd = escape_string(ecmd, 1);
wcstring tname; wcstring tname;
if (input_terminfo_get_name(seq, 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 else
{ {
const wcstring eseq = escape_string(seq, 1); 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. 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) if (terminfo)
@ -482,7 +494,7 @@ static int builtin_bind_add(const wchar_t *seq, const wchar_t *cmd, const wchar_
wcstring seq2; wcstring seq2;
if (input_terminfo_get_sequence(seq, &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 else
{ {
@ -515,7 +527,7 @@ static int builtin_bind_add(const wchar_t *seq, const wchar_t *cmd, const wchar_
} }
else else
{ {
input_mapping_add(seq, cmd, mode, new_mode); input_mapping_add(seq, cmds, cmds_len, mode, new_mode);
} }
return 0; return 0;
@ -718,18 +730,19 @@ static int builtin_bind(parser_t &parser, wchar_t **argv)
break; 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; break;
} }
default: default:
{ {
res = STATUS_BUILTIN_ERROR; builtin_bind_add(argv[woptind], (const wchar_t **)argv + (woptind + 1), argc - (woptind + 1), bind_mode, new_bind_mode, use_terminfo);
append_format(stderr_buffer, _(L"%ls: Expected zero or two parameters, got %d"), argv[0], argc-woptind);
break; break;
} }
} }
break; break;
} }

223
input.cpp
View file

@ -64,16 +64,17 @@
/** /**
Struct representing a keybinding. Returned by input_get_mappings. Struct representing a keybinding. Returned by input_get_mappings.
*/ */
struct input_mapping_t struct input_mapping_t
{ {
wcstring seq; /**< Character sequence which generates this event */ 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 mode; /**< mode in which this command should be evaluated */
wcstring new_mode; /** new mode that should be switched to after command evaluation */ 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 &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. 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) const wchar_t *mode, const wchar_t *new_mode)
{ {
CHECK(sequence,); CHECK(sequence,);
CHECK(command,); CHECK(commands,);
CHECK(mode,); CHECK(mode,);
CHECK(new_mode,); CHECK(new_mode,);
// debug( 0, L"Add mapping from %ls to %ls in mode %ls", escape(sequence, 1), escape(command, 1 ), 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++) for (size_t i=0; i<mapping_list.size(); i++)
{ {
input_mapping_t &m = mapping_list.at(i); input_mapping_t &m = mapping_list.at(i);
if (m.seq == sequence && m.mode == mode) if (m.seq == sequence && m.mode == mode)
{ {
m.command = command; m.commands = commands_vector;
m.new_mode = new_mode; m.new_mode = new_mode;
return; 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 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--)
if (code != (wchar_t)-1)
{ {
switch (code) wcstring command = m.commands.at(i);
{ wchar_t code = input_function_get_code(command);
if (code != (wchar_t)-1)
{
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(command.c_str(), io_chain_t(), TOP);
case R_SELF_INSERT: proc_set_last_status(last_status);
{
return seq[0];
}
default:
{
return code;
}
input_unreadch(R_NULL);
} }
} }
else
{
/* input_set_bind_mode(m.new_mode.c_str());
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);
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;
}
} }
@ -500,22 +491,12 @@ static wint_t input_exec_binding(const input_mapping_t &m, const wcstring &seq)
/** /**
Try reading the specified function mapping 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; int j;
/* //debug(0, L"trying mapping %ls\n", escape(m.seq.c_str(), 1));
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());
const wchar_t *str = m.seq.c_str(); const wchar_t *str = m.seq.c_str();
for (j=0; str[j] != L'\0'; j++) 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()); //debug(0, L"matched mapping %ls (%ls)\n", escape(m.seq.c_str(), 1), m.command.c_str());
/* We matched the entire sequence */ /* We matched the entire sequence */
return input_exec_binding(m, m.seq); return true;
} }
else 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); 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() wint_t input_readch()
{ {
size_t i; size_t i;
@ -573,61 +596,30 @@ wint_t input_readch()
while (1) 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); wchar_t c = input_common_readch(0);
/* If it's closed, then just return */ if (c >= R_MIN && c <= R_MAX)
if (c == R_EOF)
{ {
return WEOF; 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; 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(); 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); const input_mapping_t &m = mapping_list.at(i);
if (sequence == m.seq) if (sequence == m.seq)
{ {
cmd = m.command; cmds = m.commands;
mode = m.mode; mode = m.mode;
new_mode = m.new_mode;
return true; return true;
} }
} }

10
input.h
View file

@ -60,10 +60,13 @@ enum
R_UP_LINE, R_UP_LINE,
R_DOWN_LINE, R_DOWN_LINE,
R_SUPPRESS_AUTOSUGGESTION, 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 Initialize the terminal by calling setupterm, and set up arrays
used by readch to detect escape sequences for special keys. 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 *mode = DEFAULT_BIND_MODE,
const wchar_t *new_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 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. 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 Return the current bind mode

View file

@ -17,7 +17,7 @@ function fish_vi_key_bindings -d "vi-like key bindings for fish"
bind -k left backward-char bind -k left backward-char
bind \n execute bind \n execute
bind -m insert i force-repaint 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 \x24 end-of-line
bind \x5e beginning-of-line bind \x5e beginning-of-line