2020-03-10 22:49:10 -06:00
|
|
|
// 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"
|
|
|
|
|
2020-04-17 07:32:56 -06:00
|
|
|
"golang.org/x/tools/internal/event"
|
2020-03-10 22:49:10 -06:00
|
|
|
"golang.org/x/tools/internal/gocommand"
|
2020-03-10 21:09:39 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/debug/tag"
|
2020-03-10 22:49:10 -06:00
|
|
|
"golang.org/x/tools/internal/lsp/protocol"
|
2020-04-02 15:27:48 -06:00
|
|
|
"golang.org/x/xerrors"
|
2020-03-10 22:49:10 -06:00
|
|
|
)
|
|
|
|
|
2020-04-27 15:00:34 -06:00
|
|
|
// GenerateWorkDoneTitle is the title used in progress reporting for go
|
|
|
|
// generate commands. It is exported for testing purposes.
|
|
|
|
const GenerateWorkDoneTitle = "generate"
|
|
|
|
|
2020-03-10 22:49:10 -06:00
|
|
|
func (s *Server) runGenerate(ctx context.Context, dir string, recursive bool) {
|
|
|
|
ctx, cancel := context.WithCancel(ctx)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
er := &eventWriter{ctx: ctx}
|
2020-04-22 12:41:03 -06:00
|
|
|
wc := s.newProgressWriter(ctx, cancel)
|
2020-03-10 22:49:10 -06:00
|
|
|
defer wc.Close()
|
|
|
|
args := []string{"-x"}
|
|
|
|
if recursive {
|
|
|
|
args = append(args, "./...")
|
|
|
|
}
|
|
|
|
inv := &gocommand.Invocation{
|
|
|
|
Verb: "generate",
|
|
|
|
Args: args,
|
|
|
|
Env: s.session.Options().Env,
|
|
|
|
WorkingDir: dir,
|
|
|
|
}
|
|
|
|
stderr := io.MultiWriter(er, wc)
|
|
|
|
err := inv.RunPiped(ctx, er, stderr)
|
2020-04-02 15:27:48 -06:00
|
|
|
if err != nil {
|
2020-05-04 13:12:06 -06:00
|
|
|
event.Error(ctx, "generate: command error", err, tag.Directory.Of(dir))
|
2020-04-02 15:27:48 -06:00
|
|
|
if !xerrors.Is(err, context.Canceled) {
|
|
|
|
s.client.ShowMessage(ctx, &protocol.ShowMessageParams{
|
|
|
|
Type: protocol.Error,
|
|
|
|
Message: "go generate exited with an error, check gopls logs",
|
|
|
|
})
|
|
|
|
}
|
2020-03-10 22:49:10 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ew *eventWriter) Write(p []byte) (n int, err error) {
|
2020-04-20 10:14:12 -06:00
|
|
|
event.Log(ew.ctx, string(p), tag.Operation.Of("generate"))
|
2020-03-10 22:49:10 -06:00
|
|
|
return len(p), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// newProgressWriter returns an io.WriterCloser that can be used
|
|
|
|
// to report progress on the "go generate" command based on the
|
|
|
|
// client capabilities.
|
2020-04-22 12:41:03 -06:00
|
|
|
func (s *Server) newProgressWriter(ctx context.Context, cancel func()) io.WriteCloser {
|
2020-03-16 23:06:19 -06:00
|
|
|
if s.supportsWorkDoneProgress {
|
2020-04-27 15:00:34 -06:00
|
|
|
wd := s.StartWork(ctx, GenerateWorkDoneTitle, "running go generate", cancel)
|
2020-04-22 12:41:03 -06:00
|
|
|
return &workDoneWriter{ctx, wd}
|
2020-03-16 23:06:19 -06:00
|
|
|
}
|
2020-04-22 12:41:03 -06:00
|
|
|
mw := &messageWriter{ctx, cancel, s.client}
|
|
|
|
mw.start()
|
|
|
|
return mw
|
2020-03-10 22:49:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2020-03-16 23:06:19 -06:00
|
|
|
// Request cancellation happens synchronously through
|
|
|
|
// the ShowMessageRequest response.
|
2020-03-10 22:49:10 -06:00
|
|
|
type messageWriter struct {
|
|
|
|
ctx context.Context
|
2020-03-16 23:06:19 -06:00
|
|
|
cancel func()
|
2020-03-10 22:49:10 -06:00
|
|
|
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",
|
|
|
|
})
|
|
|
|
}
|
2020-03-16 23:06:19 -06:00
|
|
|
|
2020-04-22 12:41:03 -06:00
|
|
|
// 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.
|
2020-03-16 23:06:19 -06:00
|
|
|
type workDoneWriter struct {
|
2020-04-22 12:41:03 -06:00
|
|
|
ctx context.Context
|
|
|
|
wd *WorkDone
|
2020-03-16 23:06:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (wdw *workDoneWriter) Write(p []byte) (n int, err error) {
|
2020-04-22 12:41:03 -06:00
|
|
|
return len(p), wdw.wd.Progress(wdw.ctx, string(p), 0)
|
2020-03-16 23:06:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
func (wdw *workDoneWriter) Close() error {
|
2020-04-22 12:41:03 -06:00
|
|
|
return wdw.wd.End(wdw.ctx, "finished")
|
2020-03-16 23:06:19 -06:00
|
|
|
}
|