Zulip Chat Archive

Stream: general

Topic: Finding syntax


Damiano Testa (Oct 20 2024 at 05:20):

I wrote a very naive #find_syntax command, since this comes up every once in a while (e.g. it came up at the last community meeting). You can see below the implementation and the output on searching for .

import Mathlib

namespace Mathlib.FindSyntax

open Lean Elab Command

def extractSymbols : Expr  Array Expr
  | .app a b =>
    let rest := extractSymbols a ++ extractSymbols b
    match a.constName with
      | ``Lean.ParserDescr.symbol | ``Lean.ParserDescr.nonReservedSymbol => rest.push b
      | _ => rest
  | .letE _ a b c _ => extractSymbols a ++ extractSymbols b ++ extractSymbols c
  | .lam _ a b _ => extractSymbols a ++ extractSymbols b
  | .forallE _ a b _ => extractSymbols a ++ extractSymbols b
  | .mdata _ a => extractSymbols a
  | .proj _ _ a => extractSymbols a
  | _ => #[]

def litToString : Expr  String
  | .lit (.natVal v) => s!"{v}"
  | .lit (.strVal v) => v
  | _ => ""

elab "#find_syntax " id:str : command => do
  let prsr : Array Expr := #[.const ``Lean.ParserDescr [], .const ``TrailingParserDescr []]
  let mut hs : Std.HashSet Expr := {}
  let mut symbs : Std.HashSet (Name × Array Expr) := {}
  for (_declName, cinfo) in ( getEnv).constants do
    if prsr.contains cinfo.type && cinfo.hasValue then
      hs := hs.insert cinfo.value!
      let ls := extractSymbols cinfo.value!
      if !cinfo.name.isInternal && !ls.isEmpty then symbs := symbs.insert (cinfo.name, ls)
  let mut msgs := #[]
  for (nm, ar) in symbs.toList do
    let rem : String := " _ ".intercalate (ar.map litToString).toList
    if 2  (rem.splitOn id.getString).length then
      msgs := msgs.push <| .ofConstName nm ++ m!":\n  '{rem.trim}'\n"
  let uses := msgs.size
  let head := m!"Found {uses} use{if uses == 1 then "" else "s"} \
                among {symbs.size} syntax declarations"
  logInfo <| .joinSep (head::(msgs.push "").toList) "\n---\n\n"

end Mathlib.FindSyntax

#find_syntax "∘"
/-
Found 5 uses among 1455 syntax declarations
---

Function.«term_∘'_»:
  '∘''
---

ContinuousLinearMap.«term_∘L_»:
  '∘L'
---

LinearMap.compNotation:
  '∘ₗ'
---

ProbabilityTheory.«term_∘ₖ_»:
  '∘ₖ'
---

«term_∘_»:
  '∘'
---
-/

Damiano Testa (Oct 20 2024 at 05:21):

If you find it useful, I can think about how to produce better "usage reports".

Kyle Miller (Oct 20 2024 at 17:25):

If you use {MessageData.ofConstName nm} you can do "go to definition" on it in the output.

Damiano Testa (Oct 20 2024 at 18:18):

Ah, that's awesome! I was wondering how to hook into "Go to definition"! I had tried realize...WithInfo to no avail.

Kyle Miller (Oct 20 2024 at 18:35):

We're looking at making {nm} automatically create "go to definition" links when nm is a constant. (This might be "too convenient" though, since not every name is meant to be a constant...)

Damiano Testa (Oct 20 2024 at 18:40):

If the doc-strings of realizeGlobalConstant and resolveGlobal... mentioned how to get the "Go to def" behaviour, that would already be great! I had no idea where to look for this, except for hoping that withInfo would magically do it for me.

Damiano Testa (Oct 20 2024 at 18:48):

I updated the code above and it works great! Btw, I do not know if this is intended or not, but a lot of the term... declaration have default range, so "Go to definition" takes you to the right file, but not to the position of the syntax declaration.

Damiano Testa (Oct 20 2024 at 18:49):

(I actually exploit this in the duplicated underscore linter, so if this is something requiring a fix, then I'll have to think of a different way of automatically ignoring such declarations in the linter.)

Damiano Testa (Oct 21 2024 at 00:44):

Rather than leaving it just in Zulip, I PRed it: #17986. Comments are very welcome!


Last updated: May 02 2025 at 03:31 UTC