Zulip Chat Archive
Stream: lean4
Topic: go to definition for `open`
Julian Berman (Oct 23 2025 at 12:04):
Is there an easy-to-implement better target that go to definition could jump to for open Foo Bar Baz? Without considering how possible this is it would be amazing if clicking on Bar jumped you either to a declaration Bar or else to the namespace Bar command which is highest in the import graph.
Maybe there's a simpler place that's still reasonable? Right now it takes you to the definition of elabOpen.
Damiano Testa (Oct 23 2025 at 12:16):
I think that namespaces register themselves in an environment extension and maybe you can retrieve where that happened. I am not sure.
Damiano Testa (Oct 23 2025 at 12:23):
Thinking a bit about this, I don't think that the environment extension remembers where the namespace was added. However, you could search the environment for all declarations that have the given namespace, selects a minimal one in import order and then give the location of that declaration.
Kenny Lau (Oct 23 2025 at 12:25):
do you have the code where the env ext was initialised?
Kenny Lau (Oct 23 2025 at 12:28):
ok it seems to be here: https://github.com/leanprover/lean4/blob/f1203f3d0d4f330ceb63dfce0f11d4fec7598755/src/Lean/Namespace.lean#L18C70-L18C78
(very suspicious, it has a namespace command right above it)
Kenny Lau (Oct 23 2025 at 12:28):
and it does seem to only store the Name, so it wouldn't store the Syntax that contains the cursor position
Kenny Lau (Oct 23 2025 at 12:30):
actually, are we sure that Lean.SimplePersistentEnvExtension does not store any position information at all?
Damiano Testa (Oct 23 2025 at 13:27):
I don't know if there is no position stored, but the declRanges extension does store position information. You can access that, once you know what the declaration is.
Damiano Testa (Oct 23 2025 at 16:30):
I am not sure if I trust the namespace_where command below much, however namespace_where YourNamespace should return "something". Ideally, it is a list of import-minimal modules containing a declaration that has YourNamespace as a component and also the first declaration in the file that contains the namespace. The declaration name is control-clickable.
import Mathlib.Lean.Expr.Basic
open Lean
/-- The `<` partial order on modules: `importLT env mod₁ mod₂` means that `mod₂` imports `mod₁`. -/
def SortImports.importLT (env : Environment) (f1 f2 : Name) : Bool :=
(env.findRedundantImports #[f1, f2]).contains f1
open Lean Elab Command in
elab "namespace_where " ns:ident : command => do
let part := ns.getId
-- The collection of module names of declarations that contain `ns` as a component.
let mut candidateModules : NameSet := ∅
let env ← getEnv
-- The collection of names of declarations that contain `ns` as a component.
let mut goodNames : NameSet := ∅
-- We first loop through the constants in the environment, searching for non-blacklisted
-- names that contain the given namespace as a component.
for (n, _) in env.constants.map₁ do
if ← n.isBlackListed then continue
if n.components.contains part then
let some mod ← findModuleOf? n | throwError "Could not find module of {n}"
candidateModules := candidateModules.insert mod
goodNames := goodNames.insert n
let currMod ← getMainModule
-- We do the same loop, but over the constants in the current file.
for (n, _) in env.constants.map₂ do
if ← n.isBlackListed then continue
if n.components.contains part then
candidateModules := candidateModules.insert currMod
goodNames := goodNames.insert n
let minimalImports := env.findRedundantImports candidateModules.toArray
-- This should make the command do the right thing if the namespace appears
-- only in the current file only
let minimalImports := if minimalImports.isEmpty then {currMod} else minimalImports
let minimals := candidateModules.filter minimalImports.contains
let sorted := minimals.toArray.qsort (SortImports.importLT env)
let sortDecls := sorted.map fun mod => (mod,
let extArray :=
if let some modIdx := env.getModuleIdx? mod then
declRangeExt.getModuleEntries env modIdx
else
(declRangeExt.getState env).toArray
let extn := extArray.qsort (·.2.range.pos.line < ·.2.range.pos.line)
extn.filterMap fun (nm, rgs) =>
if goodNames.contains nm then
some (nm, rgs.range.pos)
else
none)
logInfo <| m!"\n".joinSep (sortDecls.filterMap fun (mod, nms) =>
if let some (nm, _) := nms[0]?
then
some (m!"\nModule: {mod}{indentD <| .ofConstName nm}")
else none).toList
namespace_where Grind
theorem X.A : True := trivial
theorem X.B : True := trivial
theorem X.C : True := trivial
namespace_where X
Last updated: Dec 20 2025 at 21:32 UTC