Add array support for local/export/readonly builtins

Add array length syntax ${arr[#]}

Map read path now expands variables before splitting on ., fixing map "$node" with dotted paths

Map assignment path uses quote-aware token splitting, enabling quoted keys like "--type="

Completion errors now display above prompt instead of being overwritten

Fix nested if/fi parser bug when closing keywords appear on separate lines

Add QuoteState enum, replacing ad-hoc quote tracking booleans across lexer, highlighter, and expansion

Add split_tk_at/split_tk for quote-aware token splitting with span preservation

Refactor setup_builtin to accept optional argv for deferred expansion

Add ariadne dependency (not yet wired up)
This commit is contained in:
2026-02-28 15:51:09 -05:00
parent ab5f42b281
commit 1b63eff783
26 changed files with 375 additions and 281 deletions

View File

@@ -67,13 +67,13 @@ pub const BUILTINS: [&str; 41] = [
/// * If redirections are given, the second field of the resulting tuple will
/// *always* be `Some()`
/// * If no redirections are given, the second field will *always* be `None`
type SetupReturns = ShResult<(Vec<(String, Span)>, Option<RedirGuard>)>;
type SetupReturns = ShResult<(Option<Vec<(String, Span)>>, Option<RedirGuard>)>;
pub fn setup_builtin(
argv: Vec<Tk>,
argv: Option<Vec<Tk>>,
job: &mut JobBldr,
io_mode: Option<(&mut IoStack, Vec<Redir>)>,
) -> SetupReturns {
let mut argv: Vec<(String, Span)> = prepare_argv(argv)?;
let mut argv = argv.map(|argv| prepare_argv(argv)).transpose()?;
let child_pgid = if let Some(pgid) = job.pgid() {
pgid
@@ -81,18 +81,22 @@ pub fn setup_builtin(
job.set_pgid(Pid::this());
Pid::this()
};
let cmd_name = argv.remove(0).0;
let cmd_name = argv
.as_mut()
.and_then(|argv| {
if argv.is_empty() {
None
} else {
Some(argv.remove(0).0)
}
}).unwrap_or_else(|| String::new());
let child = ChildProc::new(Pid::this(), Some(&cmd_name), Some(child_pgid))?;
job.push_child(child);
let guard = if let Some((io_stack, redirs)) = io_mode {
io_stack.append_to_frame(redirs);
let io_frame = io_stack.pop_frame();
let guard = io_frame.redirect()?;
Some(guard)
} else {
None
};
let guard = io_mode.map(|(io,rdrs)| {
io.append_to_frame(rdrs);
io.pop_frame().redirect()
}).transpose()?;
// We return the io_frame because the caller needs to also call
// io_frame.restore()