diff --git a/fish_tests.cpp b/fish_tests.cpp index bbcd35c38..41464c31a 100644 --- a/fish_tests.cpp +++ b/fish_tests.cpp @@ -535,21 +535,19 @@ static void test_utils() const wchar_t *begin = NULL, *end = NULL; parse_util_cmdsubst_extent(a, 0, &begin, &end); - if (begin != a || end - begin != wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 1, &begin, &end); - if (begin != a || end - begin != wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 2, &begin, &end); - if (begin != a || end - begin != wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 3, &begin, &end); - if (begin != a || end - begin != wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); + if (begin != a || end != begin + wcslen(begin)) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 8, &begin, &end); if (begin != a + wcslen(L"echo (")) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); parse_util_cmdsubst_extent(a, 17, &begin, &end); if (begin != a + wcslen(L"echo (echo (")) err(L"parse_util_cmdsubst_extent failed on line %ld", (long)__LINE__); - - } class lru_node_test_t : public lru_node_t @@ -707,15 +705,6 @@ static void test_abbreviations(void) { say(L"Testing abbreviations"); - const wchar_t *buff = L"echo (echo (echo (gc"; - size_t cursor_pos = wcslen(buff) - 2; - 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", buff, cursor_pos, wcstring(cmdsub_begin, cmdsub_end).c_str()); - exit(0); - const wchar_t *abbreviations = L"gc=git checkout" ARRAY_SEP_STR L"foo=" ARRAY_SEP_STR @@ -742,24 +731,31 @@ static void test_abbreviations(void) if (result != L"bar") err(L"Wrong abbreviation result for foo"); bool expanded; - expanded = reader_expand_abbreviation_in_command(L"just a command", wcslen(L"just "), &result); + expanded = reader_expand_abbreviation_in_command(L"just a command", 3, &result); if (expanded) err(L"Command wrongly expanded on line %ld", (long)__LINE__); expanded = reader_expand_abbreviation_in_command(L"gc somebranch", 0, &result); - if (expanded) err(L"Command wrongly expanded on line %ld", (long)__LINE__); - expanded = reader_expand_abbreviation_in_command(L"gc somebranch", 1, &result); - if (expanded) err(L"Command wrongly expanded on line %ld", (long)__LINE__); + if (! expanded) err(L"Command not expanded on line %ld", (long)__LINE__); - expanded = reader_expand_abbreviation_in_command(L"gc somebranch", wcslen(L"gc "), &result); + expanded = reader_expand_abbreviation_in_command(L"gc somebranch", wcslen(L"gc"), &result); if (! expanded) err(L"gc not expanded"); if (result != L"git checkout somebranch") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); - expanded = reader_expand_abbreviation_in_command(L"echo hi ; gc somebranch", wcslen(L"echo hi ; gc "), &result); + expanded = reader_expand_abbreviation_in_command(L"echo hi ; gc somebranch", wcslen(L"echo hi ; g"), &result); if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); if (result != L"echo hi ; git checkout somebranch") err(L"gc incorrectly expanded on line %ld", (long)__LINE__); - expanded = reader_expand_abbreviation_in_command(L"echo (echo (echo (echo (gc ", wcslen(L"echo (echo (echo (echo (gc "), &result); + expanded = reader_expand_abbreviation_in_command(L"echo (echo (echo (echo (gc ", wcslen(L"echo (echo (echo (echo (gc"), &result); if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); - if (result != L"echo (echo (git checkout ") err(L"gc incorrectly expanded on line %ld", (long)__LINE__); + if (result != L"echo (echo (echo (echo (git checkout ") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + + /* if commands should be expanded */ + expanded = reader_expand_abbreviation_in_command(L"if gc", wcslen(L"if gc"), &result); + if (! expanded) err(L"gc not expanded on line %ld", (long)__LINE__); + if (result != L"if git checkout") err(L"gc incorrectly expanded on line %ld to '%ls'", (long)__LINE__, result.c_str()); + + /* others should not be */ + expanded = reader_expand_abbreviation_in_command(L"of gc", wcslen(L"of gc"), &result); + if (expanded) err(L"gc incorrectly expanded on line %ld", (long)__LINE__); env_pop(); } diff --git a/highlight.cpp b/highlight.cpp index 265fd40af..606604386 100644 --- a/highlight.cpp +++ b/highlight.cpp @@ -1106,6 +1106,9 @@ static void tokenize(const wchar_t * const buff, std::vector &color, const if (! is_cmd && use_function) is_cmd = function_exists_no_autoload(cmd, vars); + if (! is_cmd) + is_cmd = expand_abbreviation(cmd, NULL); + /* Moving on to expensive tests */ diff --git a/parse_util.cpp b/parse_util.cpp index e6d04a3f9..5e6f4459b 100644 --- a/parse_util.cpp +++ b/parse_util.cpp @@ -541,7 +541,6 @@ void parse_util_set_argv(const wchar_t * const *argv, const wcstring_list_t &nam { const wchar_t * const *arg; size_t i; - for (i=0, arg=argv; i < named_arguments.size(); i++) { env_set(named_arguments.at(i).c_str(), *arg, ENV_LOCAL); @@ -549,10 +548,7 @@ void parse_util_set_argv(const wchar_t * const *argv, const wcstring_list_t &nam if (*arg) arg++; } - - } - } wchar_t *parse_util_unescape_wildcards(const wchar_t *str) diff --git a/reader.cpp b/reader.cpp index d9cb363bd..8febb7994 100644 --- a/reader.cpp +++ b/reader.cpp @@ -97,8 +97,8 @@ commence. #include "iothread.h" #include "intern.h" #include "path.h" - #include "parse_util.h" +#include "parser_keywords.h" /** Maximum length of prefix string when printing completion @@ -249,8 +249,8 @@ 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); + /** Expand abbreviations at the current cursor position. */ + bool expand_abbreviation_as_necessary(); /** The current position of the cursor in buff. */ size_t buff_pos; @@ -647,17 +647,12 @@ void reader_data_t::command_line_changed() /* 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; @@ -667,8 +662,9 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso /* 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); + assert(cursor_pos >= subcmd_offset); + size_t subcmd_cursor_pos = cursor_pos - subcmd_offset; + parse_util_token_extent(subcmd_cstr, subcmd_cursor_pos, &subcmd_tok_begin, &subcmd_tok_end, NULL, NULL); /* Compute the offset of the token before the cursor within the subcmd */ assert(subcmd_tok_begin >= subcmd_cstr); @@ -700,12 +696,29 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso } else { - /* Command. */ - had_cmd = true; - if (tok_pos == subcmd_tok_begin_offset) + const wcstring potential_cmd = tok_last(&tok); + if (parser_keywords_is_subcommand(potential_cmd)) { - /* This is the token we care about! */ - previous_token_is_cmd = true; + if (potential_cmd == L"command" || potential_cmd == L"builtin") + { + /* 'command' and 'builtin' defeat abbreviation expansion. Skip this command. */ + had_cmd = true; + } + else + { + /* Other subcommand. Pretend it doesn't exist so that we can expand the following command */ + had_cmd = false; + } + } + else + { + /* It's a normal command */ + had_cmd = true; + if (tok_pos == subcmd_tok_begin_offset) + { + /* This is the token we care about! */ + previous_token_is_cmd = true; + } } } break; @@ -763,12 +776,13 @@ bool reader_expand_abbreviation_in_command(const wcstring &cmdline, size_t curso return result; } -/* Expand abbreviations */ -bool reader_data_t::expand_abbreviation_as_necessary(void) +/* Expand abbreviations. This may change the command line but does NOT repaint it. This is to allow the caller to coalesce repaints. */ +bool reader_data_t::expand_abbreviation_as_necessary() { bool result = false; if (this->expand_abbreviations) { + /* Try expanding abbreviations */ wcstring new_cmdline; if (reader_expand_abbreviation_in_command(this->command_line, this->buff_pos, &new_cmdline)) { @@ -776,11 +790,8 @@ bool reader_data_t::expand_abbreviation_as_necessary(void) 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(); - + data->command_line_changed(); result = true; } } @@ -3418,12 +3429,14 @@ const wchar_t *reader_readline(void) /* See if this command is valid */ int command_test_result = data->test_func(data->command_line.c_str()); - if (command_test_result == 0) + if (command_test_result == 0 || command_test_result == PARSER_TEST_INCOMPLETE) { /* 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) { + /* It's our reponsibility to rehighlight and repaint. But everything we do below triggers a repaint. */ + reader_super_highlight_me_plenty(data->buff_pos); command_test_result = data->test_func(data->command_line.c_str()); } } @@ -3764,9 +3777,14 @@ const wchar_t *reader_readline(void) /* Other, if a normal character, we add it to the command */ default: { - if ((!wchar_private(c)) && (((c>31) || (c==L'\n'))&& (c != 127))) { + /* Expand abbreviations on space */ + if (c == L' ') + { + data->expand_abbreviation_as_necessary(); + } + /* Regular character */ insert_char(c); } @@ -3800,10 +3818,7 @@ const wchar_t *reader_readline(void) } writestr(L"\n"); - /* - if( comp ) - halloc_free( comp ); - */ + if (!reader_exit_forced()) { if (tcsetattr(0,TCSANOW,&old_modes)) /* return to previous mode */