Repository navigation
Proposal: Progress Events #92
Description
We propose that adapters can send progress events to a fronted, in order to display progress related UI to a user in the case of long running tasks on the adapter side. Specific examples of long running tasks that an adapter may want to report progress for include symbol loading and expression evaluation.
Progress events may be generic (unrelated to a specific request) or specific (related directly to a request). This allows frontends to organize incoming progress events and directly show progress UI or defer progress information to API related to an original request.
- An example of a generic progress event is automatic symbol loading progress, as this can happen any time a target process is running
- An example of a specific progress event is loading symbols due to a LoadSymbols request, as this action is taken as a direct cause of a request.
Progress events are grouped using a unique id. This allows adapters to report progress for a long running task and frontends to group progress events together.
Progress events can be cancellable and are cancelled by extending the existing CancelRequest.
/** Event message for 'progress' event type
* The event indicates that a long running task has progress to report that can be displayed to the user.
* Multiple progress events with the same id represent a single logical progrses operation that can be updated.
* Sequence of events is as follows (for a given id):
* - adapter sends 1 progress event of category 'new'
* - adapter sends 0 or more progress events of category 'update'
* - adapter sends 1 progress event of category complete
* After this the adapter cannot send any more progress events with the same id
*
* Progress events may be generic (not related to a specific request) or be specific to a request id
*
* Progress events may be cancellable. To cancel an operation that is reporting progress, a Cancel request is sent specifying the progressId to cancel.
*/
export interface ProgressEvent extends Event {
body: {
/**
* The unique id for this progress operation.
*/
progressId: number;
/** The request id that this progress event is related to, if any */
requestId?: number;
/** The progress category.
* Values: 'new', 'update', 'complete'
*/
category: string;
/** If cancellable is true, the task reporting progress may be canceled with a CancelRequest. If not present, assumed false */
cancellable?: boolean;
/** If 'category' is 'complete', this field may be present to report whether the completed progress task was cancelled. If not present, assumed false */
cancelled?: boolean;
/** A description of the progress event to display to the user; used to explain the long running task.
* This field should be present when category is 'new' or 'update'
*/
description?: string;
/** The current step of the progress task, 0 indexed. This field combined with 'totalSteps' can be used to report a percentage progress for the long running task.
* If both 'currentStep' and 'totalSteps' are present and totalSteps is not 0, these fields represent a percentage progress where percent is 'currentStep' / 'totalSteps'
* If both 'currentStep' and 'totalSteps' are present and are 0, the progress is 'indeterminate', i.e. an indeterminate progress bar may be shown
* If either or both 'currentStep and 'totalSteps' are not present, their is no numeric progress to report and only the 'description' should be used.
*/
currentStep?: number;
/** The total number of steps this long running task will require. This field combined with 'currentStep' can be used to report a percentage progress for the long running task.
*
*/
totalSteps?: number;
}
}
export interface CancelRequest extends Request {
...
/** The progressId of a cancellable ProgressEvent */
progressId?: number;
}CC: Andrew Crawley (@andrewcrawley) Gregg Miskelly (@gregg-miskelly)
Activity
gregg-miskelly commentedon Jan 17, 2020
A few small suggestions:
andrewcrawley commentedon Jan 17, 2020
Re: Gregg's comments:
updatemessage would only update the fields for which values were provided, so thedescriptionwould remain the same if it wasn't specified. However, this would make it kind of weird if you wanted an update to remove thedescription- I guess you'd have to pass an empty string instead of NULL?gregg-miskelly commentedon Jan 18, 2020
withProgresson this page), I believe they use both a title string (set through ProgressOptions), and a progress description string (set through theincrementcallback). Apologies in advance if I am misunderstanding how that API works.weinand commentedon Feb 21, 2020
Chuck Ries (@chuckries) thanks for this great proposal (and sorry for the long time it took me to respond to it).
My comments:
Based on these comments, I've created a variant of your proposal.
First the client capability (the client frontend signals to the DA that progress reporting is supported):
And now the three events:
/cc Gregg Miskelly (@gregg-miskelly) Andrew Crawley (@andrewcrawley) Pierson Lee (@pieandcakes) Danny Tuppeny (@DanTup) Don Jayamanne (@DonJayamanne) Holger Benl (@hbenl)
Isidor Nikolic (@isidorn) please provide feedback from the client implementation perspective
DanTup commentedon Feb 24, 2020
This sounds great to me! In Dart we already have a number of custom events sent from the debugger back to the extension to do just this, but ultimately I'd like the DA to be usable outside of Code, so the more VS Code-specific bits that move to standards, the better :-)
I presume it's ok for
progressIdto be reused (eg. once a progress completed, reusing the same ID again for a new progress later in the same session is fine)?connor4312 commentedon Feb 24, 2020
This is perfect for pretty printing in js-debug as well, as formatting large minified scripts can take several seconds. That latest proposal would work well for us.
DonJayamanne commentedon Feb 24, 2020
If I'm not mistaken this new API allows the debugger to add progress messages. I.e. this isn't about just cancellable progress messages anymore, it's more generic.
chuckries commentedon Feb 25, 2020
Andre Weinand (@weinand) Thank you for the feedback! I appreciate your changes to the proposal. I am currently implementing this in the C# Extension for VS Code Debug Adapter. I will incorporate your changes into that implementation and I will report back as I discover issues and/or have new ideas/insights.
isidorn commentedon Feb 25, 2020
Andre Weinand (@weinand) from the client implementation side this proposal makes perfect sense.
However please note that VS Code does not support to show progress perecentage-wise. We always show an infinite progress bar. So the inital implementation on the VS Code side would not respect the
percentageparameter. However if in the future we support percentage progress in vscode in general we could easily adopt this for debug.Also note that we can show progress in various places, for example debug viewlet or general workbench progress. I think we would noeed to decide on the location, or the event would have to specify this somehow.
And also we can not support two progresses in one location at the moment. So for example two workbench progress would be shown as running as long as one of them is still running.
chuckries commentedon Feb 25, 2020
Andre Weinand (@weinand) in case I wasn't clear, I suggest we don't commit this to the official protocol until I am done prototyping in the C# extension. Visual Studio's VS Code Debug Adapter Host is our target frontend for this.
connor4312 commentedon Feb 25, 2020
Yea, a way to provide priority or location for the progress may be useful. For instance, using the js-debug pretty printing example, in most cases it will take only a second or two so showing a notification would be a bit noisy, a loading indicator atop the debug view may be more appropriate. But for longer running tasks--like a hypothetical world we can offer to install Edge for the user if they don't have it already available--a notification would be appropriate. Maybe a property like
display: 'quiet' | 'verbose'could be used to indicate that.weinand commentedon Mar 11, 2020
Thanks a lot for the feedback.
Here are some replies:
cancelrequest. Progress events participate in "cancelability" through thecancellableandrequestIdproperties. To make the purpose of the proposal clearer, I will remove the word "Cancellable" from the issue's title.[-]Proposal: Cancellable Progress Events[/-][+]Proposal: Progress Events[/+]chuckries commentedon Mar 11, 2020
Andre Weinand (@weinand) One part of my original proposal that I do not see in your revisions is the ability to cancel based on progressId (as opposed to the protocol sequence id). This is important to us for cancelling "generic" progress events that are not tied to a specific request. Specifically we need this for cancelling automatic symbol loading that occurs while the target process is running.
My original proposal was to extend the CancelRequest to add an optional progressId field in order to cancel an outstanding progress operation. My idea would look something like this in protocol:
Did you have another idea for cancelling generic progress? Or can we amend your proposal to include the extension to CancelRequest?
weinand commentedon Mar 12, 2020
Chuck Ries (@chuckries) yes, that was an oversight on my part.
I think we can go with your proposal to add an additional attribute
progressIdon thecancelrequest (despite the fact that initially I thought that's a bad idea).Here is why:
I was under the assumption that the
cancelrequest already had a mandatoryrequestIdargument. So adding an alternative optionalprogressIdargument would result in the awkward situation that a caller who wants to cancel a progress, would have to pass some bogus value for the mandatoryrequestIdargument.But then I checked the spec and noticed that there is actually a misspelling that prevents the
requestIdattribute from becoming mandatory.Due to this we are shipping the spec with an optional
requestIdargument (and the comment does not mention that the argument is mandatory either).Given these lucky circumstances I propose the following:
requestIdoptional but explain this fact in the description.progressIdattribute of type string.progressIdcan now be used in a separatecancelrequest. By not reusing progress Ids we can eliminate the risk that the client (frontend) cancels the wrong progress due to race conditions.weinand commentedon Mar 12, 2020
This commit shows the (TypeScript) changes for the (experimental) progress support.
add progress support; see microsoft/debug-adapter-protocol#92
weinand commentedon Mar 23, 2020
I've released the DAP progress feature (and VS Code Insiders provides the UI for it; see microsoft/vscode#92253).
gjsjohnmurray commentedon Mar 23, 2020
Thanks Andre Weinand (@weinand)
This para of the doc seems to have a redundant line break:
weinand commentedon Mar 23, 2020
John Murray (@gjsjohnmurray) thanks for letting me know. There are more than one of these issue in the doc. I'll have to fix the Markdown generator...
chuckries commentedon Mar 25, 2020
Andre Weinand (@weinand) I missed this initially but what was the purpose of making progressId a string as opposed to an int?
weinand commentedon Mar 26, 2020
We've started to represent IDs as strings because it makes it easier to use UUIDs.