mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-25 12:23:09 +00:00
forward-word should accept a word of an autosuggestion
https://github.com/fish-shell/fish-shell/issues/435
This commit is contained in:
parent
983bc5cecc
commit
eec6db0a23
6 changed files with 136 additions and 105 deletions
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
127
reader.cpp
127
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -568,7 +568,8 @@ static void s_move(screen_t *s, data_buffer_t *b, int new_x, int 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())
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
18
tokenizer.h
18
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
|
||||
|
|
Loading…
Reference in a new issue