microsoft / vscode

Visual Studio Code
https://code.visualstudio.com
MIT License
164.5k stars 29.39k forks source link

Implement InlineValueProvider for TypeScript #119489

Closed bpasero closed 3 years ago

bpasero commented 3 years ago

I debug a test and get this:

image

Lots of inline debug values from a different unrelated test method.

Compared to devtools:

image

isidorn commented 3 years ago

For this we would need the js-debug to use the inline value provider API created by @weinand Thus forwarding to @weinand and @connor4312

connor4312 commented 3 years ago

Hm, we could parse the source and be smarter about only showing values in current and ancestor scopes.

However this requires parsing TypeScript. Several months ago I actually moved away from using typescript to parse ASTs in favor of Acorn since it's much smaller and faster, and we did not actually need to parse TypeScript in the debugger. Maybe this is something that could live in the TypeScript language features since it already has the typescript package available. I would still be happy to help author it, thoughts @mjbvz?

weinand commented 3 years ago

Yes, that's a good example where VS Code's built-in "inline values" support breaks down. Especially showing the function's source as its inline value is not really that smart ;-) (see #113987)

@isidorn yes, the now finalised "inline values provider" could help here but that would not provided by "js-debug" but by the TypeScript extension because "inline values provider" are registered for a language not a debugger (as @connor4312 already alluded to).

@connor4312 it would be excellent if you could help author it.

mjbvz commented 3 years ago

@connor4312 Should this come from the TS server? The typescript extension does not do any ast processing (and may not easily be able to since we try to strip out most all of the TypeScript files not related to tsserver)

weinand commented 3 years ago

Yes, if a language extension has no direct access to an AST, then it should delegate this functionality to a language server (e.g. by introducing a LSP extension).

mjbvz commented 3 years ago

We have this larger issue tracking debug asks from the TS team: #88857

@connor4312 If you can put together a short proposal on what you'd need specifically to implement this—basically here's the information we'll send over, what we need back, and a few illustrative examples of the expected behavior—I can try to get it scheduled for the next typescript version

connor4312 commented 3 years ago

Sure:

The InlineValuesProvider API allows languages to provide hints to the debugger what expressions or values to evaluate when debugging, showing them inline as Ben posted. VS Code has a built-in heuristic for these, but languages can provide them more intelligently.

Specifically, for JavaScript, I think the following behaviors would be a good starting point, feel free to add/remove from these suggestions:

The debugger will not cause side-effects during evaluation, so you do not need to be concerned about that. Most of these would go through InlineValueEvaluatableExpression, but should use InlineValueVariableLookup when possible.

I think I will later try to do some magic in js-debug so that evaluatable expressions are always invoked on the right call frame--since V8 optimizes away identifiers that are declared in parent scopes but unused in a child scope, calling everything on the top call frame may not always work.

connor4312 commented 3 years ago

Giving this to you, Matt, since I don't think there's any work to be done on the debug front for now

weinand commented 3 years ago

@connor4312 great proposal, thanks a lot! And if additional information is needed on the InlineValuesContext object or additional parameters on the InlineValueEvaluatableExpression object, just let me know. Those types can be extended easily.

mjbvz commented 3 years ago

@connor4312 Thanks. Could you put together a few illustrative examples for the TS team. For a given code snippet for example, here's the complete list of inline values that should be returned.

Also how will source mapping work with all this? Should TS be talking about ranges/vars in the original TS file? Or would this only be run on compiled JS?

connor4312 commented 3 years ago

Could you put together a few illustrative examples for the TS team. For a given code snippet for example, here's the complete list of inline values that should be returned.

function double(n: number) { // eval('double') -> double()
  return n * 2;
}

let x = 1; // InlineValueVariableLookup for x here

if (x === 2) { // eval(x === 2) -> false
  x *= 2; // this line should NOT be evaluated since it's not the scope or its parents
}

if (x === 1) { // eval(x === 1) -> true
  x *= 2; // InlineValueVariableLookup for x here
  const y = {
    z: x ** 2, // eval(x ** 2) -> 16
  };

  console.log(x); // <-- paused here
}

Also how will source mapping work with all this? Should TS be talking about ranges/vars in the original TS file? Or would this only be run on compiled JS?

They should report this information based on whatever file VS Code requests -- which will be the file the debugger is paused in. They don't have to do anything special to deal with sourcemaps/ranges. I don't think js-debug will either, but if it does then I'll take care of it as the TS language server is unaware of sourcemaps.

mjbvz commented 3 years ago

Opened https://github.com/microsoft/TypeScript/issues/43449 to track this upstream

@connor4312 Can you please answer any of the TypeScript team's question about the expected behavior

mjbvz commented 2 years ago

@connor4312 I'm assigning you since we decided this would need to come through a TypeScript plugin (which I think we'd have to develop)