-
Notifications
You must be signed in to change notification settings - Fork 30.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Notebook "variable provider" API #166296
Comments
Because I noticed above the We always tried to avoid "polluting" the extension API (i.e. "vscode.d.ts") with DAP types. So we neither import DAP as a whole, nor do we copy specific DAP types into "vscode.d.ts". Since the Variable provider API must work outside of debug session, the DAP types are not that important. |
I agree with that, I think that I should not worry about matching DAP types but design something that makes sense in our extension API. I also didn't realize that we have a way to map from the extension API breakpoint to a DAP breakpoint: https://github.com/w9jds/vscode/blob/e31458e5c673dcac1c6b4dcf1d80abda9544d458/src/vscode-dts/vscode.d.ts#L14567 so if there needs to be interop, I would be using a pattern like that. Notes from the API call
|
Note for the future- microsoft/vscode-jupyter#5626 shows why scopes shouldn't be left out if users will look at this while debugging |
Here's an updated sketch of a potential API after discussions way back in November. Things to point out We discussed how you can know how many array items/child props/variables are in the current set, without totally exhausting the AsyncIterable, and we discussed having the iterator return variable counts along with the variable. That is shown below. It works but maybe it's odd that I can't know how many children a We also have this UI for paging large arrays, which is driven by DAP asking for N variables at some index. I'd like to be able to support the same UI in the variables view. The AsyncIterator doesn't let us do random access. An idea I have for this is to ask for an AsyncIterator that starts at a particular index, that is shown below. It turns out... debugpy doesn't actually implement this in DAP- microsoft/debugpy#1179 but I am optimistic that they will. And another possible concern is that the AsyncIterator could lead to perf issues. To resolve one variable, I have to eval some python code which takes time. If we iterate 10 times, my obvious implementation might eval python code 10 separate times. I guess this is still similar to how export interface NotebookController {
/** Set this to attach a variable provider to this controller. */
variableProvider?: NotebookVariableProvider;
}
enum VariablesRequestKind {
Named,
Indexed
}
interface VariablesResult {
variable: Variable;
namedVariableCount: number;
indexedVariableCount: number;
}
interface NotebookVariableProvider {
/** VS Code decides when to call the provider due to execution and variable view visibility. But if other things can change variables (interactive output widgets, background tasks...?) the provider needs to signal a change. */
onDidChangeVariables: Event<void>;
/** When variablesReference is undefined, this is requesting global Variables. When a variable is passed, it's requesting child props of that Variable. */
getChildren(variable: Variable | undefined, kind: VariablesRequestKind, start: number): AsyncIterable<VariablesResult>;
}
// This is the minimum, and omits stuff that could go on the variable, that I don't want to focus on now
interface Variable {
/** The variable's name. */
name: string;
/** The variable's value.
This can be a multi-line text, e.g. for a function the body of a function.
For structured variables (which do not have a simple value), it is recommended to provide a one-line representation of the structured object. This helps to identify the structured object in the collapsed state when its children are not yet visible.
An empty string can be used if no value should be shown in the UI.
*/
value: string;
/** The type of the variable's value */
type?: string;
/** The variable size, if defined for this type (array length, dimensions of data frame) */
size?: string;
} |
For hover and inline values, my notes in the OP still hold. Will add that for hover, it would be better to be able to evaluate an expression rather than trying to look up an expression value in the variables list. I don't know where to fit in a new export interface NotebookController {
evaluate?: (expression: string) => Promise<Variable | undefined>;
} |
Notes from API call
|
My initial point of view is that we should try to consider sticking with DAP if possible. Namely, we could have a DAP 'connection' that acts like a debug session, except that there's no user control exposed to pause/set breakpoints/etc. I think this has several good benefits:
Having a tidy extension API for this is nice, but aside from that, is there anything we want to do for notebooks that DAP can't do? If so, are those things out of scope for DAP, or things it would benefit from having? |
I experimented with exactly this one year ago and built a hacked up DA that delivers kernel variables. I decided it wasn't the right direction, and here are some reasons from my notes
A notebook variables API is definitely going to have similarities to DAP, and from a UI perspective, I'm not opposed to something that uses the debug view. We can separate the UI discussion from the API side of it, because you could have vscode call a variables API and show this as if it were a special debug session internally. It's simpler to have vscode control the lifecycle too, rather than the extension figuring out that its notebook controller is active, so it should start a debug session, vscode can decide this and enforce it. And we won't be the only implementor of notebook variables and I think that having to implement a DA in this special way is just a big ask. |
Yea, I we could 'translate' to DAP under the hood or something like that for the limited purpose of reusing the Variables view and other debug components. I still wonder if there's a way we can let visualization extensions in on the action, as in my linked issue. With that issue, the hex editor would also move to using the standard visualization API -- but that API necessarily gives a handle to the DebugSession to the extension, which would not exist for a purely API-based notebook experience. It would be nice for, if/when equivalent read/writeMemory calls are available on the notebook API, the extension could plug into that. |
After syncing with Connor, we'll plan on using the nice and simple provider API for extensions to implement, and the most likely use that within vscode core to create a partial implementation of a DebugSession that is specific to variables so that we can hopefully reuse the components that work with that class. |
@roblourens - I want to make sure I'm not missing something, but with the current implementation, one |
Yeah, that is missing, I think taking a |
Also, referring to a variable solely by name isn't going to cut it when asking for the children. Names won't be unique since they can just be the child property of an object, with a shared name for every instance of that object (e.g. We'll likely need an ID in the |
But you're only asking for the children of one variable, so those would be separate variables. They just need to be unique under one parent. |
OK, I misunderstood the intent there - I wasn't thinking of actually holding references to those objects, but that makes more sense than creating the full variable object when only the name used to look up the variable. |
I made a couple changes in the attached PR:
|
We will probably want to do an enum just for consistency in extension API. I think they are nice as an extension developer because you can just import and use this value rather than introducing a bunch of strings in your source that can't be minified. |
Great to see progress here! One question: for the Julia extension it is quite key that the variable explorer API is not exclusively tied to notebooks. We also show a variable explorer for REPL instances that are launched from the extension. If we were to end up with a variable explorer implementation that only works for notebook kernels, we would probably have to stick with our custom variable explorer implementation so that we can continue to support that scenario... The details of this are here. As far as I can see, though, the current API seems to link this super tightly to notebooks? Is that right? Is that something that could still be changed? |
Good to know, and thanks for bringing that up again. The python extension is also eager to use the variable view for their REPL as well, so I've been trying to think of how to alter things to fit that in. We might be able to let extensions associate a variable provider for a particular file as well somehow... |
And from my example that I showed in the linked issue, we also at some point might want to add a variable explorer to the processes that run tests. I still kind of like the UI that we use in the Julia extension, where the top level node in the tree can be all sorts of things: notebooks, REPLs, test processes, or something entirely different. I think in my ideal world the API that VS Code has would support that kind of model. |
yes, I think we'll have to have that top level grouping if variables could be provided for multiple components that could all be active. |
I haven't followed the development of this feature over the last months, but still interested in integrating the Julia extension with this. Over at #165445 (comment) @amunger mentioned that the Python native REPL now also integrates with this, does this mean there is now a way in the API to provide variables for a process that has nothing to do with notebooks? The API at the top of this post doesn't look like it, but I'm also not sure whether it is being updated to reflect the latest state of things. |
The native REPL actually has a notebook in the backend to handle the execution history and execution through a notebook controller. So the variable provider is still registered through a notebook controller in that case. There is another API being developed for the native REPL editor #154983. |
To enable notebook kernels to show variables in a builtin variable viewer for #165445, we need an API for variable values to be reported to vscode. There's a lot of similarity between debugger variables and notebook variables, so the API should be compatible with DAP. If debug variables get any exposure in our extension API, it would share types with notebook variables.
Notes
EvaluatableExpressionProvider
from an extension, or by using a slightly hacky regexa.b.c
, which is hackyEvaluatableExpressionProvider
but calling a method likeevaluate
is tough because Jupyter can't respond to an evaluate while a cell is running except in a debug session, and we won't start a debug session unless the user is actually debuggingInlineValuesProvider
(or again, hacky fallback), and again, looking up a variable with that name in the current stackFramePossible future work
vscode/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts
Line 1352 in b4fbff3
vscode/src/vs/workbench/contrib/debug/common/debugProtocol.d.ts
Line 1197 in b4fbff3
The text was updated successfully, but these errors were encountered: