Skip to main content

rmux_core/input/
states.rs

1//! Parser states and transition tables matching tmux `input.c:390–507`.
2
3/// The 17 parser states matching tmux exactly.
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
5pub enum InputState {
6    /// Ground state — normal printable character processing.
7    Ground,
8    /// ESC received, waiting for sequence identifier.
9    EscEnter,
10    /// ESC intermediate character(s) collected.
11    EscIntermediate,
12    /// CSI (`ESC [`) received, waiting for parameters.
13    CsiEnter,
14    /// CSI parameter bytes being collected.
15    CsiParameter,
16    /// CSI intermediate character(s) collected.
17    CsiIntermediate,
18    /// CSI with invalid parameter — absorb until final byte.
19    CsiIgnore,
20    /// DCS (`ESC P`) received, waiting for parameters.
21    DcsEnter,
22    /// DCS parameter bytes being collected.
23    DcsParameter,
24    /// DCS intermediate character(s) collected.
25    DcsIntermediate,
26    /// DCS passthrough data handler.
27    DcsHandler,
28    /// ESC received inside DCS passthrough.
29    DcsEscape,
30    /// DCS with invalid parameter — absorb until ST.
31    DcsIgnore,
32    /// OSC string being collected.
33    OscString,
34    /// APC string being collected.
35    ApcString,
36    /// Screen rename (`ESC k`) string being collected.
37    RenameString,
38    /// Unknown ESC-initiated sequence — absorb until ST.
39    ConsumeSt,
40}
41
42/// Handler action to execute for a transition.
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub(crate) enum Handler {
45    None,
46    Print,
47    C0Dispatch,
48    EscDispatch,
49    CsiDispatch,
50    DcsDispatch,
51    Intermediate,
52    Parameter,
53    Input,
54    TopBitSet,
55    EndBel,
56}
57
58/// A single entry in a state transition table.
59pub(crate) struct TransitionEntry {
60    pub first: u8,
61    pub last: u8,
62    pub handler: Handler,
63    pub next_state: Option<InputState>,
64}
65
66/// Resolved transition from a table lookup.
67pub(crate) struct Transition {
68    pub handler: Handler,
69    pub next_state: Option<InputState>,
70}
71
72impl InputState {
73    /// Returns the transition table for this state.
74    pub(crate) fn transition_table(self) -> &'static [TransitionEntry] {
75        match self {
76            Self::Ground => GROUND_TABLE,
77            Self::EscEnter => ESC_ENTER_TABLE,
78            Self::EscIntermediate => ESC_INTERMEDIATE_TABLE,
79            Self::CsiEnter => CSI_ENTER_TABLE,
80            Self::CsiParameter => CSI_PARAMETER_TABLE,
81            Self::CsiIntermediate => CSI_INTERMEDIATE_TABLE,
82            Self::CsiIgnore => CSI_IGNORE_TABLE,
83            Self::DcsEnter => DCS_ENTER_TABLE,
84            Self::DcsParameter => DCS_PARAMETER_TABLE,
85            Self::DcsIntermediate => DCS_INTERMEDIATE_TABLE,
86            Self::DcsHandler => DCS_HANDLER_TABLE,
87            Self::DcsEscape => DCS_ESCAPE_TABLE,
88            Self::DcsIgnore => DCS_IGNORE_TABLE,
89            Self::OscString => OSC_STRING_TABLE,
90            Self::ApcString => APC_STRING_TABLE,
91            Self::RenameString => RENAME_STRING_TABLE,
92            Self::ConsumeSt => CONSUME_ST_TABLE,
93        }
94    }
95}
96
97// Shorthand.
98use Handler as H;
99use InputState as S;
100
101const fn e(
102    first: u8,
103    last: u8,
104    handler: Handler,
105    next_state: Option<InputState>,
106) -> TransitionEntry {
107    TransitionEntry {
108        first,
109        last,
110        handler,
111        next_state,
112    }
113}
114
115// ─── ground ────────────────────────────────────────────────────────
116static GROUND_TABLE: &[TransitionEntry] = &[
117    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
118    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
119    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
120    e(0x00, 0x17, H::C0Dispatch, None),
121    e(0x19, 0x19, H::C0Dispatch, None),
122    e(0x1c, 0x1f, H::C0Dispatch, None),
123    e(0x20, 0x7e, H::Print, None),
124    e(0x7f, 0x7f, H::None, None),
125    e(0x80, 0xff, H::TopBitSet, None),
126];
127
128// ─── esc_enter ─────────────────────────────────────────────────────
129static ESC_ENTER_TABLE: &[TransitionEntry] = &[
130    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
131    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
132    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
133    e(0x00, 0x17, H::C0Dispatch, None),
134    e(0x19, 0x19, H::C0Dispatch, None),
135    e(0x1c, 0x1f, H::C0Dispatch, None),
136    e(0x20, 0x2f, H::Intermediate, Some(S::EscIntermediate)),
137    e(0x30, 0x4f, H::EscDispatch, Some(S::Ground)),
138    e(0x50, 0x50, H::None, Some(S::DcsEnter)),
139    e(0x51, 0x57, H::EscDispatch, Some(S::Ground)),
140    e(0x58, 0x58, H::None, Some(S::ConsumeSt)),
141    e(0x59, 0x5a, H::EscDispatch, Some(S::Ground)),
142    e(0x5b, 0x5b, H::None, Some(S::CsiEnter)),
143    e(0x5c, 0x5c, H::EscDispatch, Some(S::Ground)),
144    e(0x5d, 0x5d, H::None, Some(S::OscString)),
145    e(0x5e, 0x5e, H::None, Some(S::ConsumeSt)),
146    e(0x5f, 0x5f, H::None, Some(S::ApcString)),
147    e(0x60, 0x6a, H::EscDispatch, Some(S::Ground)),
148    e(0x6b, 0x6b, H::None, Some(S::RenameString)),
149    e(0x6c, 0x7e, H::EscDispatch, Some(S::Ground)),
150    e(0x7f, 0xff, H::None, None),
151];
152
153// ─── esc_intermediate ──────────────────────────────────────────────
154static ESC_INTERMEDIATE_TABLE: &[TransitionEntry] = &[
155    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
156    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
157    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
158    e(0x00, 0x17, H::C0Dispatch, None),
159    e(0x19, 0x19, H::C0Dispatch, None),
160    e(0x1c, 0x1f, H::C0Dispatch, None),
161    e(0x20, 0x2f, H::Intermediate, None),
162    e(0x30, 0x7e, H::EscDispatch, Some(S::Ground)),
163    e(0x7f, 0xff, H::None, None),
164];
165
166// ─── csi_enter ─────────────────────────────────────────────────────
167static CSI_ENTER_TABLE: &[TransitionEntry] = &[
168    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
169    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
170    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
171    e(0x00, 0x17, H::C0Dispatch, None),
172    e(0x19, 0x19, H::C0Dispatch, None),
173    e(0x1c, 0x1f, H::C0Dispatch, None),
174    e(0x20, 0x2f, H::Intermediate, Some(S::CsiIntermediate)),
175    e(0x30, 0x39, H::Parameter, Some(S::CsiParameter)),
176    e(0x3a, 0x3a, H::Parameter, Some(S::CsiParameter)),
177    e(0x3b, 0x3b, H::Parameter, Some(S::CsiParameter)),
178    e(0x3c, 0x3f, H::Intermediate, Some(S::CsiParameter)),
179    e(0x40, 0x7e, H::CsiDispatch, Some(S::Ground)),
180    e(0x7f, 0xff, H::None, None),
181];
182
183// ─── csi_parameter ─────────────────────────────────────────────────
184static CSI_PARAMETER_TABLE: &[TransitionEntry] = &[
185    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
186    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
187    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
188    e(0x00, 0x17, H::C0Dispatch, None),
189    e(0x19, 0x19, H::C0Dispatch, None),
190    e(0x1c, 0x1f, H::C0Dispatch, None),
191    e(0x20, 0x2f, H::Intermediate, Some(S::CsiIntermediate)),
192    e(0x30, 0x39, H::Parameter, None),
193    e(0x3a, 0x3a, H::Parameter, None),
194    e(0x3b, 0x3b, H::Parameter, None),
195    e(0x3c, 0x3f, H::None, Some(S::CsiIgnore)),
196    e(0x40, 0x7e, H::CsiDispatch, Some(S::Ground)),
197    e(0x7f, 0xff, H::None, None),
198];
199
200// ─── csi_intermediate ──────────────────────────────────────────────
201static CSI_INTERMEDIATE_TABLE: &[TransitionEntry] = &[
202    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
203    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
204    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
205    e(0x00, 0x17, H::C0Dispatch, None),
206    e(0x19, 0x19, H::C0Dispatch, None),
207    e(0x1c, 0x1f, H::C0Dispatch, None),
208    e(0x20, 0x2f, H::Intermediate, None),
209    e(0x30, 0x3f, H::None, Some(S::CsiIgnore)),
210    e(0x40, 0x7e, H::CsiDispatch, Some(S::Ground)),
211    e(0x7f, 0xff, H::None, None),
212];
213
214// ─── csi_ignore ────────────────────────────────────────────────────
215static CSI_IGNORE_TABLE: &[TransitionEntry] = &[
216    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
217    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
218    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
219    e(0x00, 0x17, H::C0Dispatch, None),
220    e(0x19, 0x19, H::C0Dispatch, None),
221    e(0x1c, 0x1f, H::C0Dispatch, None),
222    e(0x20, 0x3f, H::None, None),
223    e(0x40, 0x7e, H::None, Some(S::Ground)),
224    e(0x7f, 0xff, H::None, None),
225];
226
227// ─── dcs_enter ─────────────────────────────────────────────────────
228static DCS_ENTER_TABLE: &[TransitionEntry] = &[
229    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
230    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
231    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
232    e(0x00, 0x17, H::None, None),
233    e(0x19, 0x19, H::None, None),
234    e(0x1c, 0x1f, H::None, None),
235    e(0x20, 0x2f, H::Intermediate, Some(S::DcsIntermediate)),
236    e(0x30, 0x39, H::Parameter, Some(S::DcsParameter)),
237    e(0x3a, 0x3a, H::None, Some(S::DcsIgnore)),
238    e(0x3b, 0x3b, H::Parameter, Some(S::DcsParameter)),
239    e(0x3c, 0x3f, H::Intermediate, Some(S::DcsParameter)),
240    e(0x40, 0x7e, H::Input, Some(S::DcsHandler)),
241    e(0x7f, 0xff, H::None, None),
242];
243
244// ─── dcs_parameter ─────────────────────────────────────────────────
245static DCS_PARAMETER_TABLE: &[TransitionEntry] = &[
246    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
247    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
248    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
249    e(0x00, 0x17, H::None, None),
250    e(0x19, 0x19, H::None, None),
251    e(0x1c, 0x1f, H::None, None),
252    e(0x20, 0x2f, H::Intermediate, Some(S::DcsIntermediate)),
253    e(0x30, 0x39, H::Parameter, None),
254    e(0x3a, 0x3a, H::None, Some(S::DcsIgnore)),
255    e(0x3b, 0x3b, H::Parameter, None),
256    e(0x3c, 0x3f, H::None, Some(S::DcsIgnore)),
257    e(0x40, 0x7e, H::Input, Some(S::DcsHandler)),
258    e(0x7f, 0xff, H::None, None),
259];
260
261// ─── dcs_intermediate ──────────────────────────────────────────────
262static DCS_INTERMEDIATE_TABLE: &[TransitionEntry] = &[
263    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
264    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
265    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
266    e(0x00, 0x17, H::None, None),
267    e(0x19, 0x19, H::None, None),
268    e(0x1c, 0x1f, H::None, None),
269    e(0x20, 0x2f, H::Intermediate, None),
270    e(0x30, 0x3f, H::None, Some(S::DcsIgnore)),
271    e(0x40, 0x7e, H::Input, Some(S::DcsHandler)),
272    e(0x7f, 0xff, H::None, None),
273];
274
275// ─── dcs_handler ───────────────────────────────────────────────────
276// NOTE: No INPUT_STATE_ANYWHERE — this is a deliberate tmux deviation.
277static DCS_HANDLER_TABLE: &[TransitionEntry] = &[
278    e(0x00, 0x1a, H::Input, None),
279    e(0x1b, 0x1b, H::None, Some(S::DcsEscape)),
280    e(0x1c, 0xff, H::Input, None),
281];
282
283// ─── dcs_escape ────────────────────────────────────────────────────
284// NOTE: No INPUT_STATE_ANYWHERE — deliberate tmux deviation.
285static DCS_ESCAPE_TABLE: &[TransitionEntry] = &[
286    e(0x00, 0x5b, H::Input, Some(S::DcsHandler)),
287    e(0x5c, 0x5c, H::DcsDispatch, Some(S::Ground)),
288    e(0x5d, 0xff, H::Input, Some(S::DcsHandler)),
289];
290
291// ─── dcs_ignore ────────────────────────────────────────────────────
292static DCS_IGNORE_TABLE: &[TransitionEntry] = &[
293    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
294    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
295    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
296    e(0x00, 0x17, H::None, None),
297    e(0x19, 0x19, H::None, None),
298    e(0x1c, 0x1f, H::None, None),
299    e(0x20, 0xff, H::None, None),
300];
301
302// ─── osc_string ────────────────────────────────────────────────────
303static OSC_STRING_TABLE: &[TransitionEntry] = &[
304    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
305    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
306    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
307    e(0x00, 0x06, H::None, None),
308    e(0x07, 0x07, H::EndBel, Some(S::Ground)),
309    e(0x08, 0x17, H::None, None),
310    e(0x19, 0x19, H::None, None),
311    e(0x1c, 0x1f, H::None, None),
312    e(0x20, 0xff, H::Input, None),
313];
314
315// ─── apc_string ────────────────────────────────────────────────────
316static APC_STRING_TABLE: &[TransitionEntry] = &[
317    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
318    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
319    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
320    e(0x00, 0x17, H::None, None),
321    e(0x19, 0x19, H::None, None),
322    e(0x1c, 0x1f, H::None, None),
323    e(0x20, 0xff, H::Input, None),
324];
325
326// ─── rename_string ─────────────────────────────────────────────────
327static RENAME_STRING_TABLE: &[TransitionEntry] = &[
328    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
329    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
330    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
331    e(0x00, 0x17, H::None, None),
332    e(0x19, 0x19, H::None, None),
333    e(0x1c, 0x1f, H::None, None),
334    e(0x20, 0xff, H::Input, None),
335];
336
337// ─── consume_st ────────────────────────────────────────────────────
338static CONSUME_ST_TABLE: &[TransitionEntry] = &[
339    e(0x18, 0x18, H::C0Dispatch, Some(S::Ground)),
340    e(0x1a, 0x1a, H::C0Dispatch, Some(S::Ground)),
341    e(0x1b, 0x1b, H::None, Some(S::EscEnter)),
342    e(0x00, 0x17, H::None, None),
343    e(0x19, 0x19, H::None, None),
344    e(0x1c, 0x1f, H::None, None),
345    e(0x20, 0xff, H::None, None),
346];