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// Many doc comments reference C-source pages, shell constructs, and
15// zsh-internal identifiers by name in `[...]` form; they don't resolve as
16// rustdoc intra-doc links. Silence so docs build clean on CI.
17#![allow(rustdoc::broken_intra_doc_links)]
18#![allow(rustdoc::private_intra_doc_links)]
19#![allow(rustdoc::invalid_html_tags)]
20#![allow(dead_code)]
21#![allow(unused_variables)]
22#![allow(unused_imports)]
23#![allow(unused_assignments)]
24#![allow(unused_mut)]
25#![allow(unused_parens)]
26#![allow(unused_doc_comments)]
27#![allow(unreachable_patterns)]
28#![allow(deprecated)]
29#![allow(unexpected_cfgs)]
30// Allow zsh-canonical identifier names (lowercase statics/constants/types
31// like `ca_parsed`, `convchar_t`, `P_ISBRANCH`) so the ports stay
32// faithful to the C source per PORT.md.
33#![allow(non_snake_case)]
34#![allow(non_camel_case_types)]
35#![allow(non_upper_case_globals)]
36// Function-pointer-to-integer casts appear in ported dispatch tables.
37#![allow(function_casts_as_integer)]
38// Clippy: the C → Rust ports preserve idioms from the zsh source
39// (raw pointer derefs, dead-loop `do { ... } while (0)` shapes, bitmasks
40// that look redundant but match the C, etc.). Silence the whole group so
41// port fidelity wins over Rust-idiom rewrites. New non-ported code
42// should still aim for clippy-clean, but at file/function scope, not
43// crate-wide.
44#![allow(clippy::all)]
45
46/// Runtime shell-mode flag set by the binary entrypoint (`bins/zshrs.rs`)
47/// at startup. The library can't directly read `bins/zshrs.rs::shell_mode()`
48/// (it lives in the binary crate), so the binary writes this atomic when
49/// parsing `--zsh` / `--bash` / `--posix` and the library reads it from
50/// bridge / dispatch sites that need to gate bash-compat-vs-zsh behavior.
51/// Defaults to `false` (zshrs-native mode) when not explicitly set.
52///
53/// Bugs #475 / #504 / #555 in docs/BUGS.md — bash-only builtins
54/// (`caller`/`help`/`mapfile`/`readarray`/`compgen`/etc.) should
55/// dispatch as "command not found" when this is true.
56pub static IS_ZSH_MODE: std::sync::atomic::AtomicBool = std::sync::atomic::AtomicBool::new(false);
57
58/// `compsys` submodule.
59pub mod compsys;
60/// `exec_jobs` submodule.
61pub mod exec_jobs;
62/// `extensions` submodule.
63pub mod extensions;
64/// `ported` submodule.
65pub mod ported;
66/// `subscript_escape` submodule (Rust-only; see the module docs).
67pub mod subscript_escape;
68/// `test_util` submodule.
69#[cfg(test)]
70pub mod test_util;
71/// `tiers` submodule.
72pub mod tiers;
73pub mod tolerant_sort;
74
75// Back-compat: re-export every ported submodule at the crate root so
76// historical call sites (`crate::exec::`, `crate::subst::`,
77// `crate::zle::`, `crate::modules::`, `crate::builtins::`, etc.)
78// continue to resolve unchanged after the physical move into
79// `src/ported/`. New code should prefer `crate::ported::<name>`.
80pub use ported::*;
81/// `alias_input_frames` submodule — per-thread record of popped alias
82/// input-stack frames, restoring the reachability C's manually-indexed
83/// `instack` gives `input_hasalias`.
84#[path = "extensions/alias_input_frames.rs"]
85pub mod alias_input_frames;
86/// `aot` submodule.
87#[path = "extensions/aot.rs"]
88pub mod aot;
89/// `arith_compiler` submodule.
90#[path = "extensions/arith_compiler.rs"]
91pub mod arith_compiler;
92/// `atomic_write` submodule — the shared temp-file-safe shard writer
93/// used by both rkyv caches.
94#[path = "extensions/atomic_write.rs"]
95pub mod atomic_write;
96/// `autoload_cache` submodule.
97#[path = "extensions/autoload_cache.rs"]
98pub mod autoload_cache;
99/// `autoload_prewarm` submodule.
100#[path = "extensions/autoload_prewarm.rs"]
101pub mod autoload_prewarm;
102/// `bash_complete` submodule.
103#[path = "extensions/bash_complete.rs"]
104pub mod bash_complete;
105/// `canonical_apply` submodule.
106#[path = "extensions/canonical_apply.rs"]
107pub mod canonical_apply;
108/// Shared-handle accessors for the completion match accumulators (Rust-original
109/// glue restoring C's `matches = mgroup->lmatches` pointer alias; see the module
110/// doc). Deliberately outside `src/ported/` — not a C-fn port.
111pub mod comp_match_handles;
112pub mod comp_word_tok;
113/// `compile_zsh` submodule.
114#[path = "extensions/compile_zsh.rs"]
115pub mod compile_zsh;
116/// `completion` submodule.
117#[path = "extensions/completion.rs"]
118pub mod completion;
119/// `config` submodule.
120#[path = "extensions/config.rs"]
121pub mod config;
122/// `cow_map` submodule — copy-on-write HashMap wrapper for cheap subshell
123/// snapshot/restore (Rust-only helper).
124#[path = "extensions/cow_map.rs"]
125pub mod cow_map;
126/// `daemon_presence` submodule.
127#[path = "extensions/daemon_presence.rs"]
128pub mod daemon_presence;
129/// `errflag_cell` submodule — per-thread storage for `errflag`, restoring
130/// the copy-on-fork semantics C zsh gets for free.
131#[path = "extensions/errflag_cell.rs"]
132pub mod errflag_cell;
133/// `fast_hash` submodule — dependency-free FxHash for internal name tables.
134#[path = "extensions/fast_hash.rs"]
135pub mod fast_hash;
136/// `opts_cache` submodule — fast-path `isset()` option-state cache.
137#[path = "extensions/opts_cache.rs"]
138pub mod opts_cache;
139/// `overlay_snapshot` submodule.
140#[path = "extensions/overlay_snapshot.rs"]
141pub mod overlay_snapshot;
142/// `pat_cache` submodule — global compiled-pattern cache (Rust-only opt).
143#[path = "extensions/pat_cache.rs"]
144pub mod pat_cache;
145/// `provenance` submodule — value-lineage ledger over bytecode
146/// execution (zshrs-original; ported from stryke's `provenance.rs`).
147#[path = "extensions/provenance.rs"]
148pub mod provenance;
149/// `script_cache` submodule.
150#[path = "extensions/script_cache.rs"]
151pub mod script_cache;
152/// `shout` submodule — buffered terminal-output stream for the ZLE display
153/// (the stdio buffering C gets from libc's `FILE *shout`).
154#[path = "extensions/shout.rs"]
155pub mod shout;
156/// `startup_signals` submodule.
157#[path = "extensions/startup_signals.rs"]
158pub mod startup_signals;
159/// `subexp_cleanup` submodule — RAII eviction of `__subexp_arr_*`
160/// paramtab scratch temps created during array sub-expression expansion.
161#[path = "extensions/subexp_cleanup.rs"]
162pub mod subexp_cleanup;
163/// `vm_pool` submodule — per-thread pool of recyclable fusevm VMs.
164#[path = "extensions/vm_pool.rs"]
165pub mod vm_pool;
166// Daemon lives in the `zshrs-daemon` workspace crate. Re-export it as `daemon`
167// so existing `crate::daemon::...` (in vm_helper) and `zsh::daemon::...` (in bins,
168// integration tests) paths keep resolving without churn.
169//
170// The `daemon` feature gates the actual zshrs-daemon dep. When disabled
171// (--no-default-features), a stub module covers the call sites in vm_helper.
172// This lets the library compile in isolation while the daemon crate is
173// being refactored in a concurrent session.
174#[cfg(feature = "daemon")]
175pub use zshrs_daemon as daemon;
176/// `daemon` submodule.
177#[cfg(not(feature = "daemon"))]
178pub mod daemon {
179 //! Stub module used when the `daemon` feature is disabled. Provides
180 //! the minimal surface that `src/vm_helper` calls — the real
181 //! implementation lives in the `zshrs-daemon` workspace crate.
182 pub mod builtins {
183 pub const ZSHRS_BUILTIN_NAMES: &[&str] = &[];
184 /// `is_zshrs_builtin` — see implementation.
185 pub fn is_zshrs_builtin(_name: &str) -> bool {
186 false
187 }
188 /// `try_dispatch` — see implementation.
189 pub fn try_dispatch(_name: &str, _argv: &[String]) -> Option<i32> {
190 None
191 }
192 /// `dispatch` — see implementation.
193 pub fn dispatch(_name: &str, _args: &[String]) -> Option<i32> {
194 None
195 }
196 }
197}
198/// `ast_sexp` submodule.
199#[path = "extensions/ast_sexp.rs"]
200pub mod ast_sexp;
201/// `bash_arrays` submodule — bash sparse-array holes tracker (Rust-only).
202#[path = "extensions/bash_arrays.rs"]
203pub mod bash_arrays;
204/// `dap` submodule.
205#[path = "extensions/dap.rs"]
206pub mod dap;
207/// `dash_mode` submodule — strict-dash emulation flag (Rust-only).
208#[path = "extensions/dash_mode.rs"]
209pub mod dash_mode;
210/// `dumpers` submodule.
211#[path = "extensions/dumpers.rs"]
212pub mod dumpers;
213/// `ext_builtins` submodule.
214#[path = "extensions/ext_builtins.rs"]
215pub mod ext_builtins;
216/// `fds` submodule.
217#[path = "extensions/fds.rs"]
218pub mod fds;
219/// `fish_features` submodule.
220#[path = "extensions/fish_features.rs"]
221pub mod fish_features;
222/// `fmt` submodule — zsh source formatter (CLI `--fmt` + LSP
223/// `textDocument/formatting`).
224#[path = "extensions/fmt.rs"]
225pub mod fmt;
226/// `ftime` submodule — TEMPORARY per-function timing scaffold (Rust-only).
227#[path = "extensions/ftime.rs"]
228pub mod ftime;
229/// `func_body_fmt` submodule.
230#[path = "extensions/func_body_fmt.rs"]
231pub mod func_body_fmt;
232/// `funcdef_capture` submodule — Rust-only verbatim capture of function
233/// body source text as `hgetc` consumes it, so `functions` / `typeset -f`
234/// work for functions defined interactively or on stdin (where zshrs's
235/// `LEX_INPUT` window does not exist). No C counterpart: C zsh
236/// reconstructs the text from wordcode via `getpermtext` (Src/text.c:189).
237#[path = "extensions/funcdef_capture.rs"]
238pub mod funcdef_capture;
239/// `global_rc` submodule — runtime sysconfdir resolution for the
240/// system-wide startup files (Rust-only; zsh bakes the path in at build
241/// time).
242#[path = "extensions/global_rc.rs"]
243pub mod global_rc;
244/// `lsp` submodule.
245#[path = "extensions/lsp.rs"]
246pub mod lsp;
247/// `lsp_symbols` submodule.
248#[path = "extensions/lsp_symbols.rs"]
249pub mod lsp_symbols;
250/// `native_cmds` submodule — builtins contributed by the linking binary
251/// (the fat `zshrs-native` build registers `git` / `arb` / `stryke` here).
252#[path = "extensions/native_cmds.rs"]
253pub mod native_cmds;
254// Lexer + parser live in `src/ported/lex.rs` and `src/ported/parse.rs`.
255// Re-export the modules so existing call sites (`zsh::lex::…`,
256// `zsh::parse::…`, `zsh::tokens::…`) keep resolving.
257// `tokens` aliases `lex` because tokens.rs's contents (lextok enum +
258// reserved-word table) now live inside lex.rs. Char tokens (Pound / Inpar /
259// Equals / …) and the REDIR_* / COND_* constants are not duplicated — they
260// live as flat `pub const` items in `ported::zsh_h` per `Src/zsh.h:144-679`.
261pub use ported::lex;
262pub use ported::lex as tokens;
263pub use ported::parse;
264/// `heredoc_ast` submodule.
265#[path = "extensions/heredoc_ast.rs"]
266pub mod heredoc_ast;
267/// `history` submodule.
268#[path = "extensions/history.rs"]
269pub mod history;
270/// `history_lazy` submodule — on-demand HISTFILE paging; the history
271/// is never slurped whole.
272#[path = "extensions/history_lazy.rs"]
273pub mod history_lazy;
274/// `log` submodule.
275#[path = "extensions/log.rs"]
276pub mod log;
277/// `lowfd` submodule — keeps the shell's own descriptors out of the user's fd space.
278#[path = "extensions/lowfd.rs"]
279pub mod lowfd;
280/// `zsh_ast` submodule.
281#[path = "extensions/zsh_ast.rs"]
282pub mod zsh_ast;
283// Backwards-compat flat re-exports — call sites that still write
284// `crate::datetime::…`, `crate::stat::…`, etc. resolve to the
285// `crate::modules::<modname>` ports without churn. New code should
286// reach for `crate::modules::<modname>` directly.
287pub use builtins::sched;
288pub use modules::attr;
289pub use modules::cap;
290pub use modules::clone;
291pub use modules::curses;
292pub use modules::datetime;
293pub use modules::db_gdbm;
294pub use modules::example;
295pub use modules::files;
296pub use modules::hlgroup;
297pub use modules::ksh93;
298pub use modules::langinfo;
299pub use modules::mapfile;
300pub use modules::mathfunc;
301pub use modules::nearcolor;
302pub use modules::newuser;
303pub use modules::param_private;
304pub use modules::parameter;
305pub use modules::pcre;
306pub use modules::random;
307pub use modules::random_real;
308pub use modules::regex as regex_module;
309pub use modules::socket;
310pub use modules::stat;
311pub use modules::system;
312pub use modules::tcp;
313pub use modules::termcap;
314pub use modules::terminfo;
315pub use modules::watch;
316pub use modules::zftp;
317pub use modules::zprof;
318pub use modules::zpty;
319pub use modules::zselect;
320pub use modules::zutil;
321/// `compinit_bg` submodule.
322#[path = "extensions/compinit_bg.rs"]
323pub mod compinit_bg;
324/// `fusevm_bridge` submodule.
325pub mod fusevm_bridge;
326/// `fusevm_disasm` submodule.
327pub mod fusevm_disasm;
328/// `intercepts` submodule.
329#[path = "extensions/intercepts.rs"]
330pub mod intercepts;
331/// `p10k` submodule — native powerlevel10k prompt engine.
332#[path = "extensions/p10k/mod.rs"]
333pub mod p10k;
334/// `pkg` — the `zpm` plugin package manager (global store).
335#[path = "extensions/pkg/mod.rs"]
336pub mod pkg;
337/// `plugin_cache` submodule.
338#[path = "extensions/plugin_cache.rs"]
339pub mod plugin_cache;
340/// `plugin_host` submodule — native (Rust) plugin loader (`zmodload -R`).
341#[path = "extensions/plugin_host.rs"]
342pub mod plugin_host;
343/// `recorder_ext` submodule.
344#[path = "extensions/recorder.rs"]
345pub mod recorder_ext;
346/// `rust_ffi` submodule — inline `rust { ... }` FFI desugaring.
347pub mod rust_ffi;
348// Plugin-Framework-Agnostic State-Modification Recorder. Entire module
349// is `#![cfg(feature = "recorder")]` so it disappears from the default
350// `zshrs` build at the rustc-expansion stage. See docs/RECORDER.md.
351/// `async_precmd` submodule — run precmd-style hooks on the worker pool so they
352/// don't block prompt rendering (writes into the shared param table).
353#[path = "extensions/async_precmd.rs"]
354pub mod async_precmd;
355/// `autopair` submodule — native bracket/quote auto-pairing
356/// (port of hlissner/zsh-autopair).
357#[path = "extensions/autopair.rs"]
358pub mod autopair;
359/// `autosuggest` submodule — native fish-style autosuggestions
360/// (port of the reader.rs autosuggestion state machine).
361#[path = "extensions/autosuggest.rs"]
362pub mod autosuggest;
363/// `gen_docs` submodule.
364#[path = "extensions/gen_docs.rs"]
365pub mod gen_docs;
366/// `history_search` submodule — native up-arrow prefix/substring/token history
367/// search (port of fish reader/history_search.rs).
368#[path = "extensions/history_search.rs"]
369pub mod history_search;
370/// `recorder` submodule.
371#[cfg(feature = "recorder")]
372pub mod recorder;
373/// `regex_mod` submodule.
374#[path = "extensions/regex_mod.rs"]
375pub mod regex_mod;
376/// `stringsort` submodule.
377#[path = "extensions/stringsort.rs"]
378pub mod stringsort;
379/// `syntax_highlight` submodule — native command-line syntax highlighting
380/// (port of fish highlight/highlight.rs, driven by the zshrs lexer).
381#[path = "extensions/syntax_highlight.rs"]
382pub mod syntax_highlight;
383/// `worker` submodule.
384#[path = "extensions/worker.rs"]
385pub mod worker;
386/// `zle_file_tester` submodule — file-existence/permission tests for native ZLE
387/// syntax highlighting (port of fish highlight/file_tester.rs).
388#[path = "extensions/zle_file_tester.rs"]
389pub mod zle_file_tester;
390/// `zle_fx` submodule — wiring for the native ZLE effects (autosuggest,
391/// syntax highlight, history search, autopair) into zlecore + the renderer.
392#[path = "extensions/zle_fx.rs"]
393pub mod zle_fx;
394/// `zle_param_sync` submodule — ZLE special-param write-back sync
395/// (Rust-only adapter for C's live GSU setters).
396#[path = "extensions/zle_param_sync.rs"]
397pub mod zle_param_sync;
398/// `zsh_builtin_docs` submodule.
399#[path = "extensions/zsh_builtin_docs.rs"]
400pub mod zsh_builtin_docs;
401/// `zsh_ext_builtin_docs` submodule.
402#[path = "extensions/zsh_ext_builtin_docs.rs"]
403pub mod zsh_ext_builtin_docs;
404/// `zsh_keyword_docs` submodule.
405#[path = "extensions/zsh_keyword_docs.rs"]
406pub mod zsh_keyword_docs;
407/// `zsh_option_docs` submodule.
408#[path = "extensions/zsh_option_docs.rs"]
409pub mod zsh_option_docs;
410/// `zsh_special_var_docs` submodule.
411#[path = "extensions/zsh_special_var_docs.rs"]
412pub mod zsh_special_var_docs;
413/// `ztest` submodule — shell-level unit test framework
414/// (port of `../strykelang` test framework).
415#[path = "extensions/ztest.rs"]
416pub mod ztest;
417/// `zwc` submodule.
418#[path = "extensions/zwc.rs"]
419pub mod zwc;
420/// `zwc_decode` submodule.
421#[path = "extensions/zwc_decode.rs"]
422pub mod zwc_decode;
423// Backwards-compat re-export so `crate::rlimits::…` keeps resolving.
424pub use builtins::rlimits;
425
426// Top-level shell executor state + fusevm bridge glue. Not a port of
427// any single Src/*.c file — zsh's native wordcode VM lives in `Src/exec.c`;
428// zshrs runs fusevm instead (see src/fusevm_bridge.rs).
429/// `vm_helper` submodule.
430pub mod vm_helper;
431
432pub use fish_features::{
433 autosuggest_from_history,
434 colorize_line,
435 expand_abbreviation,
436 // Syntax highlighting
437 highlight_shell,
438 // Private mode
439 is_private_mode,
440 // Killring
441 kill_add,
442 kill_replace,
443 kill_yank,
444 kill_yank_rotate,
445 set_private_mode,
446 validate_autosuggestion,
447 // Validation
448 validate_command,
449 with_abbrs,
450 with_abbrs_mut,
451 AbbrPosition,
452 // Abbreviations
453 Abbreviation,
454 AbbreviationSet,
455 // Autosuggestions
456 Autosuggestion,
457 HighlightRole,
458 HighlightSpec,
459 KillRing,
460 ValidationStatus,
461};
462pub use tokens::lextok;
463pub use vm_helper::ShellExecutor;
464
465// ── Stryke integration hook ──
466// The fat binary registers a handler for @ prefix dispatch.
467// The thin binary leaves this as None — @ is treated as a normal character.
468
469use std::sync::OnceLock;
470
471type StrykeHandler = Box<dyn Fn(&str) -> i32 + Send + Sync>;
472static STRYKE_HANDLER: OnceLock<StrykeHandler> = OnceLock::new();
473
474/// Register a handler for @ prefix lines (fat binary sets this to stryke::run).
475pub fn set_stryke_handler<F>(f: F)
476where
477 F: Fn(&str) -> i32 + Send + Sync + 'static,
478{
479 let _ = STRYKE_HANDLER.set(Box::new(f));
480}
481
482/// Try to dispatch a line starting with @ to stryke.
483/// Returns Some(exit_code) if handled, None if no handler registered.
484pub fn try_stryke_dispatch(code: &str) -> Option<i32> {
485 STRYKE_HANDLER.get().map(|f| f(code))
486}
487
488/// Register a native command contributed by the linking binary.
489///
490/// Convenience re-spelling of [`native_cmds::register`] at the crate root, so
491/// a fat binary's `main` reads as one call per runtime:
492///
493/// ```ignore
494/// zsh::register_native_command("git", |argv| zvcs::run_argv(argv));
495/// ```
496///
497/// The name then dispatches in-process — `whence -w git` says `builtin`,
498/// `${+builtins[git]}` is 1, `builtin git` reaches it, a user `git()` function
499/// still shadows it, and `command git` still runs the one on `PATH`.
500pub fn register_native_command<F>(name: &str, f: F)
501where
502 F: Fn(&[String]) -> i32 + Send + Sync + 'static,
503{
504 native_cmds::register(name, f);
505}