mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-21 23:11:17 -03:00
Initial abbreviation work. Tests currently fail.
This commit is contained in:
175
reader.cpp
175
reader.cpp
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user