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}