VSCode 1.3 has added support for adding commands to context menus. Is there a way to identify whether a file or folder is clicked to open the explorer context menu?
"menus": {
"explorer/context": [
{
"when": "????",
"command": "extension.myCommand",
"group": "myGroup"
}
]
}
Also, is there a comprehensive(ish) list of expressions that might be checked in the when clause here?
You can use "when": "explorerResourceIsFolder".
I had to dig through the code to find it (I was actually writing up a response to say it didn't exist and enumerating the possible clause values when I saw it).
As of v1.10.1:
config.<any_config_path_here>
editorIsOpen
explorerResourceIsFolder
explorerViewletFocus
explorerViewletVisible
filesExplorerFocus
globalMessageVisible
inDebugMode
inQuickOpen
inZenMode
listFocus
openEditorsFocus
resource (Uri information: path, query, scheme, etc)
resourceFilename
resourceLangId
resourceScheme
scmProvider
textCompareEditorVisible
I've submitted an issue to improve the documentation for this.
Regarding obtaining a comprehensive list of context keys: in recent VSCode versions, there's a Developer: Inspect Context Keys command. After executing the command, it lets you pick a UI element:
After that, the dev console opens, and you can expand the logged object that contains a full list of context keys and their current values in this "scope":
https://code.visualstudio.com/docs/getstarted/keybindings#_when-clause-contexts
is file: "when": "!explorerResourceIsFolder"
is dir: "when": "explorerResourceIsFolder"
You can get the list of language ids like this...
vscode.languages.getLanguages().then(l => console.log('languages', l));
I still haven't figured out how to determine if the item that was right clicked is a directory. If someone figures it out please let me know.
A write up about the feature is here. But basically:
the when is the same as the keybindings-when and can use the same keys
the when can use two new keys resourceScheme and resourceLangId which are available without an editor - think of the explorer context
menu
the when can be a boolean configuration value, e.g config.editor.lineNumbers
My menu:
"menus":{
"explorer/context": [
{
"when": "resourceLangId == sql",
"command": "extension.myCmd"
}
]
Related
I'm currently working on a custom VSCode extension that fetches data from a remote API and presents the data within a custom tree-view. The treeview itself allows to interact with the API and i.e. filter results based on some predefined criteria such as a search term or whether or not the response has some files attached to it, adding new nodes to or removing or updating existing nodes in the tree dynamically.
For simplicity reasons I created an own Github project to just focus on the basic setup and configuration of a tree that more or less operates on some fake API methods but provides similar capabilities typical CRUD APIs provide.
GIF was resized to fit the 2MB limit and thus produces ugly artifacts
While showing action-icons for commands on a TreeView, defined by a custom TreeDataProvider, and on a TreeItem are fairly straight forward by defining something like:
...
{
"contributes":
"commands": [
{
"command": "extension.myTreeView.filter",
"title": "Filter",
"icon": "$(filter)",
"when": "view == extension.myTreeView"
},
{
"command": "extension.myTreeView.resetFilter",
"title": "Reset Filter",
"shortTitle": "Reset",
"icon": "$(close)",
"enablement": "view == extension.myTreeView && extension.myTreeView.hasFilter",
"when": "view == extension.myTreeView && extension.myTreeView.hasFilter"
},
{
"command": "extension.myTreeView.delete",
"title": "Delete Item",
"shortTitle": "Delete",
"icon": "$(trash)",
"when": "view == extension.myTreeView && viewItem == archive"
}
...
],
"menus": {
"view/title": [
{
"command": "extension.myTreeView.filter",
"when": "view == extension.myTreeView"
"group": "navigation#1"
},
{
"command": "extension.myTreeView.resetFilter",
"when": "view == extension.myTreeView",
"group": "navigation#2"
},
...
],
"view/item/context": [
...
{
"command": "extension.myTreeView.delete",
"when": "view == extension.myTreeView && viewItem == archive",
"group": "inline#3"
}
]
}
},
...
in the package.json file, I somehow struggle to add enabling conditions to commands targeting a particular item itself. I.e. the tree allows the download of some files contained in (or attached to) the item represented by the node. Such nodes may or may not have files attached to them and thus enabling the download action only makes sense for nodes that at least have a file to download. While generally downloading that content or rendering the button for triggering the download action isn't the issue, the enabling option for that command is where I am a bit clueless.
I temporarily "solved" that issue by defining different kinds of contextValue's for such tree nodes, one for an archive with and one without files to download. While this in general seems to solve that issue, it a) doesn't feel proper and b) leads to downstream problems. Imagine that archives can be locked preventing users who do not belong to the group the archive was locked for the actual download. This is just a fictional case here but should illustrate that you might end up with a combination of property fields within that contextValue variable that you more or less need to parse in that enable statement. And each further possible context value option must be carried around through the whole commands and menus definitions like this:
...
{
"command": "extension.myTreeView.delete",
"when": "view == extension.myTreeView && viewItem == archive || viewItem == archiveWithFiles || viewItem == ...",
"group": "inline#2"
},
...
In general, custom context variables, such as hasFilter in the sample command configuration shown above, which can be used in the enablement and when clauses, can be used to show/hide or enable/disable certain commands but these seem to work only on the whole tree itself and not a particular tree item. I.e. the sample application uses the following code to enable/disable the resetFilter command based on whether a filter is defined or not:
...
public async updateFilter(filter: Filter): Promise<void> {
this.filter = filter;
await commands.executeCommand("setContext", TestTreeDataProvider.CONTEXT_HAS_FILTER, true);
this.items = [];
this._onDidChangeTreeData.fire();
}
private async resetFilter(): Promise<void> {
this.filter = undefined;
await commands.executeCommand("setContext", TestTreeDataProvider.CONTEXT_HAS_FILTER, false);
this.items = [];
this._onDidChangeTreeData.fire();
}
This works as this is more or less a global action on the tree itself and has a single source of property it reacts to. But such context values may only have base types assigned to them or objects where only the keys (a.k.a. property names) of that objects are checked for their availability in in clauses. I could potentially create custom context variables per tree item generated but then I need some form of addressing mechanism as each of the context value names must be different or else they are overwritten by the context value of the next node. And as the tree can hold plenty of nodes I feel adding a context value per node is also not very performant then. In the end what seems to be missing here is a way to define a function as context value that allows the current node (either the TreeItem or the actual object the node is referring to) as input and needs to return one of the base types as output, i.e. true or false to determine whether that item should be enabled or not.
While this SO question here may sound similar, it actually asks for disabling the whole tree item node when I just want the command that can be performed on that tree item to enable/disabled based on some tree item's current property value. Some styling of TreeItems based on their context can be done via FileDecorationProvider's. That question also is a result of a bug inside VSCode that doesn't seem to force a rerender on any changes done to the enablement guards.
I feel that there must be an easier solution on this matter and that's why I ask here. In short, how can a command be enabled/disabled in Visual Studio Code based on the current tree items' context (either properties held by or functions executed on that node)?
You now put a type description as the context value of the TreeItem.
Why not put the allowed operations also in the context value:
archive_Del
archive_Read_Update
Then use this in the when clause
{
"command": "extension.myTreeView.delete",
"when": "view == extension.myTreeView && viewItem =~ /^.*_Del.*$/",
"group": "inline#2"
}
{
"command": "extension.myTreeView.read",
"when": "view == extension.myTreeView && viewItem =~ /^.*_Read.*$/",
"group": "inline#2"
}
I want to createTerminalEditor if there is no terminalEditor opened yet.
Note: I'm talking about terminalEditor and not terminal.
So, I'm looking for a when arg which says something like editorAlreadyExists != terminalEditor, just like there is activeEditor which aceepts string of terminalEditor.
Is there anyway to achieve this?
Here is activeEditor example for reference, but I want to check if terminalEditor exists in all the already opened editors, not just a activeEditor.
{
"key": "ctrl+`",
"command": "workbench.action.createTerminalEditor",
"when": "activeEditor != terminalEditor"
},
I see that there is a when clause for:
terminalEditorFocus: true/false
which doesn't look like it would help except for the fact that it isn't in the list (via Developer: Inspect Context Keys) at all when there is no terminal editor open. So I thought maybe there was a keybinding when clause that could exploit this. But I tried all kinds of thing, like whether it was null or undefined or the empty string or neither true nor false, etc. but nothing worked. I think if the key terminalEditorFocus doesn't exist, then nothing at all is delivered to the keybinding resolver and it always fails.
You could file an issue asking for a specific terminalEditorExists sort of when clause.
There will be another way. There is a presently experimental api to access all the open tabs. See proposed api. So you could write an extension that checks all the open tabs and fires the workbench.action.createTerminalEditor command if none are a terminalEditor. It works right now in the Insiders Build, but when it will go final I don't know - it seems pretty solid now. Issue: Tab Model API.
const tabs = vscode.window.tabs;
const openTerminalEditor = tabs.some(tab => tab.viewId === 'terminalEditor'); // true/false
Then you could either set your own context key with setContext or run the command.
I'm trying to implement a small function by adding a shortcut for auto-correcting the last misspelled word, and this is what I get currently:
{
"key": "cmd+l",
"command": "extension.multiCommand.execute",
"args": {
"sequence": [
"cSpell.goToPreviousSpellingIssue",
"editor.action.quickFix",
"workbench.action.navigateToLastEditLocation",
"acceptSelectedSuggestionOnEnter",
"acceptSelectedSuggestion"
]
}
},
The idea is simple: jump back to the first misspelled word, and just select the first suggestion my spelling checker gives me, finally jump back to the previous edited position.
The code above is using multiCommand plug-in. Question is that I can't find any keystroke events to let me actually select the first suggestion my spelling checker gives me.
As in the config, I'm using cSpell for checking my spelling. To be specific, after I hit cmd+l, this is what I get:
snap shot
Clearly, I manage to move to the previous spelling issue, and evoke quickFix to let the suggestion widget pop up, and then move my cursor back to where I was initially. Hence, the only problem is what is the event to select that suggestion?
Really appreciate every helps, or if there is a better method to do the same thing please tell me! (I have tried every keyword I can think of, and there are not many references out there both in the official document from VS Code and google)
Because the quickfix menu is a different beast than a suggestions menu, the nextSuggestion or acceptSuggestion type of commands will not work in it. There is an open issue for navigation commands in the quickfix menu, see Missing keybinding for navigation in Quick Fix contextual menu
.
But you can get what you want another way. I found Keybinding for applying a specific code action and in it is a method for applying a quickfix with a command:
{
"key": "ctrl+shift+r ctrl+e",
"command": "editor.action.codeAction",
"args": {
"kind": "refactor.extract.function",
"apply": "first"
}
}
Valid values for "apply":
"first" — Always automatically the first available code action.
"ifSingle" — Default. Automatically the code action if only one is available. Otherwise, show the context menu.
"never" — Always show the code action context menu, even if only a single code action is available.
Adapting that for your use case, try this:
{
"key": "cmd+l", // "ctrl+l" for Windows
"command": "extension.multiCommand.execute",
"args": {
"sequence": [
"cSpell.goToPreviousSpellingIssue",
{
"command": "editor.action.codeAction",
"args": {
"kind": "quickfix",
"apply": "first" // you indicated you want the first choice
}
},
// "workbench.action.navigateToLastEditLocation"
"cursorUndo" // better than the above I believe
]
}
}
We have some custom snippets we provide as part of our VS Code extension via key bindings and a snippets json file:
{
"key": "ctrl+shift+i",
"mac": "cmd+shift+i",
"command": "editor.action.insertSnippet"
},
...
"snippets": [
{
"language": "xml",
"path": "./snippets/xml.json"
}
]
We would like a button to add one particular snippet to the editor at the current cursor position.
How do I programmatically I invoke the part of "editor.action.insertSnippet" after the user has selected the snippet?
I posted this issue on the vscode repo.
jrieken responded with the following reply:
The insertSnippet-command accepts an argument which is either the name of a snippet or a snippet itself. So, either { snippet: "console.log($1)$0"} for an inline snippet or { langId: "csharp", name: "myFavSnippet" } referencing an existing snippet.
You can run any registered command via vscode.commands.executeCommand. See also the vscode namespace API.
I'm often using VS Code for Html/Css/Js development. I'm often using the following task (from other SO post) to open HTML page in browser:
{
"version": "0.1.0",
"command": "explorer",
"windows": {
"command": "explorer.exe"
},
"args": ["${file}"]
}
Is there a way to add this task as default one for all folders?
The short answer is "no".
The slightly longer answer is, "no, but they are thinking about ways this can be done" as demonstrated in this issue.
A workaround is to add a snippet that you can easily add to tasks.json in new projects. Go to File > Preferences > User Snippets > json and add something like this:
"Explorer Task": {
"prefix": "expTask",
"body": [
'{',
'\t"version": "0.1.0",',
'\t"command": "explorer",',
'\t"windows": {',
'\t\t"command": "explorer.exe"',
'\t},',
'\t"args": ["${file}"]',
'}'
],
"description": "Explorer task that I use so often"
},
Now when you go to tasks.json in any new project, you type expTask (or whatever you name it) and you can easily drop this in.