From eec6db0a2312e923f74402bb2e714f4bb331d9bd Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Mon, 10 Dec 2012 16:23:08 -0800 Subject: [PATCH] forward-word should accept a word of an autosuggestion https://github.com/fish-shell/fish-shell/issues/435 --- autoload.h | 7 +-- builtin.cpp | 2 +- reader.cpp | 127 ++++++++++++-------------------------------------- screen.cpp | 8 ++-- tokenizer.cpp | 79 +++++++++++++++++++++++++++++++ tokenizer.h | 18 +++++++ 6 files changed, 136 insertions(+), 105 deletions(-) diff --git a/autoload.h b/autoload.h index 8bfdad6a4..6c17c0b7b 100644 --- a/autoload.h +++ b/autoload.h @@ -34,9 +34,10 @@ struct autoload_function_t : public lru_node_t bool is_internalized; /** Whether this function came from a builtin "internalized" script */ }; -struct builtin_script_t { - const wchar_t *name; - const char *def; +struct builtin_script_t +{ + const wchar_t *name; + const char *def; }; struct builtin_script_t; diff --git a/builtin.cpp b/builtin.cpp index af0da3281..f8eec3f23 100644 --- a/builtin.cpp +++ b/builtin.cpp @@ -461,7 +461,7 @@ static void builtin_bind_function_names() /** Add specified key binding. */ -static int builtin_bind_add(wchar_t *seq, wchar_t *cmd, int terminfo) +static int builtin_bind_add(const wchar_t *seq, const wchar_t *cmd, int terminfo) { if (terminfo) diff --git a/reader.cpp b/reader.cpp index f4e607fa5..744de1a34 100644 --- a/reader.cpp +++ b/reader.cpp @@ -1283,13 +1283,29 @@ static void update_autosuggestion(void) #endif } -static void accept_autosuggestion(void) +/* Accept any autosuggestion by replacing the command line with it. If full is true, take the whole thing; if it's false, then take only the first "word" */ +static void accept_autosuggestion(bool full) { - /* Accept any autosuggestion by replacing the command line with it. */ if (! data->autosuggestion.empty()) { /* Accept the autosuggestion */ - data->command_line = data->autosuggestion; + if (full) + { + /* Just take the whole thing */ + data->command_line = data->autosuggestion; + } + else + { + /* Accept characters up to a word separator */ + move_word_state_machine_t state; + for (size_t idx = data->command_line.size(); idx < data->autosuggestion.size(); idx++) + { + wchar_t wc = data->autosuggestion.at(idx); + if (! state.consume_char(wc)) + break; + data->command_line.push_back(wc); + } + } data->buff_pos = data->command_line.size(); data->command_line_changed(); reader_super_highlight_me_plenty(data->buff_pos); @@ -2026,98 +2042,6 @@ static void handle_token_history(int forward, int reset) } } -/* Our state machine that implements "one word" movement or erasure. */ -class move_word_state_machine_t -{ - enum - { - s_whitespace, - s_separator, - s_slash, - s_nonseparators_except_slash, - s_end - } state; - -public: - - move_word_state_machine_t() : state(s_whitespace) - { - } - - bool consume_char(wchar_t c) - { - //printf("state %d, consume '%lc'\n", state, c); - bool consumed = false; - /* Always treat separators as first. All this does is ensure that we treat ^ as a string character instead of as stderr redirection, which I hypothesize is usually what is desired. */ - bool was_first = true; - while (state != s_end && ! consumed) - { - switch (state) - { - case s_whitespace: - if (iswspace(c)) - { - /* Consumed whitespace */ - consumed = true; - } - else if (tok_is_string_character(c, was_first)) - { - /* String path */ - state = s_slash; - } - else - { - /* Separator path */ - state = s_separator; - } - break; - - case s_separator: - if (! iswspace(c) && ! tok_is_string_character(c, was_first)) - { - /* Consumed separator */ - consumed = true; - } - else - { - state = s_end; - } - break; - - case s_slash: - if (c == L'/') - { - /* Consumed slash */ - consumed = true; - } - else - { - state = s_nonseparators_except_slash; - } - break; - - case s_nonseparators_except_slash: - if (c != L'/' && tok_is_string_character(c, was_first)) - { - /* Consumed string character except slash */ - consumed = true; - } - else - { - state = s_end; - } - break; - - /* We won't get here, but keep the compiler happy */ - case s_end: - default: - break; - } - } - return consumed; - } -}; - /** Move buffer position one word or erase one word. This function updates both the internal buffer and the screen. It is used by @@ -3338,7 +3262,7 @@ const wchar_t *reader_readline() } else { - accept_autosuggestion(); + accept_autosuggestion(true); } break; } @@ -3367,7 +3291,14 @@ const wchar_t *reader_readline() /* move one word right*/ case R_FORWARD_WORD: { - move_word(MOVE_DIR_RIGHT, false /* do not erase */, false); + if (data->buff_pos < data->command_length()) + { + move_word(MOVE_DIR_RIGHT, false /* do not erase */, false); + } + else + { + accept_autosuggestion(false /* accept only one word */); + } break; } @@ -3440,7 +3371,7 @@ const wchar_t *reader_readline() case R_ACCEPT_AUTOSUGGESTION: { - accept_autosuggestion(); + accept_autosuggestion(true); break; } diff --git a/screen.cpp b/screen.cpp index b9bf37ce4..8e83f7dc2 100644 --- a/screen.cpp +++ b/screen.cpp @@ -566,9 +566,10 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int new_y) { if (s->actual.cursor.x == new_x && s->actual.cursor.y == new_y) return; - + // If we are at the end of our window, then either the cursor stuck to the edge or it didn't. We don't know! We can fix it up though. - if (s->actual.cursor.x == common_get_width()) { + if (s->actual.cursor.x == common_get_width()) + { // Either issue a cr to go back to the beginning of this line, or a nl to go to the beginning of the next one, depending on what we think is more efficient if (new_y <= s->actual.cursor.y) { @@ -887,7 +888,8 @@ static void s_update(screen_t *scr, const wchar_t *left_prompt, const wchar_t *r } /* If we're soft wrapped, and if we're going to change the first character of the next line, don't skip over the last two characters so that we maintain soft-wrapping */ - if (o_line.is_soft_wrapped && i + 1 < scr->desired.line_count()) { + if (o_line.is_soft_wrapped && i + 1 < scr->desired.line_count()) + { bool first_character_of_next_line_will_change = true; if (i + 1 < scr->actual.line_count()) { diff --git a/tokenizer.cpp b/tokenizer.cpp index 7eb216163..a727743a9 100644 --- a/tokenizer.cpp +++ b/tokenizer.cpp @@ -669,6 +669,85 @@ void tok_set_pos(tokenizer_t *tok, int pos) } + +move_word_state_machine_t::move_word_state_machine_t() : state(s_whitespace) +{ +} + +bool move_word_state_machine_t::consume_char(wchar_t c) +{ + //printf("state %d, consume '%lc'\n", state, c); + bool consumed = false; + /* Always treat separators as first. All this does is ensure that we treat ^ as a string character instead of as stderr redirection, which I hypothesize is usually what is desired. */ + bool was_first = true; + while (state != s_end && ! consumed) + { + switch (state) + { + case s_whitespace: + if (iswspace(c)) + { + /* Consumed whitespace */ + consumed = true; + } + else if (tok_is_string_character(c, was_first)) + { + /* String path */ + state = s_slash; + } + else + { + /* Separator path */ + state = s_separator; + } + break; + + case s_separator: + if (! iswspace(c) && ! tok_is_string_character(c, was_first)) + { + /* Consumed separator */ + consumed = true; + } + else + { + state = s_end; + } + break; + + case s_slash: + if (c == L'/') + { + /* Consumed slash */ + consumed = true; + } + else + { + state = s_nonseparators_except_slash; + } + break; + + case s_nonseparators_except_slash: + if (c != L'/' && tok_is_string_character(c, was_first)) + { + /* Consumed string character except slash */ + consumed = true; + } + else + { + state = s_end; + } + break; + + /* We won't get here, but keep the compiler happy */ + case s_end: + default: + break; + } + } + return consumed; +} + + #ifdef TOKENIZER_TEST /** diff --git a/tokenizer.h b/tokenizer.h index 23a4361ff..44438bb34 100644 --- a/tokenizer.h +++ b/tokenizer.h @@ -184,4 +184,22 @@ const wchar_t *tok_get_desc(int type); int tok_get_error(tokenizer_t *tok); +/* Our state machine that implements "one word" movement or erasure. */ +class move_word_state_machine_t +{ + enum + { + s_whitespace, + s_separator, + s_slash, + s_nonseparators_except_slash, + s_end + } state; + +public: + move_word_state_machine_t(); + bool consume_char(wchar_t c); +}; + + #endif