# always-reduce vs @skip

**URL:** https://discuss.codemirror.net/t/always-reduce-vs-skip/9901
**Category:** Lezer
**Created:** [September 27, 2026, 2:21pm UTC](https://discuss.codemirror.net/t/always-reduce-vs-skip/9901 "2026-09-27T14:21:31Z")
**Posts on this page:** 4
**Page:** 1

<div class="post-metadata">

### Author: ![comments-are-importa](https://discuss.codemirror.net/user_avatar/discuss.codemirror.net/comments-are-importa/32/5559_2.png) [@comments-are-importa](https://discuss.codemirror.net/u/comments-are-importa)
#### Post date: [September 27, 2026, 2:21pm UTC](https://discuss.codemirror.net/t/always-reduce-vs-skip/9901/1 "2026-09-27T14:21:31Z")

</div>

I started with the indentation example, but scaled back `@skip` considerably. my external `margin` token is the only thing left:

```lezer
@external tokens xt from "./support.ts" { dedent indent margin }
@skip { margin }

```

this seemed to be working well until I added a _“this is just unparsed text”_ block:

```lezer
Text { indent Line+ dedent }
@tokens { Line { ![\n]+ "\n" } }

```

it goes into a `(via always-reduce Line+)` loop and slurps up all the remaining input. I see a workaround (a fourth external could grab the whole text block in one call). but it feels like this greedy always-reduce tight loop should only ever be entered if `@skip{/*never*/}` because skipped things might show up anywhere… like between the `Line`s (sorry, couldn’t resist).

I’m new to lezer so maybe I’ve missed something (would appreciate pointers), but this behavior was surprising and confusing, so thought it worth mentioning.

---

<div class="post-metadata">

### Author: ![marijn](https://discuss.codemirror.net/user_avatar/discuss.codemirror.net/marijn/32/136_2.png) [@marijn](https://discuss.codemirror.net/u/marijn)
#### Post date: [September 27, 2026, 2:55pm UTC](https://discuss.codemirror.net/t/always-reduce-vs-skip/9901/2 "2026-09-27T14:55:28Z")

</div>

Sounds like your `dedent` token is somehow not being generated. Is the `@external tokens` declaration before the regular `@tokens` one? Any reason the external tokenizer wouldn’t fire for the line where it should end the `Text` block?

---

<div class="post-metadata">

### Author: ![comments-are-importa](https://discuss.codemirror.net/user_avatar/discuss.codemirror.net/comments-are-importa/32/5559_2.png) [@comments-are-importa](https://discuss.codemirror.net/u/comments-are-importa)
#### Post date: [September 28, 2026, 10:28am UTC](https://discuss.codemirror.net/t/always-reduce-vs-skip/9901/3 "2026-09-28T10:28:02Z")

</div>

I appreciate your desire to help fix my grammar. I saved a commit and will find some time to go back and investigate. my gut says you’re right that token precedence is a problem.

until then I want to say that my motivation for posting was partly about messages produced by `@lezer/generator`, not anything specific to my grammar. a warning along the lines of _“skip tokens {X, Y} can’t happen inside rules {Line+}”_ might be worth considering? it certainly could be intentional - a few authors might need to opt in to that effect, so leave the door open for that. but I imagine that most of the time people write `@skip` with the expectation that those tokens can happen anywhere.

---

<div class="post-metadata">

### Author: ![comments-are-importa](https://discuss.codemirror.net/user_avatar/discuss.codemirror.net/comments-are-importa/32/5559_2.png) [@comments-are-importa](https://discuss.codemirror.net/u/comments-are-importa)
#### Post date: [September 29, 2026, 11:29pm UTC](https://discuss.codemirror.net/t/always-reduce-vs-skip/9901/4 "2026-09-29T23:29:23Z")

</div>

yeah, pretty sure it was token precedence. not 100% because even with the git history I couldn’t recreate the exact weirdness that led to my posting.

at some point as I was developing my tokenizer the `@tokens{}` became entirely empty. I failed to realize that it was still having an effect for the literal `""` tokens sprinkled around in my rules. I like to keep things tidy so I deleted it, and that actually seemed to work fine for a while. later I changed a rule in a way that the token precedence actually bit me, and token precedence did not pop into my head because that’s not what I was editing.

your reply was helpful, thank you. my lezer is working fairly well now. live playground demo is up on my github pages, showing the syntax highlighting. not sure of the policy here on links, so I’ll refrain, but it shouldn’t be difficult to find the demo and my code.

thanks again!
