Skip to content

Commit 10ca53e

Browse files
Run lints on open/save/change; remove breaking checks (#4109)
Co-authored-by: Edward McFarlane <[email protected]>
1 parent 9dd7ce7 commit 10ca53e

File tree

1 file changed

+9
-165
lines changed

1 file changed

+9
-165
lines changed

private/buf/buflsp/file.go

Lines changed: 9 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
package buflsp
1818

1919
import (
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.
6054
type 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.
127120
func (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-
1088932
func (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

Comments
 (0)