// 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 }