@@ -20,6 +20,7 @@ import (
2020 "github.com/thought-machine/please/rules"
2121 "github.com/thought-machine/please/src/core"
2222 "github.com/thought-machine/please/src/fs"
23+ "github.com/thought-machine/please/src/parse"
2324 "github.com/thought-machine/please/src/parse/asp"
2425 "github.com/thought-machine/please/src/plz"
2526)
@@ -33,7 +34,7 @@ type Handler struct {
3334 mutex sync.Mutex // guards docs
3435 state * core.BuildState
3536 parser * asp.Parser
36- builtins map [string ]builtin
37+ builtins map [string ][] builtin
3738 pkgs * pkg
3839 root string
3940}
@@ -55,7 +56,7 @@ func NewHandler() *Handler {
5556 return & Handler {
5657 docs : map [string ]* doc {},
5758 pkgs : & pkg {},
58- builtins : map [string ]builtin {},
59+ builtins : map [string ][] builtin {},
5960 }
6061}
6162
@@ -195,13 +196,35 @@ func (h *Handler) initialize(params *lsp.InitializeParams) (*lsp.InitializeResul
195196 }
196197 h .state = core .NewBuildState (config )
197198 h .state .NeedBuild = false
198- // We need an unwrapped parser instance as well for raw access.
199- h .parser = asp .NewParser (h .state )
199+ // Initialize the parser on state first, so that plz.RunHost uses the same parser.
200+ // This ensures plugin subincludes are stored in the same AST cache we use.
201+ parse .InitParser (h .state )
202+ h .parser = parse .GetAspParser (h .state )
203+ if h .parser == nil {
204+ return nil , fmt .Errorf ("failed to get asp parser from state" )
205+ }
200206 // Parse everything in the repo up front.
201207 // This is a lot easier than trying to do clever partial parses later on, although
202208 // eventually we may want that if we start dealing with truly large repos.
203209 go func () {
210+ // Start a goroutine to periodically load parser functions as they become available.
211+ // This allows go-to-definition to work progressively while the full parse runs.
212+ done := make (chan struct {})
213+ go func () {
214+ ticker := time .NewTicker (2 * time .Second )
215+ defer ticker .Stop ()
216+ for {
217+ select {
218+ case <- done :
219+ h .loadParserFunctions ()
220+ return
221+ case <- ticker .C :
222+ h .loadParserFunctions ()
223+ }
224+ }
225+ }()
204226 plz .RunHost (core .WholeGraph , h .state )
227+ close (done )
205228 log .Debug ("initial parse complete" )
206229 h .buildPackageTree ()
207230 log .Debug ("built completion package tree" )
@@ -256,18 +279,46 @@ func (h *Handler) loadBuiltins() error {
256279 f := asp .NewFile (dest , data )
257280 for _ , stmt := range stmts {
258281 if stmt .FuncDef != nil {
259- h .builtins [stmt .FuncDef .Name ] = builtin {
282+ h .builtins [stmt .FuncDef .Name ] = append ( h . builtins [ stmt . FuncDef . Name ], builtin {
260283 Stmt : stmt ,
261284 Pos : f .Pos (stmt .Pos ),
262285 EndPos : f .Pos (stmt .EndPos ),
263- }
286+ })
264287 }
265288 }
266289 }
267290 log .Debug ("loaded builtin function information" )
268291 return nil
269292}
270293
294+ // loadParserFunctions loads function definitions from the parser's ASTs.
295+ // This includes plugin-defined functions like go_library, python_library, etc.
296+ func (h * Handler ) loadParserFunctions () {
297+ funcsByFile := h .parser .AllFunctionsByFile ()
298+ if funcsByFile == nil {
299+ return
300+ }
301+ h .mutex .Lock ()
302+ defer h .mutex .Unlock ()
303+ for filename , stmts := range funcsByFile {
304+ // Read the file to create a File object for position conversion
305+ data , err := os .ReadFile (filename )
306+ if err != nil {
307+ log .Warning ("failed to read file %s: %v" , filename , err )
308+ continue
309+ }
310+ file := asp .NewFile (filename , data )
311+ for _ , stmt := range stmts {
312+ name := stmt .FuncDef .Name
313+ h .builtins [name ] = append (h .builtins [name ], builtin {
314+ Stmt : stmt ,
315+ Pos : file .Pos (stmt .Pos ),
316+ EndPos : file .Pos (stmt .EndPos ),
317+ })
318+ }
319+ }
320+ }
321+
271322// fromURI converts a DocumentURI to a path.
272323func fromURI (uri lsp.DocumentURI ) string {
273324 if ! strings .HasPrefix (string (uri ), "file://" ) {
0 commit comments