microsoft / vscode

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

Lift `setContext` from a command to proper API #10471

Open farfromrefug opened 7 years ago

farfromrefug commented 7 years ago

This would be very useful to activate extension build feature per project type. would help for this issue

jrieken commented 7 years ago

Like so ..., command: "xyz", when: "workspaceConatins:foo.txt"?

@farfromrefug What exactly do you want to achieve with that?

farfromrefug commented 7 years ago

Yes exactly like that.

That's actually the only way to trigger a special build defined in an extension (see the linked issue). An extension cannot define a new task builder. So i you want to trigger, let's say, a cordova build on cmd+b but the default task in non cordova projects, that would be the only way

alexdima commented 7 years ago

@farfromrefug Your extension could activate on workspaceConatins:foo.txt and then invoke the command setContext:

vscode.commands.executeCommand('setContext', 'inCordovaProject', true);

and then you can use "when": "inCordovaProject".

@jrieken Now that there are more use-cases than vim, should we consider lifting setContext to a proper API method?

jrieken commented 7 years ago

I like. Maybe something similar to how we have it internally, like

interface ContextKey<V> {
  key: string;
  value: V;
}

function createContextKey<V>('name'):ContextKey<V>;

I'll let it cook for a little

farfromrefug commented 7 years ago

@alexandrudima i love that. Does it already works (i don't see this in the cordova plugin)? Do i have to "unset it"?

alexdima commented 7 years ago

@farfromrefug You can create an issue in the cordova extension repo and introduce them to the problem and provide them with the recommendation.

farfromrefug commented 7 years ago

@alexandrudima it s not an issue. I was just wondering if that API was already existing. And i was talking of cordova as an example. Thahnks will try it

alexdima commented 7 years ago

vscode.commands.executeCommand('setContext', 'inCordovaProject', true); works today. It is a "sort-of-hidden" API that we introduced for vim extensions in particular. We did not want to make it a top level API concept until we found more use-cases, so having found more use-cases, we are now considering making it a top level API concept, as @jrieken suggests, perhaps via a createContextKey method. We will keep this issue around to track this work.

farfromrefug commented 7 years ago

@alexandrudima i can confirm that it works! BTW I have a lot of questions concerning extension dev. My extension is doing some wrong things in VSCode, where can i ask questions? Thanks

alexdima commented 7 years ago

Usually we direct questions to Stack Overflow. You can also open issues if you feel you're running into something that looks like a bug, but each issue should be self-contained. We also have some issues that come in which are in fact questions.

TL;DR Stack Overflow preferably, otherwise issues

farfromrefug commented 7 years ago

@alexandrudima i wont create issues though it might be bug related. I am not sure what's the cause yet. Thanks

rebornix commented 7 years ago

@alexandrudima from my experience in handling keybindings in Vim, the need for lifting it to API is because we only support limited context key type https://github.com/Microsoft/vscode/blob/c67ef57cda90b5f28499646f7cc94e8dcc5b0586/src/vs/platform/contextkey/common/contextkey.ts which makes keybindings contribution doesn't always meet our need.

For example, we have quite a few Vim commands implemented in our ext and ppl always come and say, hey we want to use these Vim commands and for others, we prefer Code's. In this case, a single context key to enable/disable all Vim keybinds is not enough.

Is there any possibility that we support objects instead of just booleans for context keys then we can write below code to implement whatever we want

vscode.commands.executeCommand('setContext', 'vim.keys', {esc: true, ctrld:false})

{
  "key": "Escape",
  "command": "extension.vim_escape",
  "when": "editorTextFocus && !inDebugRepl && vim.keys.esc"
}
alexdima commented 7 years ago

@rebornix You raise a good point, but IMHO this should not be tackled with options that then pollute the keybindings. All these users can today, without any code change in vscode or in the vim extension, customize their keybindings 100%. They just need to go to their keybindings.json and write rules and IMHO that should be our de-facto answer. Introducing an option that disables a keybinding is IMHO a clutch offered to end-users that don't want to open their keybindings.json file. Also, the same users will come back in one month time and ask for a new option and so on, when we could try to educate them and let them run wild :).

For your case, the end user would need to write the following in their keybindings.json file:

{ "command": "-extension.vim_escape" }

Maybe not as easy as an option, but then that is a separate bug, that our keybindings UI is non existant.

But I agree, there is a real problem here. I install an extension and all of a sudden I need to add all these rules with { "command": "-extension.vim_escape" } to remove those keybindings. Not nice. IMHO we should tackle that by adding the possibility to disable a certain contribution point from a certain extension (think of a more fine-grained "disable extension", where instead of disabling the entire extension, you'd just disable a contribution point of an extension).

rebornix commented 7 years ago

@alexandrudima I love your proposal here. A contribution point level enable/disable makes it possible to choose a specific command for a particular key combination. A concrete example is when you install Resharper on Visual Studio, you get a chance to decide which command you want to use for any keycap you press. We can make it happen in Code by enabling a contribution for one extension and disabling all others for a single key combination :)

hoovercj commented 7 years ago

Is this documented anywhere yet?

jrieken commented 7 years ago

only in this issue

jrieken commented 7 years ago

Moving this issue to the next milestone. It's not clear to me now we bring this to the API given that there are actually different contexts active and that extensions might want to utilise that

dymo commented 6 years ago

Promoting this to be part of the extension API would make it easier to encurage developers to make more well behaved extensions, that only adds entries to the menues (especially the command palette), key bindings and maybe status bar in the right context.

given that there are actually different contexts active and that extensions might want to utilise that

How about something like this?

interface ContextChangeEvent {
    name: string;
    active: boolean;
    // Is it also possible to provide a source?
}

setContextState(name: string, active: boolean): void;

getContextState(name: string): boolean;

getActiveContexts(): string[];

onDidChangeContext: Event<ContextChangeEvent>

Is there already a generic way to subscribe to context changes?

LexiconCode commented 6 years ago

I'm also lending my support to this issue as I have a number of use cases in mind.

Sent from my Motorola XT1635-02 using FastHub

dlech commented 6 years ago

It would probably also be useful to allow string in addition to boolean (like the context on TreeItems).

felipenmoura commented 6 years ago

Can I use === or !== in the when clause? It seems like it only accepts booleans, is that so?

LexiconCode commented 6 years ago

There seems to be quite a bit of interest including myself in this being pulled into the API. What are your current thoughts @jrieken

OnlyLys commented 5 years ago

I do agree that having a getContext method would help extensions avoid 'clashes' with other built in functionality. For instance, it would be great if there is way to detect if the editor is currently in snippet mode.

tboby commented 4 years ago

Another use case: The language my LSP extension supports uses .txt for many different types of file. I want to present different actions depending on the actual type of file my language server determines the user has open.

Currently I can only do this (through the API) with code actions with range line 0 - line max, which are very hard for users to discover.

longieirl commented 4 years ago

So I'm having the issue where I want to be able to toggle on/off edit mode for our extension.

It works to a certain point i.e. in test mode, the user has to right-click the file twice where upon the second right click, the edit mode is shown. This was controlled by a config in the package.json but we've added automated tests and now need to control the edit mode functionality via an environment variable where by default its disabled.

vscode.commands.executeCommand('setContext', 'isEditMode', true);

The line above has been added to the activate method, however I'm still left with the situation where the isEditMode is defaulting to false on the first right click but active on the second click! In debug mode, I can see the isEditMode param being set.

For testing purposes the activationEvents is listed as follows:

"activationEvents": [
    "*"
  ]

Is there a better solution to this problem? Does vscode support such a flow? Can the package.json be dynamically updated on runtime to control certain menu options?

Running vscode 1.41.1.

Thanks.

hediet commented 4 years ago

I have another usecase for getContext - a shortcut visualizer: image It would be super cool if I could test whether a keybinding is active or not so that only active keybindings are shown. The visualizer could be implemented as a webview provided by an extension.

dalmo3 commented 3 years ago

I've also stumbled on this. I'm running into issues when calling executeDefinitionProvider. A getContext would allow me to check for editorHasDefinitionProvider before the call. I've asked about it on SO.

gjsjohnmurray commented 3 years ago

As an extension developer I want to add my voice to the calls for a getContext capability. For example I'd like my extension to be able to detect that the user is thinking about debugging by testing getContext('activeViewlet') == 'workbench.view.debug'

@eamodio you made a good case for it two years back in https://github.com/microsoft/vscode/issues/46445#issuecomment-377591247. Any chance you could try and make something happen? Unlike setContext, for which we have the command as a workaround, I think we're stuck until it does. And the fact that getContext isn't mentioned in the title of this issue means it's probably not even on people's radar when Backlog gets scanned.

pokey commented 3 years ago

Fwiw https://github.com/microsoft/vscode/pull/118722 would provide getContext

miketalley commented 1 year ago

One more thing to think about with this API is how to allow string interpolation of context variable string values within package.json contributes definitions. For example, something like this would be great...

// Somewhere in activate
const itemsFilePath = '/some/file/path.yml';
vscode.commands.executeCommand(
  'setContext',
  'ext.itemsFilePath',
  itemsFilePath
);

// In package.json
{
  "id": "ext.treedata.items",
  "name": "Items - File Location: ${context:ext.itemsFilePath}",
  "when": "ext.itemsFilePath"
}
wszgrcy commented 7 months ago

CompletionItem can set snippet and run command ,but it can't run comand after all input finished. eg: snippet console.log('${1/(.*)/${1:/upcase}/, I want to run command after $1 input and tab to format. so I want to get context inSnippetMode. Judge that the completion has been completed

ArturoDent commented 1 month ago

I would like to see getContext() as well for an extension I wrote. It would read the key editorReadonly which is set/unset by a user with the session read-only commands.

I could keep track of the state myself "perhaps", but that seems a shame if the key already exists for each editor.

mltony commented 1 month ago

+1 for getContext API call. I am writing screenreader accessibility extension for VSCode and I need to figure out whether cursor is in the main text editor - as opposed to panes like debug or explorer pane. It appears current extension API doesn't have a way to figure that out.

kosirm commented 1 month ago

+1 , it would be great if I could use Memento directly from webview withouth passing messages.

geekley commented 3 weeks ago

+1 for getContext API. It would allow some interoperability with other extensions that use setContext, without depending on them explicitly making an API to be consumed by other extensions. For example, if they set some state as myExtension.state.isConnected = true, another would be able to check for that value and provide better interoperability (e.g. redirecting to a feature on the other extension only when its state will allow it to work).

Citrullin commented 2 weeks ago

Please. How is this not a thing yet?