Create duplicate tab of an already open file [duplicate] - visual-studio-code

We can use the "split editor" option to make two views into one file.
I'm looking for an option to open the same file in separated tabs like I can do in Sublime Text (open new view of file). Is that possible?
Note: I want to do this without splitting the view, so there should be two tabs for the same file within the same view container.

I couldn't find anything built-in that lets you do this, nor an existing extension in the marketplace. I thought it should be quite trivial to implement a "Duplicate Tab" command yourself in a custom extension, but it turns out VSCode only allows the same resource to be opened once within the same view column.
It's still possible to do this on Windows or macOS, but only by abusing this bug:
Issues with not case/fragment-normalizing file paths (macOS, Windows) #12448
Here's what the code for the extension looks like:
'use strict';
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand("duplicateTab", () => {
var activeEditor = vscode.window.activeTextEditor;
if (activeEditor == null) {
return;
}
// HACK!
const sameFileNameButDifferent = activeEditor.document.fileName.toUpperCase();
vscode.workspace.openTextDocument(sameFileNameButDifferent).then(document => {
vscode.window.showTextDocument(document, {preview: false});
});
});
}
In package.json:
"contributes": {
"commands": [
{
"title": "Duplicate Tab",
"command": "duplicateTab"
}
]
},

Related

How to get the name of selected files in source control via VSCode extension API?

I can manage to get one file name of source control with following code. The file name is from the line 'await vscode.commands.executeCommand('copyFilePath');' I can get the file name by reading the clipboard text. But when I select multiple files, still the first file name is available. Is it possible to get all files' name?
let copySelectedFileName = vscode.commands.registerCommand('folder-operations.copySelectedFileName', async (folder) => {
let newUri = folder; // folder will be undefined when triggered by keybinding
console.log('folder'+folder);
if (!folder) { // so triggered by a keybinding
await vscode.commands.executeCommand('copyFilePath');
}
console.log(newUri);
});
I try another way: add one command to SCM as below shown.
I use parameter in command to retrieve the selected files' name. But the size of the array is 1 even if I choose more than 2 files.
let copySelectedFileNameSCM = vscode.commands.registerCommand('testSource.copySelectedFileNameSCM', async (...file) => {
console.log('file:'+file);
});
Add your command to this context menu in your package.json:
"contributes": {
"menus": {
"scm/resourceState/context": [
{
"command": "testSource.copySelectedFileNameSCM"
}
]
}
}
It looks like you were adding it to the wrong menu. That may be the only menu that will return selected files.
Then in your command:
let copySelectedFileNameSCM = vscode.commands.registerCommand('testSource.copySelectedFileNameSCM', async (...file) => {
console.log('file:'+file);
});
file will be an array of all selected items in the scm view when you trigger a context menu on one or more selected files.

Can I trigger a code snippet programmatically in an extension?

I have an extension that provides some snippets. I want to add a command that creates a new file with the snippet automatically triggered for the user. The idea is to get the user to edit the tabstops immediately without any further action. Something like:
Run the command (from the command palette, or a keybinding)
VSCode opens a new file with the snippet already evaluated and waiting on the first tabstop for user input
I have scouted the APIs but I haven't found anything to trigger a snippet. The most relevant APIs are those about the CompletionItemProvider, which can be used to provide a snippet.
Does anybody know how to trigger/expand a snippet automatically?
There is also an insertSnippet method on the TextEditor, see https://code.visualstudio.com/api/references/vscode-api#TextEditor.
So you could do this:
const editor = vscode.window.activeTextEditor;
// build your snippet with the SnippetString methods
const snippet = new vscode.SnippetString("option1 attr1=${2:Option 1 Placeholder 1} attr2=${3:Option 1 Placeholder 2}");
await editor.insertSnippet(snippet);
You can also run the command insertSnippet like this:
// body of snippet here is exactly as you would write it in a keybinding
await vscode.commands.executeCommand("editor.action.insertSnippet", { "snippet": "${CURRENT_YEAR}-${CURRENT_MONTH}-${CURRENT_DATE}T${CURRENT_HOUR}:${CURRENT_MINUTE}:${CURRENT_SECOND}Z" });
// the below uses a pre-existing snippet with a name 'Custom Header'
await vscode.commands.executeCommand("editor.action.insertSnippet", { "name": "Custom Header"});
This last executeCommand can have a reference to a pre-existing snippet that you contributed in your extension. For more see below.
All these commands will insert at the cursor with the snippets in the first tabstop position as you wanted.
Contributing snippets in your extension:
In your package.json:
"contributes": {
"snippets": [
{
"language": "javascript",
"path": "./snippets/javascript.json"
}
]
}
You create a folder named snippets in your extension and a file named javascript.json for javascript snippets. And then use the usual snippet format in that file, like:
{
"Custom Header2": { // use this 'name'
"prefix": "reactpure",
"body": [
"import React from 'react';",
"import PropTypes from 'prop-types';",
"import './${1:ComponentName}.module.css';",
"const ${1:ComponentName} = ({ ${2:propValue=[] } }) => (",
"<${3:rootelement}>${4:content}</${3:rootelement}>",
")",
"${1:ComponentName}.propTypes = {",
"${5:propValue}: PropTypes.string",
"};",
"export default ${1:ComponentName};",
"$0"
],
"description": "Create a react pure component"
}
}
Then you can use that name in your extension
// using an extension-contributed snippet
await vscode.commands.executeCommand("editor.action.insertSnippet", { "name": "Custom Header2"});
``

