1717package buflsp
1818
1919import (
20- "bytes"
2120 "context"
2221 "errors"
2322 "fmt"
@@ -28,7 +27,6 @@ import (
2827 "os"
2928 "slices"
3029 "strings"
31- "time"
3230
3331 "buf.build/go/standard/xio"
3432 "buf.build/go/standard/xlog/xslog"
@@ -50,17 +48,12 @@ import (
5048 "go.lsp.dev/uri"
5149)
5250
53- const (
54- checkRefreshPeriod = 3 * time .Second
55- )
56-
5751// file is a file that has been opened by the client.
5852//
5953// Mutating a file is thread-safe.
6054type file struct {
61- lsp * lsp
62- uri protocol.URI
63- checkWork chan <- struct {}
55+ lsp * lsp
56+ uri protocol.URI
6457
6558 file * report.File
6659 // Version is an opaque version identifier given to us by the LSP client. This
@@ -82,7 +75,7 @@ type file struct {
8275 referenceSymbols []* symbol
8376 symbols []* symbol
8477 diagnostics []protocol.Diagnostic
85- image , againstImage bufimage.Image
78+ image bufimage.Image
8679}
8780
8881// IsLocal returns whether this is a local file, i.e. a file that the editor
@@ -126,10 +119,6 @@ func (f *file) Reset(ctx context.Context) {
126119// for this file.
127120func (f * file ) Close (ctx context.Context ) {
128121 f .Manager ().Close (ctx , f .uri )
129- if f .checkWork != nil {
130- close (f .checkWork )
131- f .checkWork = nil
132- }
133122 if f .workspace != nil {
134123 f .workspace .Release ()
135124 f .workspace = nil
@@ -298,7 +287,10 @@ func (f *file) Refresh(ctx context.Context) {
298287 f .IndexSymbols (ctx )
299288
300289 progress .Report (ctx , "Running Checks" , 5.0 / 5 )
301- f .RunChecks (ctx )
290+ if f .IsOpenInEditor () {
291+ f .BuildImage (ctx )
292+ f .RunLints (ctx )
293+ }
302294
303295 progress .Done (ctx )
304296
@@ -864,60 +856,6 @@ func (f *file) SymbolAt(ctx context.Context, cursor protocol.Position) *symbol {
864856 return symbol
865857}
866858
867- // RunChecks initiates background checks (lint and breaking) on this file and
868- // returns immediately.
869- //
870- // Checks are executed in a background goroutine to avoid blocking the LSP
871- // call. Each call to RunChecks invalidates any ongoing checks, triggering a
872- // fresh run. However, previous checks are not interrupted. The checks acquire
873- // the LSP mutex. Subsequent LSP calls will wait for the current check to
874- // complete before proceeding.
875- //
876- // Checks are debounce (with the delay defined by checkRefreshPeriod) to avoid
877- // overwhelming the client with expensive checks. If the file is not open in the
878- // editor, checks are skipped. Diagnostics are published after checks are run.
879- //
880- // This operation requires IndexImports..
881- func (f * file ) RunChecks (ctx context.Context ) {
882- // If we have not yet started a goroutine to run checks, start one.
883- // This goroutine will run checks in the background and publish diagnostics.
884- // We debounce checks to avoid spamming the client.
885- if f .checkWork == nil {
886- // We use a buffered channel of length one as the check invalidation mechanism.
887- work := make (chan struct {}, 1 )
888- f .checkWork = work
889- runChecks := func (ctx context.Context ) {
890- f .lsp .lock .Lock ()
891- defer f .lsp .lock .Unlock ()
892- if ! f .IsOpenInEditor () {
893- // Skip checks if the file is not open in the editor.
894- return
895- }
896- f .lsp .logger .Info (fmt .Sprintf ("running checks for %v, %v" , f .uri , f .version ))
897- f .BuildImages (ctx )
898- f .RunLints (ctx )
899- f .RunBreaking (ctx )
900- f .PublishDiagnostics (ctx ) // Publish the latest diagnostics.
901- }
902- // Start a goroutine to process checks.
903- go func () {
904- // Detach from the parent RPC context.
905- ctx := context .WithoutCancel (ctx )
906- for range work {
907- runChecks (ctx )
908- // Debounce checks to prevent thrashing expensive checks.
909- time .Sleep (checkRefreshPeriod )
910- }
911- }()
912- }
913- // Signal the goroutine to invalidate and rerun checks.
914- select {
915- case f .checkWork <- struct {}{}:
916- default :
917- // Channel is full, checks are already invalidated and will be rerun.
918- }
919- }
920-
921859// newFileOpener returns a fileOpener for the context of this file.
922860//
923861// May return nil, if insufficient information is present to open the file.
@@ -936,57 +874,11 @@ func (f *file) newFileOpener() fileOpener {
936874 }
937875}
938876
939- // newAgainstFileOpener returns a fileOpener for building the --against file
940- // for this file. In other words, this pulls files out of the git index, if
941- // necessary.
942- //
943- // May return nil, if there is insufficient information to build an --against
944- // file.
945- func (f * file ) newAgainstFileOpener (ctx context.Context ) fileOpener {
946- if ! f .IsLocal () {
947- return nil
948- }
949-
950- if f .againstStrategy == againstGit && f .againstGitRef == "" {
951- return nil
952- }
953-
954- return func (path string ) (io.ReadCloser , error ) {
955- var file * file
956- if f .objectInfo .Path () == path {
957- file = f
958- } else {
959- file = f .importToFile [path ]
960- }
961- if file == nil {
962- return nil , fmt .Errorf ("%s: %w" , path , fs .ErrNotExist )
963- }
964- var (
965- data []byte
966- err error
967- )
968- if f .againstGitRef != "" {
969- data , err = git .ReadFileAtRef (
970- ctx ,
971- f .lsp .container ,
972- file .objectInfo .LocalPath (),
973- f .againstGitRef ,
974- )
975- }
976-
977- if data == nil || errors .Is (err , git .ErrInvalidGitCheckout ) {
978- return os .Open (file .objectInfo .LocalPath ())
979- }
980-
981- return xio .CompositeReadCloser (bytes .NewReader (data ), xio .NopCloser ), err
982- }
983- }
984-
985- // BuildImages builds Buf Images for this file, to be used with linting
877+ // BuildImage builds an image for linting.
986878// routines.
987879//
988880// This operation requires IndexImports.
989- func (f * file ) BuildImages (ctx context.Context ) {
881+ func (f * file ) BuildImage (ctx context.Context ) {
990882 if f .objectInfo == nil {
991883 return
992884 }
@@ -1000,18 +892,6 @@ func (f *file) BuildImages(ctx context.Context) {
1000892 } else {
1001893 f .lsp .logger .Warn ("not building image" , slog .String ("uri" , string (f .uri )))
1002894 }
1003-
1004- if opener := f .newAgainstFileOpener (ctx ); opener != nil {
1005- // We explicitly throw the diagnostics away.
1006- image , diagnostics := buildImage (ctx , f .objectInfo .Path (), f .lsp .logger , opener )
1007-
1008- f .againstImage = image
1009- if image == nil {
1010- f .lsp .logger .Warn ("failed to build --against image" , slog .Any ("diagnostics" , diagnostics ))
1011- }
1012- } else {
1013- f .lsp .logger .Warn ("not building --against image" , slog .String ("uri" , string (f .uri )))
1014- }
1015895}
1016896
1017897// RunLints runs linting on this file. Returns whether any lints failed.
@@ -1049,42 +929,6 @@ func (f *file) RunLints(ctx context.Context) bool {
1049929 ))
1050930}
1051931
1052- // RunBreaking runs breaking lints on this file. Returns whether any lints failed.
1053- //
1054- // This operation requires BuildImage.
1055- func (f * file ) RunBreaking (ctx context.Context ) bool {
1056- if f .IsWKT () {
1057- // Well-known types are not linted.
1058- return false
1059- }
1060-
1061- workspace := f .workspace .Workspace ()
1062- module := f .workspace .GetModule (f .uri )
1063- checkClient := f .workspace .CheckClient ()
1064- if workspace == nil {
1065- f .lsp .logger .Warn (fmt .Sprintf ("could not find workspace for %q" , f .uri ))
1066- return false
1067- }
1068- if module == nil || f .image == nil || f .againstImage == nil {
1069- f .lsp .logger .Warn (fmt .Sprintf ("could not find --against image for %q" , f .uri ))
1070- return false
1071- }
1072- if checkClient == nil {
1073- f .lsp .logger .Warn (fmt .Sprintf ("could not find check client for %q" , f .uri ))
1074- return false
1075- }
1076-
1077- f .lsp .logger .Debug (fmt .Sprintf ("running breaking for %q in %v" , f .uri , module .FullName ()))
1078- return f .appendLintErrors ("buf breaking" , checkClient .Breaking (
1079- ctx ,
1080- workspace .GetBreakingConfigForOpaqueID (module .OpaqueID ()),
1081- f .image ,
1082- f .againstImage ,
1083- bufcheck .WithPluginConfigs (workspace .PluginConfigs ()... ),
1084- bufcheck .WithPolicyConfigs (workspace .PolicyConfigs ()... ),
1085- ))
1086- }
1087-
1088932func (f * file ) appendLintErrors (source string , err error ) bool {
1089933 if err == nil {
1090934 f .lsp .logger .Debug (fmt .Sprintf ("%s generated no errors for %s" , source , f .uri ))
0 commit comments