Skip to content

Commit 78b1585

Browse files
committed
internal/lsp/fake: reflect on-disk changes in clean buffers
If an open buffer is unmodified from the on-disk version, and that on-disk content changes, VS Code will update the buffer with the change. This is relevant for our quick fixes that use the go command to make changes to go.mod/sum -- we want tests to pick up the changes without needing to close/reopen the buffer. For whatever reason, VS Code doesn't do this for deletions, and it made the implementation easier, so I followed suit. I also mimicked its behavior of sending both in-memory and on-disk change notifications. Change-Id: I838a64b2f48f3cbe1a86035293923951b53aecf3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/267577 Trust: Heschi Kreinick <[email protected]> Run-TryBot: Heschi Kreinick <[email protected]> gopls-CI: kokoro <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent bc9fc8d commit 78b1585

File tree

4 files changed

+57
-21
lines changed

4 files changed

+57
-21
lines changed

gopls/internal/regtest/codelens_test.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010
"testing"
1111

12+
"golang.org/x/tools/internal/lsp"
1213
"golang.org/x/tools/internal/lsp/protocol"
1314
"golang.org/x/tools/internal/lsp/source"
1415
"golang.org/x/tools/internal/lsp/tests"
@@ -110,7 +111,8 @@ func main() {
110111
runner.Run(t, shouldUpdateDep, func(t *testing.T, env *Env) {
111112
env.OpenFile("go.mod")
112113
env.ExecuteCodeLensCommand("go.mod", source.CommandUpgradeDependency)
113-
got := env.ReadWorkspaceFile("go.mod")
114+
env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1))
115+
got := env.Editor.BufferText("go.mod")
114116
const wantGoMod = `module mod.com
115117
116118
go 1.12
@@ -164,7 +166,8 @@ func main() {
164166
runner.Run(t, shouldRemoveDep, func(t *testing.T, env *Env) {
165167
env.OpenFile("go.mod")
166168
env.ExecuteCodeLensCommand("go.mod", source.CommandTidy)
167-
got := env.ReadWorkspaceFile("go.mod")
169+
env.Await(CompletedWork(lsp.DiagnosticWorkTitle(lsp.FromDidChangeWatchedFiles), 1))
170+
got := env.Editor.BufferText("go.mod")
168171
const wantGoMod = `module mod.com
169172
170173
go 1.14

gopls/internal/regtest/diagnostics_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,6 @@ var ErrHelpWanted error
639639

640640
// Test for golang/go#38211.
641641
func Test_Issue38211(t *testing.T) {
642-
t.Skip("Requires CL 267577 to work without the save hook.")
643642
testenv.NeedsGo1Point(t, 14)
644643
const ardanLabs = `
645644
-- go.mod --

gopls/internal/regtest/modfile_test.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,8 @@ require (
379379
example.com/blah/v2 v2.0.0
380380
)
381381
`
382-
// We need to read from disk even though go.mod is open -- the regtests
383-
// currently don't apply on-disk changes to open but unmodified buffers
384-
// like most editors would.
385-
if got := env.ReadWorkspaceFile("go.mod"); got != want {
382+
env.Await(EmptyDiagnostics("go.mod"))
383+
if got := env.Editor.BufferText("go.mod"); got != want {
386384
t.Fatalf("suggested fixes failed:\n%s", tests.Diff(want, got))
387385
}
388386
})

internal/lsp/fake/editor.go

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type buffer struct {
4747
version int
4848
path string
4949
content []string
50+
dirty bool
5051
}
5152

5253
func (b buffer) text() string {
@@ -270,10 +271,28 @@ func (e *Editor) onFileChanges(ctx context.Context, evts []FileEvent) {
270271
if e.Server == nil {
271272
return
272273
}
274+
e.mu.Lock()
273275
var lspevts []protocol.FileEvent
274276
for _, evt := range evts {
277+
// Always send an on-disk change, even for events that seem useless
278+
// because they're shadowed by an open buffer.
275279
lspevts = append(lspevts, evt.ProtocolEvent)
280+
281+
if buf, ok := e.buffers[evt.Path]; ok {
282+
// Following VS Code, don't honor deletions or changes to dirty buffers.
283+
if buf.dirty || evt.ProtocolEvent.Type == protocol.Deleted {
284+
continue
285+
}
286+
287+
content, err := e.sandbox.Workdir.ReadFile(evt.Path)
288+
if err != nil {
289+
continue // A race with some other operation.
290+
}
291+
// During shutdown, this call will fail. Ignore the error.
292+
_ = e.setBufferContentLocked(ctx, evt.Path, false, strings.Split(content, "\n"), nil)
293+
}
276294
}
295+
e.mu.Unlock()
277296
e.Server.DidChangeWatchedFiles(ctx, &protocol.DidChangeWatchedFilesParams{
278297
Changes: lspevts,
279298
})
@@ -285,15 +304,7 @@ func (e *Editor) OpenFile(ctx context.Context, path string) error {
285304
if err != nil {
286305
return err
287306
}
288-
return e.CreateBuffer(ctx, path, content)
289-
}
290-
291-
func newBuffer(path, content string) buffer {
292-
return buffer{
293-
version: 1,
294-
path: path,
295-
content: strings.Split(content, "\n"),
296-
}
307+
return e.createBuffer(ctx, path, false, content)
297308
}
298309

299310
func textDocumentItem(wd *Workdir, buf buffer) protocol.TextDocumentItem {
@@ -314,7 +325,16 @@ func textDocumentItem(wd *Workdir, buf buffer) protocol.TextDocumentItem {
314325
// CreateBuffer creates a new unsaved buffer corresponding to the workdir path,
315326
// containing the given textual content.
316327
func (e *Editor) CreateBuffer(ctx context.Context, path, content string) error {
317-
buf := newBuffer(path, content)
328+
return e.createBuffer(ctx, path, true, content)
329+
}
330+
331+
func (e *Editor) createBuffer(ctx context.Context, path string, dirty bool, content string) error {
332+
buf := buffer{
333+
version: 1,
334+
path: path,
335+
content: strings.Split(content, "\n"),
336+
dirty: dirty,
337+
}
318338
e.mu.Lock()
319339
e.buffers[path] = buf
320340
item := textDocumentItem(e.sandbox.Workdir, buf)
@@ -396,6 +416,12 @@ func (e *Editor) SaveBufferWithoutActions(ctx context.Context, path string) erro
396416
if err := e.sandbox.Workdir.WriteFile(ctx, path, content); err != nil {
397417
return errors.Errorf("writing %q: %w", path, err)
398418
}
419+
420+
e.mu.Lock()
421+
buf.dirty = false
422+
e.buffers[path] = buf
423+
e.mu.Unlock()
424+
399425
if e.Server != nil {
400426
params := &protocol.DidSaveTextDocumentParams{
401427
TextDocument: protocol.VersionedTextDocumentIdentifier{
@@ -534,7 +560,7 @@ func (e *Editor) SetBufferContent(ctx context.Context, path, content string) err
534560
e.mu.Lock()
535561
defer e.mu.Unlock()
536562
lines := strings.Split(content, "\n")
537-
return e.setBufferContentLocked(ctx, path, lines, nil)
563+
return e.setBufferContentLocked(ctx, path, true, lines, nil)
538564
}
539565

540566
// BufferText returns the content of the buffer with the given name.
@@ -563,16 +589,17 @@ func (e *Editor) editBufferLocked(ctx context.Context, path string, edits []Edit
563589
if err != nil {
564590
return err
565591
}
566-
return e.setBufferContentLocked(ctx, path, content, edits)
592+
return e.setBufferContentLocked(ctx, path, true, content, edits)
567593
}
568594

569-
func (e *Editor) setBufferContentLocked(ctx context.Context, path string, content []string, fromEdits []Edit) error {
595+
func (e *Editor) setBufferContentLocked(ctx context.Context, path string, dirty bool, content []string, fromEdits []Edit) error {
570596
buf, ok := e.buffers[path]
571597
if !ok {
572598
return fmt.Errorf("unknown buffer %q", path)
573599
}
574600
buf.content = content
575601
buf.version++
602+
buf.dirty = dirty
576603
e.buffers[path] = buf
577604
// A simple heuristic: if there is only one edit, send it incrementally.
578605
// Otherwise, send the entire content.
@@ -739,7 +766,16 @@ func (e *Editor) ExecuteCommand(ctx context.Context, params *protocol.ExecuteCom
739766
if !match {
740767
return nil, fmt.Errorf("unsupported command %q", params.Command)
741768
}
742-
return e.Server.ExecuteCommand(ctx, params)
769+
result, err := e.Server.ExecuteCommand(ctx, params)
770+
if err != nil {
771+
return nil, err
772+
}
773+
// Some commands use the go command, which writes directly to disk.
774+
// For convenience, check for those changes.
775+
if err := e.sandbox.Workdir.CheckForFileChanges(ctx); err != nil {
776+
return nil, err
777+
}
778+
return result, nil
743779
}
744780

745781
func convertEdits(protocolEdits []protocol.TextEdit) []Edit {

0 commit comments

Comments
 (0)