Skip to content

Commit 3efce68

Browse files
fix: trailing whitespace for syntax that contains trailing nulls (#696)
1 parent 635ff30 commit 3efce68

File tree

3 files changed

+60
-5
lines changed

3 files changed

+60
-5
lines changed

src/tests/Tests/CommentSkipping.lean

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
44
Author: David Thrane Christiansen
55
-/
66
import Tests.CommentSkipping.Doc
7+
import Tests.CommentSkipping.Doc2
78

89
/-!
910
This test ensures that Lean's parser doesn't skip Lean comment syntax while parsing Verso blocks as
@@ -27,3 +28,17 @@ info: Verso.Doc.Part.mk
2728
-/
2829
#guard_msgs in
2930
#eval %doc Tests.CommentSkipping.Doc
31+
32+
/--
33+
info: Verso.Doc.Part.mk
34+
#[Verso.Doc.Inline.text "Title"]
35+
"Title"
36+
none
37+
#[Verso.Doc.Block.blockquote #[],
38+
Verso.Doc.Block.blockquote
39+
#[(Verso.Doc.Block.para #[Verso.Doc.Inline.link #[(Verso.Doc.Inline.text "abc")] "http://example.com"])],
40+
Verso.Doc.Block.blockquote #[(Verso.Doc.Block.para #[Verso.Doc.Inline.text "C", Verso.Doc.Inline.linebreak "\n"])]]
41+
#[]
42+
-/
43+
#guard_msgs in
44+
#eval %doc Tests.CommentSkipping.Doc2
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/-
2+
Copyright (c) 2026 Lean FRO LLC. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Author: David Thrane Christiansen
5+
-/
6+
7+
import Verso
8+
9+
/-!
10+
This test checks a regression that happened where syntax that contained trailing null nodes wouldn't
11+
have trailing whitespace updated properly, leading to failed parses.
12+
-/
13+
14+
15+
16+
#doc (.none) "Title" =>
17+
18+
19+
>
20+
> [abc](http://example.com)
21+
22+
> C

src/verso/Verso/Doc/Concrete.lean

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,31 @@ private meta partial def updateSyntaxTrailing (trailing : Substring.Raw) : Synta
139139
| .atom info val => .atom (updateInfoTrailing trailing info) val
140140
| .ident info rawVal val pre => .ident (updateInfoTrailing trailing info) rawVal val pre
141141
| n@(.node info k args) =>
142-
if h : args.size = 0 then n
143-
else
144-
let i := args.size - 1
142+
if let some i := findIdxRev? (not ∘ empty) args then
145143
let last := updateSyntaxTrailing trailing args[i]
146144
let args := args.set i last;
147145
Syntax.node info k args
146+
else n
148147
| s => s
149148
where
149+
findIdxRev? {α} (p : α → Bool) (xs : Array α) : Option (Fin xs.size) := do
150+
if h : xs.size = 0 then failure
151+
else
152+
let mut n : Fin xs.size := ⟨xs.size - 1, by grind⟩
153+
repeat
154+
if p xs[n] then return n
155+
if n.val = 0 then
156+
break
157+
else
158+
n := ⟨n.val - 1, by grind⟩
159+
failure
160+
161+
empty : Syntax → Bool
162+
| .atom .. | .ident .. | .missing => false
163+
| .node .none _ args | .node (.synthetic ..) _ args => args.all empty
164+
| .node (.original leading _ trailing _) _ args =>
165+
leading.startPos == leading.stopPos && trailing.startPos == trailing.stopPos && args.all empty
166+
150167
updateInfoTrailing (trailing : Substring.Raw) : SourceInfo → SourceInfo
151168
| .original leading pos _ endPos => .original leading pos trailing endPos
152169
| .none =>
@@ -199,8 +216,9 @@ where
199216
let top := s.stxStack.back
200217
if let some ⟨_, tr⟩ := getTailContext? c.fileMap.source top then
201218
let tr := { tr with stopPos := s.pos }
202-
s.popSyntax.pushSyntax (updateSyntaxTrailing tr top)
203-
else s
219+
s.popSyntax.pushSyntax <| updateSyntaxTrailing tr top
220+
else
221+
s
204222

205223
/--
206224
As we elaborate a `#doc` command top-level-block by top-level-block, the Lean environment will

0 commit comments

Comments
 (0)