blob: c5e70cf903483fe7f9c2330272ab323592bc44c2 [file] [log] [blame]
// 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"
"errors"
"math/rand"
"strconv"
"golang.org/x/tools/internal/event"
"golang.org/x/tools/internal/lsp/protocol"
)
// WorkDone represents a unit of work that is reported to the client via the
// progress API.
type WorkDone struct {
client protocol.Client
startErr error
token string
cancel func()
cleanup func()
}
// StartWork creates a unique token and issues a $/progress notification to
// begin a unit of work on the server. The returned WorkDone handle may be used
// to report incremental progress, and to report work completion. In
// particular, it is an error to call StartWork and not call End(...) on the
// returned WorkDone handle.
//
// The progress item is considered cancellable if the given cancel func is
// non-nil.
//
// Example:
// func Generate(ctx) (err error) {
// ctx, cancel := context.WithCancel(ctx)
// defer cancel()
// work := s.StartWork(ctx, "generate", "running go generate", cancel)
// defer func() {
// if err != nil {
// work.End(ctx, fmt.Sprintf("generate failed: %v", err))
// } else {
// work.End(ctx, "done")
// }
// }()
// // Do the work...
// }
//
func (s *Server) StartWork(ctx context.Context, title, message string, cancel func()) *WorkDone {
wd := &WorkDone{
client: s.client,
token: strconv.FormatInt(rand.Int63(), 10),
cancel: cancel,
}
if !s.supportsWorkDoneProgress {
wd.startErr = errors.New("workdone reporting is not supported")
return wd
}
err := wd.client.WorkDoneProgressCreate(ctx, &protocol.WorkDoneProgressCreateParams{
Token: wd.token,
})
if err != nil {
wd.startErr = err
event.Error(ctx, "starting work for "+title, err)
return wd
}
s.addInProgress(wd)
wd.cleanup = func() {
s.removeInProgress(wd.token)
}
err = wd.client.Progress(ctx, &protocol.ProgressParams{
Token: wd.token,
Value: &protocol.WorkDoneProgressBegin{
Kind: "begin",
Cancellable: wd.cancel != nil,
Message: message,
Title: title,
},
})
if err != nil {
event.Error(ctx, "generate progress begin", err)
}
return wd
}
// Progress reports an update on WorkDone progress back to the client.
func (wd *WorkDone) Progress(ctx context.Context, message string, percentage float64) error {
if wd.startErr != nil {
return wd.startErr
}
return wd.client.Progress(ctx, &protocol.ProgressParams{
Token: wd.token,
Value: &protocol.WorkDoneProgressReport{
Kind: "report",
// Note that in the LSP spec, the value of Cancellable may be changed to
// control whether the cancel button in the UI is enabled. Since we don't
// yet use this feature, the value is kept constant here.
Cancellable: wd.cancel != nil,
Message: message,
Percentage: percentage,
},
})
}
// End reports a workdone completion back to the client.
func (wd *WorkDone) End(ctx context.Context, message string) error {
if wd.startErr != nil {
return wd.startErr
}
err := wd.client.Progress(ctx, &protocol.ProgressParams{
Token: wd.token,
Value: protocol.WorkDoneProgressEnd{
Kind: "end",
Message: message,
},
})
if wd.cleanup != nil {
wd.cleanup()
}
return err
}