aboutsummaryrefslogtreecommitdiffhomepage
path: root/reader.cpp
diff options
context:
space:
mode:
authorGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-07-17 00:38:04 -0700
committerGravatar ridiculousfish <corydoras@ridiculousfish.com>2013-07-19 12:41:34 -0700
commit92099c7af23d0cebf52f89de4f9d829825e53ac8 (patch)
treeb9251df8338f19a58bb4f9fd32b968ac6ced98f9 /reader.cpp
parent58ad04b61cf567c7a8756869f19d6d5682122b12 (diff)
Initial abbreviation work. Tests currently fail.
Diffstat (limited to 'reader.cpp')
-rw-r--r--reader.cpp175
1 files changed, 171 insertions, 4 deletions
diff --git a/reader.cpp b/reader.cpp
index 9b2eea14..d9cb363b 100644
--- a/reader.cpp
+++ b/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: