You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Provide a way for debugger variables to include fields that are expensive/have side-effects and can be lazily evaluated #134450
Many languages have "properties" on classes that require executing code when executed. Users often want to see these in the debug views (Watch, Variables, Evaluation) but executing them can be expensive and/or produce side-effects.
In Dart, we evaluate these by default but have a setting to turn it off - however it's a little hard to discover this if you don't know where to look.
It would be nice if there was some way to return some "placeholder" variables from a variablesRequest that could require some user-action to evaluate. For example, given the code:
classPerson {
String name ="Danny";
Stringget email {
return"foo@example.bar";
}
}
final a =Person();
When evaluating a, I would like to be able to return a response that includes the value for name but a lazy placeholder for email that the user can trigger to get the value. It should look natural in the UI (eg. not wrapping the value for email in some dummy object):
a
name: Danny
email: (click to evaluate)
The "click to evaluate" could perhaps just trigger evaluation of the evaluateName on the variable?
This would also potentially allow common some well-known methods like toString() to be included in the list without having to execute them (but providing an easy/convenient way for the user to see them).
Danny Tuppeny (@DanTup) this feature can be easily implemented by representing an "expensive property" as a placeholder object that the user can "expand" to trigger the expensive evaluation.
(You've alluded to this approach already in your initial comment: "wrapping the value ... in some dummy object")
VS Code's built-in js-debug uses this approach for JS/TS getters. Here is an example:
The screencast clearly shows that the intermediate object is a bit noisy and makes it difficult to grasp the effective data structure. But I still think that the approach is the best what a debug adapter can achieve today within DAP's constrains and existing client UIs.
Proposal:
Instead of inventing new DAP requests I suggest that we try to find a minimal addition (e.g. "a presentation hint") to the variables request so that a client can eliminate the intermediate object from the UI. This will allow existing implementations to profit from a new clean UI without code changes and new implementations can continue to use variables requests and don't have to deal with a new DAP concept.
Some details:
a new boolean property lazy on the Variable indicates that the client should create a "placeholder" UI element for it.
a new "placeholder" UI element shows an ellipsis ('…') on the right hand side of the value (instead of an expander arrow, aka "twisty"). Clicking on the ellipsis triggers the "lazy loading" by issuing a variables request for the "lazy" variable.
a "lazy" variable is expected to return only a single child variable which is used to replace the properties of the placeholder. As a consequence the ellipsis disappears and a twisty is shown if the variable has children.
if a client does not understand "lazy" variables, it will continue to work and shows an intermediate variable like js-debug today.
We can use VariablePresentationHint to add a lazy property to a Variable. But how to send a second Variables request for the "lazy" variable? I don't see that current Variables request allows to add extra property.
Does the user have to click twice to expand the children of a lazy variable? Click once to fetch lazy part, click twice to expand its children. If so, that seems inconvenient. Can we just clicking on ellipsis (it could become a button to be more visible) to fetch lazy part?
the debug adapter has to introduce an intermediate "lazy" placeholder variable (as it is done already in js-debug). Today this indirection will be visible in the UI (see screen recording from above). By adding the lazy property to that placeholder, a conforming client (e.g. VS Code) will hide the placeholder in the UI as soon as the lazy operation has returned a result.
no, only a single "lazy" click is needed: if the result is a simple variable its value will be shown in the placeholder. If the result is a structured variable its value will be shown in the placeholder and its children will be added expanded under the placeholder. Or in other terms: after the "lazy" click, the placeholder is replaced by the result of the "lazy" evaluation.
The "lazy" presentationHint sounds good, and benefits from being backwards and forwards-compatible.
We can also then move the debug.javascript.autoExpandGetters to be some debug.autoExpandLazy setting. We added that since quite a few users complained when we started requiring the second click.
My thinking is that if I took a specific user action to access that property - adding $lazyInteger to a watch, debug console, or on hover, it would show the value immediately and it would not be lazy. But in the variables view which exists without my interaction, they would be lazy. This is actually what I see in chrome devtools. All of these non-lazy methods go through evaluate. (In watch/console/hover, a lazy property in an object that I expand would still have the lazy behavior, I'm just talking about the top level.)
This means that the lazy property on EvaluateResponse would be ignored. That doesn't seem right. Assuming we wouldn't change the DAP, then it seems like the right solution would be to allow a lazy EvaluateResponse but then have the client resolve it immediately.
Actually, presentationHint on EvaluateResponse is currently not referenced anywhere in vscode.
I think this is probably the right thing to do - render them with whatever placeholder value the DA sends. Not sure of a better way to handle it, but I can make them refresh when the value is expanded in the variables view, at least
On the other hand when you send an "evaluate" request for foo.lazy, it makes sense that the client wants the real value at that moment, and it would probably make some sense to not allow lazy variables returned in an EvaluateResponse if we want to go that route.
My thinking is that if I took a specific user action to access that property - adding $lazyInteger to a watch, debug console, or on hover, it would show the value immediately and it would not be lazy.
yes, correct.
But in the variables view which exists without my interaction, they would be lazy.
yes. And the same applies to non-top-level lazy properties of watch/console/hover too.
This means that the lazy property on EvaluateResponse would be ignored. That doesn't seem right. Assuming we wouldn't change the DAP, then it seems like the right solution would be to allow a lazy EvaluateResponse but then have the client resolve it immediately.
yes, a debug adapter can either return a "lazy" top-level variable from the evaluate request and VS Code will resolve that immediately for all predefined contexts ('watch', 'repl', 'hover', 'clipboard'), or the debug adapter can return the variable without a "lazy" intermediary (in which case VS Code does not have to resolve anything).
inline hints ... render them with whatever placeholder value the DA sends. Not sure of a better way to handle it, but I can make them refresh when the value is expanded in the variables view, at least
yes, if a "lazy" var has a (placeholder) value, we could show that. And if the value is empty we would omit the lazy var from the inline values. Alternatively we could always omit lazy vars from the inline values completely to avoid the visual noise...
Refreshing inline values on expand in the variables view is "a nice to have" only if we show the placeholder value.
Activity
weinand commentedon Nov 17, 2021
Danny Tuppeny (@DanTup) this feature can be easily implemented by representing an "expensive property" as a placeholder object that the user can "expand" to trigger the expensive evaluation.
(You've alluded to this approach already in your initial comment: "wrapping the value ... in some dummy object")
VS Code's built-in js-debug uses this approach for JS/TS getters. Here is an example:
... and here how it looks and behaves:
The screencast clearly shows that the intermediate object is a bit noisy and makes it difficult to grasp the effective data structure. But I still think that the approach is the best what a debug adapter can achieve today within DAP's constrains and existing client UIs.
Proposal:
Instead of inventing new DAP requests I suggest that we try to find a minimal addition (e.g. "a presentation hint") to the
variablesrequest so that a client can eliminate the intermediate object from the UI. This will allow existing implementations to profit from a new clean UI without code changes and new implementations can continue to usevariablesrequests and don't have to deal with a new DAP concept.Some details:
lazyon theVariableindicates that the client should create a "placeholder" UI element for it.variablesrequest for the "lazy" variable.Connor Peet (@connor4312) roblourens what do you think?
DanTup commentedon Nov 17, 2021
Andre Weinand (@weinand) that approach sounds good to me :-)
testforstephen commentedon Nov 18, 2021
We can use VariablePresentationHint to add a
lazyproperty to a Variable. But how to send a secondVariablesrequest for the "lazy" variable? I don't see that current Variables request allows to add extra property.Does the user have to click twice to expand the children of a lazy variable? Click once to fetch lazy part, click twice to expand its children. If so, that seems inconvenient. Can we just clicking on ellipsis (it could become a button to be more visible) to fetch lazy part?

weinand commentedon Nov 18, 2021
Jinbo Wang (@testforstephen) thanks for your great questions:
lazyproperty to that placeholder, a conforming client (e.g. VS Code) will hide the placeholder in the UI as soon as the lazy operation has returned a result.connor4312 commentedon Nov 18, 2021
The "lazy" presentationHint sounds good, and benefits from being backwards and forwards-compatible.
We can also then move the
debug.javascript.autoExpandGettersto be somedebug.autoExpandLazysetting. We added that since quite a few users complained when we started requiring the second click.weinand commentedon Nov 29, 2021
We now have a plan how to implement this. But the implementation will is planned for an upcoming milestone.
7 remaining items
roblourens commentedon Feb 19, 2022
My thinking is that if I took a specific user action to access that property - adding
$lazyIntegerto a watch, debug console, or on hover, it would show the value immediately and it would not be lazy. But in the variables view which exists without my interaction, they would be lazy. This is actually what I see in chrome devtools. All of these non-lazy methods go throughevaluate. (In watch/console/hover, a lazy property in an object that I expand would still have the lazy behavior, I'm just talking about the top level.)This means that the
lazyproperty on EvaluateResponse would be ignored. That doesn't seem right. Assuming we wouldn't change the DAP, then it seems like the right solution would be to allow a lazy EvaluateResponse but then have the client resolve it immediately.Actually,
presentationHinton EvaluateResponse is currently not referenced anywhere in vscode.roblourens commentedon Feb 19, 2022
For the inline hints
I think this is probably the right thing to do - render them with whatever placeholder value the DA sends. Not sure of a better way to handle it, but I can make them refresh when the value is expanded in the variables view, at least
roblourens commentedon Feb 21, 2022
On the other hand when you send an "evaluate" request for
foo.lazy, it makes sense that the client wants the real value at that moment, and it would probably make some sense to not allow lazy variables returned in anEvaluateResponseif we want to go that route.weinand commentedon Feb 21, 2022
yes, correct.
yes. And the same applies to non-top-level lazy properties of watch/console/hover too.
yes, a debug adapter can either return a "lazy" top-level variable from the
evaluaterequest and VS Code will resolve that immediately for all predefined contexts ('watch', 'repl', 'hover', 'clipboard'), or the debug adapter can return the variable without a "lazy" intermediary (in which case VS Code does not have to resolve anything).yes, if a "lazy" var has a (placeholder) value, we could show that. And if the value is empty we would omit the lazy var from the inline values. Alternatively we could always omit lazy vars from the inline values completely to avoid the visual noise...
Refreshing inline values on expand in the variables view is "a nice to have" only if we show the placeholder value.
Support lazy resolving for variable view #134450
weinand commentedon Mar 1, 2022
Danny Tuppeny (@DanTup) the February release has a first cut of a "lazy variables" feature.
It would be great if you could give it a try and provide feedback.
What you have to do is explained here.
DanTup commentedon Mar 2, 2022
Andre Weinand (@weinand) nice! I'll try to take a look soon, thanks!