diff --git a/src/main.rs b/src/main.rs index 3ca003f..078a1a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,7 +31,6 @@ use nix::unistd::read; use crate::builtin::keymap::KeyMapMatch; use crate::builtin::trap::TrapTarget; use crate::libsh::error::{self, ShErr, ShErrKind, ShResult}; -use crate::libsh::guards::scope_guard; use crate::libsh::sys::TTY_FILENO; use crate::libsh::utils::AutoCmdVecUtils; use crate::parse::execute::{exec_dash_c, exec_input}; diff --git a/src/parse/execute.rs b/src/parse/execute.rs index 60a8116..7c7d8cb 100644 --- a/src/parse/execute.rs +++ b/src/parse/execute.rs @@ -803,7 +803,7 @@ impl Dispatcher { self.job_stack.new_job(); if cmds.len() == 1 { self.fg_job = !is_bg && self.interactive; - let mut cmd = cmds.into_iter().next().unwrap(); + let cmd = cmds.into_iter().next().unwrap(); if is_bg && !matches!(cmd.class, NdRule::Command { .. }) { self.run_fork( &cmd.get_command().map(|t| t.to_string()).unwrap_or_default(), diff --git a/src/parse/lex.rs b/src/parse/lex.rs index 2d7a764..2f86156 100644 --- a/src/parse/lex.rs +++ b/src/parse/lex.rs @@ -217,6 +217,32 @@ impl Tk { }; self.span.as_str().trim() == ";;" } + + pub fn is_opener(&self) -> bool { + OPENERS.contains(&self.as_str()) || + matches!(self.class, TkRule::BraceGrpStart) || + matches!(self.class, TkRule::CasePattern) + } + pub fn is_closer(&self) -> bool { + matches!(self.as_str(), "fi" | "done" | "esac") || + self.has_double_semi() || + matches!(self.class, TkRule::BraceGrpEnd) + } + + pub fn is_closer_for(&self, other: &Tk) -> bool { + if (matches!(other.class, TkRule::BraceGrpStart) && matches!(self.class, TkRule::BraceGrpEnd)) + || (matches!(other.class, TkRule::CasePattern) && self.has_double_semi()) { + return true; + } + match other.as_str() { + "for" | + "while" | + "until" => matches!(self.as_str(), "done"), + "if" => matches!(self.as_str(), "fi"), + "case" => matches!(self.as_str(), "esac"), + _ => false + } + } } impl Display for Tk { diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 9904095..eccf6ed 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -971,6 +971,7 @@ impl ParseStream { Ok(Some(node)) } fn parse_brc_grp(&mut self, from_func_def: bool) -> ShResult> { + log::debug!("Trying to parse a brace group"); let mut node_tks: Vec = vec![]; let mut body: Vec = vec![]; let mut redirs: Vec = vec![]; @@ -983,6 +984,7 @@ impl ParseStream { self.catch_separator(&mut node_tks); loop { + log::debug!("Parsing a brace group body"); if *self.next_tk_class() == TkRule::BraceGrpEnd { node_tks.push(self.next_tk().unwrap()); break; @@ -990,9 +992,26 @@ impl ParseStream { if let Some(node) = self.parse_cmd_list()? { node_tks.extend(node.tokens.clone()); body.push(node); + } else if *self.next_tk_class() != TkRule::BraceGrpEnd { + let next = self.peek_tk().cloned(); + let err = match next { + Some(tk) => Err(parse_err_full( + &format!("Unexpected token '{}' in brace group body", tk.as_str()), + &tk.span, + self.context.clone(), + )), + None => Err(parse_err_full( + "Unexpected end of input while parsing brace group body", + &node_tks.get_span().unwrap(), + self.context.clone(), + )), + }; + self.panic_mode(&mut node_tks); + return err; } self.catch_separator(&mut node_tks); if !self.next_tk_is_some() { + log::debug!("Hit end of input while parsing a brace group body, entering panic mode"); self.panic_mode(&mut node_tks); return Err(parse_err_full( "Expected a closing brace for this brace group", @@ -1002,10 +1021,14 @@ impl ParseStream { } } + log::debug!("Finished parsing brace group body, now looking for redirections if it's not a function definition"); + if !from_func_def { self.parse_redir(&mut redirs, &mut node_tks)?; } + log::debug!("Finished parsing brace group redirections, constructing node"); + let node = Node { class: NdRule::BraceGrp { body }, flags: NdFlags::empty(), @@ -1550,7 +1573,7 @@ impl ParseStream { node_tks.push(prefix_tk.clone()); assignments.push(assign) } else if is_keyword { - return Ok(None); + return Ok(None) } else if prefix_tk.class == TkRule::Sep { // Separator ends the prefix section - add it so commit() consumes it node_tks.push(prefix_tk.clone()); @@ -2566,4 +2589,9 @@ pub mod tests { let input = "if if while if if until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; else while :; do :; done; fi; then if until :; do :; done; then until :; do :; done; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; else while :; do :; done; fi; elif while while :; do :; done; do until :; do :; done; done; then while until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; elif until case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; then until if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; else case foo in; foo) while :; do :; done;; bar) until :; do :; done;; biz) until :; do :; done;; esac; fi; do while case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) case foo in; foo) :;; bar) :;; biz) :;; esac;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; done; then until while if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; do until while :; do :; done; do until :; do :; done; done; done; elif until until until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; do case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) if :; then :; elif :; then :; elif :; then :; else :; fi;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; done; then case foo in; foo) case foo in; foo) while :; do :; done;; bar) while :; do :; done;; biz) until :; do :; done;; esac;; bar) if until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; else while :; do :; done; fi;; biz) if until :; do :; done; then until :; do :; done; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; else while :; do :; done; fi;; esac; elif case foo in; foo) while while :; do :; done; do until :; do :; done; done;; bar) while until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done;; biz) until case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done;; esac; then if until if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; then case foo in; foo) while :; do :; done;; bar) until :; do :; done;; biz) until :; do :; done;; esac; elif case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) case foo in; foo) :;; bar) :;; biz) :;; esac;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; then if if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; elif while :; do :; done; then until :; do :; done; elif until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; elif if if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif while :; do :; done; then while :; do :; done; elif until :; do :; done; then until :; do :; done; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; then while case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; else while if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; fi; else if until while :; do :; done; do until :; do :; done; done; then until until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; elif case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) if :; then :; elif :; then :; elif :; then :; else :; fi;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; then case foo in; foo) while :; do :; done;; bar) while :; do :; done;; biz) until :; do :; done;; esac; elif if until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; else while :; do :; done; fi; then if until :; do :; done; then until :; do :; done; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; else while :; do :; done; fi; else while while :; do :; done; do until :; do :; done; done; fi; fi; then while while while until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; do until case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; done; do while until if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; do case foo in; foo) while :; do :; done;; bar) until :; do :; done;; biz) until :; do :; done;; esac; done; done; elif until until case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) case foo in; foo) :;; bar) :;; biz) :;; esac;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; do if if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; elif while :; do :; done; then until :; do :; done; elif until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; done; do until if if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif while :; do :; done; then while :; do :; done; elif until :; do :; done; then until :; do :; done; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; do while case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; done; done; then case foo in; foo) case foo in; foo) while if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done;; bar) until while :; do :; done; do until :; do :; done; done;; biz) until until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done;; esac;; bar) case foo in; foo) case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) if :; then :; elif :; then :; elif :; then :; else :; fi;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac;; bar) case foo in; foo) while :; do :; done;; bar) while :; do :; done;; biz) until :; do :; done;; esac;; biz) if until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; else while :; do :; done; fi;; esac;; biz) if if until :; do :; done; then until :; do :; done; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; else while :; do :; done; fi; then while while :; do :; done; do until :; do :; done; done; elif while until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; then until case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; elif until if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; then case foo in; foo) while :; do :; done;; bar) until :; do :; done;; biz) until :; do :; done;; esac; else case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) case foo in; foo) :;; bar) :;; biz) :;; esac;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; fi;; esac; elif if if if if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; elif while :; do :; done; then until :; do :; done; elif until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; then if if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif while :; do :; done; then while :; do :; done; elif until :; do :; done; then until :; do :; done; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; elif while case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; then while if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; elif until while :; do :; done; do until :; do :; done; done; then until until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; else case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) if :; then :; elif :; then :; elif :; then :; else :; fi;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; fi; then while case foo in; foo) while :; do :; done;; bar) while :; do :; done;; biz) until :; do :; done;; esac; do if until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; else while :; do :; done; fi; done; elif while if until :; do :; done; then until :; do :; done; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; else while :; do :; done; fi; do while while :; do :; done; do until :; do :; done; done; done; then until while until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; do until case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; done; elif until until if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; do case foo in; foo) while :; do :; done;; bar) until :; do :; done;; biz) until :; do :; done;; esac; done; then case foo in; foo) case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) case foo in; foo) :;; bar) :;; biz) :;; esac;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac;; bar) if if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; elif while :; do :; done; then until :; do :; done; elif until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi;; biz) if if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif while :; do :; done; then while :; do :; done; elif until :; do :; done; then until :; do :; done; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi;; esac; else case foo in; foo) while case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done;; bar) while if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done;; biz) until while :; do :; done; do until :; do :; done; done;; esac; fi; then if if until until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; then case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) if :; then :; elif :; then :; elif :; then :; else :; fi;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; elif case foo in; foo) while :; do :; done;; bar) while :; do :; done;; biz) until :; do :; done;; esac; then if until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; else while :; do :; done; fi; elif if until :; do :; done; then until :; do :; done; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; else while :; do :; done; fi; then while while :; do :; done; do until :; do :; done; done; else while until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; fi; then if until case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; then until if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; elif case foo in; foo) while :; do :; done;; bar) until :; do :; done;; biz) until :; do :; done;; esac; then case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) case foo in; foo) :;; bar) :;; biz) :;; esac;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; elif if if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; elif while :; do :; done; then until :; do :; done; elif until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; then if if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif while :; do :; done; then while :; do :; done; elif until :; do :; done; then until :; do :; done; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; else while case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; fi; elif while while if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; do until while :; do :; done; do until :; do :; done; done; done; then while until until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; do case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) if :; then :; elif :; then :; elif :; then :; else :; fi;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; done; elif until case foo in; foo) while :; do :; done;; bar) while :; do :; done;; biz) until :; do :; done;; esac; do if until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; else while :; do :; done; fi; done; then until if until :; do :; done; then until :; do :; done; elif case foo in; foo) :;; bar) :;; biz) :;; esac; then case foo in; foo) :;; bar) :;; biz) :;; esac; elif if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; else while :; do :; done; fi; do while while :; do :; done; do until :; do :; done; done; done; else case foo in; foo) while until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done;; bar) until case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done;; biz) until if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done;; esac; fi; else while case foo in; foo) case foo in; foo) while :; do :; done;; bar) until :; do :; done;; biz) until :; do :; done;; esac;; bar) case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) case foo in; foo) :;; bar) :;; biz) :;; esac;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac;; biz) if if :; then :; elif :; then :; elif :; then :; else :; fi; then while :; do :; done; elif while :; do :; done; then until :; do :; done; elif until :; do :; done; then case foo in; foo) :;; bar) :;; biz) :;; esac; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi;; esac; do if if if :; then :; elif :; then :; elif :; then :; else :; fi; then if :; then :; elif :; then :; elif :; then :; else :; fi; elif while :; do :; done; then while :; do :; done; elif until :; do :; done; then until :; do :; done; else case foo in; foo) :;; bar) :;; biz) :;; esac; fi; then while case foo in; foo) :;; bar) :;; biz) :;; esac; do if :; then :; elif :; then :; elif :; then :; else :; fi; done; elif while if :; then :; elif :; then :; elif :; then :; else :; fi; do while :; do :; done; done; then until while :; do :; done; do until :; do :; done; done; elif until until :; do :; done; do case foo in; foo) :;; bar) :;; biz) :;; esac; done; then case foo in; foo) case foo in; foo) :;; bar) :;; biz) :;; esac;; bar) if :; then :; elif :; then :; elif :; then :; else :; fi;; biz) if :; then :; elif :; then :; elif :; then :; else :; fi;; esac; else case foo in; foo) while :; do :; done;; bar) while :; do :; done;; biz) until :; do :; done;; esac; fi; done; fi"; assert!(get_ast(input).is_ok()); // lets spare our sanity and just say that "ok" means "it parsed correctly" } + #[test] + fn parse_stray_keyword_in_brace_group() { + let input = "{ echo bar case foo in bar) echo fizz ;; buzz) echo buzz ;; esac }"; + assert!(get_ast(input).is_err()); + } } diff --git a/src/readline/complete.rs b/src/readline/complete.rs index b5264f3..b92f44d 100644 --- a/src/readline/complete.rs +++ b/src/readline/complete.rs @@ -1,7 +1,7 @@ use std::{ collections::HashSet, fmt::{Debug, Write}, - path::{Path, PathBuf}, + path::PathBuf, sync::Arc, }; diff --git a/src/readline/linebuf.rs b/src/readline/linebuf.rs index ebcfd2e..679469b 100644 --- a/src/readline/linebuf.rs +++ b/src/readline/linebuf.rs @@ -15,7 +15,7 @@ use crate::{ libsh::{error::ShResult, guards::var_ctx_guard}, parse::{ execute::exec_input, - lex::{LexFlags, LexStream, QuoteState, Tk, TkFlags, TkRule}, + lex::{LexFlags, LexStream, QuoteState, Tk}, }, prelude::*, readline::{ @@ -350,6 +350,51 @@ impl ClampedUsize { } } +#[derive(Default, Clone, Debug)] +pub struct DepthCalc { + depth: usize, + ctx: Vec, +} + +impl DepthCalc { + pub fn new() -> Self { Self::default() } + + pub fn descend(&mut self, tk: Tk) { + self.ctx.push(tk); + self.depth += 1; + } + + pub fn ascend(&mut self) { + self.depth = self.depth.saturating_sub(1); + self.ctx.pop(); + } + + pub fn check_tk(&mut self, tk: Tk) { + if tk.is_opener() { + self.descend(tk); + } else if self.ctx.last().is_some_and(|t| tk.is_closer_for(t)) { + self.ascend(); + } + } + + pub fn calculate(&mut self, input: &str) -> usize { + if input.ends_with("\\\n") { + self.depth += 1; // Line continuation, so we need to add an extra level + } + let input = Arc::new(input.to_string()); + let Ok(tokens) = LexStream::new(input.clone(), LexFlags::LEX_UNFINISHED).collect::>>() else { + log::error!("Lexing failed during depth calculation: {:?}", input); + return 0; + }; + + for tk in tokens { + self.check_tk(tk); + } + + self.depth + } +} + #[derive(Default, Clone, Debug)] pub struct LineBuf { pub buffer: String, @@ -829,7 +874,7 @@ impl LineBuf { } Some(self.line_bounds(line_no)) } - pub fn word_at(&mut self, pos: usize, word: Word) -> (usize, usize) { + pub fn word_at(&mut self, _pos: usize, word: Word) -> (usize, usize) { let start = if self.is_word_bound(self.cursor.get(), word, Direction::Backward) { self.cursor.get() } else { @@ -2032,50 +2077,14 @@ impl LineBuf { self.buffer.replace_range(start..end, new); } pub fn calc_indent_level(&mut self) { - // FIXME: This implementation is extremely naive but it kind of sort of works for now - // Need to re-implement it and write tests let to_cursor = self .slice_to_cursor() .map(|s| s.to_string()) .unwrap_or(self.buffer.clone()); - let mut level: usize = 0; + let mut calc = DepthCalc::new(); - if to_cursor.ends_with("\\\n") { - level += 1; // Line continuation, so we need to add an extra level - } - - let input = Arc::new(to_cursor); - let Ok(tokens) = LexStream::new(input, LexFlags::LEX_UNFINISHED).collect::>>() - else { - log::error!("Failed to lex buffer for indent calculation"); - return; - }; - let mut last_keyword: Option = None; - for tk in tokens { - if tk.flags.contains(TkFlags::KEYWORD) { - match tk.as_str() { - "in" => { - if last_keyword.as_deref() == Some("case") { - level += 1; - } else { - // 'in' is also used in for loops, but we already increment level on 'do' for those - // so we just skip it here - } - } - "then" | "do" => level += 1, - "done" | "fi" | "esac" => level = level.saturating_sub(1), - _ => { /* Continue */ } - } - last_keyword = Some(tk.to_string()); - } else if tk.class == TkRule::BraceGrpStart { - level += 1; - } else if tk.class == TkRule::BraceGrpEnd { - level = level.saturating_sub(1); - } - } - - self.auto_indent_level = level; + self.auto_indent_level = calc.calculate(&to_cursor); } pub fn eval_motion(&mut self, verb: Option<&Verb>, motion: MotionCmd) -> MotionKind { let buffer = self.buffer.clone(); @@ -2918,34 +2927,28 @@ impl LineBuf { self.insert_at_cursor(ch); self.cursor.add(1); let before = self.auto_indent_level; - if read_shopts(|o| o.prompt.auto_indent) - && let Some(line_content) = self.this_line_content() - { - match line_content.trim() { - "esac" | "done" | "fi" | "}" => { - self.calc_indent_level(); - if self.auto_indent_level < before { - let delta = before - self.auto_indent_level; - let line_start = self.start_of_line(); - for _ in 0..delta { - if self.grapheme_at(line_start).is_some_and(|gr| gr == "\t") { - self.remove(line_start); - if !self.cursor_at_max() { - self.cursor.sub(1); - } - } - } - } - } - _ => {} - } - } + if read_shopts(|o| o.prompt.auto_indent) { + self.calc_indent_level(); + if self.auto_indent_level < before { + let delta = before - self.auto_indent_level; + let line_start = self.start_of_line(); + for _ in 0..delta { + if self.grapheme_at(line_start).is_some_and(|gr| gr == "\t") { + self.remove(line_start); + if !self.cursor_at_max() { + self.cursor.sub(1); + } + } + } + } + } } fn verb_insert(&mut self, string: String) { self.insert_str_at_cursor(&string); let graphemes = string.graphemes(true).count(); self.cursor.add(graphemes); } + #[allow(clippy::unnecessary_to_owned)] fn verb_indent(&mut self, motion: MotionKind) -> ShResult<()> { let Some((start, end)) = self.range_from_motion(&motion) else { return Ok(()); @@ -2975,6 +2978,7 @@ impl LineBuf { } Ok(()) } + #[allow(clippy::unnecessary_to_owned)] fn verb_dedent(&mut self, motion: MotionKind) -> ShResult<()> { let Some((start, mut end)) = self.range_from_motion(&motion) else { return Ok(()); @@ -3198,7 +3202,6 @@ impl LineBuf { } Ok(()) } - #[allow(clippy::unnecessary_to_owned)] pub fn exec_verb( &mut self, verb: Verb, @@ -3285,10 +3288,10 @@ impl LineBuf { /* * Let's evaluate the motion now - * If we got some weird command like 'dvw' we will have to simulate a visual - * selection to get the range If motion is None, we will try to use - * self.select_range If self.select_range is None, we will use - * MotionKind::Null + * If we got some weird command like 'dvw' we will + * have to simulate a visual selection to get the range + * If motion is None, we will try to use self.select_range + * If self.select_range is None, we will use MotionKind::Null */ let motion_eval = if flags.intersects(CmdFlags::VISUAL | CmdFlags::VISUAL_LINE | CmdFlags::VISUAL_BLOCK) { diff --git a/src/testutil.rs b/src/testutil.rs index e289d3a..6cd3dc0 100644 --- a/src/testutil.rs +++ b/src/testutil.rs @@ -98,7 +98,7 @@ impl TestGuard { } } - pub fn pty_slave(&self) -> BorrowedFd { + pub fn pty_slave(&self) -> BorrowedFd<'_> { unsafe { BorrowedFd::borrow_raw(self.pty_slave.as_raw_fd()) } } @@ -191,7 +191,7 @@ impl crate::parse::Node { if offender.is_none() && expected_rule .as_ref() - .map_or(true, |e| *e != s.class.as_nd_kind()) + .is_none_or(|e| *e != s.class.as_nd_kind()) { offender = Some((s.class.as_nd_kind(), expected_rule)); } else if offender.is_none() {