diff options
author | ridiculousfish <corydoras@ridiculousfish.com> | 2013-07-17 00:38:04 -0700 |
---|---|---|
committer | ridiculousfish <corydoras@ridiculousfish.com> | 2013-07-19 12:41:34 -0700 |
commit | 92099c7af23d0cebf52f89de4f9d829825e53ac8 (patch) | |
tree | b9251df8338f19a58bb4f9fd32b968ac6ced98f9 /reader.cpp | |
parent | 58ad04b61cf567c7a8756869f19d6d5682122b12 (diff) |
Initial abbreviation work. Tests currently fail.
Diffstat (limited to 'reader.cpp')
-rw-r--r-- | reader.cpp | 175 |
1 files changed, 171 insertions, 4 deletions
@@ -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: |