Skip to main content

zsh/
lib.rs

1//! Zsh interpreter and parser in Rust
2//!
3//! This crate provides:
4//! - A complete zsh lexer (`lexer` module)
5//! - A zsh parser (`parser` module)  
6//! - Shell execution engine (`exec` module)
7//! - Job control (`jobs` module)
8//! - History management (`history` module)
9//! - ZLE (Zsh Line Editor) support (`zle` module)
10//! - ZWC (compiled zsh) support (`zwc` module)
11//! - Fish-style features (`fish_features` module)
12//! - Mathematical expression evaluation (`math` module)
13
14#![allow(dead_code)]
15#![allow(unused_variables)]
16#![allow(unused_imports)]
17#![allow(unused_assignments)]
18#![allow(unused_mut)]
19#![allow(unused_parens)]
20#![allow(unused_doc_comments)]
21#![allow(unreachable_patterns)]
22#![allow(deprecated)]
23#![allow(unexpected_cfgs)]
24// Allow zsh-canonical identifier names (lowercase statics/constants/types
25// like `ca_parsed`, `convchar_t`, `P_ISBRANCH`) so the ports stay
26// faithful to the C source per PORT.md.
27#![allow(non_snake_case)]
28#![allow(non_camel_case_types)]
29#![allow(non_upper_case_globals)]
30// Function-pointer-to-integer casts appear in ported dispatch tables.
31#![allow(function_casts_as_integer)]
32// Clippy: the C → Rust ports preserve idioms from the zsh source
33// (raw pointer derefs, dead-loop `do { ... } while (0)` shapes, bitmasks
34// that look redundant but match the C, etc.). Silence the whole group so
35// port fidelity wins over Rust-idiom rewrites. New non-ported code
36// should still aim for clippy-clean, but at file/function scope, not
37// crate-wide.
38#![allow(clippy::all)]
39
40pub mod exec_jobs;
41pub mod extensions;
42pub mod ported;
43
44// Back-compat: re-export every ported submodule at the crate root so
45// historical call sites (`crate::exec::`, `crate::subst::`,
46// `crate::zle::`, `crate::modules::`, `crate::builtins::`, etc.)
47// continue to resolve unchanged after the physical move into
48// `src/ported/`. New code should prefer `crate::ported::<name>`.
49pub use ported::*;
50
51#[path = "extensions/aot.rs"] pub mod aot;
52#[path = "extensions/arith_compiler.rs"] pub mod arith_compiler;
53#[path = "extensions/autoload_cache.rs"] pub mod autoload_cache;
54#[path = "extensions/script_cache.rs"] pub mod script_cache;
55#[path = "extensions/compile_zsh.rs"] pub mod compile_zsh;
56#[path = "extensions/completion.rs"] pub mod completion;
57#[path = "extensions/bash_complete.rs"] pub mod bash_complete;
58#[path = "extensions/config.rs"] pub mod config;
59#[path = "extensions/canonical_apply.rs"] pub mod canonical_apply;
60#[path = "extensions/overlay_snapshot.rs"] pub mod overlay_snapshot;
61#[path = "extensions/daemon_presence.rs"] pub mod daemon_presence;
62// Daemon lives in the `zshrs-daemon` workspace crate. Re-export it as `daemon`
63// so existing `crate::daemon::...` (in exec.rs) and `zsh::daemon::...` (in bins,
64// integration tests) paths keep resolving without churn.
65//
66// The `daemon` feature gates the actual zshrs-daemon dep. When disabled
67// (--no-default-features), a stub module covers the call sites in exec.rs.
68// This lets the library compile in isolation while the daemon crate is
69// being refactored in a concurrent session.
70#[cfg(feature = "daemon")]
71pub use zshrs_daemon as daemon;
72
73#[cfg(not(feature = "daemon"))]
74pub mod daemon {
75    //! Stub module used when the `daemon` feature is disabled. Provides
76    //! the minimal surface that `src/exec.rs` calls — the real
77    //! implementation lives in the `zshrs-daemon` workspace crate.
78    pub mod builtins {
79        pub const ZSHRS_BUILTIN_NAMES: &[&str] = &[];
80        pub fn is_zshrs_builtin(_name: &str) -> bool {
81            false
82        }
83        pub fn try_dispatch(_name: &str, _argv: &[String]) -> Option<i32> {
84            None
85        }
86        pub fn dispatch(_name: &str, _args: &[String]) -> Option<i32> {
87            None
88        }
89    }
90}
91#[path = "extensions/ext_builtins.rs"] pub mod ext_builtins;
92#[path = "extensions/func_body_fmt.rs"] pub mod func_body_fmt;
93#[path = "extensions/fds.rs"] pub mod fds;
94#[path = "extensions/fish_features.rs"] pub mod fish_features;
95#[path = "extensions/ast_sexp.rs"] pub mod ast_sexp;
96#[path = "extensions/dumpers.rs"] pub mod dumpers;
97// Lexer + parser live in `src/ported/lex.rs` and `src/ported/parse.rs`.
98// Re-export the modules so existing call sites (`zsh::lex::…`,
99// `zsh::parse::…`, `zsh::tokens::…`) keep resolving.
100// `tokens` aliases `lex` because tokens.rs's contents (lextok enum +
101// reserved-word table) now live inside lex.rs. Char tokens (Pound / Inpar /
102// Equals / …) and the REDIR_* / COND_* constants are not duplicated — they
103// live as flat `pub const` items in `ported::zsh_h` per `Src/zsh.h:144-679`.
104pub use ported::lex;
105pub use ported::lex as tokens;
106pub use ported::parse;
107#[path = "extensions/history.rs"] pub mod history;
108#[path = "extensions/heredoc_ast.rs"] pub mod heredoc_ast;
109#[path = "extensions/zsh_ast.rs"] pub mod zsh_ast;
110#[path = "extensions/log.rs"] pub mod log;
111// Backwards-compat flat re-exports — call sites that still write
112// `crate::datetime::…`, `crate::stat::…`, etc. resolve to the
113// `crate::modules::<modname>` ports without churn. New code should
114// reach for `crate::modules::<modname>` directly.
115pub use modules::attr;
116pub use modules::cap;
117pub use modules::clone;
118pub use modules::curses;
119pub use modules::datetime;
120pub use modules::db_gdbm;
121pub use modules::example;
122pub use modules::files;
123pub use modules::hlgroup;
124pub use modules::ksh93;
125pub use modules::langinfo;
126pub use modules::mapfile;
127pub use modules::mathfunc;
128pub use modules::nearcolor;
129pub use modules::newuser;
130pub use modules::param_private;
131pub use modules::parameter;
132pub use modules::pcre;
133pub use modules::random;
134pub use modules::random_real;
135pub use modules::regex as regex_module;
136pub use builtins::sched;
137pub use modules::socket;
138pub use modules::stat;
139pub use modules::system;
140pub use modules::tcp;
141pub use modules::termcap;
142pub use modules::terminfo;
143pub use modules::watch;
144pub use modules::zftp;
145pub use modules::zprof;
146pub use modules::zpty;
147pub use modules::zselect;
148pub use modules::zutil;
149#[path = "extensions/plugin_cache.rs"] pub mod plugin_cache;
150#[path = "extensions/recorder.rs"] pub mod recorder_ext;
151#[path = "extensions/intercepts.rs"] pub mod intercepts;
152#[path = "extensions/compinit_bg.rs"] pub mod compinit_bg;
153pub mod fusevm_bridge;
154// Plugin-Framework-Agnostic State-Modification Recorder. Entire module
155// is `#![cfg(feature = "recorder")]` so it disappears from the default
156// `zshrs` build at the rustc-expansion stage. See docs/RECORDER.md.
157#[cfg(feature = "recorder")]
158pub mod recorder;
159#[path = "extensions/regex_mod.rs"] pub mod regex_mod;
160#[path = "extensions/stringsort.rs"] pub mod stringsort;
161#[path = "extensions/worker.rs"] pub mod worker;
162#[path = "extensions/zwc.rs"] pub mod zwc;
163#[path = "extensions/zwc_decode.rs"] pub mod zwc_decode;
164// Backwards-compat re-export so `crate::rlimits::…` keeps resolving.
165pub use builtins::rlimits;
166
167// Top-level shell executor state + fusevm bridge glue. Not a port of
168// any single Src/*.c file — `Src/exec.c` is replaced by the fusevm
169// bytecode VM (see src/fusevm_bridge.rs).
170pub mod exec;
171
172pub use exec::ShellExecutor;
173pub use fish_features::{
174    autosuggest_from_history,
175    colorize_line,
176    expand_abbreviation,
177    // Syntax highlighting
178    highlight_shell,
179    // Private mode
180    is_private_mode,
181    // Killring
182    kill_add,
183    kill_replace,
184    kill_yank,
185    kill_yank_rotate,
186    set_private_mode,
187    validate_autosuggestion,
188    // Validation
189    validate_command,
190    with_abbrs,
191    with_abbrs_mut,
192    AbbrPosition,
193    // Abbreviations
194    Abbreviation,
195    AbbreviationSet,
196    // Autosuggestions
197    Autosuggestion,
198    HighlightRole,
199    HighlightSpec,
200    KillRing,
201    ValidationStatus,
202};
203pub use tokens::lextok;
204
205// ── Stryke integration hook ──
206// The fat binary registers a handler for @ prefix dispatch.
207// The thin binary leaves this as None — @ is treated as a normal character.
208
209use std::sync::OnceLock;
210
211type StrykeHandler = Box<dyn Fn(&str) -> i32 + Send + Sync>;
212static STRYKE_HANDLER: OnceLock<StrykeHandler> = OnceLock::new();
213
214/// Register a handler for @ prefix lines (fat binary sets this to stryke::run).
215pub fn set_stryke_handler<F>(f: F)
216where
217    F: Fn(&str) -> i32 + Send + Sync + 'static,
218{
219    let _ = STRYKE_HANDLER.set(Box::new(f));
220}
221
222/// Try to dispatch a line starting with @ to stryke.
223/// Returns Some(exit_code) if handled, None if no handler registered.
224pub fn try_stryke_dispatch(code: &str) -> Option<i32> {
225    STRYKE_HANDLER.get().map(|f| f(code))
226}