Initial abbreviation work. Tests currently fail.

This commit is contained in:
ridiculousfish
2013-07-17 00:38:04 -07:00
parent 58ad04b61c
commit 92099c7af2
6 changed files with 285 additions and 5 deletions

View File

@@ -182,6 +182,8 @@ static pthread_key_t generation_count_key;
/* A color is an int */
typedef int color_t;
static void set_command_line_and_position(const wcstring &new_str, size_t pos);
/**
A struct describing the state of the interactive reader. These
states can be stacked, in case reader_readline() calls are
@@ -203,6 +205,9 @@ public:
/** When backspacing, we temporarily suppress autosuggestions */
bool suppress_autosuggestion;
/** Whether abbreviations are expanded */
bool expand_abbreviations;
/** The representation of the current screen contents */
screen_t screen;
@@ -244,6 +249,9 @@ public:
/** Do what we need to do whenever our command line changes */
void command_line_changed(void);
/** Expand abbreviations at the current cursor position. Returns true if the command line changed. */
bool expand_abbreviation_as_necessary(void);
/** The current position of the cursor in buff. */
size_t buff_pos;
@@ -326,6 +334,7 @@ public:
reader_data_t() :
allow_autosuggestion(0),
suppress_autosuggestion(0),
expand_abbreviations(0),
history(0),
token_history_pos(0),
search_pos(0),
@@ -635,6 +644,148 @@ void reader_data_t::command_line_changed()
s_generation_count++;
}
/* Expand abbreviations at the given cursor position. Does NOT inspect 'data'. */
bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t cursor_pos, wcstring *output)
{
/* Can't have the cursor at the beginning */
if (cursor_pos == 0)
return false;
/* See if we are at "command position". Get the surrounding command substitution, and get the extent of the first token. */
const wchar_t * const buff = cmdline.c_str();
const wchar_t *cmdsub_begin = NULL, *cmdsub_end = NULL;
parse_util_cmdsubst_extent(buff, cursor_pos, &cmdsub_begin, &cmdsub_end);
assert(cmdsub_begin != NULL && cmdsub_begin >= buff);
assert(cmdsub_end != NULL && cmdsub_end >= cmdsub_begin);
fprintf(stderr, "cmdsub of '%ls' at %lu is '%ls'\n", cmdline.c_str(), cursor_pos, wcstring(cmdsub_begin, cmdsub_end).c_str());
/* Determine the offset of this command substitution */
const size_t subcmd_offset = cmdsub_begin - buff;
const wcstring subcmd = wcstring(cmdsub_begin, cmdsub_end - cmdsub_begin);
const wchar_t *subcmd_cstr = subcmd.c_str();
/* Get the token before the cursor */
const wchar_t *subcmd_tok_begin = NULL, *subcmd_tok_end = NULL;
size_t subcmd_cursor_pos = cursor_pos + subcmd_offset;
parse_util_token_extent(subcmd_cstr, subcmd_cursor_pos, NULL, NULL, &subcmd_tok_begin, &subcmd_tok_end);
/* Compute the offset of the token before the cursor within the subcmd */
assert(subcmd_tok_begin >= subcmd_cstr);
assert(subcmd_tok_end >= subcmd_tok_begin);
const size_t subcmd_tok_begin_offset = subcmd_tok_begin - subcmd_cstr;
const size_t subcmd_tok_length = subcmd_tok_end - subcmd_tok_begin;
/* Now parse the subcmd, looking for commands */
bool had_cmd = false, previous_token_is_cmd = false;
tokenizer_t tok(subcmd_cstr, TOK_ACCEPT_UNFINISHED | TOK_SQUASH_ERRORS);
for (; tok_has_next(&tok); tok_next(&tok))
{
size_t tok_pos = static_cast<size_t>(tok_get_pos(&tok));
if (tok_pos > subcmd_tok_begin_offset)
{
/* We've passed the token we're interested in */
break;
}
int last_type = tok_last_type(&tok);
switch (last_type)
{
case TOK_STRING:
{
if (had_cmd)
{
/* Parameter to the command. */
}
else
{
/* Command. */
had_cmd = true;
if (tok_pos == subcmd_tok_begin_offset)
{
/* This is the token we care about! */
previous_token_is_cmd = true;
}
}
break;
}
case TOK_REDIRECT_NOCLOB:
case TOK_REDIRECT_OUT:
case TOK_REDIRECT_IN:
case TOK_REDIRECT_APPEND:
case TOK_REDIRECT_FD:
{
if (!had_cmd)
{
break;
}
tok_next(&tok);
break;
}
case TOK_PIPE:
case TOK_BACKGROUND:
case TOK_END:
{
had_cmd = false;
break;
}
case TOK_COMMENT:
case TOK_ERROR:
default:
{
break;
}
}
}
bool result = false;
if (previous_token_is_cmd)
{
/* The token is a command. Try expanding it as an abbreviation. */
const wcstring token = wcstring(subcmd, subcmd_tok_begin_offset, subcmd_tok_length);
wcstring abbreviation;
if (expand_abbreviation(token, &abbreviation))
{
/* There was an abbreviation! Replace the token in the full command. Maintain the relative position of the cursor. */
if (output != NULL)
{
size_t cmd_tok_begin_offset = subcmd_tok_begin_offset + subcmd_offset;
output->assign(cmdline);
output->replace(cmd_tok_begin_offset, subcmd_tok_length, abbreviation);
}
result = true;
}
}
return result;
}
/* Expand abbreviations */
bool reader_data_t::expand_abbreviation_as_necessary(void)
{
bool result = false;
if (this->expand_abbreviations)
{
wcstring new_cmdline;
if (reader_expand_abbreviation_in_command(this->command_line, this->buff_pos, &new_cmdline))
{
/* We expanded an abbreviation! The cursor moves by the difference in the command line lengths. */
size_t new_buff_pos = this->buff_pos + new_cmdline.size() - this->command_line.size();
this->command_line.swap(new_cmdline);
data->command_line_changed();
data->buff_pos = new_buff_pos;
reader_super_highlight_me_plenty(data->buff_pos);
reader_repaint();
result = true;
}
}
return result;
}
/** Sorts and remove any duplicate completions in the list. */
static void sort_and_make_unique(std::vector<completion_t> &l)
@@ -1980,8 +2131,7 @@ void reader_sanity_check()
}
/**
Set the specified string from the history as the current buffer. Do
not modify prefix_width.
Set the specified string as the current buffer.
*/
static void set_command_line_and_position(const wcstring &new_str, size_t pos)
{
@@ -2462,6 +2612,11 @@ void reader_set_allow_autosuggesting(bool flag)
data->allow_autosuggestion = flag;
}
void reader_set_expand_abbreviations(bool flag)
{
data->expand_abbreviations = flag;
}
void reader_set_complete_function(complete_function_t f)
{
data->complete_func = f;
@@ -2712,6 +2867,7 @@ static int read_i(void)
reader_set_highlight_function(&highlight_shell);
reader_set_test_function(&reader_shell_test);
reader_set_allow_autosuggesting(true);
reader_set_expand_abbreviations(true);
reader_import_history_if_necessary();
parser_t &parser = parser_t::principal_parser();
@@ -2851,7 +3007,6 @@ const wchar_t *reader_readline(void)
data->search_buff.clear();
data->search_mode = NO_SEARCH;
exec_prompt();
reader_super_highlight_me_plenty(data->buff_pos);
@@ -3261,7 +3416,19 @@ const wchar_t *reader_readline(void)
}
}
switch (data->test_func(data->command_line.c_str()))
/* See if this command is valid */
int command_test_result = data->test_func(data->command_line.c_str());
if (command_test_result == 0)
{
/* This command is valid, but an abbreviation may make it invalid. If so, we will have to test again. */
bool abbreviation_expanded = data->expand_abbreviation_as_necessary();
if (abbreviation_expanded)
{
command_test_result = data->test_func(data->command_line.c_str());
}
}
switch (command_test_result)
{
case 0: