Skip to content
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

Extension API to Access ExceptionWidget (or ZoneWidget) #140752

Open
ericdrobinson opened this issue Jan 15, 2022 · 18 comments
Open

Extension API to Access ExceptionWidget (or ZoneWidget) #140752

ericdrobinson opened this issue Jan 15, 2022 · 18 comments
Assignees
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality
Milestone

Comments

@ericdrobinson
Copy link

As an extension developer, I need a way to report exceptions outside of a Debug Session context. This occurs because the extension I'm working on offers a command to evaluate a script in a host engine directly (no Debug Session necessary). When an error occurs during evaluation, I would like to present the error to the user in a native manner that uses the same design language that VSCode users are accustomed to. Currently, this means using the debug ExceptionWidget.

I have evaluated the following options:

  • Diagnostics - While this is visually close to the desired effect, the UX is fundamentally different. As an LSP feature, it expects the server to constantly keep the system updated based on status. The UX I'm looking for here would involve someting temporary that the user could manually close with a button in, say, the top right of the message portion.
  • Comment - Presumably this API is used by extensions like CodeTour and enables in-line comment threading. (It is difficult to say, really, because I can't find an official example in VSCode's documentation.) The UX for this (as I understand it) is also not a good fit as there isn't a way to format this comment to appear as an error.
  • TextEditorDecorationType - This actually gets us closest to what we want to provide as an experience to our users. We can use this feature to adjust the color of the background to the debugExceptionWidget.background theme color and provide a "hover message". While there's no "close" button, the decoration does automatically clear when the TextEditor to which it is applied becomes a background tab. That said, this is still a poor-person's approximation of the excellent Debug Exception Widget UI/UX and I would very much prefer using it instead of managing this kludge myself.

As mentioned above, the closest we're currently able to get is by using the TextEditorDecorationType which gets us something that looks like this:

image

However, I think we can all agree that this is a far cry from the target of:

image

To that end, it would be very helpful to extension developers to add APIs to either control the ExceptionWidget directly or to create our own custom ZoneWidget instances.

@ericdrobinson ericdrobinson changed the title Extension API to Access ExceptionWidget Extension API to Access ExceptionWidget (or ZoneWidget) Jan 15, 2022
@weinand weinand assigned jrieken and unassigned weinand Jan 15, 2022
@jrieken jrieken removed their assignment Jan 17, 2022
@weinand
Copy link
Contributor

weinand commented Jan 17, 2022

@ericdrobinson there are no plans to offer debug UI widgets (e.g. the ExceptionWidget) outside of debug sessions.

I see two workarounds:

  • we add API for creating custom ZoneWidget instances (@jrieken is this realistic?)
  • you start to use a debug session but configure it for a "simple" UI (DebugSessionOptions.debugUI.simple) so that the debug toolbar will not be shown for this session, the window statusbar color will not be changed, and the debug viewlet will not be automatically revealed.

/cc @roblourens please comment if you see more options.

@weinand weinand added debug Debug viewlet, configurations, breakpoints, adapter issues and removed triage-needed labels Jan 17, 2022
@jrieken
Copy link
Member

jrieken commented Jan 17, 2022

Arbitrary zone widgets are tracked here: #3220

@ericdrobinson
Copy link
Author

ericdrobinson commented Jan 17, 2022

Arbitrary zone widgets are tracked here: #3220

@jrieken I had seen that issue before, but felt that it was a bit.... all-over-the-place and wasn't sure that my use case really fit with the generic problem-set the request was proposing to solve. The term "Zone Widgets" doesn't even show up and "Peek" is its own thing. (Thanks to your link, the term "ZoneWidget" does now appear on that page [back-link to this request], but I'm not sure that GitHub's index system includes API links in searches...)

@weinand Understood and reasonable. That said, our goal is to at least emulate VSCode design language for that UX. Also, with respect to this:

you start to use a debug session but configure it for a "simple" UI (DebugSessionOptions.debugUI.simple) so that the debug toolbar will not be shown for this session, the window statusbar color will not be changed, and the debug viewlet will not be automatically revealed.

What exactly are you referring to here? The API docs for DebugSessionOptions makes no mention of debugUI or even simple. Investigating the options available to us, most seem to be related to parent-child relationship handling. (Indeed, this is passed in as a parameter named parentSessionOrOptions...) The closest I see is noDebug, which has the following explanation:

Controls whether this session should run without debugging, thus ignoring breakpoints. When this property is not specified, the value from the parent session (if there is one) is used.

and makes no mention about how VSCode's UI handles such a scenario. Is this perhaps what you're referring to? Is there some other undocumented API you're suggesting here?

@weinand
Copy link
Contributor

weinand commented Jan 17, 2022

@ericdrobinson DebugSessionOptions.debugUI.simple is proposed API that was introduced to support something similar to your use case: executing notebook cells without showing the full debug UI.

Please see:

Please note that you would still have to implement a minimal debug adapter to evaluate your script, but on the positive side you would get all standard debug UI automatically.

@ericdrobinson
Copy link
Author

@weinand I see. Thanks for the explanation. Two questions:

  1. Do simple Debug Sessions interact with any UI at all or are they 100% invisible to the user via standard debug UI?
  2. Is there a way to detect when the simple Debug Session should shut down based on interactions with the ExceptionWidget? (I.e. someone clicked the close button...)

@roblourens
Copy link
Member

So in this scenario, you are running a script but not debugging it? This sounds similar to the "Run Without Debugging" scenario that we have for the JS debugger. Would it make sense to still use a DebugSession for this so the user can see that it is running and click Stop if they want to?

The "simple" debug session is not 100% invisible - the difference is mainly that the debug widget is not shown, and the debug viewlet/console is not revealed when the session starts.

That api is proposed so you won't be able to publish an extension with it yet, but you can test it out. It's still proposed because it only serves one specific scenario and I want to think about other cases where it may be useful (like this one, maybe)

@ericdrobinson
Copy link
Author

ericdrobinson commented Jan 18, 2022

So in this scenario, you are running a script but not debugging it?

@roblourens That is correct. If a user is using VSCode as an Editor and doesn't care about debugging, they can still use the extension to "send the script to the host for evaluation". The Debug Session is 100% optional.

This can also be a thing if the user detaches a debug session while at a breakpoint (which results in the host continuing with evaluation) and then the host application reports an error result from the issued evaluation request. Users have expressed the desire that not only should such results be posted to the Output Log (and in an "Error/Information Message"), but that the offending line be highlighted in the Text Editor. The desire here is to showcase it like you would an exception shown during a live debug session.

This sounds similar to the "Run Without Debugging" scenario that we have for the JS debugger.

It is very similar to Run Mode, yes. I honestly wasn't previously aware of that feature. Is there a good resource to learn more about how the debugger works differently in Run Mode? Is there something in the Debug Adapter Protocol for this?

We've been trying to figure out how to handle the integration with VSCCode. Currently we're looking at the following:

  • Attach Configuration: Attaches a Debug Session to a specific ExtendScript (think JavaScript) engine within a target application (think Photoshop). Some applications can host multiple engines, so there's flexibility here. "Evaluating a Script" is a secondary function that can be triggered at any time. This is important because interactions with host engines can also trigger debugger events (e.g. clicking a button that triggers a callback in a host engine - this may have nothing to do with a script that we explicitly evaluated).
  • Launch Configuration: Directly ties the concept of a Debug Session to the lifespan of an evaluation of a specific script. When the user uses a Launch configuration, they specify a "program" (script) to run. When the user stops the Debug Session, the script is told to Stop (unless they explicitly use the "Detach" option as an escape hatch). When the script naturally completes its evaluation, the Debug Session automatically ends. This is important because some users want a "single button" approach to "Running and Debugging a script". This would not work for UI-related callback stuff that is better handled with the Attach Configuration approach.

It sounds like Run Mode is very similar to what we have defined for "Launch Configuration". Does Run Mode actually also listen for breakpoints and trigger a break in the UI? Or is it simply used as a bit of a kludge to add a "Run this Script" feature wherein all debug messages from the host are simply ignored?

Would it make sense to still use a DebugSession for this so the user can see that it is running and click Stop if they want to?

Sort of? But we have two separate issues with such an approach:

  1. We divorced the meaning of "Start Debug Session" from "Start Script Evaluation". When you "Attach" a Debug Session, you connect to a host and then listen for debugger events. This shouldn't (and doesn't currently) stop you from also evaluating a script, whether that be in the application+engine the Debugger Session is connected to or some other unrelated application+engine combination.
  2. The Debugger Actions UI has a lot of buttons that are meaningless when simply running the script.

Our solution thus far has been to manage this through Status Bar Buttons (StatusBarItem). When a .js/.jsx file (standard ExtendScript file extensions) is focused (this will be configurable), we will add an "▶️ Evaluate Script..." button to the status bar. When an "Attach"ed Debug Session is active, we add another button that is specific to that session (e.g. "▶️ Evaluate Script in Photoshop..."). As you can have multiple debug sessions active for various host+engine combinations at the same time, this might get crowded (this is very likely to be a very rare thing). When a script is evaluating in a given App+Engine combination, it gets a "Halt Evaluation..." Status Bar Button. When a "Debug Session-affiliated 'Evaluate' button" is clicked, it turns into the "Halt Evaluation" button for that process as you cannot have multiple simultaneous evaluations ongoing at once.

Make sense?

@weinand
Copy link
Contributor

weinand commented Jan 18, 2022

@ericdrobinson "Run Mode" is just a flag ("noDebug") that exists in the client in order to disable breakpoints. If "noDebug" is true, VS Code will not register any breakpoints with the debug adapter. In addition the flag is passed to the debug adapter side-by-side with the launch configuration.

A debug adapter honoring the flag has complete freedom how to implement "noDebug". Some debug adapters bypass the underlying debugger completely (e.g. for performance reasons) and run the program directly on the "bare metal". These DAs typically end the debug session immediately after launch so that VS Code will leave debug mode and remove the debug toolbar.

Other debug adapters run the program via the debugger, but turn off all debugging functionality. In this case VS Code stays in debug mode and shows the debug toolbar which allows to terminate the program with a button. In addition if an exception occurs, VS Code will show the exception information with the regular debug UI.

@ericdrobinson
Copy link
Author

@weinand Thanks very much for the explanation! That context is extremely helpful for evaluating what such an implementation might look like for us.

Other debug adapters run the program via the debugger, but turn off all debugging functionality. In this case VS Code stays in debug mode and shows the debug toolbar which allows to terminate the program with a button. In addition if an exception occurs, VS Code will show the exception information with the regular debug UI.

The thing is that we have scenarios where a user stops the Debug Session and then the script evaluation ends. When that occurs, there is no longer a Debug Session to which to send the exception info and show the ExceptionWidget. What's more, we don't necessarily want all of the extra cruft that comes along with that process (call stack info, variables, etc.). We can't provide that information because the script execution is over - the context no longer exists. What we have is the error report and we'd like to point it out to users and say "Look here!"

@weinand
Copy link
Contributor

weinand commented Jan 18, 2022

@ericdrobinson if the debug session is created programmatically (not directly by the user) and if no debug toolbar is shown (because most of the debug UI is disabled), then the user cannot stop the debug session. This ensures that the debug UI (e.g. the exception widget) is available as long as the script is running.

@ericdrobinson
Copy link
Author

if the debug session is created programmatically (not directly by the user) and if no debug toolbar is shown (because most of the debug UI is disabled), then the user cannot stop the debug session. This ensures that the debug UI (e.g. the exception widget) is available as long as the script is running.

@weinand But noDebug won't entirely hide the Debug Session from the Debug Sidebar will it? If so, we would have to probably give the Debug Session a name like "[Host] Eval Exception" and add some explanation about why this oddity appears to the Readme...

Our issue isn't generally about "user's stopping this mutant debug session prematurely", but rather having to manage yet another Debug Session instance "type" and communicate it (provided, at least, that it is still visible in the UI in any way). And I'm still not convinced that there's a clean way for us to handle this.

We're trying to highlight the source of an error/exception when there is no running process. This can happen in two ways:

  1. Exception Before Script is Even Run (e.g. Syntax Errors detected)
    1. User clicks "Run Script".
    2. Host receives script, parses it, detects syntax error.
    3. Host stops evaluation and returns error message indicating problem line.
    4. User sees "Syntax Error at Line X - [details...]"
  2. Exception After Script is Run (e.g. Error was thrown)
    1. User clicks "Run Script".
    2. Host receives script, parses it, evaluates it.
    3. Evaluation results in a new thrown error.
    4. Host stops evaluation and returns error message indicating problem line.
    5. User sees "Script Terminated with Error at Line X - [details...]"

These are both variations of a single point: the script "evaluation" process is 100% complete by the time we get the report to show.

That said, my understanding from this conversation is that if we wanted to show the existing ExceptionWidget in the above scenarios, we would:

  1. Start up a noDebug Debug Session.
  2. Hand this Debug Session the error information.
  3. Send a StoppedEvent with some exception details.
  4. Handle the exceptionInfoRequest, handing it exception details.
  5. Ignore any other requests (e.g. scopesRequest or variablesRequest).

If we did all this then the ExceptionWidget would show up and report our error information. Is that correct?

But... how do we know when to stop this Debug Session? Specifically, we receive zero indication that the user has closed the ExceptionWidget. So they could close that widget and then... they'd be stuck with this Debug Session sitting around. We have no idea when to "safely" stop that Debug Session.

@roblourens
Copy link
Member

Users have expressed the desire that not only should such results be posted to the Output Log (and in an "Error/Information Message"), but that the offending line be highlighted in the Text Editor. The desire here is to showcase it like you would an exception shown during a live debug session.

I'm not a user of this but it's a little confusing to me that I would detach the debug session, but still get some of the same UI features popping up in my editor from the run. I'd assume that if I've detached, I don't want to be bothered by it anymore.

Then regarding

  1. Exception Before Script is Even Run (e.g. Syntax Errors detected)
  2. Exception After Script is Run (e.g. Error was thrown)

I agree that these scenarios aren't really served currently by the exception widget shown during a debug session, which is only shown when you pause at the error.

The first I think is better served by a language server/Diagnostic updated as the user types. If you aren't able to provide that, another example where we do something similar is if you define a preLaunchTask in your launch config, and the Task reports errors, then you will get a modal dialog reporting that there were errors, and you can click to debug anyway or cancel debugging.

Besides that, I think that these two scenarios could both be described as "the debug session terminated with this error".

Something like that could almost make sense in DAP, except that I don't know that it's usually possible for a DA to provide this information. e.g. js-debug can't say "this exception is the reason the process stopped".

Sorry if this was mentioned somewhere above but have you considered just popping up a notification or using a Diagnostic?

@ericdrobinson
Copy link
Author

I'm not a user of this but it's a little confusing to me that I would detach the debug session, but still get some of the same UI features popping up in my editor from the run. I'd assume that if I've detached, I don't want to be bothered by it anymore.

I agree. This may be a specific case that we have to explicitly capture in our flow internally and handle. It will depend upon user feedback.

The first I think is better served by a language server/Diagnostic updated as the user types. If you aren't able to provide that, another example where we do something similar is if you define a preLaunchTask in your launch config, and the Task reports errors, then you will get a modal dialog reporting that there were errors, and you can click to debug anyway or cancel debugging.

When parsing a script, the underlying engine returns the first syntax error it encounters and stops. Users with multiple errors would see only one diagnostic at a time - clear one and, if another was "hidden" behind it, another would suddenly appear. I assume that would get annoying and might force users to adjust their understanding if the Diagnostics UI in a negative way ("the Diagnostics lie - there's more than 1 error here, it just only ever shows me one").

That's also still only half the problem. We still have "script syntax was fine but an error occurred during evaluation".

Besides that, I think that these two scenarios could both be described as "the debug session terminated with this error".

Well, except that they may have nothing to do with a Debug Session at all. They are simply "your script produced an error right here when it ran" (or it has an error right here that stopped it from running).

Something like that could almost make sense in DAP, except that I don't know that it's usually possible for a DA to provide this information. e.g. js-debug can't say "this exception is the reason the process stopped".

Right. Part of the problem is that by the time we get these exceptions/errors, we no longer have a debug context to show (or never did in the first place... in our case). The call stack simply doesn't exist.

Sorry if this was mentioned somewhere above but have you considered just popping up a notification or using a Diagnostic?

Currently we do pop up a notification. Specifically we show an Error Message (which appears like a little popup notification) with the info. Two things that make this problematic:

  1. Users have requested that the line causing the issue be highlighted.
  2. We have zero control over the formatting of the contents of the message. We cannot add newlines to break up the script path and line number from the error message so we end up having to break things up with symbols.

We don't use Diagnostics because we don't have a good way to know when to clear those diagnostics (this speaks to the "error result" case, rather than the "syntax error" one).


ExtendScript as a language is a dinosaur - just... a useful dinosaur. It is basically ECMAScript 3 with E4X extensions and some minor customizations. When it hears terms like "language server", it starts telling you about how it had to walk up hill both ways to the host application for evaluation. We do the best we can and right now we're just trying to point the user to a specific line (clearly dressed as "error") with some context and let them hit a close button to dismiss the UI.

@weinand
Copy link
Contributor

weinand commented Jan 24, 2022

@ericdrobinson I've read through the whole discussion again and I can see your reasoning why the existing VS code concepts don't fit well with the "ExtendScript" user interactions. As a result, you need to implement the full "Evaluate ExtendScript" experience as an extension (and that's probably what you're already doing).

I agree that an "ExceptionWidget" API would make your life easier - but please understand that we don't have the time to add this API ourselves. However, we are willing to discuss and review a proposal for an "ExceptionWidget" API and possibly accept a PR with an implementation.

@ericdrobinson
Copy link
Author

@weinand Thanks very much for taking the time to read the discussion over again. I do understand that you are very busy and that this won't be a priority internally.

However, we are willing to discuss and review a proposal for an "ExceptionWidget" API and possibly accept a PR with an implementation.

Sounds good! I would be happy to contribute such a proposal (and possibly a PR). Do you have references or examples of model proposals from the past that I could work from? Any pointers on how to go about doing this for an API in this area of the code?

@weinand weinand removed their assignment Oct 19, 2022
NWYLZW added a commit to NWYLZW/vscode-comment-queries that referenced this issue Nov 28, 2022
@NWYLZW
Copy link

NWYLZW commented Nov 28, 2022

I'm also developing a function that requires an API exposed like ZoneWidget. I don't know whether there is any new progress or whether I can create a ZoneWidget by myself in some way.

orta/vscode-twoslash-queries#2

@roblourens roblourens added the feature-request Request for new features or functionality label Dec 6, 2022
@vscodenpa vscodenpa added this to the Backlog Candidates milestone Dec 6, 2022
@vscodenpa
Copy link

This feature request is now a candidate for our backlog. The community has 60 days to upvote the issue. If it receives 20 upvotes we will move it to our backlog. If not, we will close it. To learn more about how we handle feature requests, please see our documentation.

Happy Coding!

@vscodenpa vscodenpa removed this from the Backlog Candidates milestone Dec 7, 2022
@vscodenpa
Copy link

🙂 This feature request received a sufficient number of community upvotes and we moved it to our backlog. To learn more about how we handle feature requests, please see our documentation.

Happy Coding!

@vscodenpa vscodenpa added this to the Backlog milestone Dec 7, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
debug Debug viewlet, configurations, breakpoints, adapter issues feature-request Request for new features or functionality
Projects
None yet
Development

No branches or pull requests

7 participants
@roblourens @jrieken @weinand @ericdrobinson @NWYLZW @vscodenpa and others