1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//! !!! WARNING: RUST-ONLY MODULE — NOT A PORT OF ANY ZSH C FILE !!!
//!
//! Verbatim capture of function-body source text as the lexer consumes it.
//!
//! C zsh does not store function source at all. `par_funcdef`
//! (Src/parse.c:1672) compiles the body into the `ecbuf` wordcode array,
//! and `functions` / `typeset -f` / `whence -f` RECONSTRUCT the printed
//! text from that wordcode via `getpermtext` -> `gettext2`
//! (Src/text.c:189 / :296) — which is why zsh always prints canonical
//! tab-indented text regardless of how the user spaced the original.
//!
//! zshrs runs function bodies through the fusevm compile path, which never
//! builds a C-shaped `Eprog`; `shfunc.funcdef` (zsh_h.rs:868) is therefore
//! always `None` and `printshfuncnode` (hashtable.rs:1796) prints the RAW
//! SOURCE the parser captured instead. Until the fusevm path grows a real
//! `Eprog`, that raw source has to come from somewhere for EVERY input
//! source.
//!
//! It used to come only from a slice of the Rust-only `LEX_INPUT` window
//! (`lex::input_slice`), which exists for file / `-c` / `eval` input.
//! Interactive and `-s` stdin input reaches the lexer through
//! `hgetc` -> `ingetc` -> the `inbuf` stack and never touches `LEX_INPUT`,
//! so `LEX_POS` never moved and every function defined at a prompt or piped
//! in on stdin printed as `name () { }`.
//!
//! C's own history-line buffer (`chline`) is NOT a usable substitute: at
//! c:Src/hist.c:1119 `hbegin` sets
//! `stophist = (!interact || unset(SHINSTDIN)) ? 2 : 0` and then c:1129
//! `chline = hptr = NULL`, so for non-interactive stdin there is no history
//! line at all. The one funnel every input source does pass through is
//! `hgetc`, so this module hangs an echo buffer off it, active only between
//! [`body_mark_begin`] and [`body_text`].
//!
//! Lives outside `src/ported/` because it has no C counterpart (same
//! reasoning as `crate::tolerant_sort`).
use crate;
thread_local!
/// !!! WARNING: RUST-ONLY HELPER !!!
///
/// A parser-side bookmark for "where the text of a function body starts".
/// C has no analogue — `par_funcdef` (Src/parse.c:1672) writes the body
/// into the `ecbuf` wordcode array and `functions` re-derives the printed
/// text from it (`getpermtext`, Src/text.c:189). zshrs's fusevm path never
/// builds that wordcode `Eprog`, so the parser must keep the raw source.
///
/// Carries BOTH cursors because zshrs has two input sources: the Rust-only
/// `LEX_INPUT` window (file / `-c` / `eval` text) and the ported `inbuf`
/// stack that `hgetc` reads for interactive and `-s` stdin input. See
/// [`LEX_SRC_CAPTURE`] for why the second one needs an echo buffer.
/// !!! WARNING: RUST-ONLY HELPER !!!
///
/// Open a function-body capture and return the mark to close it with.
/// Every character `hgetc` returns from now until the matching
/// [`body_text`] is echoed into [`LEX_SRC_CAPTURE`]. See that static for
/// why this exists.
/// !!! WARNING: RUST-ONLY HELPER !!!
///
/// Move an already-open mark forward to the current input position,
/// discarding what has been captured so far. The funcdef parsers take the
/// mark before `()` and then re-take it past every separator (`pos()` after
/// a `zshlex` is already one token ahead, so the mark has to be re-read
/// BEFORE each advance); this is that re-read for the capture side.
/// !!! WARNING: RUST-ONLY HELPER !!!
///
/// Close the capture opened by [`body_mark_begin`] and return the raw body
/// text, untrimmed. `LEX_INPUT` wins when it actually advanced (the file /
/// `-c` / `eval` paths, whose slice behaviour is unchanged by this
/// helper); the `hgetc` echo buffer is the fallback for the interactive
/// and `-s` stdin paths, where `LEX_POS` never moves.
/// !!! WARNING: RUST-ONLY HELPER !!!
///
/// Drop one level of capture nesting; returns the remaining depth.
/// !!! WARNING: RUST-ONLY HELPER !!!
///
/// Drop any capture left open by a parse that bailed out, so a syntax error
/// in one function body cannot leak text into the next one. Called from
/// `lex_init` (src/ported/lex.rs) alongside the other lexer-state resets;
/// the `BodyMark` `Drop` guard covers the ordinary error returns.
/// !!! WARNING: RUST-ONLY HELPER !!!
///
/// Echo one consumed character into [`LEX_SRC_CAPTURE`]. Returns whether it
/// was recorded, so `hungetc`'s pop and `hgetc`'s re-read stay symmetric.
/// Called from `hgetc` (src/ported/lex.rs) only.
/// !!! WARNING: RUST-ONLY HELPER !!!
///
/// Undo the last [`src_capture_add`], but only if the buffer really ends
/// with `c` — a character the `counts_lineno` gate refused was never
/// recorded and must not be taken from an earlier one. Returns whether
/// anything was removed. Called from `hungetc` (src/ported/lex.rs) only.