@@ -55,6 +55,25 @@ local function apply_lean_syntax(buffer, last_line)
5555 end )
5656end
5757
58+ --- Find the byte index of the top-level `:` in a Lean signature, i.e. the
59+ --- colon that separates the binder list from the result type. Bracket-aware
60+ --- so colons inside binders such as `(x : T)` or `{x : T}` are skipped.
61+ --- @param signature string
62+ --- @return integer ? index 1-based byte index of the ` :` character , or nil if none
63+ local function top_level_colon (signature )
64+ local depth = 0
65+ for i = 1 , # signature do
66+ local c = signature :sub (i , i )
67+ if c == ' (' or c == ' {' or c == ' [' then
68+ depth = depth + 1
69+ elseif c == ' )' or c == ' }' or c == ' ]' then
70+ depth = depth - 1
71+ elseif c == ' :' and depth == 0 and signature :sub (i + 1 , i + 1 ) ~= ' =' then
72+ return i
73+ end
74+ end
75+ end
76+
5877--- Extract the signature and documentation from a Lean hover result.
5978---
6079--- Lean's hover markdown has the form:
6887--- *import Module*
6988---
7089--- We show the type interactively via RPC, so we extract the expression
71- --- name/signature (everything before the final ` : ` in the code fence)
90+ --- name/signature (everything before the top-level `: ` in the code fence)
7291--- and the documentation (everything after the first `***` separator).
7392--- @param result table the LSP hover result
7493--- @return string ? signature the expression signature (without the type )
@@ -79,12 +98,20 @@ local function extract_hover(result)
7998 return
8099 end
81100
82- -- Extract the expression name from inside the code fence.
83- -- The fence contains lines like "Nat.add : Nat → Nat → Nat" or
84- -- "foo (n : Nat) : Nat". The greedy (.+) finds the last " : ",
85- -- which separates the expression from its type.
101+ -- Extract the expression name from inside the code fence. The fence
102+ -- contains lines like `Nat.add : Nat → Nat → Nat` or `foo (n : Nat) : Nat`,
103+ -- and for multi-line signatures the top-level `:` can be at end of a line
104+ -- with no trailing space. We walk the fence tracking bracket depth so
105+ -- colons inside binders never split the signature.
86106 local fence = value :match ' ^```lean\n (.-)```'
87- local signature = fence and vim .trim (fence ):match ' (.+) : '
107+ local signature
108+ if fence then
109+ local trimmed = vim .trim (fence )
110+ local colon = top_level_colon (trimmed )
111+ if colon then
112+ signature = vim .trim (trimmed :sub (1 , colon - 1 ))
113+ end
114+ end
88115
89116 -- Extract documentation after the first *** separator. Lean uses `***`
90117 -- as a thematic break; we convert to `---` for nicer rendering, but pad
0 commit comments