Skip to main content

zsh/ported/zle/
compctl_h.rs

1//! `compctl.h` port — completion descriptor types + `CC_*` / `CCT_*`
2//! flag constants used by the legacy `compctl` builtin.
3//!
4//! Port of `Src/Zle/compctl.h`. Canonical home for the four typedefs
5//! (`Compctlp`/`Compctl`/`Compcond`/`Patcomp`) and the two flag-bit
6//! families: 30 `CC_*` primary completion-target flags (mask) and 7
7//! `CC_*` secondary flags (mask2), plus 14 `CCT_*` `-x` condition
8//! types.
9//!
10//! C source: 4 typedefs + 4 structs (`compctlp`, `patcomp`,
11//! `compcond`, `compctl`), 14 `CCT_*` constants (c:76-89), 30
12//! primary `CC_*` constants (c:118-149), 7 secondary `CC_*` constants
13//! (c:152-158). 0 functions.
14//!
15//! `compctl.rs` (the .c port) re-exports these via `pub use
16//! super::compctl_h::*;` so existing `cc_flags::FILES` / `cct::POS`
17//! call sites keep compiling alongside the C-canonical
18//! `CC_FILES` / `CCT_POS` names.
19
20// ---------------------------------------------------------------------------
21// `-x` condition type constants (c:76-89).
22// ---------------------------------------------------------------------------
23
24
25// --- AUTO: cross-zle hoisted-fn use glob ---
26#[allow(unused_imports)]
27#[allow(unused_imports)]
28use crate::ported::zle::zle_main::*;
29#[allow(unused_imports)]
30use crate::ported::zle::zle_misc::*;
31#[allow(unused_imports)]
32use crate::ported::zle::zle_hist::*;
33#[allow(unused_imports)]
34use crate::ported::zle::zle_move::*;
35#[allow(unused_imports)]
36use crate::ported::zle::zle_word::*;
37#[allow(unused_imports)]
38use crate::ported::zle::zle_params::*;
39#[allow(unused_imports)]
40use crate::ported::zle::zle_vi::*;
41#[allow(unused_imports)]
42use crate::ported::zle::zle_utils::*;
43#[allow(unused_imports)]
44use crate::ported::zle::zle_refresh::*;
45#[allow(unused_imports)]
46use crate::ported::zle::zle_tricky::*;
47#[allow(unused_imports)]
48use crate::ported::zle::textobjects::*;
49#[allow(unused_imports)]
50use crate::ported::zle::deltochar::*;
51
52pub const CCT_UNUSED:   i32 = 0;                                         // c:76
53pub const CCT_POS:      i32 = 1;                                         // c:77
54pub const CCT_CURSTR:   i32 = 2;                                         // c:78
55pub const CCT_CURPAT:   i32 = 3;                                         // c:79
56pub const CCT_WORDSTR:  i32 = 4;                                         // c:80
57pub const CCT_WORDPAT:  i32 = 5;                                         // c:81
58pub const CCT_CURSUF:   i32 = 6;                                         // c:82
59pub const CCT_CURPRE:   i32 = 7;                                         // c:83
60pub const CCT_CURSUB:   i32 = 8;                                         // c:84
61pub const CCT_CURSUBC:  i32 = 9;                                         // c:85
62pub const CCT_NUMWORDS: i32 = 10;                                        // c:86
63pub const CCT_RANGESTR: i32 = 11;                                        // c:87
64pub const CCT_RANGEPAT: i32 = 12;                                        // c:88
65pub const CCT_QUOTE:    i32 = 13;                                        // c:89
66
67// ---------------------------------------------------------------------------
68// Primary completion-target flags (`mask`, c:118-149).
69// Each bit selects one completion-source kind (files, vars, jobs, ...)
70// the compctl spec expands.
71// ---------------------------------------------------------------------------
72
73pub const CC_FILES:      u64 = 1 <<  0;                                  // c:118
74pub const CC_COMMPATH:   u64 = 1 <<  1;                                  // c:119
75pub const CC_REMOVE:     u64 = 1 <<  2;                                  // c:120
76pub const CC_OPTIONS:    u64 = 1 <<  3;                                  // c:121
77pub const CC_VARS:       u64 = 1 <<  4;                                  // c:122
78pub const CC_BINDINGS:   u64 = 1 <<  5;                                  // c:123
79pub const CC_ARRAYS:     u64 = 1 <<  6;                                  // c:124
80pub const CC_INTVARS:    u64 = 1 <<  7;                                  // c:125
81pub const CC_SHFUNCS:    u64 = 1 <<  8;                                  // c:126
82pub const CC_PARAMS:     u64 = 1 <<  9;                                  // c:127
83pub const CC_ENVVARS:    u64 = 1 << 10;                                  // c:128
84pub const CC_JOBS:       u64 = 1 << 11;                                  // c:129
85pub const CC_RUNNING:    u64 = 1 << 12;                                  // c:130
86pub const CC_STOPPED:    u64 = 1 << 13;                                  // c:131
87pub const CC_BUILTINS:   u64 = 1 << 14;                                  // c:132
88pub const CC_ALREG:      u64 = 1 << 15;                                  // c:133
89pub const CC_ALGLOB:     u64 = 1 << 16;                                  // c:134
90pub const CC_USERS:      u64 = 1 << 17;                                  // c:135
91pub const CC_DISCMDS:    u64 = 1 << 18;                                  // c:136
92pub const CC_EXCMDS:     u64 = 1 << 19;                                  // c:137
93pub const CC_SCALARS:    u64 = 1 << 20;                                  // c:138
94pub const CC_READONLYS:  u64 = 1 << 21;                                  // c:139
95pub const CC_SPECIALS:   u64 = 1 << 22;                                  // c:140
96pub const CC_DELETE:     u64 = 1 << 23;                                  // c:141
97pub const CC_NAMED:      u64 = 1 << 24;                                  // c:142
98pub const CC_QUOTEFLAG:  u64 = 1 << 25;                                  // c:143
99pub const CC_EXTCMDS:    u64 = 1 << 26;                                  // c:144
100pub const CC_RESWDS:     u64 = 1 << 27;                                  // c:145
101pub const CC_DIRS:       u64 = 1 << 28;                                  // c:146
102pub const CC_EXPANDEXPL: u64 = 1 << 30;                                  // c:148
103pub const CC_RESERVED:   u64 = 1 << 31;                                  // c:149
104
105// ---------------------------------------------------------------------------
106// Secondary completion-target flags (`mask2`, c:152-158).
107// ---------------------------------------------------------------------------
108
109pub const CC_NOSORT:  u64 = 1 << 0;                                      // c:152
110pub const CC_XORCONT: u64 = 1 << 1;                                      // c:153
111pub const CC_CCCONT:  u64 = 1 << 2;                                      // c:154
112pub const CC_PATCONT: u64 = 1 << 3;                                      // c:155
113pub const CC_DEFCONT: u64 = 1 << 4;                                      // c:156
114pub const CC_UNIQCON: u64 = 1 << 5;                                      // c:157
115pub const CC_UNIQALL: u64 = 1 << 6;                                      // c:158
116
117// ---------------------------------------------------------------------------
118// Typedef structs (c:32-115).
119//
120// C uses linked lists threaded through `next` pointers. The Rust
121// port substitutes `Option<Box<...>>` for the same self-referential
122// chain; the linked-list semantics are preserved.
123// ---------------------------------------------------------------------------
124
125/// Port of `struct compctlp` from `Src/Zle/compctl.h:39-42`. Hash
126/// table node entry holding a pointer to the compctl descriptor.
127///
128/// C definition (c:39-42):
129/// ```c
130/// struct compctlp {
131///     struct hashnode node;
132///     Compctl cc;
133/// };
134/// ```
135///
136/// The Rust port omits the `hashnode` head (zshrs's hashtable
137/// machinery threads name+next through a separate scaffold) and
138/// keeps the semantic payload — a pointer to the compctl descriptor.
139#[derive(Debug, Clone)]
140#[allow(non_camel_case_types)]
141pub struct Compctlp {                                                    // c:39
142    pub cc: std::sync::Arc<Compctl>,                                                // c:41
143}
144
145/// Port of `struct patcomp` from `Src/Zle/compctl.h:46-50`. Linked-
146/// list node for the pattern-compctl registry (entries created by
147/// `compctl -p PATTERN ...`).
148///
149/// C definition (c:46-50):
150/// ```c
151/// struct patcomp {
152///     Patcomp next;
153///     char *pat;
154///     Compctl cc;
155/// };
156/// ```
157#[derive(Debug, Clone)]
158#[allow(non_camel_case_types)]
159pub struct Patcomp {                                                     // c:46
160    pub next: Option<Box<Patcomp>>,                                      // c:47
161    pub pat: String,                                                     // c:48
162    pub cc: std::sync::Arc<Compctl>,                                                // c:49
163}
164
165/// Port of `struct compcond` from `Src/Zle/compctl.h:54-74`. The
166/// per-condition descriptor for `compctl -x`.
167///
168/// C definition (c:54-74):
169/// ```c
170/// struct compcond {
171///     Compcond and, or;
172///     int type;            /* one of CCT_* */
173///     int n;               /* array length */
174///     union {
175///         struct { int *a, *b; } r;       /* CCT_POS, CCT_NUMWORDS */
176///         struct { int *p; char **s; } s;  /* CCT_CURSTR, CCT_CURPAT, ... */
177///         struct { char **a, **b; } l;     /* CCT_RANGESTR, ... */
178///     } u;
179/// };
180/// ```
181///
182/// The Rust port collapses C's `union` into an explicit enum
183/// (`CompcondData`) since Rust unions require unsafe; the dispatch
184/// is by `typ` per the C convention.
185#[derive(Debug, Clone, Default)]
186#[allow(non_camel_case_types)]
187pub struct Compcond {                                                    // c:54
188    pub and: Option<Box<Compcond>>,                                      // c:55
189    pub or:  Option<Box<Compcond>>,                                      // c:55
190    pub typ: i32,                                                        // c:56  (Rust keyword `type`)
191    pub n:   i32,                                                        // c:57
192    pub u:   CompcondData,                                               // c:58 union
193}
194
195/// Port of the anonymous `union { struct r,s,l }` inside `compcond`
196/// at `Src/Zle/compctl.h:58-73`. The C union is dispatched by
197/// `typ` (one of the `CCT_*` constants).
198#[derive(Debug, Clone, Default)]
199#[allow(non_camel_case_types)]
200pub enum CompcondData {                                                  // c:58
201    /// Port of `struct { int *a, *b; } r` (c:59-62) — used by
202    /// `CCT_POS`, `CCT_NUMWORDS`.
203    R { a: Vec<i32>, b: Vec<i32> },
204    /// Port of `struct { int *p; char **s; } s` (c:63-66) — used by
205    /// `CCT_CURSTR`, `CCT_CURPAT`, `CCT_CURSUF`, `CCT_CURPRE`,
206    /// `CCT_CURSUB`, `CCT_CURSUBC`, `CCT_WORDSTR`, `CCT_WORDPAT`,
207    /// `CCT_QUOTE`.
208    S { p: Vec<i32>, s: Vec<String> },
209    /// Port of `struct { char **a, **b; } l` (c:68-71) — used by
210    /// `CCT_RANGESTR`, `CCT_RANGEPAT`.
211    L { a: Vec<String>, b: Vec<String> },
212    /// Empty (CCT_UNUSED).
213    #[default]
214    Unused,
215}
216
217/// Port of `struct compctl` from `Src/Zle/compctl.h:93-115`. The
218/// real per-command compctl descriptor — what `compctl name args`
219/// allocates and registers in the `compctltab` hashtable.
220///
221/// C definition (c:93-115) — 22 fields. Field names + types
222/// preserved verbatim; pointer types collapse to `Option<String>` /
223/// `Option<std::sync::Arc<Compctl>>` etc. as appropriate.
224#[derive(Debug, Clone, Default)]
225#[allow(non_camel_case_types)]
226pub struct Compctl {                                                     // c:93
227    /// Reference count.
228    pub refc: i32,                                                       // c:94
229    /// Next compctl in a `-x` chain.
230    pub next: Option<std::sync::Arc<Compctl>>,                                      // c:95
231    /// Mask of completion-target flags (`CC_*`).
232    pub mask: u64,                                                       // c:96
233    /// Secondary mask of completion-target flags (`CC_*`, mask2).
234    pub mask2: u64,                                                      // c:96
235    /// `-k` variable name.
236    pub keyvar: Option<String>,                                          // c:97
237    /// `-g` glob pattern.
238    pub glob: Option<String>,                                            // c:98
239    /// `-s` expansion string.
240    pub str: Option<String>,                                            // c:99 (Rust keyword `str`)
241    /// `-K` function name.
242    pub func: Option<String>,                                            // c:100
243    /// `-X` explanation.
244    pub explain: Option<String>,                                         // c:101
245    /// `-y` user-defined description for listing.
246    pub ylist: Option<String>,                                           // c:102
247    /// `-P` prefix.
248    pub prefix: Option<String>,                                          // c:103
249    /// `-S` suffix.
250    pub suffix: Option<String>,                                          // c:103
251    /// `-l` command name to use.
252    pub subcmd: Option<String>,                                          // c:104
253    /// `-1` command name to use.
254    pub substr: Option<String>,                                          // c:105
255    /// `-w` with-directory.
256    pub withd: Option<String>,                                           // c:106
257    /// `-H` history pattern.
258    pub hpat: Option<String>,                                            // c:107
259    /// `-H` number of events to search.
260    pub hnum: i32,                                                       // c:108
261    /// `-J`/`-V` group name.
262    pub gname: Option<String>,                                           // c:109
263    /// `-x` first compctl in the chain.
264    pub ext: Option<std::sync::Arc<Compctl>>,                                       // c:110
265    /// `-x` condition for this compctl.
266    pub cond: Option<Box<Compcond>>,                                     // c:111
267    /// `+` xor'ed compctl chain.
268    pub xor: Option<std::sync::Arc<Compctl>>,                                       // c:112
269    /// `-M` matcher control — head of the Cmatcher chain compiled
270    /// from this compctl's match-spec arg.
271    pub matcher: Option<Box<crate::ported::zle::comp_h::Cmatcher>>,      // c:113
272    /// `-M` matcher string.
273    pub mstr: Option<String>,                                            // c:114
274}
275
276#[cfg(test)]
277mod tests {
278    use super::*;
279
280    /// Verifies CCT_* values per c:76-89.
281    #[test]
282    fn cct_constants_correct() {
283        let _g = crate::ported::zle::zle_main::zle_test_setup();
284        assert_eq!(CCT_UNUSED, 0);
285        assert_eq!(CCT_POS, 1);
286        assert_eq!(CCT_CURSTR, 2);
287        assert_eq!(CCT_QUOTE, 13);
288    }
289
290    /// Verifies CC_* primary mask values per c:118-149 — single-bit,
291    /// non-overlapping.
292    #[test]
293    fn cc_primary_mask_bits_distinct() {
294        let _g = crate::ported::zle::zle_main::zle_test_setup();
295        let all = CC_FILES | CC_COMMPATH | CC_REMOVE | CC_OPTIONS
296                | CC_VARS | CC_BINDINGS | CC_ARRAYS | CC_INTVARS
297                | CC_SHFUNCS | CC_PARAMS | CC_ENVVARS | CC_JOBS
298                | CC_RUNNING | CC_STOPPED | CC_BUILTINS | CC_ALREG
299                | CC_ALGLOB | CC_USERS | CC_DISCMDS | CC_EXCMDS
300                | CC_SCALARS | CC_READONLYS | CC_SPECIALS | CC_DELETE
301                | CC_NAMED | CC_QUOTEFLAG | CC_EXTCMDS | CC_RESWDS
302                | CC_DIRS | CC_EXPANDEXPL | CC_RESERVED;
303        assert_eq!(all.count_ones(), 31);  // 30 sequential + 30 + 31 (skips bit 29)
304    }
305
306    /// Verifies the secondary mask values per c:152-158.
307    #[test]
308    fn cc_secondary_mask_values() {
309        let _g = crate::ported::zle::zle_main::zle_test_setup();
310        assert_eq!(CC_NOSORT, 1);
311        assert_eq!(CC_XORCONT, 2);
312        assert_eq!(CC_UNIQALL, 1 << 6);
313    }
314
315    /// Verifies Compctl Default initialiser zeroes every field per
316    /// the C convention of `(Compctl) calloc(1, sizeof(...))`.
317    #[test]
318    fn compctl_default_zeros_fields() {
319        let _g = crate::ported::zle::zle_main::zle_test_setup();
320        let cc = Compctl::default();
321        assert_eq!(cc.refc, 0);
322        assert!(cc.next.is_none());
323        assert_eq!(cc.mask, 0);
324        assert_eq!(cc.mask2, 0);
325        assert!(cc.keyvar.is_none());
326        assert!(cc.cond.is_none());
327        assert!(cc.xor.is_none());
328        assert_eq!(cc.hnum, 0);
329    }
330
331    /// Verifies Compcond Default starts in CCT_UNUSED state.
332    #[test]
333    fn compcond_default_is_unused() {
334        let _g = crate::ported::zle::zle_main::zle_test_setup();
335        let c = Compcond::default();
336        assert_eq!(c.typ, CCT_UNUSED);
337        assert!(matches!(c.u, CompcondData::Unused));
338    }
339
340    /// Verifies the CompcondData variants align with the C union
341    /// dispatch per c:58-73.
342    #[test]
343    fn compcond_data_variants() {
344        let _g = crate::ported::zle::zle_main::zle_test_setup();
345        let r = CompcondData::R { a: vec![0, 1], b: vec![2, 3] };
346        if let CompcondData::R { a, b } = r {
347            assert_eq!(a, vec![0, 1]);
348            assert_eq!(b, vec![2, 3]);
349        } else {
350            panic!("expected R variant");
351        }
352        let s = CompcondData::S { p: vec![1], s: vec!["x".into()] };
353        assert!(matches!(s, CompcondData::S { .. }));
354        let l = CompcondData::L { a: vec!["lo".into()], b: vec!["hi".into()] };
355        assert!(matches!(l, CompcondData::L { .. }));
356    }
357}