You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(lexer): compute span ends by character count, not byte length (#72)
## Summary
The lexer scans the source as a `Vec<char>`, so token positions and AST
`Span`s are **character indices**. But span *ends* were computed as `pos
+ value.len()` — adding the token value's **byte** length to a
**character** position. For any token containing multibyte UTF-8, the
end overshoots by the byte/char delta, so `Node::source_text` returns
the wrong slice for every **non-final** token.
The existing `source_text_multibyte_utf8` test never caught this because
its multibyte word was the *last* token, where the overshoot clamps
harmlessly to the source end.
### Example (before)
```
echo "✓ valid" || echo "✗ bad"
Command 0..16 source_text = `echo "✓ valid" |` ← wrong (bleeds into the `||`)
```
### After
```
Command 0..14 source_text = `echo "✓ valid"` ← correct
```
## The fix
Use `value.chars().count()` at the four sites that derive an end offset
from a start position:
- `lexer/mod.rs` — `last_token_end` (the source of most node ends via
`parser/mod.rs:134`)
- `parser/simple_command.rs` — word-node span
- `parser/case_select.rs` — pattern word span
- `token.rs` — `Token::adjacent_to`
…and correct the `Span` doc comment to state offsets are character
indices.
## Downstream impact
Surfaced as [tokf #383](mpecan/tokf#383):
compound shell commands mixing an emoji/`✓`/`✗` `echo` with a rewritten
pipe were spliced into invalid shell (`| ||`, dropped `; `). tokf
consumes these spans to split compound commands.
## Tests
Adds regression tests for multibyte in **non-final** words, compound
segments, pipelines, and astral-plane emoji (`🎉`). Full suite: **262
passing**, clippy + fmt clean. No ASCII spans change (char count == byte
len for ASCII), so no existing test moved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
0 commit comments