How do I contribute a command to the VS Code explorer title bar or command palette that is only active when my webview is focused?

I'm writing an extension that uses VS Code's webview api. My extension also registers a refresh preview command that updates the webview's content. How can I make it so that:
The refresh preview command only shows up in the command palette when my webview is focused?
The refresh preview command shows in the webview's editor title bar?
First, create a custom context key that tracks when your webview is focused. Use VS Code's setContext command to make this context key track when your webview is focused:
const myWebview = ...;
// Make the context key track when one of your webviews is focused
myWebview.onDidChangeViewState(({ webviewPanel }) => {
vscode.commands.executeCommand('setContext',
'myWebviewFocused',
webviewPanel.active);
});
Then use your context key in the when clauses of your extension's menus contribution point
For a command with the id myExtension.refreshPreview, to ensure that command only shows up in the command palette when your webview is focused, use the following contribution:
{
"contributes": {
"menus": {
"commandPalette": [
{
"command": "myExtension.refreshPreview",
"when": "myWebviewFocused",
}
]
}
}
}
For adding a command (possible with icon) to the editor title bar of your webview, use the editor/title contribution point:
{
"contributes": {
"menus": {
"editor/title": [
{
"command": "myExtension.refreshPreview",
"when": "myWebviewFocused",
}
]
}
}
}
Check out VS Code's built-in markdown extension for a more advanced example of this.

How to prevent commands from being displayed if the extension is not active?

I am writing an extension and I'm providing a custom command, declare in the package.json as:
{
"contributes": {
"commands": [
{
"command": "myext.doSomething",
"title": "Do something"
}
]
}
}
I'm am then registering it in the extension, when it activates:
commands.registerCommand("myext.doSomething", () => console.log("hi"))
This works, but the Do Something command is present in the command palette even if the extension is not active.
This means that if the user selects the command when the extension is not active, an error along the lines of
command myext.doSomething not found
Is there a way to prevent custom commands to be displayed in the command palette unless the extension has been activated?
Instead of not showing your command when the extension is not active, you can just add it to the activationEvents like this in your package.json. In your case:
{
"activationEvents": [
"onCommand:myext.doSomething"
]
}
This will run the exported activate function of your extension before the command is invoked.
Also the when keyword could be an option for you. I answered a similar question on that topic here.
Edit:
You can control a command's visibility in the command pallette by additionally contributing a contextual menu (docs). Then you can for instance only display the command when the editor's file has a specific language id.
Example:
{
"menus": {
"commandPalette": [
{
"command": "myext.doSomething",
"when": "editorLangId==scala"
}
]
}
}

Within a VSCode extension is it possible to have a panel that switches between a webview and tree view

i want to add a new explorer panel into vscode. I want it to display either a treeView or a webView depending on if the user has connected to my backend application. I can see something similar in the base of vscode in the folder view. When no folder is open this view is shown
and when you have a folder open it looks like
For anyone else who finds this question, the behaviour seen in the file explorer is achievable through a Welcome Message.
A view's welcome message will show when the tree for that view is empty.
Preview
Welcome Message
Normal tree view
Example
In your package.json, declare:
The view
The view welcome message
The command which the welcome message button should execute
"contributes": {
"commands": [
{
"command": "myExtension.myCommand",
"title": "My Custom Command"
}
],
"views": {
"explorer": [
{
"id": "myCustomView",
"name": "My Custom View",
"contextualTitle": "My Custom View"
}
]
},
"viewsWelcome": [
{
"view": "myCustomView",
"contents": "Welcome to my custom view! [learn more](https://google.com/).\n[Get Started](command:myExtension.myCommand)"
}
]
}
In your extension.ts
Define the button command
Hook up the view to the view provider
import * as vscode from 'vscode';
import { CustomViewProvider } from './CustomViewProvider';
export function activate(context: vscode.ExtensionContext) {
// Add the custom view
const customViewProvider = new CustomViewProvider();
vscode.window.registerTreeDataProvider('myCustomView', customViewProvider);
// Add the command
let myCustomCommand = vscode.commands.registerCommand('myExtension.myCommand', () => {
vscode.window.showInformationMessage('This is my custom command!');
});
context.subscriptions.push(myCustomCommand);
}
export function deactivate() { }
In CustomViewProvider.ts, define when your view is empty or not.
import * as vscode from 'vscode';
export class CustomViewProvider implements vscode.TreeDataProvider<vscode.TreeItem> {
getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
return element;
}
getChildren(element?: vscode.TreeItem): Thenable<vscode.TreeItem[]> {
// NOTE:
// When TRUE, the welcome message will show
// When FALSE, the welcome message will NOT show
var showEmptyView = true;
if (showEmptyView) {
return Promise.resolve([]);
}
return Promise.resolve([
new vscode.TreeItem('This view is not empty!')
]);
}
}
As of VS Code 1.25, views may only contain tree views. Support for showing a webview in the side bar is tracked by https://github.com/Microsoft/vscode/issues/46585
If all you need is a button or simple prompt, you can use a tree view with a single node in the first case