>Instead of run-time reflection on values, I think an IDE implementing 
jump to definition should use source location and binding information 
from syntax objects. That's how DrRacket does it.

>The attached DrRacket screenshot (of [1]), in which I've tacked a bunch 
of binding arrows, shows how local definitions and complex 
macro-introduced binding structures are supported. It also works across 
modules, even with renaming: in the program `#lang racket (define one 
1)`, right-clicking on `define` and choosing "Jump to Binding 
Occurrence" will highlight `racket`, because the initial import of the 
racket language establishes the applicable binding of define, whereas 
choosing "Open Defining File" will open the racket/private/kw module, 
and then choosing "Jump to Definition (in Other File)" will jump to the 
definition of `new-define` on line 1171 of kw.rkt, which is renamed 
elsewhere to become the `define` of `#lang racket`.

>Since syntax objects from the expander encode all the details of scope, 
this works mostly automatically. DrRacket makes this functionality 
available as a library, so navigation features can also be used from 
Emacs with racket-xp-mode [2] or other editors with the Language Server 
Protocol [3].

I suppose this could work, but it seems the wrong layer of abstraction and a 
bit round-about to me, it also only works on languages that use Scheme-style 
syntax. Instead, I’d propose compiling the relevant code to Tree-IL (without 
optimisations, should work with most languages).  (For Scheme, this is 
essentially macro expansion, but the resulting object won’t technically be a 
syntax object.)

In Tree-IL, all syntax has been expanded (in tree form), which eliminates a lot 
of complications. Yet, source location information remains available (albeit 
not documented ...)! And the original variable names remain available (together 
with an unshadow-ified version, so for local variables you can move upwards to 
find the corresponding definition and in particular its source location.

A large upside is that this should work for most languages (not only Scheme) 
and is relatively straightforward to implement, a small downside is that 
information on definitions like (let-syntax ((f [...])) ...) aren’t available – 
you only see the return of (f stuff), not ‘f’ itself.

(But this downside applies to the ‘syntax’ route as well, unless you are doing 
complicated (but possible!) DIY partial macro expansion shenanigans.)

Best regards,
Maxime Devos.

Reply via email to