Skip to main content

zsh/ported/zle/
comp_h.rs

1//! `comp.h` port — completion descriptor types + flag constants.
2//!
3//! Port of `Src/Zle/comp.h`. Canonical home for the new-style
4//! completion machinery (compsys / compadd / addmatches / etc.) —
5//! distinct from the legacy `compctl.h` machinery (which lives in
6//! `compctl_h.rs`).
7//!
8//! C source: 10 typedefs (`Cmatcher`/`Cmlist`/`Cpattern`/`Menuinfo`/
9//! `Cexpl`/`Cmgroup`/`Cmatch`/`Cline`/`Aminfo`/`Cadata`/`Cldata`/
10//! `Chdata`), 13 structs (`cexpl`/`cmgroup`/`cmatch`/`cmlist`/
11//! `cmatcher`/`cpattern`/`cline`/`aminfo`/`menuinfo`/`ccmakedat`/
12//! `chdata`/`cadata`/`cldata`), 1 enum (`cpat`), and ~80 flag
13//! constants. 0 functions.
14//!
15//! All UPPERCASE C constants (CGF_*, CMF_*, CLF_*, CAF_*, FC_*,
16//! CP_*, CPN_*) preserved verbatim per the macro casing rule.
17//! Struct names match C casing (Cexpl, Cmgroup, Cmatch, etc.) with
18//! `#[allow(non_camel_case_types)]` silencing the convention warning.
19
20// ---------------------------------------------------------------------------
21// Group-flag constants (c:85-95) — flags on `cmgroup.flags`.
22// ---------------------------------------------------------------------------
23/// `CGF_NOSORT` constant.
24pub const CGF_NOSORT: i32 = 1; // c:85
25/// `CGF_LINES` constant.
26pub const CGF_LINES: i32 = 2; // c:86
27/// `CGF_HASDL` constant.
28pub const CGF_HASDL: i32 = 4; // c:87
29/// `CGF_UNIQALL` constant.
30pub const CGF_UNIQALL: i32 = 8; // c:88
31/// `CGF_UNIQCON` constant.
32pub const CGF_UNIQCON: i32 = 16; // c:89
33/// `CGF_PACKED` constant.
34pub const CGF_PACKED: i32 = 32; // c:90
35/// `CGF_ROWS` constant.
36pub const CGF_ROWS: i32 = 64; // c:91
37/// `CGF_FILES` constant.
38pub const CGF_FILES: i32 = 128; // c:92
39/// `CGF_MATSORT` constant.
40pub const CGF_MATSORT: i32 = 256; // c:93
41/// `CGF_NUMSORT` constant.
42pub const CGF_NUMSORT: i32 = 512; // c:94
43/// `CGF_REVSORT` constant.
44pub const CGF_REVSORT: i32 = 1024; // c:95
45
46// ---------------------------------------------------------------------------
47// Match-flag constants (c:127-143) — flags on `cmatch.flags`.
48// ---------------------------------------------------------------------------
49/// `CMF_FILE` constant.
50pub const CMF_FILE: i32 = 1 << 0; // c:127
51/// `CMF_REMOVE` constant.
52pub const CMF_REMOVE: i32 = 1 << 1; // c:128
53/// `CMF_ISPAR` constant.
54pub const CMF_ISPAR: i32 = 1 << 2; // c:129
55/// `CMF_PARBR` constant.
56pub const CMF_PARBR: i32 = 1 << 3; // c:130
57/// `CMF_PARNEST` constant.
58pub const CMF_PARNEST: i32 = 1 << 4; // c:131
59/// `CMF_NOLIST` constant.
60pub const CMF_NOLIST: i32 = 1 << 5; // c:132
61/// `CMF_DISPLINE` constant.
62pub const CMF_DISPLINE: i32 = 1 << 6; // c:133
63/// `CMF_HIDE` constant.
64pub const CMF_HIDE: i32 = 1 << 7; // c:134
65/// `CMF_NOSPACE` constant.
66pub const CMF_NOSPACE: i32 = 1 << 8; // c:135
67/// `CMF_PACKED` constant.
68pub const CMF_PACKED: i32 = 1 << 9; // c:136
69/// `CMF_ROWS` constant.
70pub const CMF_ROWS: i32 = 1 << 10; // c:137
71/// `CMF_MULT` constant.
72pub const CMF_MULT: i32 = 1 << 11; // c:138
73/// `CMF_FMULT` constant.
74pub const CMF_FMULT: i32 = 1 << 12; // c:139
75/// `CMF_ALL` constant.
76pub const CMF_ALL: i32 = 1 << 13; // c:140
77/// `CMF_DUMMY` constant.
78pub const CMF_DUMMY: i32 = 1 << 14; // c:141
79/// `CMF_MORDER` constant.
80pub const CMF_MORDER: i32 = 1 << 15; // c:142
81/// `CMF_DELETE` constant.
82pub const CMF_DELETE: i32 = 1 << 16; // c:143
83
84// ---------------------------------------------------------------------------
85// Cmatcher flag constants (c:172-178) — flags on `cmatcher.flags`.
86//
87// NOTE: C uses the same `CMF_*` prefix for both `cmatch.flags` and
88// `cmatcher.flags`. The values are different (cmatcher uses
89// 1/2/4/8 vs cmatch's bitfields) but the names overlap (CMF_LINE
90// here is 1, while there's no CMF_LINE in cmatch's set). Rust
91// preserves both verbatim — the per-struct dispatch context tells
92// callers which set applies.
93// ---------------------------------------------------------------------------
94/// `CMF_LINE` constant.
95pub const CMF_LINE: i32 = 1; // c:172
96/// `CMF_LEFT` constant.
97pub const CMF_LEFT: i32 = 2; // c:174
98/// `CMF_RIGHT` constant.
99pub const CMF_RIGHT: i32 = 4; // c:176
100/// `CMF_INTER` constant.
101pub const CMF_INTER: i32 = 8; // c:178
102
103// ---------------------------------------------------------------------------
104// Cpattern type discriminators (c:184-190).
105// ---------------------------------------------------------------------------
106
107/// Port of `enum { CPAT_CCLASS, ... }` from `Src/Zle/comp.h:184-190`.
108/// C uses an anonymous int-constant enum — Rust ports as `pub const`s
109/// to avoid a Rust-only enum type. The discriminator drives
110/// `freecpattern()` and `cpattern.tp` dispatch (see `Cpattern` below).
111pub const CPAT_CCLASS: i32 = 0; // c:185
112/// `CPAT_NCLASS` constant.
113pub const CPAT_NCLASS: i32 = 1; // c:186
114/// `CPAT_EQUIV` constant.
115pub const CPAT_EQUIV: i32 = 2; // c:187
116/// `CPAT_ANY` constant.
117pub const CPAT_ANY: i32 = 3; // c:188
118/// `CPAT_CHAR` constant.
119pub const CPAT_CHAR: i32 = 4; // c:189
120
121// ---------------------------------------------------------------------------
122// Cline flag constants (c:259-267) — flags on `cline.flags`.
123// ---------------------------------------------------------------------------
124/// `CLF_MISS` constant.
125pub const CLF_MISS: i32 = 1; // c:259
126/// `CLF_DIFF` constant.
127pub const CLF_DIFF: i32 = 2; // c:260
128/// `CLF_SUF` constant.
129pub const CLF_SUF: i32 = 4; // c:261
130/// `CLF_MID` constant.
131pub const CLF_MID: i32 = 8; // c:262
132/// `CLF_NEW` constant.
133pub const CLF_NEW: i32 = 16; // c:263
134/// `CLF_LINE` constant.
135pub const CLF_LINE: i32 = 32; // c:264
136/// `CLF_JOIN` constant.
137pub const CLF_JOIN: i32 = 64; // c:265
138/// `CLF_MATCHED` constant.
139pub const CLF_MATCHED: i32 = 128; // c:266
140/// `CLF_SKIP` constant.
141pub const CLF_SKIP: i32 = 256; // c:267
142
143// ---------------------------------------------------------------------------
144// compadd / addmatches() flag constants (c:299-309).
145// ---------------------------------------------------------------------------
146/// `CAF_QUOTE` constant.
147pub const CAF_QUOTE: i32 = 1; // c:299
148/// `CAF_NOSORT` constant.
149pub const CAF_NOSORT: i32 = 2; // c:300
150/// `CAF_MATCH` constant.
151pub const CAF_MATCH: i32 = 4; // c:301
152/// `CAF_UNIQCON` constant.
153pub const CAF_UNIQCON: i32 = 8; // c:302
154/// `CAF_UNIQALL` constant.
155pub const CAF_UNIQALL: i32 = 16; // c:303
156/// `CAF_ARRAYS` constant.
157pub const CAF_ARRAYS: i32 = 32; // c:304
158/// `CAF_KEYS` constant.
159pub const CAF_KEYS: i32 = 64; // c:305
160/// `CAF_ALL` constant.
161pub const CAF_ALL: i32 = 128; // c:306
162/// `CAF_MATSORT` constant.
163pub const CAF_MATSORT: i32 = 256; // c:307
164/// `CAF_NUMSORT` constant.
165pub const CAF_NUMSORT: i32 = 512; // c:308
166/// `CAF_REVSORT` constant.
167pub const CAF_REVSORT: i32 = 1024; // c:309
168
169// ---------------------------------------------------------------------------
170// Fromcomp flags (c:359-360).
171// ---------------------------------------------------------------------------
172/// `FC_LINE` constant.
173pub const FC_LINE: i32 = 1; // c:359
174/// `FC_INWORD` constant.
175pub const FC_INWORD: i32 = 2; // c:360
176
177// ---------------------------------------------------------------------------
178// Special-parameter index constants — `comprpms` / "real params"
179// (c:364-386). For each parameter there's a `CPN_*` index and a
180// `CP_*` bitmask `(1 << CPN_*)`.
181// ---------------------------------------------------------------------------
182/// `CPN_WORDS` constant.
183pub const CPN_WORDS: i32 = 0; // c:364
184/// `CP_WORDS` constant.
185pub const CP_WORDS: u32 = 1 << CPN_WORDS; // c:365
186/// `CPN_REDIRS` constant.
187pub const CPN_REDIRS: i32 = 1; // c:366
188/// `CP_REDIRS` constant.
189pub const CP_REDIRS: u32 = 1 << CPN_REDIRS; // c:367
190/// `CPN_CURRENT` constant.
191pub const CPN_CURRENT: i32 = 2; // c:368
192/// `CP_CURRENT` constant.
193pub const CP_CURRENT: u32 = 1 << CPN_CURRENT; // c:369
194/// `CPN_PREFIX` constant.
195pub const CPN_PREFIX: i32 = 3; // c:370
196/// `CP_PREFIX` constant.
197pub const CP_PREFIX: u32 = 1 << CPN_PREFIX; // c:371
198/// `CPN_SUFFIX` constant.
199pub const CPN_SUFFIX: i32 = 4; // c:372
200/// `CP_SUFFIX` constant.
201pub const CP_SUFFIX: u32 = 1 << CPN_SUFFIX; // c:373
202/// `CPN_IPREFIX` constant.
203pub const CPN_IPREFIX: i32 = 5; // c:374
204/// `CP_IPREFIX` constant.
205pub const CP_IPREFIX: u32 = 1 << CPN_IPREFIX; // c:375
206/// `CPN_ISUFFIX` constant.
207pub const CPN_ISUFFIX: i32 = 6; // c:376
208/// `CP_ISUFFIX` constant.
209pub const CP_ISUFFIX: u32 = 1 << CPN_ISUFFIX; // c:377
210/// `CPN_QIPREFIX` constant.
211pub const CPN_QIPREFIX: i32 = 7; // c:378
212/// `CP_QIPREFIX` constant.
213pub const CP_QIPREFIX: u32 = 1 << CPN_QIPREFIX; // c:379
214/// `CPN_QISUFFIX` constant.
215pub const CPN_QISUFFIX: i32 = 8; // c:380
216/// `CP_QISUFFIX` constant.
217pub const CP_QISUFFIX: u32 = 1 << CPN_QISUFFIX; // c:381
218/// `CPN_COMPSTATE` constant.
219pub const CPN_COMPSTATE: i32 = 9; // c:382
220/// `CP_COMPSTATE` constant.
221pub const CP_COMPSTATE: u32 = 1 << CPN_COMPSTATE; // c:383
222
223/// Port of `#define CP_REALPARAMS` from `Src/Zle/comp.h:385`. Total
224/// number of "real" comp parameters.
225pub const CP_REALPARAMS: i32 = 10; // c:385
226
227/// Port of `#define CP_ALLREALS` from `Src/Zle/comp.h:386`. Mask
228/// covering every CP_* "real" param flag (bits 0..9 set).
229pub const CP_ALLREALS: u32 = 0x3ff; // c:386
230
231// ---------------------------------------------------------------------------
232// Special-parameter index constants — `compkpms` / "key params"
233// (c:389-442).
234// ---------------------------------------------------------------------------
235/// `CPN_NMATCHES` constant.
236pub const CPN_NMATCHES: i32 = 0; // c:389
237/// `CP_NMATCHES` constant.
238pub const CP_NMATCHES: u32 = 1 << CPN_NMATCHES; // c:390
239/// `CPN_CONTEXT` constant.
240pub const CPN_CONTEXT: i32 = 1; // c:391
241/// `CP_CONTEXT` constant.
242pub const CP_CONTEXT: u32 = 1 << CPN_CONTEXT; // c:392
243/// `CPN_PARAMETER` constant.
244pub const CPN_PARAMETER: i32 = 2; // c:393
245/// `CP_PARAMETER` constant.
246pub const CP_PARAMETER: u32 = 1 << CPN_PARAMETER; // c:394
247/// `CPN_REDIRECT` constant.
248pub const CPN_REDIRECT: i32 = 3; // c:395
249/// `CP_REDIRECT` constant.
250pub const CP_REDIRECT: u32 = 1 << CPN_REDIRECT; // c:396
251/// `CPN_QUOTE` constant.
252pub const CPN_QUOTE: i32 = 4; // c:397
253/// `CP_QUOTE` constant.
254pub const CP_QUOTE: u32 = 1 << CPN_QUOTE; // c:398
255/// `CPN_QUOTING` constant.
256pub const CPN_QUOTING: i32 = 5; // c:399
257/// `CP_QUOTING` constant.
258pub const CP_QUOTING: u32 = 1 << CPN_QUOTING; // c:400
259/// `CPN_RESTORE` constant.
260pub const CPN_RESTORE: i32 = 6; // c:401
261/// `CP_RESTORE` constant.
262pub const CP_RESTORE: u32 = 1 << CPN_RESTORE; // c:402
263/// `CPN_LIST` constant.
264pub const CPN_LIST: i32 = 7; // c:403
265/// `CP_LIST` constant.
266pub const CP_LIST: u32 = 1 << CPN_LIST; // c:404
267/// `CPN_INSERT` constant.
268pub const CPN_INSERT: i32 = 8; // c:405
269/// `CP_INSERT` constant.
270pub const CP_INSERT: u32 = 1 << CPN_INSERT; // c:406
271/// `CPN_EXACT` constant.
272pub const CPN_EXACT: i32 = 9; // c:407
273/// `CP_EXACT` constant.
274pub const CP_EXACT: u32 = 1 << CPN_EXACT; // c:408
275/// `CPN_EXACTSTR` constant.
276pub const CPN_EXACTSTR: i32 = 10; // c:409
277/// `CP_EXACTSTR` constant.
278pub const CP_EXACTSTR: u32 = 1 << CPN_EXACTSTR; // c:410
279/// `CPN_PATMATCH` constant.
280pub const CPN_PATMATCH: i32 = 11; // c:411
281/// `CP_PATMATCH` constant.
282pub const CP_PATMATCH: u32 = 1 << CPN_PATMATCH; // c:412
283/// `CPN_PATINSERT` constant.
284pub const CPN_PATINSERT: i32 = 12; // c:413
285/// `CP_PATINSERT` constant.
286pub const CP_PATINSERT: u32 = 1 << CPN_PATINSERT; // c:414
287/// `CPN_UNAMBIG` constant.
288pub const CPN_UNAMBIG: i32 = 13; // c:415
289/// `CP_UNAMBIG` constant.
290pub const CP_UNAMBIG: u32 = 1 << CPN_UNAMBIG; // c:416
291/// `CPN_UNAMBIGC` constant.
292pub const CPN_UNAMBIGC: i32 = 14; // c:417
293/// `CP_UNAMBIGC` constant.
294pub const CP_UNAMBIGC: u32 = 1 << CPN_UNAMBIGC; // c:418
295/// `CPN_UNAMBIGP` constant.
296pub const CPN_UNAMBIGP: i32 = 15; // c:419
297/// `CP_UNAMBIGP` constant.
298pub const CP_UNAMBIGP: u32 = 1 << CPN_UNAMBIGP; // c:420
299/// `CPN_INSERTP` constant.
300pub const CPN_INSERTP: i32 = 16; // c:421
301/// `CP_INSERTP` constant.
302pub const CP_INSERTP: u32 = 1 << CPN_INSERTP; // c:422
303/// `CPN_LISTMAX` constant.
304pub const CPN_LISTMAX: i32 = 17; // c:423
305/// `CP_LISTMAX` constant.
306pub const CP_LISTMAX: u32 = 1 << CPN_LISTMAX; // c:424
307/// `CPN_LASTPROMPT` constant.
308pub const CPN_LASTPROMPT: i32 = 18; // c:425
309/// `CP_LASTPROMPT` constant.
310pub const CP_LASTPROMPT: u32 = 1 << CPN_LASTPROMPT; // c:426
311/// `CPN_TOEND` constant.
312pub const CPN_TOEND: i32 = 19; // c:427
313/// `CP_TOEND` constant.
314pub const CP_TOEND: u32 = 1 << CPN_TOEND; // c:428
315/// `CPN_OLDLIST` constant.
316pub const CPN_OLDLIST: i32 = 20; // c:429
317/// `CP_OLDLIST` constant.
318pub const CP_OLDLIST: u32 = 1 << CPN_OLDLIST; // c:430
319/// `CPN_OLDINS` constant.
320pub const CPN_OLDINS: i32 = 21; // c:431
321/// `CP_OLDINS` constant.
322pub const CP_OLDINS: u32 = 1 << CPN_OLDINS; // c:432
323/// `CPN_VARED` constant.
324pub const CPN_VARED: i32 = 22; // c:433
325/// `CP_VARED` constant.
326pub const CP_VARED: u32 = 1 << CPN_VARED; // c:434
327/// `CPN_LISTLINES` constant.
328pub const CPN_LISTLINES: i32 = 23; // c:435
329/// `CP_LISTLINES` constant.
330pub const CP_LISTLINES: u32 = 1 << CPN_LISTLINES; // c:436
331/// `CPN_QUOTES` constant.
332pub const CPN_QUOTES: i32 = 24; // c:437
333/// `CP_QUOTES` constant.
334pub const CP_QUOTES: u32 = 1 << CPN_QUOTES; // c:438
335/// `CPN_IGNORED` constant.
336pub const CPN_IGNORED: i32 = 25; // c:439
337/// `CP_IGNORED` constant.
338pub const CP_IGNORED: u32 = 1 << CPN_IGNORED; // c:440
339
340/// Port of `#define CP_KEYPARAMS` from `Src/Zle/comp.h:442`. Total
341/// number of "key" comp parameters.
342pub const CP_KEYPARAMS: i32 = 26; // c:442
343
344/// Port of `#define CP_ALLKEYS` from `Src/Zle/comp.h:443`. Mask
345/// covering every CP_* "key" param flag (bits 0..25 set).
346pub const CP_ALLKEYS: u32 = 0x3ffffff; // c:443
347
348// ---------------------------------------------------------------------------
349// Hook indexes (c:447-451).
350// ---------------------------------------------------------------------------
351/// `INSERTMATCHHOOK_OFFSET` constant.
352pub const INSERTMATCHHOOK_OFFSET: usize = 0; // c:447
353/// `MENUSTARTHOOK_OFFSET` constant.
354pub const MENUSTARTHOOK_OFFSET: usize = 1; // c:448
355/// `COMPCTLMAKEHOOK_OFFSET` constant.
356pub const COMPCTLMAKEHOOK_OFFSET: usize = 2; // c:449
357/// `COMPCTLCLEANUPHOOK_OFFSET` constant.
358pub const COMPCTLCLEANUPHOOK_OFFSET: usize = 3; // c:450
359/// `COMPLISTMATCHESHOOK_OFFSET` constant.
360pub const COMPLISTMATCHESHOOK_OFFSET: usize = 4; // c:451
361
362// ---------------------------------------------------------------------------
363// Misc constants.
364// ---------------------------------------------------------------------------
365
366/// Port of `#define CM_SPACE` from `Src/Zle/comp.h:474`. Number of
367/// columns to leave empty between rows of matches.
368pub const CM_SPACE: i32 = 2; // c:474
369
370// ---------------------------------------------------------------------------
371// Typedef structs (c:30-470). C uses linked lists threaded through
372// `next`/`prev` pointers; Rust ports as `Option<Box<T>>` for the
373// owning side.
374// ---------------------------------------------------------------------------
375
376/// Port of `struct cexpl` from `Src/Zle/comp.h:40-45`. Explanation
377/// string entry attached to a match group.
378#[derive(Debug, Clone, Default)]
379#[allow(non_camel_case_types)]
380pub struct Cexpl {
381    // c:40
382    /// Display even without matches.
383    pub always: i32, // c:41
384    /// The string itself.
385    pub str: Option<String>, // c:42 (Rust keyword `str`)
386    /// Number of matches.
387    pub count: i32, // c:43
388    /// Number of matches with fignore ignored.
389    pub fcount: i32, // c:44
390}
391
392/// Port of `struct cmgroup` from `Src/Zle/comp.h:49-82`. A group of
393/// completion matches (one per `compadd -J GROUP`).
394#[derive(Debug, Clone, Default)]
395#[allow(non_camel_case_types)]
396pub struct Cmgroup {
397    // c:49
398    /// Group name.
399    pub name: Option<String>, // c:50
400    /// Previous group in the list.
401    pub prev: Option<Box<Cmgroup>>, // c:51
402    /// Next group in the list.
403    pub next: Option<Box<Cmgroup>>, // c:52
404    /// CGF_* flags.
405    pub flags: i32, // c:53
406    /// Number of matches.
407    pub mcount: i32, // c:54
408    /// The matches.
409    pub matches: Vec<Cmatch>, // c:55
410    /// Number of things to list here.
411    pub lcount: i32, // c:56
412    /// Number of line-displays.
413    pub llcount: i32, // c:57
414    /// Things to list.
415    pub ylist: Vec<String>, // c:58
416    /// Number of explanation strings.
417    pub ecount: i32, // c:59
418    /// Explanation strings.
419    pub expls: Vec<Cexpl>, // c:60
420    /// Number of compctls used.
421    pub ccount: i32, // c:61
422    /// LinkList of explanations (mid-build accumulator before `expls`).
423    pub lexpls: Vec<Cexpl>, // c:62
424    /// LinkList of matches (mid-build accumulator before `matches`).
425    pub lmatches: Vec<Cmatch>, // c:63
426    /// LinkList of matches with fignore-removed entries kept.
427    pub lfmatches: Vec<Cmatch>, // c:64
428    /// LinkList of compctls used (mid-build accumulator).
429    pub lallccs: Vec<String>, // c:65
430    /// Group number.
431    pub num: i32, // c:66
432    /// Number of opened braces.
433    pub nbrbeg: i32, // c:67
434    /// Number of closed braces.
435    pub nbrend: i32, // c:68
436    /// New matches since last permalloc().
437    pub new_: i32, // c:69 (Rust keyword `new`)
438    // c:71-77 — listing accumulators.
439    /// Number of matches to list in columns.
440    pub dcount: i32, // c:71
441    /// Number of columns.
442    pub cols: i32, // c:72
443    /// Number of lines.
444    pub lins: i32, // c:73
445    /// Column width.
446    pub width: i32, // c:74
447    /// Per-column widths for listpacked.
448    pub widths: Vec<i32>, // c:75
449    /// Total length.
450    pub totl: i32, // c:76
451    /// Length of shortest match.
452    pub shortest: i32, // c:77
453    /// Permanent-alloc version of this group (the C source's
454    /// shadow-copy used to survive heap resets).
455    pub perm: Option<Box<Cmgroup>>, // c:78
456}
457
458/// Port of `struct brinfo` from `Src/Zle/zle.h:368-375`. Brace-info
459/// node — tracks one `{` or `}` position in the pattern being
460/// completed, used by `compadd -b` brace-aware matching.
461#[derive(Debug, Clone, Default)]
462#[allow(non_camel_case_types)]
463pub struct Brinfo {
464    // zle.h:368
465    /// Next in list.
466    pub next: Option<Box<Brinfo>>, // zle.h:369
467    /// Previous (only for closing braces).
468    pub prev: Option<Box<Brinfo>>, // zle.h:370
469    /// The string to insert.
470    pub str: Option<String>, // zle.h:371
471    /// Original position.
472    pub pos: i32, // zle.h:372
473    /// Original position with quoting.
474    pub qpos: i32, // zle.h:373
475    /// Position for current match.
476    pub curpos: i32, // zle.h:374
477}
478
479/// Port of `struct cmatch` from `Src/Zle/comp.h:99-125`. A single
480/// completion match.
481#[derive(Debug, Clone, Default)]
482#[allow(non_camel_case_types)]
483pub struct Cmatch {
484    // c:99
485    /// The match itself.
486    pub str: Option<String>, // c:100 (Rust keyword)
487    /// The match string unquoted.
488    pub orig: Option<String>, // c:101
489    /// Ignored prefix, has to be re-inserted.
490    pub ipre: Option<String>, // c:102
491    /// Ignored prefix, unquoted.
492    pub ripre: Option<String>, // c:103
493    /// Ignored suffix.
494    pub isuf: Option<String>, // c:104
495    /// The path prefix.
496    pub ppre: Option<String>, // c:105
497    /// The path suffix.
498    pub psuf: Option<String>, // c:106
499    /// Path prefix for opendir.
500    pub prpre: Option<String>, // c:107
501    /// Prefix string from -P.
502    pub pre: Option<String>, // c:108
503    /// Suffix string from -S.
504    pub suf: Option<String>, // c:109
505    /// String to display (compadd -d).
506    pub disp: Option<String>, // c:110
507    /// Closing quote to add automatically.
508    pub autoq: Option<String>, // c:111
509    /// CMF_* flags (cmatch namespace).
510    pub flags: i32, // c:112
511    /// Places where to put the brace prefixes.
512    pub brpl: Vec<i32>, // c:113
513    /// ...and the suffixes.
514    pub brsl: Vec<i32>, // c:114
515    /// When to remove the suffix.
516    pub rems: Option<String>, // c:115
517    /// Shell function to call for suffix-removal.
518    pub remf: Option<String>, // c:116
519    /// Length of quote-prefix.
520    pub qipl: i32, // c:117
521    /// Length of quote-suffix.
522    pub qisl: i32, // c:118
523    /// Group-relative number.
524    pub rnum: i32, // c:119
525    /// Global number.
526    pub gnum: i32, // c:120
527    /// `mode` field of a stat.
528    pub mode: u32, // c:121 (mode_t → u32)
529    /// LIST_TYPE-character for mode or 0.
530    pub modec: char, // c:122
531    /// `mode` field of a stat, following symlink.
532    pub fmode: u32, // c:123 (mode_t → u32)
533    /// LIST_TYPE-character for fmode or 0.
534    pub fmodec: char, // c:124
535}
536
537/// Port of `struct cmlist` from `Src/Zle/comp.h:147-151`. Linked
538/// list of global matchers.
539#[derive(Debug, Clone)]
540#[allow(non_camel_case_types)]
541pub struct Cmlist {
542    // c:147
543    /// Next entry in the list.
544    pub next: Option<Box<Cmlist>>, // c:148
545    /// The matcher definition.
546    pub matcher: Box<Cmatcher>, // c:149
547    /// The string for it.
548    pub str: String, // c:150
549}
550
551/// Port of `struct cmatcher` from `Src/Zle/comp.h:153-167`. Matcher
552/// specification — what to match on the line vs in the trial word,
553/// with optional left/right anchors.
554#[derive(Debug, Clone, Default)]
555#[allow(non_camel_case_types)]
556pub struct Cmatcher {
557    // c:153
558    /// Reference counter.
559    pub refc: i32, // c:154
560    /// Next matcher.
561    pub next: Option<Box<Cmatcher>>, // c:155
562    /// CMF_LINE/CMF_LEFT/CMF_RIGHT/CMF_INTER (cmatcher namespace).
563    pub flags: i32, // c:156
564    /// What matches on the line.
565    pub line: Option<Box<Cpattern>>, // c:157
566    /// Length of line pattern.
567    pub llen: i32, // c:158
568    /// What matches in the word.
569    pub word: Option<Box<Cpattern>>, // c:159
570    /// Length of word pattern, or:
571    /// -1: word pattern is one asterisk
572    /// -2: word pattern is two asterisks
573    pub wlen: i32, // c:160
574    /// Left anchor.
575    pub left: Option<Box<Cpattern>>, // c:163
576    /// Length of left anchor.
577    pub lalen: i32, // c:164
578    /// Right anchor.
579    pub right: Option<Box<Cpattern>>, // c:165
580    /// Length of right anchor.
581    pub ralen: i32, // c:166
582}
583
584/// Port of `struct cpattern` from `Src/Zle/comp.h:197-210`. A
585/// single pattern element in a matcher specification — represents
586/// one character either in the trial completion or in the word on
587/// the command line.
588///
589/// The C `union { char *str; convchar_t chr; } u` is dispatched by
590/// `tp` (a `CPAT_*` constant). The Rust port keeps both fields with
591/// `Option`s so the dispatcher reads only the live one.
592#[derive(Debug, Clone, Default)]
593#[allow(non_camel_case_types)]
594pub struct Cpattern {
595    // c:197
596    /// Next sub-pattern.
597    pub next: Option<Box<Cpattern>>, // c:198
598    /// Type of object — one of CPAT_*.
599    pub tp: i32, // c:199
600    /// If a character class (CPAT_CCLASS/CPAT_NCLASS/CPAT_EQUIV),
601    /// the objects in it as a metafied byte sequence — the encoded
602    /// format matches `Src/pattern.c`'s `patmatchindex` reader:
603    /// `0x80 + PP_*` for POSIX classes, `0x80 + PP_RANGE` + two
604    /// bytes for ranges, plain bytes for literals. Storing as
605    /// `Vec<u8>` (not `String`) preserves the 0x80-0xBF marker
606    /// bytes that would otherwise mangle under UTF-8 validation.
607    pub str: Option<Vec<u8>>, // c:201 union.u.str
608    /// If a single character (CPAT_CHAR), it.
609    pub chr: u32, // c:208 union.u.chr (convchar_t)
610}
611
612/// Port of `struct cline` from `Src/Zle/comp.h:245-257`. One
613/// word-part in the unambiguous-line-string list. Threaded prefix /
614/// suffix sub-lists via the `prefix`/`suffix` fields.
615#[derive(Debug, Clone, Default)]
616#[allow(non_camel_case_types)]
617pub struct Cline {
618    // c:245
619    /// Next sibling word-part.
620    pub next: Option<Box<Cline>>, // c:246
621    /// CLF_* flags.
622    pub flags: i32, // c:247
623    /// Line string for this part.
624    pub line: Option<String>, // c:248
625    /// Length of `line`.
626    pub llen: i32, // c:249
627    /// Word string for this part.
628    pub word: Option<String>, // c:250
629    /// Length of `word`.
630    pub wlen: i32, // c:251
631    /// Original (unjoined) string.
632    pub orig: Option<String>, // c:252
633    /// Length of `orig`.
634    pub olen: i32, // c:253
635    /// String length (the join-up version).
636    pub slen: i32, // c:254
637    /// Prefix sub-list.
638    pub prefix: Option<Box<Cline>>, // c:255
639    /// Suffix sub-list.
640    pub suffix: Option<Box<Cline>>, // c:255
641    /// Min length seen for this part (joining metric).
642    pub min: i32, // c:256
643    /// Max length seen for this part (joining metric).
644    pub max: i32, // c:256
645}
646
647/// Port of `struct aminfo` from `Src/Zle/comp.h:274-280`. Holds
648/// info about ambiguous completions — there's one for fignore-
649/// ignored and one for normal completion.
650#[derive(Debug, Clone, Default)]
651#[allow(non_camel_case_types)]
652pub struct Aminfo {
653    // c:274
654    /// The first match.
655    pub firstm: Option<Box<Cmatch>>, // c:275
656    /// If there was an exact match.
657    pub exact: i32, // c:276
658    /// The exact match (if any).
659    pub exactm: Option<Box<Cmatch>>, // c:277
660    /// Number of matches.
661    pub count: i32, // c:278
662    /// Unambiguous line string.
663    pub line: Option<Box<Cline>>, // c:279
664}
665
666/// Port of `struct menuinfo` from `Src/Zle/comp.h:284-295`.
667/// Menu-completion state.
668#[derive(Debug, Clone, Default)]
669#[allow(non_camel_case_types)]
670pub struct Menuinfo {
671    // c:284
672    /// Position in the group list.
673    pub group: Option<Box<Cmgroup>>, // c:285
674    /// Match currently inserted.
675    pub cur: Option<Box<Cmatch>>, // c:286
676    /// Begin on line.
677    pub pos: i32, // c:287
678    /// Length of inserted string.
679    pub len: i32, // c:288
680    /// End on the line.
681    pub end: i32, // c:289
682    /// Non-zero if the cursor was at the end.
683    pub we: i32, // c:290
684    /// Length of suffix inserted.
685    pub insc: i32, // c:291
686    /// We asked if the list should be shown.
687    pub asked: i32, // c:292
688    /// Prefix before a brace, if any.
689    pub prebr: Option<String>, // c:293
690    /// Suffix after a brace.
691    pub postbr: Option<String>, // c:294
692}
693
694/// Port of `struct ccmakedat` from `Src/Zle/comp.h:455-459`. Hook
695/// data passed to the compctl-make path.
696#[derive(Debug, Clone, Default)]
697#[allow(non_camel_case_types)]
698pub struct Ccmakedat {
699    // c:455
700    /// String passed to the hook.
701    pub str: Option<String>, // c:456
702    /// Whether we're in a command position.
703    pub incmd: i32, // c:457
704    /// List flag.
705    pub lst: i32, // c:458
706}
707
708/// Port of `struct chdata` from `Src/Zle/comp.h:465-470`. Data
709/// given to `offered` hooks.
710#[derive(Debug, Clone, Default)]
711#[allow(non_camel_case_types)]
712pub struct Chdata {
713    // c:465
714    /// The matches generated.
715    pub matches: Option<Box<Cmgroup>>, // c:466
716    /// Number of matches.
717    pub num: i32, // c:467
718    /// Number of messages.
719    pub nmesg: i32, // c:468
720    /// Current match or None.
721    pub cur: Option<Box<Cmatch>>, // c:469
722}
723
724/// Port of `struct cadata` from `Src/Zle/comp.h:315-337`. Data
725/// passed to compadd / addmatches().
726#[derive(Debug, Clone, Default)]
727#[allow(non_camel_case_types)]
728pub struct Cadata {
729    // c:315
730    /// Ignored prefix (-i).
731    pub ipre: Option<String>, // c:316
732    /// Ignored suffix (-I).
733    pub isuf: Option<String>, // c:317
734    /// `path` prefix (-p).
735    pub ppre: Option<String>, // c:318
736    /// `path` suffix (-s).
737    pub psuf: Option<String>, // c:319
738    /// Expanded `path` prefix (-W).
739    pub prpre: Option<String>, // c:320
740    /// Prefix to insert (-P).
741    pub pre: Option<String>, // c:321
742    /// Suffix to insert (-S).
743    pub suf: Option<String>, // c:322
744    /// Name of the group (`-[JV]`).
745    pub group: Option<String>, // c:323
746    /// Remove suffix on chars... (-r).
747    pub rems: Option<String>, // c:324
748    /// Function to remove suffix (-R).
749    pub remf: Option<String>, // c:325
750    /// Ignored suffixes (-F).
751    pub ign: Option<String>, // c:326
752    /// CMF_* flags (`-[fqn]`).
753    pub flags: i32, // c:327
754    /// CAF_* flags (`-[QUa]`).
755    pub aflags: i32, // c:328
756    /// Match spec (parsed from -M).
757    pub match_: Option<Box<Cmatcher>>, // c:329 (Rust keyword)
758    /// Explanation (-X).
759    pub exp: Option<String>, // c:330
760    /// Array to store matches in (-A).
761    pub apar: Option<String>, // c:331
762    /// Array to store originals in (-O).
763    pub opar: Option<String>, // c:332
764    /// Arrays to delete non-matches in (-D).
765    pub dpar: Vec<String>, // c:333
766    /// Array with display lists (-d).
767    pub disp: Option<String>, // c:334
768    /// Message to show unconditionally (-x).
769    pub mesg: Option<String>, // c:335
770    /// Add that many dummy matches.
771    pub dummies: i32, // c:336
772}
773
774/// Port of `struct cldata` from `Src/Zle/comp.h:343-353`. List data
775/// for the matches-listing path.
776#[derive(Debug, Clone, Default)]
777#[allow(non_camel_case_types)]
778pub struct Cldata {
779    // c:343
780    /// Screen width.
781    pub zterm_columns: i32, // c:344
782    /// Screen height.
783    pub zterm_lines: i32, // c:345
784    /// Value of global menuacc.
785    pub menuacc: i32, // c:346
786    /// No need to calculate anew.
787    pub valid: i32, // c:347
788    /// Number of matches to list.
789    pub nlist: i32, // c:348
790    /// Number of lines needed.
791    pub nlines: i32, // c:349
792    /// != 0 if there are hidden matches.
793    pub hidden: i32, // c:350
794    /// != 0 if only explanations to print.
795    pub onlyexpl: i32, // c:351
796    /// != 0 if hidden matches should be shown.
797    pub showall: i32, // c:352
798}
799
800#[cfg(test)]
801mod tests {
802    use super::*;
803    use crate::ported::zle::zle_main::zle_test_setup;
804
805    /// Verifies CGF_* group flag values per c:85-95.
806    #[test]
807    fn cgf_flags_correct() {
808        let _g = crate::test_util::global_state_lock();
809        let _g = zle_test_setup();
810        assert_eq!(CGF_NOSORT, 1);
811        assert_eq!(CGF_LINES, 2);
812        assert_eq!(CGF_HASDL, 4);
813        assert_eq!(CGF_REVSORT, 1024);
814    }
815
816    /// Verifies CMF_* match flags are non-overlapping single-bits
817    /// per c:127-143.
818    #[test]
819    fn cmf_match_flags_distinct() {
820        let _g = crate::test_util::global_state_lock();
821        let _g = zle_test_setup();
822        let all = CMF_FILE
823            | CMF_REMOVE
824            | CMF_ISPAR
825            | CMF_PARBR
826            | CMF_PARNEST
827            | CMF_NOLIST
828            | CMF_DISPLINE
829            | CMF_HIDE
830            | CMF_NOSPACE
831            | CMF_PACKED
832            | CMF_ROWS
833            | CMF_MULT
834            | CMF_FMULT
835            | CMF_ALL
836            | CMF_DUMMY
837            | CMF_MORDER
838            | CMF_DELETE;
839        assert_eq!(all.count_ones(), 17);
840    }
841
842    /// Verifies CMF_LINE/LEFT/RIGHT/INTER cmatcher flags per c:172-178.
843    #[test]
844    fn cmf_matcher_flags_correct() {
845        let _g = crate::test_util::global_state_lock();
846        let _g = zle_test_setup();
847        assert_eq!(CMF_LINE, 1);
848        assert_eq!(CMF_LEFT, 2);
849        assert_eq!(CMF_RIGHT, 4);
850        assert_eq!(CMF_INTER, 8);
851    }
852
853    /// Verifies CPAT_* enum values per c:184-190.
854    #[test]
855    fn cpat_enum_values_correct() {
856        let _g = crate::test_util::global_state_lock();
857        let _g = zle_test_setup();
858        assert_eq!(CPAT_CCLASS, 0);
859        assert_eq!(CPAT_NCLASS, 1);
860        assert_eq!(CPAT_EQUIV, 2);
861        assert_eq!(CPAT_ANY, 3);
862        assert_eq!(CPAT_CHAR, 4);
863    }
864
865    /// Verifies CP_REALPARAMS / CP_ALLREALS aggregate per c:385-386.
866    #[test]
867    fn cp_realparams_mask_covers_10_bits() {
868        let _g = crate::test_util::global_state_lock();
869        let _g = zle_test_setup();
870        assert_eq!(CP_REALPARAMS, 10);
871        assert_eq!(CP_ALLREALS, 0x3ff);
872        assert_eq!(CP_ALLREALS.count_ones(), 10);
873        assert_eq!(
874            CP_WORDS
875                | CP_REDIRS
876                | CP_CURRENT
877                | CP_PREFIX
878                | CP_SUFFIX
879                | CP_IPREFIX
880                | CP_ISUFFIX
881                | CP_QIPREFIX
882                | CP_QISUFFIX
883                | CP_COMPSTATE,
884            CP_ALLREALS
885        );
886    }
887
888    /// Verifies CP_KEYPARAMS / CP_ALLKEYS aggregate per c:442-443.
889    #[test]
890    fn cp_keyparams_mask_covers_26_bits() {
891        let _g = crate::test_util::global_state_lock();
892        let _g = zle_test_setup();
893        assert_eq!(CP_KEYPARAMS, 26);
894        assert_eq!(CP_ALLKEYS, 0x3ffffff);
895        assert_eq!(CP_ALLKEYS.count_ones(), 26);
896    }
897
898    /// Verifies CAF_* compadd flags per c:299-309.
899    #[test]
900    fn caf_flags_correct() {
901        let _g = crate::test_util::global_state_lock();
902        let _g = zle_test_setup();
903        assert_eq!(CAF_QUOTE, 1);
904        assert_eq!(CAF_NOSORT, 2);
905        assert_eq!(CAF_REVSORT, 1024);
906    }
907
908    /// Verifies hook offset constants per c:447-451.
909    #[test]
910    fn hook_offsets_sequential() {
911        let _g = crate::test_util::global_state_lock();
912        let _g = zle_test_setup();
913        assert_eq!(INSERTMATCHHOOK_OFFSET, 0);
914        assert_eq!(MENUSTARTHOOK_OFFSET, 1);
915        assert_eq!(COMPCTLMAKEHOOK_OFFSET, 2);
916        assert_eq!(COMPCTLCLEANUPHOOK_OFFSET, 3);
917        assert_eq!(COMPLISTMATCHESHOOK_OFFSET, 4);
918    }
919
920    /// Verifies CM_SPACE per c:474.
921    #[test]
922    fn cm_space_is_2() {
923        let _g = crate::test_util::global_state_lock();
924        let _g = zle_test_setup();
925        assert_eq!(CM_SPACE, 2);
926    }
927
928    /// Verifies the structs construct cleanly with `Default`.
929    #[test]
930    fn structs_default_construct() {
931        let _g = crate::test_util::global_state_lock();
932        let _g = zle_test_setup();
933        let _ = Cexpl::default();
934        let _ = Cmgroup::default();
935        let _ = Cmatch::default();
936        let _ = Cmatcher::default();
937        let _ = Cpattern::default();
938        let _ = Cline::default();
939        let _ = Aminfo::default();
940        let _ = Menuinfo::default();
941        let _ = Ccmakedat::default();
942        let _ = Chdata::default();
943        let _ = Cadata::default();
944        let _ = Cldata::default();
945    }
946
947    /// `Src/Zle/comp.h:85-95` — `CGF_*` completion group flags.
948    /// Pin every bit value vs the canonical C define.
949    #[test]
950    fn cgf_flags_match_c_comp_h_canonical_values() {
951        let _g = crate::test_util::global_state_lock();
952        assert_eq!(CGF_NOSORT, 1, "c:85");
953        assert_eq!(CGF_LINES, 2, "c:86");
954        assert_eq!(CGF_HASDL, 4, "c:87");
955        assert_eq!(CGF_UNIQALL, 8, "c:88");
956        assert_eq!(CGF_UNIQCON, 16, "c:89");
957        assert_eq!(CGF_PACKED, 32, "c:90");
958        assert_eq!(CGF_ROWS, 64, "c:91");
959        assert_eq!(CGF_FILES, 128, "c:92");
960        assert_eq!(CGF_MATSORT, 256, "c:93");
961        assert_eq!(CGF_NUMSORT, 512, "c:94");
962        assert_eq!(CGF_REVSORT, 1024, "c:95");
963    }
964
965    /// `Src/Zle/comp.h:127-143` — `CMF_*` completion-match flags
966    /// (Cmatch struct, not Cmatcher). 17 flags total.
967    #[test]
968    fn cmf_match_flags_match_c_comp_h_canonical_values() {
969        let _g = crate::test_util::global_state_lock();
970        assert_eq!(CMF_FILE, 1 << 0, "c:127");
971        assert_eq!(CMF_REMOVE, 1 << 1, "c:128");
972        assert_eq!(CMF_ISPAR, 1 << 2, "c:129");
973        assert_eq!(CMF_PARBR, 1 << 3, "c:130");
974        assert_eq!(CMF_PARNEST, 1 << 4, "c:131");
975        assert_eq!(CMF_NOLIST, 1 << 5, "c:132");
976        assert_eq!(CMF_DISPLINE, 1 << 6, "c:133");
977        assert_eq!(CMF_HIDE, 1 << 7, "c:134");
978        assert_eq!(CMF_NOSPACE, 1 << 8, "c:135");
979        assert_eq!(CMF_PACKED, 1 << 9, "c:136");
980        assert_eq!(CMF_ROWS, 1 << 10, "c:137");
981        assert_eq!(CMF_MULT, 1 << 11, "c:138");
982        assert_eq!(CMF_FMULT, 1 << 12, "c:139");
983        assert_eq!(CMF_ALL, 1 << 13, "c:140");
984        assert_eq!(CMF_DUMMY, 1 << 14, "c:141");
985        assert_eq!(CMF_MORDER, 1 << 15, "c:142");
986        assert_eq!(CMF_DELETE, 1 << 16, "c:143");
987    }
988
989    /// c:85-95 — CGF_* group flags are distinct single bits.
990    /// Pin the bit-packing because the c:85 mask is OR'd into a
991    /// shared `cgflags` field.
992    #[test]
993    fn cgf_group_flags_are_distinct_single_bits() {
994        let _g = crate::test_util::global_state_lock();
995        let flags = [
996            CGF_NOSORT,
997            CGF_LINES,
998            CGF_HASDL,
999            CGF_UNIQALL,
1000            CGF_UNIQCON,
1001            CGF_PACKED,
1002            CGF_ROWS,
1003            CGF_FILES,
1004            CGF_MATSORT,
1005            CGF_NUMSORT,
1006            CGF_REVSORT,
1007        ];
1008        for &f in &flags {
1009            assert_eq!(
1010                (f as u32).count_ones(),
1011                1,
1012                "CGF flag {} = {:#x} must be a single bit",
1013                f,
1014                f
1015            );
1016        }
1017        let mut all: u32 = 0;
1018        for &f in &flags {
1019            let bit = f as u32;
1020            assert_eq!(
1021                all & bit,
1022                0,
1023                "CGF flag {:#x} overlaps with existing bits",
1024                bit
1025            );
1026            all |= bit;
1027        }
1028    }
1029
1030    /// c:127-143 — CMF_* flags are likewise distinct single bits.
1031    /// Pin no overlap across the 17 entries.
1032    #[test]
1033    fn cmf_match_flags_are_distinct_single_bits() {
1034        let _g = crate::test_util::global_state_lock();
1035        let flags = [
1036            CMF_FILE,
1037            CMF_REMOVE,
1038            CMF_ISPAR,
1039            CMF_PARBR,
1040            CMF_PARNEST,
1041            CMF_NOLIST,
1042            CMF_DISPLINE,
1043            CMF_HIDE,
1044            CMF_NOSPACE,
1045            CMF_PACKED,
1046            CMF_ROWS,
1047            CMF_MULT,
1048            CMF_FMULT,
1049            CMF_ALL,
1050            CMF_DUMMY,
1051            CMF_MORDER,
1052            CMF_DELETE,
1053        ];
1054        for &f in &flags {
1055            assert_eq!(
1056                (f as u32).count_ones(),
1057                1,
1058                "CMF flag {} = {:#x} must be a single bit",
1059                f,
1060                f
1061            );
1062        }
1063        let mut all: u32 = 0;
1064        for &f in &flags {
1065            let bit = f as u32;
1066            assert_eq!(all & bit, 0, "CMF flag {:#x} overlaps", bit);
1067            all |= bit;
1068        }
1069    }
1070
1071    /// c:85 — CGF_NOSORT must be bit 0 (the lowest). The C source
1072    /// uses `cgflags & CGF_NOSORT` in many places; the bit order
1073    /// being load-bearing on bit 0 is the convention for the
1074    /// `nosort` early-exit branch.
1075    #[test]
1076    fn cgf_nosort_is_bit_zero() {
1077        let _g = crate::test_util::global_state_lock();
1078        assert_eq!(
1079            CGF_NOSORT, 1,
1080            "CGF_NOSORT must be bit 0 — the early-exit `!sort` test"
1081        );
1082    }
1083
1084    /// c:127 — CMF_FILE must be bit 0. Pin the convention because
1085    /// file-matches are the most-common dispatch case and the
1086    /// fast-path bit-test relies on bit 0.
1087    #[test]
1088    fn cmf_file_is_bit_zero() {
1089        let _g = crate::test_util::global_state_lock();
1090        assert_eq!(
1091            CMF_FILE, 1,
1092            "CMF_FILE must be bit 0 — the file-match fast-path"
1093        );
1094    }
1095
1096    /// c:85-95 — Sanity: CGF flag values match what the C source
1097    /// declares. Spot-check the high end of the table.
1098    #[test]
1099    fn cgf_top_flags_match_canonical_high_bits() {
1100        let _g = crate::test_util::global_state_lock();
1101        assert_eq!(CGF_PACKED, 32);
1102        assert_eq!(CGF_ROWS, 64);
1103        assert_eq!(CGF_FILES, 128);
1104        assert_eq!(CGF_MATSORT, 256);
1105        assert_eq!(CGF_NUMSORT, 512);
1106        assert_eq!(CGF_REVSORT, 1024);
1107    }
1108
1109    // ═══════════════════════════════════════════════════════════════════
1110    // Additional C-parity tests for Src/Zle/comp.h CMF_* + CGF_*.
1111    // ═══════════════════════════════════════════════════════════════════
1112
1113    /// c:127-134 — first 8 CMF_* flags (1<<0 through 1<<7).
1114    #[test]
1115    fn cmf_low_byte_flags_are_single_bits() {
1116        assert_eq!(CMF_FILE, 1);
1117        assert_eq!(CMF_REMOVE, 2);
1118        assert_eq!(CMF_ISPAR, 4);
1119        assert_eq!(CMF_PARBR, 8);
1120        assert_eq!(CMF_PARNEST, 16);
1121        assert_eq!(CMF_NOLIST, 32);
1122        assert_eq!(CMF_DISPLINE, 64);
1123        assert_eq!(CMF_HIDE, 128);
1124    }
1125
1126    /// c:135-143 — second half of CMF_* (1<<8 through 1<<16).
1127    #[test]
1128    fn cmf_high_byte_flags_are_single_bits() {
1129        assert_eq!(CMF_NOSPACE, 1 << 8);
1130        assert_eq!(CMF_PACKED, 1 << 9);
1131        assert_eq!(CMF_ROWS, 1 << 10);
1132        assert_eq!(CMF_MULT, 1 << 11);
1133        assert_eq!(CMF_FMULT, 1 << 12);
1134        assert_eq!(CMF_ALL, 1 << 13);
1135        assert_eq!(CMF_DUMMY, 1 << 14);
1136        assert_eq!(CMF_MORDER, 1 << 15);
1137        assert_eq!(CMF_DELETE, 1 << 16);
1138    }
1139
1140    /// c:127-143 — Cmatch CMF_* flags pairwise disjoint single-bit
1141    /// values (the 17-flag bitfield).
1142    #[test]
1143    fn cmf_match_flags_pairwise_disjoint() {
1144        let flags = [
1145            CMF_FILE,
1146            CMF_REMOVE,
1147            CMF_ISPAR,
1148            CMF_PARBR,
1149            CMF_PARNEST,
1150            CMF_NOLIST,
1151            CMF_DISPLINE,
1152            CMF_HIDE,
1153            CMF_NOSPACE,
1154            CMF_PACKED,
1155            CMF_ROWS,
1156            CMF_MULT,
1157            CMF_FMULT,
1158            CMF_ALL,
1159            CMF_DUMMY,
1160            CMF_MORDER,
1161            CMF_DELETE,
1162        ];
1163        for i in 0..flags.len() {
1164            for j in (i + 1)..flags.len() {
1165                assert_eq!(
1166                    flags[i] & flags[j],
1167                    0,
1168                    "CMF_* flags {} and {} must not overlap",
1169                    flags[i],
1170                    flags[j]
1171                );
1172            }
1173        }
1174    }
1175
1176    /// c:85-95 — CGF_* flags pairwise disjoint single bits.
1177    #[test]
1178    fn cgf_flags_pairwise_disjoint() {
1179        let flags = [
1180            CGF_NOSORT,
1181            CGF_LINES,
1182            CGF_HASDL,
1183            CGF_UNIQALL,
1184            CGF_UNIQCON,
1185            CGF_PACKED,
1186            CGF_ROWS,
1187            CGF_FILES,
1188            CGF_MATSORT,
1189            CGF_NUMSORT,
1190        ];
1191        for i in 0..flags.len() {
1192            for j in (i + 1)..flags.len() {
1193                assert_eq!(
1194                    flags[i] & flags[j],
1195                    0,
1196                    "CGF_* flags {} and {} must not overlap",
1197                    flags[i],
1198                    flags[j]
1199                );
1200            }
1201        }
1202    }
1203
1204    /// c:85-89 — low CGF_* bit values (1, 2, 4, 8, 16).
1205    #[test]
1206    fn cgf_low_bits_canonical() {
1207        assert_eq!(CGF_NOSORT, 1);
1208        assert_eq!(CGF_LINES, 2);
1209        assert_eq!(CGF_HASDL, 4);
1210        assert_eq!(CGF_UNIQALL, 8);
1211        assert_eq!(CGF_UNIQCON, 16);
1212    }
1213
1214    // ═══════════════════════════════════════════════════════════════════
1215    // Additional C-parity tests for Src/Zle/comp.h
1216    // c:185-189 CPAT_* / c:259-267 CLF_* / c:299-... CAF_* / c:172-178 CMF_*
1217    // ═══════════════════════════════════════════════════════════════════
1218
1219    /// c:185-189 — CPAT_* enum values are sequential 0..5.
1220    #[test]
1221    fn cpat_enum_sequential_0_through_4() {
1222        assert_eq!(CPAT_CCLASS, 0, "c:185");
1223        assert_eq!(CPAT_NCLASS, 1, "c:186");
1224        assert_eq!(CPAT_EQUIV, 2, "c:187");
1225        assert_eq!(CPAT_ANY, 3, "c:188");
1226        assert_eq!(CPAT_CHAR, 4, "c:189");
1227    }
1228
1229    /// c:185-189 — CPAT_* are all distinct.
1230    #[test]
1231    fn cpat_enum_pairwise_distinct() {
1232        let codes = [CPAT_CCLASS, CPAT_NCLASS, CPAT_EQUIV, CPAT_ANY, CPAT_CHAR];
1233        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1234        assert_eq!(unique.len(), codes.len(), "CPAT_* must be distinct");
1235    }
1236
1237    /// c:259-267 — CLF_* flags are powers of 2 (single bits).
1238    #[test]
1239    fn clf_flags_are_single_bits() {
1240        for &v in &[
1241            CLF_MISS,
1242            CLF_DIFF,
1243            CLF_SUF,
1244            CLF_MID,
1245            CLF_NEW,
1246            CLF_LINE,
1247            CLF_JOIN,
1248            CLF_MATCHED,
1249            CLF_SKIP,
1250        ] {
1251            assert!(
1252                (v as u32).is_power_of_two(),
1253                "CLF_* flag {} must be a single bit",
1254                v
1255            );
1256        }
1257    }
1258
1259    /// c:259-267 — CLF_* canonical low-bit values.
1260    #[test]
1261    fn clf_canonical_values() {
1262        assert_eq!(CLF_MISS, 1, "c:259");
1263        assert_eq!(CLF_DIFF, 2, "c:260");
1264        assert_eq!(CLF_SUF, 4, "c:261");
1265        assert_eq!(CLF_MID, 8, "c:262");
1266        assert_eq!(CLF_NEW, 16, "c:263");
1267        assert_eq!(CLF_LINE, 32, "c:264");
1268        assert_eq!(CLF_JOIN, 64, "c:265");
1269        assert_eq!(CLF_MATCHED, 128, "c:266");
1270        assert_eq!(CLF_SKIP, 256, "c:267");
1271    }
1272
1273    /// c:259-267 — CLF_* flags pairwise distinct.
1274    #[test]
1275    fn clf_flags_pairwise_distinct() {
1276        let codes = [
1277            CLF_MISS,
1278            CLF_DIFF,
1279            CLF_SUF,
1280            CLF_MID,
1281            CLF_NEW,
1282            CLF_LINE,
1283            CLF_JOIN,
1284            CLF_MATCHED,
1285            CLF_SKIP,
1286        ];
1287        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1288        assert_eq!(unique.len(), codes.len(), "CLF_* must be pairwise distinct");
1289    }
1290
1291    /// c:299-302 — CAF_* canonical values.
1292    #[test]
1293    fn caf_canonical_values() {
1294        assert_eq!(CAF_QUOTE, 1, "c:299");
1295        assert_eq!(CAF_NOSORT, 2, "c:300");
1296        assert_eq!(CAF_MATCH, 4, "c:301");
1297        assert_eq!(CAF_UNIQCON, 8, "c:302");
1298    }
1299
1300    /// c:299-302 — CAF_* are single bits.
1301    #[test]
1302    fn caf_flags_are_single_bits() {
1303        for &v in &[CAF_QUOTE, CAF_NOSORT, CAF_MATCH, CAF_UNIQCON] {
1304            assert!(
1305                (v as u32).is_power_of_two(),
1306                "CAF_* flag {} must be a single bit",
1307                v
1308            );
1309        }
1310    }
1311
1312    /// c:172-178 — secondary CMF_* (LINE/LEFT/RIGHT/INTER) overlap with
1313    /// the main CMF_FILE/REMOVE/ISPAR/PARBR by design (different namespace).
1314    /// Pin canonical secondary values.
1315    #[test]
1316    fn cmf_secondary_canonical_values() {
1317        assert_eq!(CMF_LINE, 1, "c:172");
1318        assert_eq!(CMF_LEFT, 2, "c:174");
1319        assert_eq!(CMF_RIGHT, 4, "c:176");
1320        assert_eq!(CMF_INTER, 8, "c:178");
1321    }
1322
1323    /// c:172-178 — secondary CMF_* are single bits in their namespace.
1324    #[test]
1325    fn cmf_secondary_flags_are_single_bits() {
1326        for &v in &[CMF_LINE, CMF_LEFT, CMF_RIGHT, CMF_INTER] {
1327            assert!(
1328                (v as u32).is_power_of_two(),
1329                "CMF secondary flag {} must be a single bit",
1330                v
1331            );
1332        }
1333    }
1334
1335    /// c:127-143 — all CMF_* main flags are non-negative i32.
1336    #[test]
1337    fn cmf_main_flags_all_non_negative() {
1338        for &v in &[
1339            CMF_FILE,
1340            CMF_REMOVE,
1341            CMF_ISPAR,
1342            CMF_PARBR,
1343            CMF_PARNEST,
1344            CMF_NOLIST,
1345            CMF_DISPLINE,
1346            CMF_HIDE,
1347            CMF_NOSPACE,
1348            CMF_PACKED,
1349            CMF_ROWS,
1350            CMF_MULT,
1351            CMF_FMULT,
1352            CMF_ALL,
1353            CMF_DUMMY,
1354            CMF_MORDER,
1355            CMF_DELETE,
1356        ] {
1357            assert!(v >= 0, "CMF_* flag {} must be ≥ 0", v);
1358        }
1359    }
1360
1361    /// c:85-95 — CGF_* are powers of two (full sweep).
1362    #[test]
1363    fn cgf_all_flags_are_single_bits() {
1364        for &v in &[
1365            CGF_NOSORT,
1366            CGF_LINES,
1367            CGF_HASDL,
1368            CGF_UNIQALL,
1369            CGF_UNIQCON,
1370            CGF_PACKED,
1371            CGF_ROWS,
1372            CGF_FILES,
1373            CGF_MATSORT,
1374            CGF_NUMSORT,
1375            CGF_REVSORT,
1376        ] {
1377            assert!(
1378                (v as u32).is_power_of_two(),
1379                "CGF_* flag {} must be a single bit",
1380                v
1381            );
1382        }
1383    }
1384
1385    // ═══════════════════════════════════════════════════════════════════
1386    // Additional C-parity tests for Src/Zle/comp.h
1387    // c:364-440 CPN_*/CP_* completion-param flags + hook offsets c:447-451
1388    // ═══════════════════════════════════════════════════════════════════
1389
1390    /// c:364-440 — every CP_* equals 1 << CPN_* (definition contract).
1391    #[test]
1392    fn cp_flags_match_cpn_shift_definition() {
1393        assert_eq!(CP_WORDS, 1u32 << CPN_WORDS, "c:365");
1394        assert_eq!(CP_REDIRS, 1u32 << CPN_REDIRS, "c:367");
1395        assert_eq!(CP_CURRENT, 1u32 << CPN_CURRENT, "c:369");
1396        assert_eq!(CP_PREFIX, 1u32 << CPN_PREFIX, "c:371");
1397        assert_eq!(CP_SUFFIX, 1u32 << CPN_SUFFIX, "c:373");
1398    }
1399
1400    /// c:419-440 — high-bit CP_* still match `1 << CPN_*`.
1401    #[test]
1402    fn cp_high_bit_flags_match_cpn_shift_definition() {
1403        assert_eq!(CP_UNAMBIGP, 1u32 << CPN_UNAMBIGP, "c:420");
1404        assert_eq!(CP_INSERTP, 1u32 << CPN_INSERTP, "c:422");
1405        assert_eq!(CP_LISTMAX, 1u32 << CPN_LISTMAX, "c:424");
1406        assert_eq!(CP_LASTPROMPT, 1u32 << CPN_LASTPROMPT, "c:426");
1407        assert_eq!(CP_TOEND, 1u32 << CPN_TOEND, "c:428");
1408        assert_eq!(CP_OLDLIST, 1u32 << CPN_OLDLIST, "c:430");
1409        assert_eq!(CP_OLDINS, 1u32 << CPN_OLDINS, "c:432");
1410        assert_eq!(CP_VARED, 1u32 << CPN_VARED, "c:434");
1411        assert_eq!(CP_LISTLINES, 1u32 << CPN_LISTLINES, "c:436");
1412        assert_eq!(CP_QUOTES, 1u32 << CPN_QUOTES, "c:438");
1413        assert_eq!(CP_IGNORED, 1u32 << CPN_IGNORED, "c:440");
1414    }
1415
1416    /// c:364-440 — CPN_* indices are pairwise distinct and in 0..=25.
1417    #[test]
1418    fn cpn_indices_pairwise_distinct_and_in_range() {
1419        let cpns: Vec<i32> = vec![
1420            CPN_WORDS,
1421            CPN_REDIRS,
1422            CPN_CURRENT,
1423            CPN_PREFIX,
1424            CPN_SUFFIX,
1425            CPN_UNAMBIGP,
1426            CPN_INSERTP,
1427            CPN_LISTMAX,
1428            CPN_LASTPROMPT,
1429            CPN_TOEND,
1430            CPN_OLDLIST,
1431            CPN_OLDINS,
1432            CPN_VARED,
1433            CPN_LISTLINES,
1434            CPN_QUOTES,
1435            CPN_IGNORED,
1436        ];
1437        let unique: std::collections::HashSet<_> = cpns.iter().copied().collect();
1438        assert_eq!(unique.len(), cpns.len(), "CPN_* must be pairwise distinct");
1439        for &v in &cpns {
1440            assert!(
1441                v >= 0 && v <= 25,
1442                "CPN_* index {} must be in 0..=25 range",
1443                v
1444            );
1445        }
1446    }
1447
1448    /// c:443 — `CP_ALLKEYS = 0x3ffffff` (low 26 bits set).
1449    #[test]
1450    fn cp_allkeys_is_low_26_bits() {
1451        assert_eq!(
1452            CP_ALLKEYS,
1453            (1u32 << 26) - 1,
1454            "CP_ALLKEYS must be (1<<26)-1 = 0x3ffffff"
1455        );
1456    }
1457
1458    /// c:442 — `CP_KEYPARAMS = 26` (matches the bit-count of CP_ALLKEYS).
1459    #[test]
1460    fn cp_keyparams_count_matches_allkeys_bit_width() {
1461        assert_eq!(
1462            CP_KEYPARAMS, 26,
1463            "CP_KEYPARAMS must equal CP_ALLKEYS bit count"
1464        );
1465        assert_eq!(
1466            (CP_ALLKEYS + 1).trailing_zeros() as i32,
1467            CP_KEYPARAMS,
1468            "CP_ALLKEYS+1 leading bit position = CP_KEYPARAMS"
1469        );
1470    }
1471
1472    /// c:447-451 — hook offsets are contiguous 0..=4.
1473    #[test]
1474    fn hook_offsets_contiguous_zero_through_four() {
1475        assert_eq!(INSERTMATCHHOOK_OFFSET, 0, "c:447");
1476        assert_eq!(MENUSTARTHOOK_OFFSET, 1, "c:448");
1477        assert_eq!(COMPCTLMAKEHOOK_OFFSET, 2, "c:449");
1478        assert_eq!(COMPCTLCLEANUPHOOK_OFFSET, 3, "c:450");
1479        assert_eq!(COMPLISTMATCHESHOOK_OFFSET, 4, "c:451");
1480    }
1481
1482    /// c:447-451 — hook offsets are pairwise distinct.
1483    #[test]
1484    fn hook_offsets_pairwise_distinct() {
1485        let offs = [
1486            INSERTMATCHHOOK_OFFSET,
1487            MENUSTARTHOOK_OFFSET,
1488            COMPCTLMAKEHOOK_OFFSET,
1489            COMPCTLCLEANUPHOOK_OFFSET,
1490            COMPLISTMATCHESHOOK_OFFSET,
1491        ];
1492        let unique: std::collections::HashSet<_> = offs.iter().copied().collect();
1493        assert_eq!(
1494            unique.len(),
1495            offs.len(),
1496            "hook offsets must be pairwise distinct"
1497        );
1498    }
1499
1500    /// c:474 — CM_SPACE = 2 (canonical match-spec flag).
1501    #[test]
1502    fn cm_space_canonical_value() {
1503        assert_eq!(CM_SPACE, 2, "c:474");
1504    }
1505
1506    /// c:364-440 — every CPN_* fits the u32 shift width (< 32).
1507    #[test]
1508    fn cpn_indices_fit_in_u32_shift() {
1509        for &v in &[
1510            CPN_WORDS,
1511            CPN_REDIRS,
1512            CPN_CURRENT,
1513            CPN_PREFIX,
1514            CPN_SUFFIX,
1515            CPN_UNAMBIGP,
1516            CPN_INSERTP,
1517            CPN_LISTMAX,
1518            CPN_LASTPROMPT,
1519            CPN_TOEND,
1520            CPN_OLDLIST,
1521            CPN_OLDINS,
1522            CPN_VARED,
1523            CPN_LISTLINES,
1524            CPN_QUOTES,
1525            CPN_IGNORED,
1526        ] {
1527            assert!((v as u32) < 32, "CPN_* index {} must fit u32 shift", v);
1528        }
1529    }
1530
1531    /// c:447-451 — every hook offset is a usize (compile-time type pin).
1532    #[test]
1533    fn hook_offsets_are_valid_usize_indices() {
1534        let _: usize = INSERTMATCHHOOK_OFFSET;
1535        let _: usize = MENUSTARTHOOK_OFFSET;
1536        let _: usize = COMPCTLMAKEHOOK_OFFSET;
1537        let _: usize = COMPCTLCLEANUPHOOK_OFFSET;
1538        let _: usize = COMPLISTMATCHESHOOK_OFFSET;
1539    }
1540
1541    /// c:443 — CP_WORDS (bit 0) and CP_IGNORED (bit 25) are both inside
1542    /// CP_ALLKEYS — bitwise AND yields the flag itself.
1543    #[test]
1544    fn cp_allkeys_contains_endpoints() {
1545        assert_eq!(
1546            CP_WORDS & CP_ALLKEYS,
1547            CP_WORDS,
1548            "CP_WORDS (bit 0) ⊆ CP_ALLKEYS"
1549        );
1550        assert_eq!(
1551            CP_IGNORED & CP_ALLKEYS,
1552            CP_IGNORED,
1553            "CP_IGNORED (bit 25) ⊆ CP_ALLKEYS"
1554        );
1555    }
1556
1557    // ═══════════════════════════════════════════════════════════════════
1558    // Additional C-parity tests for Src/Zle/comp.h
1559    // c:85-95 CGF_* / c:127-140 CMF_*
1560    // ═══════════════════════════════════════════════════════════════════
1561
1562    /// c:85-95 — all CGF_* flags are i32 (compile-time type pin).
1563    #[test]
1564    fn cgf_flags_all_i32_type() {
1565        let _: i32 = CGF_NOSORT;
1566        let _: i32 = CGF_LINES;
1567        let _: i32 = CGF_REVSORT;
1568    }
1569
1570    /// c:85-95 — CGF_NOSORT (bit 0) through CGF_REVSORT (bit 10) cover
1571    /// contiguous bits 0..=10.
1572    #[test]
1573    fn cgf_flags_dense_low_bits() {
1574        let all = [
1575            CGF_NOSORT,
1576            CGF_LINES,
1577            CGF_HASDL,
1578            CGF_UNIQALL,
1579            CGF_UNIQCON,
1580            CGF_PACKED,
1581            CGF_ROWS,
1582            CGF_FILES,
1583            CGF_MATSORT,
1584            CGF_NUMSORT,
1585            CGF_REVSORT,
1586        ];
1587        let or_all: i32 = all.iter().fold(0, |acc, &v| acc | v);
1588        let expected = (1i32 << 11) - 1;
1589        assert_eq!(or_all, expected, "CGF_* must cover bits 0..=10 (no gaps)");
1590    }
1591
1592    /// c:85-95 — all CGF_* are powers of 2 (single-bit flags).
1593    #[test]
1594    fn cgf_flags_all_powers_of_two() {
1595        for &v in &[
1596            CGF_NOSORT,
1597            CGF_LINES,
1598            CGF_HASDL,
1599            CGF_UNIQALL,
1600            CGF_UNIQCON,
1601            CGF_PACKED,
1602            CGF_ROWS,
1603            CGF_FILES,
1604            CGF_MATSORT,
1605            CGF_NUMSORT,
1606            CGF_REVSORT,
1607        ] {
1608            assert!(
1609                (v as u32).is_power_of_two(),
1610                "CGF_* {} must be a single bit",
1611                v
1612            );
1613        }
1614    }
1615
1616    /// c:85-95 — CGF_* pairwise distinct.
1617    #[test]
1618    fn cgf_flags_pairwise_distinct() {
1619        let codes = [
1620            CGF_NOSORT,
1621            CGF_LINES,
1622            CGF_HASDL,
1623            CGF_UNIQALL,
1624            CGF_UNIQCON,
1625            CGF_PACKED,
1626            CGF_ROWS,
1627            CGF_FILES,
1628            CGF_MATSORT,
1629            CGF_NUMSORT,
1630            CGF_REVSORT,
1631        ];
1632        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1633        assert_eq!(unique.len(), codes.len(), "CGF_* must be pairwise distinct");
1634    }
1635
1636    /// c:85 — CGF_NOSORT is bit 0 (= 1) (alt name pin).
1637    #[test]
1638    fn cgf_nosort_is_bit_zero_alt() {
1639        assert_eq!(CGF_NOSORT, 1, "c:85 — NOSORT is bit 0");
1640    }
1641
1642    /// c:127-140 — all CMF_* are i32 (compile-time type pin).
1643    #[test]
1644    fn cmf_flags_all_i32_type() {
1645        let _: i32 = CMF_FILE;
1646        let _: i32 = CMF_ALL;
1647    }
1648
1649    /// c:127 — CMF_FILE is bit 0 (alt name pin).
1650    #[test]
1651    fn cmf_file_is_bit_zero_alt() {
1652        assert_eq!(CMF_FILE, 1i32 << 0, "c:127 — FILE is bit 0");
1653    }
1654
1655    /// c:127-140 — CMF_FILE through CMF_ALL form contiguous bits 0..=13.
1656    #[test]
1657    fn cmf_flags_dense_bits_zero_through_13() {
1658        let all = [
1659            CMF_FILE,
1660            CMF_REMOVE,
1661            CMF_ISPAR,
1662            CMF_PARBR,
1663            CMF_PARNEST,
1664            CMF_NOLIST,
1665            CMF_DISPLINE,
1666            CMF_HIDE,
1667            CMF_NOSPACE,
1668            CMF_PACKED,
1669            CMF_ROWS,
1670            CMF_MULT,
1671            CMF_FMULT,
1672            CMF_ALL,
1673        ];
1674        let or_all: i32 = all.iter().fold(0, |acc, &v| acc | v);
1675        let expected = (1i32 << 14) - 1;
1676        assert_eq!(or_all, expected, "CMF_* must cover bits 0..=13 (no gaps)");
1677    }
1678
1679    /// c:127-140 — CMF_* pairwise distinct.
1680    #[test]
1681    fn cmf_flags_pairwise_distinct() {
1682        let codes = [
1683            CMF_FILE,
1684            CMF_REMOVE,
1685            CMF_ISPAR,
1686            CMF_PARBR,
1687            CMF_PARNEST,
1688            CMF_NOLIST,
1689            CMF_DISPLINE,
1690            CMF_HIDE,
1691            CMF_NOSPACE,
1692            CMF_PACKED,
1693            CMF_ROWS,
1694            CMF_MULT,
1695            CMF_FMULT,
1696            CMF_ALL,
1697        ];
1698        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1699        assert_eq!(unique.len(), codes.len(), "CMF_* must be pairwise distinct");
1700    }
1701
1702    /// c:127-140 — all CMF_* powers of 2.
1703    #[test]
1704    fn cmf_flags_all_powers_of_two() {
1705        for &v in &[
1706            CMF_FILE,
1707            CMF_REMOVE,
1708            CMF_ISPAR,
1709            CMF_PARBR,
1710            CMF_PARNEST,
1711            CMF_NOLIST,
1712            CMF_DISPLINE,
1713            CMF_HIDE,
1714            CMF_NOSPACE,
1715            CMF_PACKED,
1716            CMF_ROWS,
1717            CMF_MULT,
1718            CMF_FMULT,
1719            CMF_ALL,
1720        ] {
1721            assert!(
1722                (v as u32).is_power_of_two(),
1723                "CMF_* {} must be a single bit",
1724                v
1725            );
1726        }
1727    }
1728
1729    // ═══════════════════════════════════════════════════════════════════
1730    // Additional C-parity pins for Src/Zle/comp.h
1731    // c:85-95 CGF_* / c:185-189 CPAT_* / c:259-261 CLF_* /
1732    // c:389-440 CPN_*/CP_* duality / c:443 CP_ALLKEYS /
1733    // c:447-451 hook offsets / c:474 CM_SPACE
1734    // ═══════════════════════════════════════════════════════════════════
1735
1736    /// c:85-95 — CGF_* all powers of 2 (alt pin).
1737    #[test]
1738    fn cgf_flags_all_powers_of_two_alt() {
1739        for &v in &[
1740            CGF_NOSORT,
1741            CGF_LINES,
1742            CGF_HASDL,
1743            CGF_UNIQALL,
1744            CGF_UNIQCON,
1745            CGF_PACKED,
1746            CGF_ROWS,
1747            CGF_FILES,
1748            CGF_MATSORT,
1749            CGF_NUMSORT,
1750            CGF_REVSORT,
1751        ] {
1752            assert!(
1753                (v as u32).is_power_of_two(),
1754                "CGF_* {} must be a single bit",
1755                v
1756            );
1757        }
1758    }
1759
1760    /// c:85-95 — CGF_* pairwise distinct (alt pin).
1761    #[test]
1762    fn cgf_flags_pairwise_distinct_alt() {
1763        let codes = [
1764            CGF_NOSORT,
1765            CGF_LINES,
1766            CGF_HASDL,
1767            CGF_UNIQALL,
1768            CGF_UNIQCON,
1769            CGF_PACKED,
1770            CGF_ROWS,
1771            CGF_FILES,
1772            CGF_MATSORT,
1773            CGF_NUMSORT,
1774            CGF_REVSORT,
1775        ];
1776        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1777        assert_eq!(unique.len(), codes.len(), "CGF_* pairwise distinct");
1778    }
1779
1780    /// c:85-95 — CGF_* OR covers bits 0..=10.
1781    #[test]
1782    fn cgf_or_covers_low_11_bits() {
1783        let or_all = CGF_NOSORT
1784            | CGF_LINES
1785            | CGF_HASDL
1786            | CGF_UNIQALL
1787            | CGF_UNIQCON
1788            | CGF_PACKED
1789            | CGF_ROWS
1790            | CGF_FILES
1791            | CGF_MATSORT
1792            | CGF_NUMSORT
1793            | CGF_REVSORT;
1794        assert_eq!(
1795            or_all,
1796            (1i32 << 11) - 1,
1797            "CGF_* must cover bits 0..=10 (no gaps)"
1798        );
1799    }
1800
1801    /// c:185-189 — CPAT_* values 0..=4 (sequential).
1802    #[test]
1803    fn cpat_values_sequential_0_to_4() {
1804        assert_eq!(CPAT_CCLASS, 0);
1805        assert_eq!(CPAT_NCLASS, 1);
1806        assert_eq!(CPAT_EQUIV, 2);
1807        assert_eq!(CPAT_ANY, 3);
1808        assert_eq!(CPAT_CHAR, 4);
1809    }
1810
1811    /// c:185-189 — CPAT_* pairwise distinct.
1812    #[test]
1813    fn cpat_pairwise_distinct() {
1814        let codes = [CPAT_CCLASS, CPAT_NCLASS, CPAT_EQUIV, CPAT_ANY, CPAT_CHAR];
1815        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1816        assert_eq!(unique.len(), codes.len(), "CPAT_* pairwise distinct");
1817    }
1818
1819    /// c:259-261 — CLF_MISS / CLF_DIFF / CLF_SUF are powers of 2.
1820    #[test]
1821    fn clf_flags_powers_of_two() {
1822        for &v in &[CLF_MISS, CLF_DIFF, CLF_SUF] {
1823            assert!(
1824                (v as u32).is_power_of_two(),
1825                "CLF_* {} must be a single bit",
1826                v
1827            );
1828        }
1829    }
1830
1831    /// c:259-261 — CLF_MISS=1, CLF_DIFF=2, CLF_SUF=4 (verbatim values).
1832    #[test]
1833    fn clf_flags_exact_values() {
1834        assert_eq!(CLF_MISS, 1);
1835        assert_eq!(CLF_DIFF, 2);
1836        assert_eq!(CLF_SUF, 4);
1837    }
1838
1839    /// c:389-440 — CP_X = 1 << CPN_X for every X (duality invariant).
1840    #[test]
1841    fn cp_equals_one_shift_cpn_duality() {
1842        // Sample 6 pairs across the range.
1843        assert_eq!(CP_EXACTSTR, 1u32 << CPN_EXACTSTR);
1844        assert_eq!(CP_PATMATCH, 1u32 << CPN_PATMATCH);
1845        assert_eq!(CP_PATINSERT, 1u32 << CPN_PATINSERT);
1846        assert_eq!(CP_UNAMBIG, 1u32 << CPN_UNAMBIG);
1847        assert_eq!(CP_IGNORED, 1u32 << CPN_IGNORED);
1848        assert_eq!(CP_TOEND, 1u32 << CPN_TOEND);
1849    }
1850
1851    /// c:443 — `CP_ALLKEYS = 0x3ffffff` (covers bits 0..=25, all 26 CPN_*).
1852    #[test]
1853    fn cp_allkeys_covers_26_bits() {
1854        assert_eq!(CP_ALLKEYS, 0x3ffffffu32);
1855        assert_eq!(CP_ALLKEYS, (1u32 << 26) - 1, "26 bits set");
1856        assert_eq!(CP_ALLKEYS.count_ones(), 26);
1857    }
1858
1859    /// c:447-451 — hook offsets sequential 0..=4.
1860    #[test]
1861    fn hook_offsets_sequential_0_to_4() {
1862        assert_eq!(INSERTMATCHHOOK_OFFSET, 0);
1863        assert_eq!(MENUSTARTHOOK_OFFSET, 1);
1864        assert_eq!(COMPCTLMAKEHOOK_OFFSET, 2);
1865        assert_eq!(COMPCTLCLEANUPHOOK_OFFSET, 3);
1866        assert_eq!(COMPLISTMATCHESHOOK_OFFSET, 4);
1867    }
1868
1869    /// c:447-451 — hook offsets pairwise distinct (alt pin).
1870    #[test]
1871    fn hook_offsets_pairwise_distinct_alt() {
1872        let codes = [
1873            INSERTMATCHHOOK_OFFSET,
1874            MENUSTARTHOOK_OFFSET,
1875            COMPCTLMAKEHOOK_OFFSET,
1876            COMPCTLCLEANUPHOOK_OFFSET,
1877            COMPLISTMATCHESHOOK_OFFSET,
1878        ];
1879        let unique: std::collections::HashSet<_> = codes.iter().copied().collect();
1880        assert_eq!(unique.len(), codes.len(), "hook offsets pairwise distinct");
1881    }
1882
1883    /// c:474 — `CM_SPACE = 2`.
1884    #[test]
1885    fn cm_space_is_two() {
1886        assert_eq!(CM_SPACE, 2);
1887    }
1888
1889    /// c:172-178 — CMF_LINE/LEFT/RIGHT/INTER are 1,2,4,8 (powers of 2).
1890    #[test]
1891    fn cmf_line_class_flags_are_powers_of_two() {
1892        for &v in &[CMF_LINE, CMF_LEFT, CMF_RIGHT, CMF_INTER] {
1893            assert!(
1894                (v as u32).is_power_of_two(),
1895                "CMF_* line/edge {} must be single bit",
1896                v
1897            );
1898        }
1899    }
1900}