mirror of
https://github.com/golang/go
synced 2024-09-30 14:18:32 -06:00
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>
This commit is contained in:
parent
130c9f19d3
commit
c80dc571df
@ -10,6 +10,8 @@ import (
|
||||
"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 @@ func (s *Server) executeCommand(ctx context.Context, params *protocol.ExecuteCom
|
||||
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 @@ func (s *Server) runTest(ctx context.Context, snapshot source.Snapshot, funcName
|
||||
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 @@ func (s *Server) runTest(ctx context.Context, snapshot source.Snapshot, funcName
|
||||
})
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
@ -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")
|
||||
}
|
@ -7,10 +7,12 @@ package lsp
|
||||
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 @@ func (wd *WorkDone) End(ctx context.Context, message string) error {
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user