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 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
View file

@ -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
View file

@ -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

View file

@ -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