microsoft / vscode

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

Provide activation reason to extensions in activate call #44711

Open DanTup opened 6 years ago

DanTup commented 6 years ago

It would be useful to know if the extension is being activated due to a command (in my case I want to a show a more specific message if the extension fails to activate due to a missing SDK, and the command gives me more context to tailor this message to the user).

alexdima commented 6 years ago

This is a bit hard to retrofit at this time.

Reasons why an extension would get activated:

  1. an activation event occurred (<--- this is mostly what folks think about).
  2. a. the extension host has crashed and it got restarted and the extension got reactivated.
  3. b. in a multi-root workspace, the first folder is removed, and we restart the extension host and the extension got reactivated.
  4. there is another extension that gets activated and has an extension dependency to the extension.
  5. there is another extension that uses the vscode extension API to imperatively activate the extension.

In a way, it is helpful that we don't let the extension know why it got activated, as for example reasons 2.a. and 2.b. got added only a few months ago... i.e. we might have more reasons in the future.

DanTup commented 6 years ago

In this case, my extension activation code searches for SDKs in the users PATH. There are two SDKs that are relevant to my extension:

  1. Dart - if you're working on Dart CLI apps
  2. Flutter - if you're working on Flutter mobile apps (this includes its own copy of the Dart SDK)

Normally when the extension activates for a folder (or set of folders) we can look in the folder and figure out whether the project needs the Flutter SDK or not, and then display a useful message to the user about where to get the SDK if we don't find it.

However, we now use onCommand to allow the user to create new projects and it can be executed with no folders open. This means at activation time when we're doing the SDK search we don't know whether or not we should be telling the user about missing Flutter SDKs or not.

My workaround is to put a setTimeout in displaying the error message so that the command can change it, but it's a hack (and means the message is delayed, which makes it look slower to the user).

If the extension knew (in its activate method) that it was being activated because the command flutter.createProject was running, it would provide a much better error message to the user when the SDK(s) cannot be found.

In a way, it is helpful that we don't let the extension know why it got activated, as for example reasons 2.a. and 2.b. got added only a few months ago... i.e. we might have more reasons in the future.

I don't really agree with this - the extension doesn't need to be able to handle all of the reasons, it just needs the ability to identify the ones it cares about. If, a fix for me would be something like:

if (context.activationReason == ActivationReason.OnCommand && context.activationCommand.startsWith(`flutter.`)) {
  // Show flutter-specific errors if SDKs not found
}

Adding more reasons in future would not break this code. If people write code that makes assumptions about covering all reasons, that's on them, but shouldn't prevent us from being able to detect this.

(@weinand suggested raising this here so I don't know if he has thoughts on this)

amp343 commented 6 years ago

b. in a multi-root workspace, the first folder is removed, and we restart the extension host and the extension got reactivated.

This is what was biting me -- thanks for surfacing those reasons for a sanity check

phaumer commented 5 years ago

I would also like to request this feature: the activation needs to know which specific event triggered activation. In our case we provide multiple language servers in the extension and depending on which language the user requested we want to start that server first to have it respond faster. Plus we want to log a telemetry event on which language was requested.

kankri commented 4 years ago

Running code --performance I've noticed that some extensions get started by the event:

workspaceContainsTimeout:**/some-trigger-file

I'm thinking the timeout is partly due to to those recursive searches, but the main problem is that the extensions get very confused when they are activated and the trigger file is not there. The worse part of this is that the extension gets loaded and keeps using resources (even starting subprocesses) in every VS Code window unrelated to its functionality.

If the activation reason was passed in the activate call in the context object, the extension could see that the trigger had a timeout and make a sanity check and deactivate. The extension should do the sanity check (e.g. ask the user or search for the trigger file again) only in this case.

Even in the non-timeout case I think it would be beneficial if the extension got the actual file which matched the "workspaceContains: pattern".

DanTup commented 4 years ago

@kankri I've filed an issue about this behaviour at https://github.com/microsoft/vscode/issues/73656. VS Code bails out after 7 seconds of searching the tree (to save resources) and just activates the extension.

IMO this is a false-economy because now the extension has to either a) do the full search anyway, eating up the time VS Code saved on top of the 7s it already spent or b) activate the language server anyway, which is likely even more expensive than scanning the tree 😞

Implementing this request would help though, because in the case of a timeout the extension could choose to delay activating until a language file was opened, rather than either of the options above.

eamodio commented 4 years ago

While there could be many reasons (and they can grow over time) why an extension was activated, it would still be nice to know why for at least the cases we know -- onCommand, onFileSystem, onView, etc. I think it would be perfectly acceptable to have an unknown reason (or it just be missing) if we weren't sure. This would still allow the happy path activations to be differentiated.

DanTup commented 4 years ago

I think it would be perfectly acceptable to have an unknown reason (or it just be missing) if we weren't sure

Is there any reason an extension would ever be activated without knowing why? Seems like there is no reason to activate it without a reason?

Slightly off-topic, but I really think it'd help a lot just having some guidance on how language servers should handle activation. I'm often raising issues because my extension seems to be eagerly activated and I'm never sure whether the problem is that VS Code is being too agressive with activation, or whether I'm doing the wrong thing by unconditionally spawning a language server on activation. If it's likely that VS Code will activate the extension much more often than I want, I would consider making my activation more lazy - but it's not worth doing that if VS Code generally doesn't want to activate extensions until it's really needed.

(For some more concrete examples of unwanted activations see https://github.com/microsoft/vscode/issues/88617 and https://github.com/microsoft/vscode/issues/108578 - though the latter has been fixed).

ratmice commented 2 years ago

In the case of my language server the activation can't actually succeed in activation without knowing the path to the specific workspaceContains:**/foo.toml which caused it to activate. As this will cause it to read the actual foo.toml file and build documentSelectors patterns. It would be possible to walk the directories again recursively looking for foo.toml files. That would work, but seems odd given that vscode just did that.

tboby commented 1 year ago

I've been running into this problem a lot recently.

My language server has support for 8+ languages, which are all subtle variations on the same underlying language. The only way to detect language is by folder name (game modding), so I rely on filepath patterns to set the language id, which I then read from the active document on startup to determine which language.

Over the last year or two I've been seeing more and more activations where onLanguage must be being called without the active text editor being that language. If I don't know what language triggered the extension, and no open documents have a language id, how can I set up correctly?

If I knew what the activation event was, I would either know, or know how to find out!