@@ -24,26 +24,23 @@ import (
2424 "io/fs"
2525 "iter"
2626 "log/slog"
27- "os"
2827 "slices"
2928 "strings"
3029
31- "buf.build/go/standard/xio"
3230 "buf.build/go/standard/xlog/xslog"
3331 "buf.build/go/standard/xslices"
3432 "github.com/bufbuild/buf/private/bufpkg/bufanalysis"
3533 "github.com/bufbuild/buf/private/bufpkg/bufcheck"
3634 "github.com/bufbuild/buf/private/bufpkg/bufimage"
35+ "github.com/bufbuild/buf/private/bufpkg/bufmodule"
3736 "github.com/bufbuild/buf/private/pkg/storage"
3837 "github.com/bufbuild/protocompile/experimental/ast/predeclared"
3938 "github.com/bufbuild/protocompile/experimental/incremental"
4039 "github.com/bufbuild/protocompile/experimental/incremental/queries"
4140 "github.com/bufbuild/protocompile/experimental/ir"
4241 "github.com/bufbuild/protocompile/experimental/report"
4342 "github.com/bufbuild/protocompile/experimental/seq"
44- "github.com/bufbuild/protocompile/experimental/source"
4543 "go.lsp.dev/protocol"
46- "go.lsp.dev/uri"
4744)
4845
4946// file is a file that has been opened by the client.
@@ -60,10 +57,8 @@ type file struct {
6057 version int32
6158 hasText bool // Whether this file has ever had text read into it.
6259
63- workspace * workspace // May be nil.
64-
65- objectInfo storage.ObjectInfo
66- importToFile map [string ]* file
60+ workspace * workspace // May be nil.
61+ objectInfo storage.ObjectInfo // Info in the context of the workspace.
6762
6863 ir ir.File
6964 referenceableSymbols map [string ]* symbol
@@ -102,10 +97,6 @@ func (f *file) Reset(ctx context.Context) {
10297 f .diagnostics = nil
10398 f .symbols = nil
10499 f .image = nil
105- for _ , imported := range f .importToFile {
106- imported .Close (ctx )
107- }
108- f .importToFile = nil
109100}
110101
111102// Close marks a file as closed.
@@ -126,28 +117,39 @@ func (f *file) Close(ctx context.Context) {
126117// Some files may be opened as dependencies, so we want to avoid doing extra
127118// work like sending progress notifications.
128119func (f * file ) IsOpenInEditor () bool {
129- return f .version != - 1 // See [file.ReadFromDisk ].
120+ return f .version != - 1 // See [file.ReadFromWorkspace ].
130121}
131122
132- // ReadFromDisk reads this file from disk if it has never had data loaded into it before.
123+ // ReadFromWorkspace reads this file from the workspace if it has never had data loaded into it
124+ // before.
133125//
134- // If it has been read from disk before, or has received updates from the LSP client, this
135- // function returns nil.
136- func (f * file ) ReadFromDisk (ctx context.Context ) (err error ) {
126+ // If it has been read from disk before, or has received updates from the LSP client, this function
127+ // returns nil.
128+ func (f * file ) ReadFromWorkspace (ctx context.Context ) (err error ) {
137129 if f .hasText {
138130 return nil
139131 }
140132
141133 fileName := f .uri .Filename ()
142- reader , err := os .Open (fileName )
134+ var reader io.ReadCloser
135+ switch info := f .objectInfo .(type ) {
136+ case bufmodule.FileInfo :
137+ reader , err = info .Module ().GetFile (ctx , info .Path ())
138+ case wktObjectInfo :
139+ reader , err = f .lsp .wktBucket .Get (ctx , info .Path ())
140+ default :
141+ return fmt .Errorf ("unsupported objectInfo type %T" , f .objectInfo )
142+ }
143143 if err != nil {
144- return fmt .Errorf ("could not open file %q from disk: %w" , f . uri , err )
144+ return fmt .Errorf ("read file %q from workspace" , err )
145145 }
146146 defer reader .Close ()
147- text , err := readAllAsString (reader )
148- if err != nil {
149- return fmt .Errorf ("could not read file %q from disk: %w" , f .uri , err )
147+
148+ var builder strings.Builder
149+ if _ , err := io .Copy (& builder , reader ); err != nil {
150+ return fmt .Errorf ("could not read file %q from workspace" , err )
150151 }
152+ text := builder .String ()
151153
152154 f .version = - 1
153155 f .file = report .NewFile (fileName , text )
@@ -176,19 +178,16 @@ func (f *file) Refresh(ctx context.Context) {
176178 }
177179 progress .Begin (ctx , "Indexing" )
178180
179- progress .Report (ctx , "Setting workspace" , 1.0 / 5 )
181+ progress .Report (ctx , "Setting workspace" , 1.0 / 4 )
180182 f .RefreshWorkspace (ctx )
181183
182- progress .Report (ctx , "Indexing imports" , 2.0 / 5 )
183- f .IndexImports (ctx )
184-
185- progress .Report (ctx , "Parsing IR" , 3.0 / 5 )
184+ progress .Report (ctx , "Parsing IR" , 2.0 / 4 )
186185 f .RefreshIR (ctx )
187186
188- progress .Report (ctx , "Indexing Symbols" , 4 .0/ 5 )
187+ progress .Report (ctx , "Indexing Symbols" , 3 .0/ 4 )
189188 f .IndexSymbols (ctx )
190189
191- progress .Report (ctx , "Running Checks" , 5 .0/ 5 )
190+ progress .Report (ctx , "Running Checks" , 4 .0/ 4 )
192191 if f .IsOpenInEditor () {
193192 f .BuildImage (ctx )
194193 f .RunLints (ctx )
@@ -233,75 +232,6 @@ func (f *file) RefreshWorkspace(ctx context.Context) {
233232 }
234233}
235234
236- // IndexImports keeps track of importable files.
237- //
238- // This operation requires RefreshWorkspace.
239- func (f * file ) IndexImports (ctx context.Context ) {
240- defer xslog .DebugProfile (f .lsp .logger , slog .String ("uri" , string (f .uri )))
241- if f .importToFile != nil {
242- return
243- }
244- fileInfos , err := f .getWorkspaceFileInfos (ctx )
245- if err != nil {
246- f .lsp .logger .Error (
247- "failed to get importable files" ,
248- slog .String ("file" , f .uri .Filename ()),
249- )
250- }
251- f .importToFile = make (map [string ]* file )
252- for _ , importable := range fileInfos {
253- if importable .ExternalPath () == f .uri .Filename () {
254- f .objectInfo = importable
255- if err := f .ReadFromDisk (ctx ); err != nil {
256- f .lsp .logger .Error (
257- "failed to read contents for file" ,
258- xslog .ErrorAttr (err ),
259- slog .String ("file" , importable .Path ()),
260- )
261- }
262- continue
263- }
264- importableFile := f .Manager ().Track (uri .File (importable .LocalPath ()))
265- if importableFile .objectInfo == nil {
266- importableFile .objectInfo = importable
267- }
268- if err := importableFile .ReadFromDisk (ctx ); err != nil {
269- f .lsp .logger .Error (
270- "failed to read contents for file" ,
271- xslog .ErrorAttr (err ),
272- slog .String ("file" , importable .Path ()),
273- )
274- }
275- f .importToFile [importableFile .objectInfo .Path ()] = importableFile
276- }
277- }
278-
279- // getWorkspaceFileInfos returns all files within the workspace.
280- //
281- // Note that this performs no validation on these files, because those files might be open in the
282- // editor and might contain invalid syntax at the moment. We only want to get their paths and nothing
283- // more.
284- //
285- // This operation requires RefreshWorkspace.
286- func (f * file ) getWorkspaceFileInfos (ctx context.Context ) ([]storage.ObjectInfo , error ) {
287- seen := make (map [string ]struct {})
288- var fileInfos []storage.ObjectInfo
289- for fileInfo := range f .workspace .FileInfo () {
290- fileInfos = append (fileInfos , fileInfo )
291- seen [fileInfo .Path ()] = struct {}{}
292- }
293- // Add all wellknown types if not provided within the workspace.
294- if err := f .lsp .wktBucket .Walk (ctx , "" , func (objectInfo storage.ObjectInfo ) error {
295- if _ , ok := seen [objectInfo .Path ()]; ! ok {
296- fileInfos = append (fileInfos , wktObjectInfo {objectInfo })
297- }
298- return nil
299- }); err != nil {
300- return nil , err
301- }
302- return fileInfos , nil
303- }
304-
305235// RefreshIR queries for the IR of the file and the IR of each import file.
306236// Diagnostics from the compiler are returned when applicable.
307237//
@@ -313,19 +243,11 @@ func (f *file) RefreshIR(ctx context.Context) {
313243 slog .Int ("version" , int (f .version )),
314244 )
315245
316- openerMap := map [string ]string {
317- f .objectInfo .Path (): f .file .Text (),
318- }
319- files := []* file {f }
320- for path , file := range f .importToFile {
321- openerMap [path ] = file .file .Text ()
322- files = append (files , file )
323- }
324- opener := source .NewMap (openerMap )
246+ files := xslices .MapValuesToSlice (f .workspace .PathToFile ())
325247 session := new (ir.Session )
326248 queries := xslices .Map (files , func (file * file ) incremental.Query [ir.File ] {
327249 return queries.IR {
328- Opener : opener ,
250+ Opener : f . workspace ,
329251 Path : file .objectInfo .Path (),
330252 Session : session ,
331253 }
@@ -346,7 +268,7 @@ func (f *file) RefreshIR(ctx context.Context) {
346268 }
347269 for i , file := range files {
348270 file .ir = results [i ].Value
349- if i > 0 {
271+ if f != file {
350272 // Update symbols for imports.
351273 file .IndexSymbols (ctx )
352274 }
@@ -420,7 +342,7 @@ func (f *file) IndexSymbols(ctx context.Context) {
420342 )
421343 continue
422344 }
423- file , ok := f .importToFile [ref .def .Span ().Path ()]
345+ file , ok := f .workspace . PathToFile () [ref .def .Span ().Path ()]
424346 if ! ok {
425347 // Check current file
426348 if ref .def .Span ().Path () != f .objectInfo .Path () {
@@ -451,7 +373,10 @@ func (f *file) IndexSymbols(ctx context.Context) {
451373 }
452374
453375 // Resolve all references outside of this file to symbols in this file
454- for _ , file := range f .importToFile {
376+ for _ , file := range f .workspace .PathToFile () {
377+ if f == file {
378+ continue // ignore self
379+ }
455380 for _ , sym := range file .referenceSymbols {
456381 ref , ok := sym .kind .(* reference )
457382 if ! ok {
@@ -703,7 +628,7 @@ func (f *file) importToSymbol(imp ir.Import) *symbol {
703628 file : f ,
704629 span : imp .Decl .Span (),
705630 kind : & imported {
706- file : f .importToFile [imp .File .Path ()],
631+ file : f .workspace . PathToFile () [imp .File .Path ()],
707632 },
708633 }
709634}
@@ -767,12 +692,12 @@ func (f *file) newFileOpener() fileOpener {
767692 if f .objectInfo .Path () == path {
768693 file = f
769694 } else {
770- file = f .importToFile [path ]
695+ file = f .workspace . PathToFile () [path ]
771696 }
772697 if file == nil {
773698 return nil , fmt .Errorf ("%s: %w" , path , fs .ErrNotExist )
774699 }
775- return xio . CompositeReadCloser (strings .NewReader (file .file .Text ()), xio . NopCloser ), nil
700+ return io . NopCloser (strings .NewReader (file .file .Text ())), nil
776701 }
777702}
778703
@@ -897,14 +822,6 @@ type wktObjectInfo struct {
897822 storage.ObjectInfo
898823}
899824
900- func readAllAsString (reader io.Reader ) (string , error ) {
901- var builder strings.Builder
902- if _ , err := io .Copy (& builder , reader ); err != nil {
903- return "" , err
904- }
905- return builder .String (), nil
906- }
907-
908825// GetSymbols retrieves symbols for the file. If a query is passed, matches only symbols matching
909826// the case-insensitive substring match to the symbol.
910827//
0 commit comments