mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-11-23 15:16:40 +08:00
Move readline loop state into reader state
To be used by the next commit.
This commit is contained in:
parent
f9bdad3f77
commit
9d7116c12d
151
src/reader.rs
151
src/reader.rs
|
@ -373,8 +373,6 @@ enum JumpPrecision {
|
|||
}
|
||||
|
||||
/// readline_loop_state_t encapsulates the state used in a readline loop.
|
||||
/// It is always stack allocated transient. This state should not be "publicly visible"; public
|
||||
/// state should be in reader_data_t.
|
||||
struct ReadlineLoopState {
|
||||
/// The last command that was executed.
|
||||
last_cmd: Option<ReadlineCmd>,
|
||||
|
@ -550,6 +548,8 @@ pub struct ReaderData {
|
|||
/// If these differs from the text of the command line, then we must kick off a new request.
|
||||
in_flight_highlight_request: WString,
|
||||
in_flight_autosuggest_request: WString,
|
||||
|
||||
rls: Option<ReadlineLoopState>,
|
||||
}
|
||||
|
||||
/// Read commands from \c fd until encountering EOF.
|
||||
|
@ -1093,6 +1093,7 @@ impl ReaderData {
|
|||
last_jump_precision: JumpPrecision::To,
|
||||
in_flight_highlight_request: Default::default(),
|
||||
in_flight_autosuggest_request: Default::default(),
|
||||
rls: None,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -1143,6 +1144,13 @@ impl ReaderData {
|
|||
&self.parser_ref
|
||||
}
|
||||
|
||||
fn rls(&self) -> &ReadlineLoopState {
|
||||
self.rls.as_ref().unwrap()
|
||||
}
|
||||
fn rls_mut(&mut self) -> &mut ReadlineLoopState {
|
||||
self.rls.as_mut().unwrap()
|
||||
}
|
||||
|
||||
// We repaint our prompt if fstat reports the tty as having changed.
|
||||
// But don't react to tty changes that we initiated, because of commands or
|
||||
// on-variable events (e.g. for fish_bind_mode). See #3481.
|
||||
|
@ -1746,7 +1754,7 @@ impl ReaderData {
|
|||
/// Read a command to execute, respecting input bindings.
|
||||
/// \return the command, or none if we were asked to cancel (e.g. SIGHUP).
|
||||
fn readline(&mut self, nchars: Option<NonZeroUsize>) -> Option<WString> {
|
||||
let mut rls = ReadlineLoopState::new();
|
||||
self.rls = Some(ReadlineLoopState::new());
|
||||
|
||||
// Suppress fish_trace during executing key bindings.
|
||||
// This is simply to reduce noise.
|
||||
|
@ -1763,7 +1771,7 @@ impl ReaderData {
|
|||
|
||||
// If nchars_or_0 is positive, then that's the maximum number of chars. Otherwise keep it at
|
||||
// SIZE_MAX.
|
||||
rls.nchars = nchars;
|
||||
zelf.rls_mut().nchars = nchars;
|
||||
|
||||
// The command line before completion.
|
||||
zelf.cycle_command_line.clear();
|
||||
|
@ -1820,8 +1828,8 @@ impl ReaderData {
|
|||
// Start out as initially dirty.
|
||||
zelf.force_exec_prompt_and_repaint = true;
|
||||
|
||||
while !rls.finished && !check_exit_loop_maybe_warning(Some(&mut zelf)) {
|
||||
if zelf.handle_char_event(&mut rls, None).is_break() {
|
||||
while !zelf.rls().finished && !check_exit_loop_maybe_warning(Some(&mut zelf)) {
|
||||
if zelf.handle_char_event(None).is_break() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1833,7 +1841,7 @@ impl ReaderData {
|
|||
}
|
||||
|
||||
// Finish syntax highlighting (but do not wait forever).
|
||||
if rls.finished {
|
||||
if zelf.rls().finished {
|
||||
zelf.finish_highlighting_before_exec();
|
||||
}
|
||||
|
||||
|
@ -1872,7 +1880,12 @@ impl ReaderData {
|
|||
.borrow_mut()
|
||||
.set_color(RgbColor::RESET, RgbColor::RESET);
|
||||
}
|
||||
rls.finished.then(|| zelf.command_line.text().to_owned())
|
||||
let result = zelf
|
||||
.rls()
|
||||
.finished
|
||||
.then(|| zelf.command_line.text().to_owned());
|
||||
zelf.rls = None;
|
||||
result
|
||||
}
|
||||
|
||||
fn eval_bind_cmd(&mut self, cmd: &wstr) {
|
||||
|
@ -1907,10 +1920,10 @@ impl ReaderData {
|
|||
|
||||
/// Read normal characters, inserting them into the command line.
|
||||
/// \return the next unhandled event.
|
||||
fn read_normal_chars(&mut self, rls: &mut ReadlineLoopState) -> Option<CharEvent> {
|
||||
fn read_normal_chars(&mut self) -> Option<CharEvent> {
|
||||
let mut event_needing_handling = None;
|
||||
let limit = std::cmp::min(
|
||||
rls.nchars.map_or(usize::MAX, |nchars| {
|
||||
self.rls().nchars.map_or(usize::MAX, |nchars| {
|
||||
usize::from(nchars) - self.command_line.len()
|
||||
}),
|
||||
READAHEAD_MAX,
|
||||
|
@ -1954,7 +1967,7 @@ impl ReaderData {
|
|||
}
|
||||
|
||||
// Since we handled a normal character, we don't have a last command.
|
||||
rls.last_cmd = None;
|
||||
self.rls_mut().last_cmd = None;
|
||||
}
|
||||
|
||||
event_needing_handling
|
||||
|
@ -1974,15 +1987,11 @@ impl ReaderData {
|
|||
self.force_exec_prompt_and_repaint = false;
|
||||
}
|
||||
|
||||
fn handle_char_event(
|
||||
&mut self,
|
||||
rls: &mut ReadlineLoopState,
|
||||
injected_event: Option<CharEvent>,
|
||||
) -> ControlFlow<()> {
|
||||
fn handle_char_event(&mut self, injected_event: Option<CharEvent>) -> ControlFlow<()> {
|
||||
if self.reset_loop_state {
|
||||
self.reset_loop_state = false;
|
||||
rls.last_cmd = None;
|
||||
rls.complete_did_insert = false;
|
||||
self.rls_mut().last_cmd = None;
|
||||
self.rls_mut().complete_did_insert = false;
|
||||
}
|
||||
// Perhaps update the termsize. This is cheap if it has not changed.
|
||||
self.update_termsize();
|
||||
|
@ -1990,21 +1999,23 @@ impl ReaderData {
|
|||
// Repaint as needed.
|
||||
self.color_suggest_repaint_now();
|
||||
|
||||
if rls
|
||||
if self
|
||||
.rls()
|
||||
.nchars
|
||||
.is_some_and(|nchars| usize::from(nchars) <= self.command_line.len())
|
||||
{
|
||||
// We've already hit the specified character limit.
|
||||
rls.finished = true;
|
||||
self.rls_mut().finished = true;
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
|
||||
let event_needing_handling = injected_event.or_else(|| loop {
|
||||
let event_needing_handling = self.read_normal_chars(rls);
|
||||
let event_needing_handling = self.read_normal_chars();
|
||||
if event_needing_handling.is_some() {
|
||||
break event_needing_handling;
|
||||
}
|
||||
if rls
|
||||
if self
|
||||
.rls()
|
||||
.nchars
|
||||
.is_some_and(|nchars| usize::from(nchars) <= self.command_line.len())
|
||||
{
|
||||
|
@ -2030,8 +2041,11 @@ impl ReaderData {
|
|||
return ControlFlow::Continue(());
|
||||
}
|
||||
|
||||
if !matches!(rls.last_cmd, Some(ReadlineCmd::Yank | ReadlineCmd::YankPop)) {
|
||||
rls.yank_len = 0;
|
||||
if !matches!(
|
||||
self.rls().last_cmd,
|
||||
Some(ReadlineCmd::Yank | ReadlineCmd::YankPop)
|
||||
) {
|
||||
self.rls_mut().yank_len = 0;
|
||||
}
|
||||
|
||||
match event_needing_handling {
|
||||
|
@ -2050,7 +2064,7 @@ impl ReaderData {
|
|||
self.clear_pager();
|
||||
}
|
||||
|
||||
self.handle_readline_command(readline_cmd, rls);
|
||||
self.handle_readline_command(readline_cmd);
|
||||
|
||||
if self.history_search.active() && command_ends_history_search(readline_cmd) {
|
||||
// "cancel" means to abort the whole thing, other ending commands mean to finish the
|
||||
|
@ -2063,7 +2077,7 @@ impl ReaderData {
|
|||
self.command_line_has_transient_edit = false;
|
||||
}
|
||||
|
||||
rls.last_cmd = Some(readline_cmd);
|
||||
self.rls_mut().last_cmd = Some(readline_cmd);
|
||||
}
|
||||
CharEvent::Command(command) => {
|
||||
self.run_input_command_scripts(&command);
|
||||
|
@ -2087,7 +2101,7 @@ impl ReaderData {
|
|||
}
|
||||
}
|
||||
}
|
||||
rls.last_cmd = None;
|
||||
self.rls_mut().last_cmd = None;
|
||||
}
|
||||
CharEvent::Eof | CharEvent::CheckExit => {
|
||||
panic!("Should have a char, readline or command")
|
||||
|
@ -2098,7 +2112,7 @@ impl ReaderData {
|
|||
}
|
||||
|
||||
impl ReaderData {
|
||||
fn handle_readline_command(&mut self, c: ReadlineCmd, rls: &mut ReadlineLoopState) {
|
||||
fn handle_readline_command(&mut self, c: ReadlineCmd) {
|
||||
type rl = ReadlineCmd;
|
||||
match c {
|
||||
rl::BeginningOfLine => {
|
||||
|
@ -2163,8 +2177,11 @@ impl ReaderData {
|
|||
// but never complete{,_and_search})
|
||||
//
|
||||
// Also paging is already cancelled above.
|
||||
if rls.complete_did_insert
|
||||
&& matches!(rls.last_cmd, Some(rl::Complete | rl::CompleteAndSearch))
|
||||
if self.rls().complete_did_insert
|
||||
&& matches!(
|
||||
self.rls().last_cmd,
|
||||
Some(rl::Complete | rl::CompleteAndSearch)
|
||||
)
|
||||
{
|
||||
let (elt, el) = self.active_edit_line_mut();
|
||||
el.undo();
|
||||
|
@ -2208,9 +2225,9 @@ impl ReaderData {
|
|||
return;
|
||||
}
|
||||
if self.is_navigating_pager_contents()
|
||||
|| (!rls.comp.is_empty()
|
||||
&& !rls.complete_did_insert
|
||||
&& rls.last_cmd == Some(rl::Complete))
|
||||
|| (!self.rls().comp.is_empty()
|
||||
&& !self.rls().complete_did_insert
|
||||
&& self.rls().last_cmd == Some(rl::Complete))
|
||||
{
|
||||
// The user typed complete more than once in a row. If we are not yet fully
|
||||
// disclosed, then become so; otherwise cycle through our available completions.
|
||||
|
@ -2228,7 +2245,7 @@ impl ReaderData {
|
|||
}
|
||||
} else {
|
||||
// Either the user hit tab only once, or we had no visible completion list.
|
||||
self.compute_and_apply_completions(c, rls);
|
||||
self.compute_and_apply_completions(c);
|
||||
}
|
||||
}
|
||||
rl::PagerToggleSearch => {
|
||||
|
@ -2266,7 +2283,12 @@ impl ReaderData {
|
|||
|
||||
let range = begin..end;
|
||||
if !range.is_empty() {
|
||||
self.kill(elt, range, Kill::Append, rls.last_cmd != Some(rl::KillLine));
|
||||
self.kill(
|
||||
elt,
|
||||
range,
|
||||
Kill::Append,
|
||||
self.rls().last_cmd != Some(rl::KillLine),
|
||||
);
|
||||
}
|
||||
}
|
||||
rl::BackwardKillLine => {
|
||||
|
@ -2297,7 +2319,7 @@ impl ReaderData {
|
|||
elt,
|
||||
end - len..end,
|
||||
Kill::Prepend,
|
||||
rls.last_cmd != Some(rl::BackwardKillLine),
|
||||
self.rls().last_cmd != Some(rl::BackwardKillLine),
|
||||
);
|
||||
}
|
||||
rl::KillWholeLine | rl::KillInnerLine => {
|
||||
|
@ -2342,20 +2364,25 @@ impl ReaderData {
|
|||
assert!(end >= begin);
|
||||
|
||||
if end > begin {
|
||||
self.kill(elt, begin..end, Kill::Append, rls.last_cmd != Some(c));
|
||||
self.kill(
|
||||
elt,
|
||||
begin..end,
|
||||
Kill::Append,
|
||||
self.rls().last_cmd != Some(c),
|
||||
);
|
||||
}
|
||||
}
|
||||
rl::Yank => {
|
||||
let yank_str = kill_yank();
|
||||
self.insert_string(self.active_edit_line_tag(), &yank_str);
|
||||
rls.yank_len = yank_str.len();
|
||||
self.rls_mut().yank_len = yank_str.len();
|
||||
if self.cursor_end_mode == CursorEndMode::Inclusive {
|
||||
let (_elt, el) = self.active_edit_line();
|
||||
self.update_buff_pos(self.active_edit_line_tag(), Some(el.position() - 1));
|
||||
}
|
||||
}
|
||||
rl::YankPop => {
|
||||
if rls.yank_len != 0 {
|
||||
if self.rls().yank_len != 0 {
|
||||
let (elt, el) = self.active_edit_line();
|
||||
let yank_str = kill_yank_rotate();
|
||||
let new_yank_len = yank_str.len();
|
||||
|
@ -2364,11 +2391,11 @@ impl ReaderData {
|
|||
} else {
|
||||
0
|
||||
};
|
||||
let begin = el.position() + bias - rls.yank_len;
|
||||
let begin = el.position() + bias - self.rls().yank_len;
|
||||
let end = el.position() + bias;
|
||||
self.replace_substring(elt, begin..end, yank_str);
|
||||
self.update_buff_pos(elt, None);
|
||||
rls.yank_len = new_yank_len;
|
||||
self.rls_mut().yank_len = new_yank_len;
|
||||
self.suppress_autosuggestion = true;
|
||||
}
|
||||
}
|
||||
|
@ -2397,7 +2424,7 @@ impl ReaderData {
|
|||
}
|
||||
}
|
||||
rl::Execute => {
|
||||
if !self.handle_execute(rls) {
|
||||
if !self.handle_execute() {
|
||||
event::fire_generic(
|
||||
self.parser(),
|
||||
L!("fish_posterror").to_owned(),
|
||||
|
@ -2584,7 +2611,7 @@ impl ReaderData {
|
|||
};
|
||||
// Is this the same killring item as the last kill?
|
||||
let newv = !matches!(
|
||||
rls.last_cmd,
|
||||
self.rls().last_cmd,
|
||||
Some(
|
||||
rl::BackwardKillWord
|
||||
| rl::BackwardKillPathComponent
|
||||
|
@ -2612,7 +2639,7 @@ impl ReaderData {
|
|||
MoveWordDir::Right,
|
||||
/*erase=*/ true,
|
||||
style,
|
||||
rls.last_cmd != Some(c),
|
||||
self.rls().last_cmd != Some(c),
|
||||
);
|
||||
}
|
||||
rl::BackwardWord | rl::BackwardBigword | rl::PrevdOrBackwardWord => {
|
||||
|
@ -2938,7 +2965,7 @@ impl ReaderData {
|
|||
self.update_buff_pos(self.active_edit_line_tag(), Some(tmp));
|
||||
}
|
||||
rl::KillSelection => {
|
||||
let newv = rls.last_cmd != Some(rl::KillSelection);
|
||||
let newv = self.rls().last_cmd != Some(rl::KillSelection);
|
||||
if let Some(selection) = self.get_selection() {
|
||||
self.kill(EditableLineTag::Commandline, selection, Kill::Append, newv);
|
||||
}
|
||||
|
@ -3099,7 +3126,7 @@ impl ReaderData {
|
|||
// unfinished. It may also set 'finished' and 'cmd' inside the rls.
|
||||
// \return true on success, false if we got an error, in which case the caller should fire the
|
||||
// error event.
|
||||
fn handle_execute(&mut self, rls: &mut ReadlineLoopState) -> bool {
|
||||
fn handle_execute(&mut self) -> bool {
|
||||
// Evaluate. If the current command is unfinished, or if the charater is escaped
|
||||
// using a backslash, insert a newline.
|
||||
// If the user hits return while navigating the pager, it only clears the pager.
|
||||
|
@ -3169,7 +3196,7 @@ impl ReaderData {
|
|||
}
|
||||
|
||||
self.add_to_history();
|
||||
rls.finished = true;
|
||||
self.rls_mut().finished = true;
|
||||
self.update_buff_pos(elt, Some(self.command_line.len()));
|
||||
true
|
||||
}
|
||||
|
@ -5176,7 +5203,7 @@ fn get_best_rank(comp: &[Completion]) -> u32 {
|
|||
|
||||
impl ReaderData {
|
||||
/// Compute completions and update the pager and/or commandline as needed.
|
||||
fn compute_and_apply_completions(&mut self, c: ReadlineCmd, rls: &mut ReadlineLoopState) {
|
||||
fn compute_and_apply_completions(&mut self, c: ReadlineCmd) {
|
||||
assert!(matches!(
|
||||
c,
|
||||
ReadlineCmd::Complete | ReadlineCmd::CompleteAndSearch
|
||||
|
@ -5236,8 +5263,8 @@ impl ReaderData {
|
|||
return;
|
||||
}
|
||||
ExpandResultCode::ok => {
|
||||
rls.comp.clear();
|
||||
rls.complete_did_insert = false;
|
||||
self.rls_mut().comp.clear();
|
||||
self.rls_mut().complete_did_insert = false;
|
||||
self.push_edit(
|
||||
EditableLineTag::Commandline,
|
||||
Edit::new(token_range, wc_expanded),
|
||||
|
@ -5258,31 +5285,38 @@ impl ReaderData {
|
|||
CompletionRequestOptions::normal(),
|
||||
&self.parser().context(),
|
||||
);
|
||||
rls.comp = comp;
|
||||
self.rls_mut().comp = comp;
|
||||
|
||||
let el = &self.command_line;
|
||||
// User-supplied completions may have changed the commandline - prevent buffer
|
||||
// overflow.
|
||||
token_range.start = std::cmp::min(token_range.start, el.text().len());
|
||||
token_range.end = std::cmp::min(token_range.end, el.text().len());
|
||||
|
||||
// Munge our completions.
|
||||
sort_and_prioritize(&mut rls.comp, CompletionRequestOptions::default());
|
||||
sort_and_prioritize(
|
||||
&mut self.rls_mut().comp,
|
||||
CompletionRequestOptions::default(),
|
||||
);
|
||||
|
||||
let el = &self.command_line;
|
||||
// Record our cycle_command_line.
|
||||
self.cycle_command_line = el.text().to_owned();
|
||||
self.cycle_cursor_pos = token_range.end;
|
||||
|
||||
rls.complete_did_insert = self.handle_completions(&rls.comp, token_range);
|
||||
self.rls_mut().complete_did_insert = self.handle_completions(token_range);
|
||||
|
||||
// Show the search field if requested and if we printed a list of completions.
|
||||
if c == ReadlineCmd::CompleteAndSearch && !rls.complete_did_insert && !self.pager.is_empty()
|
||||
if c == ReadlineCmd::CompleteAndSearch
|
||||
&& !self.rls().complete_did_insert
|
||||
&& !self.pager.is_empty()
|
||||
{
|
||||
self.pager.set_search_field_shown(true);
|
||||
self.select_completion_in_direction(SelectionMotion::Next, false);
|
||||
}
|
||||
}
|
||||
|
||||
fn try_insert(&mut self, c: &Completion, tok: &wstr, token_range: Range<usize>) {
|
||||
fn try_insert(&mut self, c: Completion, tok: &wstr, token_range: Range<usize>) {
|
||||
// If this is a replacement completion, check that we know how to replace it, e.g. that
|
||||
// the token doesn't contain evil operators like {}.
|
||||
if !c.flags.contains(CompleteFlags::REPLACES_TOKEN) || reader_can_replace(tok, c.flags) {
|
||||
|
@ -5305,9 +5339,10 @@ impl ReaderData {
|
|||
/// \param token_end the position after the token to complete
|
||||
///
|
||||
/// Return true if we inserted text into the command line, false if we did not.
|
||||
fn handle_completions(&mut self, comp: &[Completion], token_range: Range<usize>) -> bool {
|
||||
fn handle_completions(&mut self, token_range: Range<usize>) -> bool {
|
||||
let tok = self.command_line.text()[token_range.clone()].to_owned();
|
||||
|
||||
let comp = &self.rls().comp;
|
||||
// Check trivial cases.
|
||||
let len = comp.len();
|
||||
if len == 0 {
|
||||
|
@ -5317,7 +5352,7 @@ impl ReaderData {
|
|||
} else if len == 1 {
|
||||
// Exactly one suitable completion found - insert it.
|
||||
let c = &comp[0];
|
||||
self.try_insert(c, &tok, token_range);
|
||||
self.try_insert(c.clone(), &tok, token_range);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -5367,7 +5402,7 @@ impl ReaderData {
|
|||
// the token is "cma" and the options are "cmake/" and "CMakeLists.txt"
|
||||
// it would be nice if we could figure
|
||||
// out how to use it more.
|
||||
let c = &surviving_completions[0];
|
||||
let c = std::mem::take(&mut surviving_completions[0]);
|
||||
|
||||
self.try_insert(c, &tok, token_range);
|
||||
return true;
|
||||
|
|
Loading…
Reference in New Issue
Block a user