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}