internal/lsp: refactor generate code lens code
This change moves the "generic" go generate code lens code into
progress.go. Fix up a few generate-specific things in the progress
writers.
The remaining generate code isn't much, and is moved into command.go.
Change-Id: I2b7b6279da2442c0b92758b9b3e259f25787fabc
Reviewed-on: https://go-review.googlesource.com/c/tools/+/242919
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
diff --git a/internal/lsp/command.go b/internal/lsp/command.go
index f10b69a..18eee18 100644
--- a/internal/lsp/command.go
+++ b/internal/lsp/command.go
@@ -10,6 +10,8 @@
"io"
"strings"
+ "golang.org/x/tools/internal/event"
+ "golang.org/x/tools/internal/lsp/debug/tag"
"golang.org/x/tools/internal/lsp/protocol"
"golang.org/x/tools/internal/lsp/source"
"golang.org/x/tools/internal/span"
@@ -57,7 +59,7 @@
if err != nil {
return nil, err
}
- go s.runGenerate(xcontext.Detach(ctx), dir, recursive)
+ go s.runGoGenerate(xcontext.Detach(ctx), dir, recursive)
case source.CommandRegenerateCgo:
mod := source.FileModification{
URI: protocol.DocumentURI(params.Arguments[0].(string)).SpanURI(),
@@ -104,7 +106,8 @@
defer cancel()
ew := &eventWriter{ctx: ctx, operation: "test"}
- wc := s.newProgressWriter(ctx, "test", "running "+funcName, cancel)
+ msg := fmt.Sprintf("testing %s", funcName)
+ wc := s.newProgressWriter(ctx, "test", msg, msg, cancel)
defer wc.Close()
messageType := protocol.Info
@@ -124,6 +127,42 @@
})
}
+// GenerateWorkDoneTitle is the title used in progress reporting for go
+// generate commands. It is exported for testing purposes.
+const GenerateWorkDoneTitle = "generate"
+
+func (s *Server) runGoGenerate(ctx context.Context, dir string, recursive bool) error {
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ er := &eventWriter{ctx: ctx, operation: "generate"}
+ wc := s.newProgressWriter(ctx, GenerateWorkDoneTitle, "running go generate", "started go generate, check logs for progress", cancel)
+ defer wc.Close()
+ args := []string{"-x"}
+ if recursive {
+ args = append(args, "./...")
+ }
+
+ stderr := io.MultiWriter(er, wc)
+ uri := span.URIFromPath(dir)
+ view, err := s.session.ViewOf(uri)
+ if err != nil {
+ return err
+ }
+ snapshot := view.Snapshot()
+ if err := snapshot.RunGoCommandPiped(ctx, "generate", args, er, stderr); err != nil {
+ if errors.Is(err, context.Canceled) {
+ return nil
+ }
+ event.Error(ctx, "generate: command error", err, tag.Directory.Of(uri.Filename()))
+ return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
+ Type: protocol.Error,
+ Message: "go generate exited with an error, check gopls logs",
+ })
+ }
+ return nil
+}
+
func getRunTestArguments(args []interface{}) (string, span.URI, error) {
if len(args) != 2 {
return "", "", errors.Errorf("expected one test func name and one file path, got %v", args)
diff --git a/internal/lsp/generate.go b/internal/lsp/generate.go
deleted file mode 100644
index d850dec..0000000
--- a/internal/lsp/generate.go
+++ /dev/null
@@ -1,138 +0,0 @@
-// Copyright 2020 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package lsp
-
-import (
- "context"
- "io"
-
- "golang.org/x/tools/internal/event"
- "golang.org/x/tools/internal/lsp/debug/tag"
- "golang.org/x/tools/internal/lsp/protocol"
- "golang.org/x/tools/internal/span"
- errors "golang.org/x/xerrors"
-)
-
-// GenerateWorkDoneTitle is the title used in progress reporting for go
-// generate commands. It is exported for testing purposes.
-const GenerateWorkDoneTitle = "generate"
-
-func (s *Server) runGenerate(ctx context.Context, dir string, recursive bool) error {
- ctx, cancel := context.WithCancel(ctx)
- defer cancel()
-
- er := &eventWriter{ctx: ctx, operation: "generate"}
- wc := s.newProgressWriter(ctx, GenerateWorkDoneTitle, "running go generate", cancel)
- defer wc.Close()
- args := []string{"-x"}
- if recursive {
- args = append(args, "./...")
- }
-
- stderr := io.MultiWriter(er, wc)
- uri := span.URIFromPath(dir)
- view, err := s.session.ViewOf(uri)
- if err != nil {
- return err
- }
- snapshot := view.Snapshot()
- if err := snapshot.RunGoCommandPiped(ctx, "generate", args, er, stderr); err != nil {
- if errors.Is(err, context.Canceled) {
- return nil
- }
- event.Error(ctx, "generate: command error", err, tag.Directory.Of(dir))
- return s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
- Type: protocol.Error,
- Message: "go generate exited with an error, check gopls logs",
- })
- }
- return nil
-}
-
-// eventWriter writes every incoming []byte to
-// event.Print with the operation=generate tag
-// to distinguish its logs from others.
-type eventWriter struct {
- ctx context.Context
- operation string
-}
-
-func (ew *eventWriter) Write(p []byte) (n int, err error) {
- event.Log(ew.ctx, string(p), tag.Operation.Of(ew.operation))
- return len(p), nil
-}
-
-// newProgressWriter returns an io.WriterCloser that can be used
-// to report progress on a command based on the client capabilities.
-func (s *Server) newProgressWriter(ctx context.Context, title, message string, cancel func()) io.WriteCloser {
- if s.supportsWorkDoneProgress {
- wd := s.StartWork(ctx, title, message, cancel)
- return &workDoneWriter{ctx, wd}
- }
- mw := &messageWriter{ctx, cancel, s.client}
- mw.start()
- return mw
-}
-
-// messageWriter implements progressWriter
-// and only tells the user that "go generate"
-// has started through window/showMessage but does not
-// report anything afterwards. This is because each
-// log shows up as a separate window and therefore
-// would be obnoxious to show every incoming line.
-// Request cancellation happens synchronously through
-// the ShowMessageRequest response.
-type messageWriter struct {
- ctx context.Context
- cancel func()
- client protocol.Client
-}
-
-func (lw *messageWriter) Write(p []byte) (n int, err error) {
- return len(p), nil
-}
-
-func (lw *messageWriter) start() {
- go func() {
- msg, err := lw.client.ShowMessageRequest(lw.ctx, &protocol.ShowMessageRequestParams{
- Type: protocol.Log,
- Message: "go generate has started, check logs for progress",
- Actions: []protocol.MessageActionItem{{
- Title: "Cancel",
- }},
- })
- if err != nil {
- event.Error(lw.ctx, "error sending initial generate msg", err)
- return
- }
- if msg != nil && msg.Title == "Cancel" {
- lw.cancel()
- }
- }()
-}
-
-func (lw *messageWriter) Close() error {
- return lw.client.ShowMessage(lw.ctx, &protocol.ShowMessageParams{
- Type: protocol.Info,
- Message: "go generate has finished",
- })
-}
-
-// workDoneWriter implements progressWriter by sending $/progress notifications
-// to the client. Request cancellations happens separately through the
-// window/workDoneProgress/cancel request, in which case the given context will
-// be rendered done.
-type workDoneWriter struct {
- ctx context.Context
- wd *WorkDone
-}
-
-func (wdw *workDoneWriter) Write(p []byte) (n int, err error) {
- return len(p), wdw.wd.Progress(wdw.ctx, string(p), 0)
-}
-
-func (wdw *workDoneWriter) Close() error {
- return wdw.wd.End(wdw.ctx, "finished")
-}
diff --git a/internal/lsp/progress.go b/internal/lsp/progress.go
index c5e70cf..c5431f9 100644
--- a/internal/lsp/progress.go
+++ b/internal/lsp/progress.go
@@ -7,10 +7,12 @@
import (
"context"
"errors"
+ "io"
"math/rand"
"strconv"
"golang.org/x/tools/internal/event"
+ "golang.org/x/tools/internal/lsp/debug/tag"
"golang.org/x/tools/internal/lsp/protocol"
)
@@ -121,3 +123,90 @@
}
return err
}
+
+// eventWriter writes every incoming []byte to
+// event.Print with the operation=generate tag
+// to distinguish its logs from others.
+type eventWriter struct {
+ ctx context.Context
+ operation string
+}
+
+func (ew *eventWriter) Write(p []byte) (n int, err error) {
+ event.Log(ew.ctx, string(p), tag.Operation.Of(ew.operation))
+ return len(p), nil
+}
+
+// newProgressWriter returns an io.WriterCloser that can be used
+// to report progress on a command based on the client capabilities.
+func (s *Server) newProgressWriter(ctx context.Context, title, beginMsg, msg string, cancel func()) io.WriteCloser {
+ if s.supportsWorkDoneProgress {
+ wd := s.StartWork(ctx, title, beginMsg, cancel)
+ return &workDoneWriter{ctx, wd}
+ }
+ mw := &messageWriter{ctx, cancel, s.client}
+ mw.start(msg)
+ return mw
+}
+
+// messageWriter implements progressWriter
+// and only tells the user that "go generate"
+// has started through window/showMessage but does not
+// report anything afterwards. This is because each
+// log shows up as a separate window and therefore
+// would be obnoxious to show every incoming line.
+// Request cancellation happens synchronously through
+// the ShowMessageRequest response.
+type messageWriter struct {
+ ctx context.Context
+ cancel func()
+ client protocol.Client
+}
+
+func (lw *messageWriter) Write(p []byte) (n int, err error) {
+ return len(p), nil
+}
+
+func (lw *messageWriter) start(msg string) {
+ go func() {
+ const cancel = "Cancel"
+ item, err := lw.client.ShowMessageRequest(lw.ctx, &protocol.ShowMessageRequestParams{
+ Type: protocol.Log,
+ Message: msg,
+ Actions: []protocol.MessageActionItem{{
+ Title: "Cancel",
+ }},
+ })
+ if err != nil {
+ event.Error(lw.ctx, "error sending message request", err)
+ return
+ }
+ if item != nil && item.Title == "Cancel" {
+ lw.cancel()
+ }
+ }()
+}
+
+func (lw *messageWriter) Close() error {
+ return lw.client.ShowMessage(lw.ctx, &protocol.ShowMessageParams{
+ Type: protocol.Info,
+ Message: "go generate has finished",
+ })
+}
+
+// workDoneWriter implements progressWriter by sending $/progress notifications
+// to the client. Request cancellations happens separately through the
+// window/workDoneProgress/cancel request, in which case the given context will
+// be rendered done.
+type workDoneWriter struct {
+ ctx context.Context
+ wd *WorkDone
+}
+
+func (wdw *workDoneWriter) Write(p []byte) (n int, err error) {
+ return len(p), wdw.wd.Progress(wdw.ctx, string(p), 0)
+}
+
+func (wdw *workDoneWriter) Close() error {
+ return wdw.wd.End(wdw.ctx, "finished")
+}