lean4-htt/tests/lean/interactive/goTo.lean
Marc Huisinga 8b8561a699
feat: improved go to definition (#9040)
This PR improves the 'Go to Definition' UX, specifically:
- Using 'Go to Definition' on a type class projection will now extract
the specific instances that were involved and provide them as locations
to jump to. For example, using 'Go to Definition' on the `toString` of
`toString 0` will yield results for `ToString.toString` and `ToString
Nat`.
- Using 'Go to Definition' on a macro that produces syntax with type
class projections will now also extract the specific instances that were
involved and provide them as locations to jump to. For example, using
'Go to Definition' on the `+` of `1 + 1` will yield results for
`HAdd.hAdd`, `HAdd α α α` and `Add Nat`.
- Using 'Go to Declaration' will now provide all the results of 'Go to
Definition' in addition to the elaborator and the parser that were
involved. For example, using 'Go to Declaration' on the `+` of `1 + 1`
will yield results for `HAdd.hAdd`, `HAdd α α α`, `Add Nat`,
``macro_rules | `($x + $y) => ...`` and `infixl:65 " + " => HAdd.hAdd`.
- Using 'Go to Type Definition' on a value with a type that contains
multiple constants will now provide 'Go to Definition' results for each
constant. For example, using 'Go to Type Definition' on `x` for `x :
Array Nat` will yield results for `Array` and `Nat`.

### Details
'Go to Definition' for type class projections was first implemented by
#1767, but there were still a couple of shortcomings with the
implementation. E.g. in order to jump to the instance in `toString 0`,
one had to add another space within the application and then use 'Go to
Definition' on that, or macros would block instances from being
displayed. Then, when the .ilean format was added, most 'Go to
Definition' requests were already handled using the .ileans in the
watchdog process, and so the file worker never received them to handle
them with the semantic information that it has available.

This PR resolves most of the issues with the previous implementation and
refactors the 'Go to Definition' control flow so that 'Go to Definition'
requests are always handled by the file worker, with the watchdog merely
using its .ilean position information to update the positions in the
response to a more up-to-date state. This is necessary because the file
worker obtains its position information from the .oleans, which need to
be rebuilt in order to be up-to-date, while the watchdog always receives
.ilean update notifications from each active file worker with the
current position information in the editor.

Finally, all of the 'Go to Definition' code is refactored to be easier
to maintain.

### Breaking changes
`InfoTree.hoverableInfoAt?` has been generalized to
`InfoTree.hoverableInfoAtM?` and now takes a general `filter` argument
instead of several boolean flags, as was the case before.
2025-07-21 15:47:44 +00:00

114 lines
2.4 KiB
Text
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import Lean.Elab
--^ waitForILeans
structure Bar where
structure Foo where
foo₁ : Nat
foo₂ : Nat
bar : Bar
def mkFoo₁ : Foo := {
--v textDocument/definition
foo₁ := 1
--^ textDocument/definition
--v textDocument/declaration
foo₂ := 2
--v textDocument/typeDefinition
bar := ⟨⟩
}
--v textDocument/definition
#check (Bar)
structure HandWrittenStruct where
n : Nat
-- def HandWrittenStruct.n := fun | mk n => n
--v textDocument/definition
def hws : HandWrittenStruct := {
--v textDocument/definition
n := 3
}
--v textDocument/declaration
def mkFoo₂ := mkFoo₁
syntax (name := elabTest) "test" : term
@[term_elab elabTest] def elabElabTest : Lean.Elab.Term.TermElab := fun orig _ => do
let stx ← `(Nat.succ (Nat.succ Nat.zero))
Lean.Elab.withMacroExpansionInfo orig stx $ Lean.Elab.Term.elabTerm stx none
--v textDocument/declaration
#check test
--^ textDocument/definition
def Baz (α : Type) := α
#check fun (b : Baz Foo) => b
--^ textDocument/typeDefinition
example : Nat :=
let a := 1
--v textDocument/definition
a + b
--^ textDocument/definition
where
b := 2
macro_rules | `(test) => `(Nat.succ (Nat.succ Nat.zero))
#check test
--^ textDocument/definition
class Foo2 where
foo : Nat → Nat
foo' : Nat
class Foo3 [Foo2] where
foo : [Foo2] → Nat
class inductive Foo4 : Nat → Type where
| mk : Nat → Foo4 0
def Foo4.foo : [Foo4 n] → Nat
| .mk n => n
class Foo5 where
foo : Foo2
instance : Foo2 := .mk id 0
instance : Foo3 := .mk 0
instance : Foo4 0 := .mk 0
instance [foo2 : Foo2] : Foo5 := .mk foo2
-- should go-to instance
--v textDocument/definition
#check Foo2.foo 2
--^ textDocument/definition
#check (Foo2.foo)
--^ textDocument/definition
#check (Foo2.foo')
--^ textDocument/definition
-- should go-to projection
#check @Foo2.foo
--^ textDocument/definition
-- test that the correct instance index is extracted
#check (Foo3.foo)
--^ textDocument/definition
-- non-projections should not go-to instance
#check (Foo4.foo)
--^ textDocument/definition
set_option pp.all true in
-- test that multiple instances can be extracted
#check (Foo5.foo)
--^ textDocument/definition
-- duplicate definitions link to the original
def mkFoo₁ := 1
--^ textDocument/definition