@@ -2,9 +2,12 @@ package lsp
22
33import (
44 "errors"
5+ "os"
56 "slices"
7+ "strings"
68
79 "github.com/lets-cli/lets/internal/util"
10+ "github.com/tliron/commonlog"
811 "github.com/tliron/glsp"
912 lsp "github.com/tliron/glsp/protocol_3_16"
1013)
@@ -41,8 +44,42 @@ func (s *lspServer) setTrace(context *glsp.Context, params *lsp.SetTraceParams)
4144 return nil
4245}
4346
47+ // loadMixins reads local mixin files referenced by a document and adds them to storage and index.
48+ func (s * lspServer ) loadMixins (uri string ) {
49+ doc := s .storage .GetDocument (uri )
50+ if doc == nil {
51+ return
52+ }
53+
54+ path := normalizePath (uri )
55+
56+ for _ , filename := range s .parser .getMixinFilenames (doc ) {
57+ mixinPath := replacePathFilename (path , strings .TrimPrefix (filename , "-" ))
58+ if ! util .FileExists (mixinPath ) {
59+ s .log .Debugf ("mixin target does not exist: %s" , mixinPath )
60+ continue
61+ }
62+
63+ data , err := os .ReadFile (mixinPath )
64+ if err != nil {
65+ s .log .Warningf ("failed to read mixin %s: %v" , mixinPath , err )
66+ continue
67+ }
68+
69+ mixinURI := pathToURI (mixinPath )
70+ text := string (data )
71+
72+ s .storage .AddDocument (mixinURI , text )
73+ s .index .IndexDocument (mixinURI , text )
74+ }
75+ }
76+
4477func (s * lspServer ) textDocumentDidOpen (context * glsp.Context , params * lsp.DidOpenTextDocumentParams ) error {
4578 s .storage .AddDocument (params .TextDocument .URI , params .TextDocument .Text )
79+
80+ go s .index .IndexDocument (params .TextDocument .URI , params .TextDocument .Text )
81+ go s .loadMixins (params .TextDocument .URI )
82+
4683 return nil
4784}
4885
@@ -51,6 +88,9 @@ func (s *lspServer) textDocumentDidChange(context *glsp.Context, params *lsp.Did
5188 switch c := change .(type ) {
5289 case lsp.TextDocumentContentChangeEventWhole :
5390 s .storage .AddDocument (params .TextDocument .URI , c .Text )
91+
92+ go s .index .IndexDocument (params .TextDocument .URI , c .Text )
93+ go s .loadMixins (params .TextDocument .URI )
5494 case lsp.TextDocumentContentChangeEvent :
5595 return errors .New ("incremental changes not supported" )
5696 }
@@ -60,7 +100,9 @@ func (s *lspServer) textDocumentDidChange(context *glsp.Context, params *lsp.Did
60100}
61101
62102type definitionHandler struct {
103+ log commonlog.Logger
63104 parser * parser
105+ index * index
64106}
65107
66108func (h * definitionHandler ) findMixinsDefinition (doc * string , params * lsp.DefinitionParams ) (any , error ) {
@@ -89,46 +131,48 @@ func (h *definitionHandler) findMixinsDefinition(doc *string, params *lsp.Defini
89131 }, nil
90132}
91133
134+ func locationForCommand (uri string , position lsp.Position ) lsp.Location {
135+ return lsp.Location {
136+ URI : uri ,
137+ Range : lsp.Range {
138+ Start : lsp.Position {
139+ Line : position .Line ,
140+ Character : 2 , // TODO: do we have to assume indentation?
141+ },
142+ End : lsp.Position {
143+ Line : position .Line ,
144+ Character : 2 , // TODO: do we need + len ?
145+ },
146+ },
147+ }
148+ }
149+
92150func (h * definitionHandler ) findCommandDefinition (doc * string , params * lsp.DefinitionParams ) (any , error ) {
93151 path := normalizePath (params .TextDocument .URI )
94152
95153 commandName := h .parser .extractCommandReference (doc , params .Position )
96154 if commandName == "" {
97- h .parser . log .Debugf ("no command reference resolved at %s:%d:%d" , path , params .Position .Line , params .Position .Character )
155+ h .log .Debugf ("no command reference resolved at %s:%d:%d" , path , params .Position .Line , params .Position .Character )
98156 return nil , nil
99157 }
100158
101- command := h .parser .findCommand (doc , commandName )
102- if command == nil {
103- h .parser . log .Debugf ("command reference %q did not match any local command" , commandName )
159+ commandInfo , found := h .index .findCommand (commandName )
160+ if ! found {
161+ h .log .Debugf ("command reference %q did not match any local command" , commandName )
104162 return nil , nil
105163 }
106164
107- h .parser . log .Debugf (
165+ h .log .Debugf (
108166 "resolved command definition %q -> %s:%d:%d" ,
109167 commandName ,
110168 path ,
111- command .position .Line ,
112- command .position .Character ,
169+ commandInfo .position .Line ,
170+ commandInfo .position .Character ,
113171 )
114172
115- // TODO: theoretically we can have multiple commands with the same name if we have mixins
116- return []lsp.Location {
117- {
118- // TODO: support commands in other files
119- URI : params .TextDocument .URI ,
120- Range : lsp.Range {
121- Start : lsp.Position {
122- Line : command .position .Line ,
123- Character : 2 , // TODO: do we have to assume indentation?
124- },
125- End : lsp.Position {
126- Line : command .position .Line ,
127- Character : 2 , // TODO: do we need + len ?
128- },
129- },
130- },
131- }, nil
173+ loc := locationForCommand (commandInfo .fileURI , commandInfo .position )
174+
175+ return []lsp.Location {loc }, nil
132176}
133177
134178type completionHandler struct {
@@ -165,12 +209,13 @@ func (h *completionHandler) buildDependsCompletions(doc *string, params *lsp.Com
165209// Returns: Location | []Location | []LocationLink | nil.
166210func (s * lspServer ) textDocumentDefinition (context * glsp.Context , params * lsp.DefinitionParams ) (any , error ) {
167211 definitionHandler := definitionHandler {
168- parser : newParser (s .log ),
212+ log : s .log ,
213+ parser : s .parser ,
214+ index : s .index ,
169215 }
170216 doc := s .storage .GetDocument (params .TextDocument .URI )
171217
172- p := newParser (s .log )
173- positionType := p .getPositionType (doc , params .Position )
218+ positionType := s .parser .getPositionType (doc , params .Position )
174219 s .log .Debugf (
175220 "definition request uri=%s line=%d char=%d type=%s" ,
176221 normalizePath (params .TextDocument .URI ),
@@ -193,12 +238,11 @@ func (s *lspServer) textDocumentDefinition(context *glsp.Context, params *lsp.De
193238// Returns: []CompletionItem | CompletionList | nil.
194239func (s * lspServer ) textDocumentCompletion (context * glsp.Context , params * lsp.CompletionParams ) (any , error ) {
195240 completionHandler := completionHandler {
196- parser : newParser ( s . log ) ,
241+ parser : s . parser ,
197242 }
198243 doc := s .storage .GetDocument (params .TextDocument .URI )
199244
200- p := newParser (s .log )
201- switch p .getPositionType (doc , params .Position ) {
245+ switch s .parser .getPositionType (doc , params .Position ) {
202246 case PositionTypeDepends :
203247 return completionHandler .buildDependsCompletions (doc , params )
204248 default :
0 commit comments