Is there a vscode startup complete event or folder open event? - visual-studio-code

Can extension code be set to run when startup of vscode has completed? Or when a folder has been opened?
How to write an extension that opens a folder in a new vscode window, and then opens a text file in that folder?
I have the open the folder part working. And I am using global state to store the name of the file to open.
// store in name of file to open in global state.
context.globalState.update('fileToOpen', './src/index.html');
// open folder in a new vscode instance.
const uri_path = `file:///c:/web/tester/parcel`;
const uri = vscode.Uri.parse(uri_path);
await vscode.commands.executeCommand('vscode.openFolder', uri, true);
Then, when my extension is activated in the new vscode instance, I want to read the file name from global state, wait for vscode to open the folder, then run openTextDocument to open the file.

Since v1.46 there's a onStartupFinished activation event. So your package.json would have it:
...
"activationEvents": [
"onStartupFinished"
]
...
Then proceed to check the state upon activation
export function activate(context: vscode.ExtensionContext) {
const fileToOpen = context.globalState.get('fileToOpen')
if (fileToOpen) {
// open file
let document = await vscode.workspace.openTextDocument(fileToOpen);
await vscode.window.showTextDocument(document);
// reset state
context.globalState.update('fileToOpen', undefined);
} else {
// store in name of file to open in global state.
context.globalState.update('fileToOpen', './src/index.html');
// open folder in a new vscode instance.
const uri_path = `file:///c:/web/tester/parcel`;
const uri = vscode.Uri.parse(uri_path);
await vscode.commands.executeCommand('vscode.openFolder', uri, true);
}
...
}

Related

Insert default text into newly created files using vscod extension api

I'm trying to create a simple vscode extension that will insert some default text into a newly created file. What I want is for the vscode.workspace.createFileSystemWatcher to call a function that gets the activeTextEditor and writes to the new file. Here is what I've tried:
import * as vscode from "vscode";
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
"default-text-generator.generate",
() => {
function _watcherChangeApplied(editor?: vscode.TextEditor) {
if (editor) {
editor.edit((editBuilder) => {
editBuilder.insert(editor.selection.active, "Hello World");
});
}
}
const editor = vscode.window.activeTextEditor;
let uri: vscode.Uri | undefined = editor?.document.uri;
if (uri) {
let watcher = vscode.workspace.createFileSystemWatcher(
new vscode.RelativePattern(
vscode.workspace.getWorkspaceFolder(uri)!,
"**/*.ts"
),
false,
false,
false
);
watcher.onDidCreate(() => _watcherChangeApplied(editor));
}
}
);
context.subscriptions.push(disposable);
}
// this method is called when your extension is deactivated
export function deactivate(): void {
//deactivate
}
Here's what's happening. The editor seems to insert the text, then immediately gets overwritten back to a blank page. I can't seem to figure out why.
The problem happens because the editor you are referring to is not what you think to be. It is not the newly created editor but instead, the editor that you are focused/active when the new .ts file is created.
The FileSystemWatcher.onDidCreate event provides you a Uri to the newly created file inside your workspace, but not necessarily opened in VS Code. Try creating a file via terminal and you will see what I mean. The file is created, the event is fired, but no editor is opened in VS Code.
So, you won't be able to use the editor.edit API to manipulate the file. Instead, you should edit the file using RAW/Node functions. But, in this case, maybe/probably you will clash with the external tool that is creating the .ts file (which may not be VS Code, if you use the FileWatcher). If only files created via VS Code must be detected, you should change to the workspace.onDidCreateFiles event instead. But yet, it also only provides you the Uri, not the Editor.
Hope this helps
This works:
let disposable2 = vscode.commands.registerCommand('yourCommand.here', async (...file) => {
async function _watcherChangeApplied(uri) {
if (uri) {
const editor = await vscode.window.showTextDocument(uri);
editor.edit((editBuilder) => {
editBuilder.insert(editor.selection.active, "Hello World");
});
await editor.document.save();
}
}
const editor = vscode.window.activeTextEditor;
let uri = editor?.document.uri;
if (uri) {
let watcher = vscode.workspace.createFileSystemWatcher(
new vscode.RelativePattern(
vscode.workspace.getWorkspaceFolder(uri),
"**/*.ts"
),
false,
false,
false
);
// watcher.onDidCreate(() => _watcherChangeApplied(editor));
watcher.onDidCreate((uri) => _watcherChangeApplied(uri));
}
}
The key point is that watcher.onDidCreate() will return the uri of the newly created file. You can pass that to your _watcherChangeApplied(uri) function.
In _watcherChangeApplied(uri) you can show the created file via await vscode.window.showTextDocument(uri) and that function returns an editor that you can use with its edit functions.
The code works whether you create the file within vscode (like the New File... icon button at the top of the explorer) or via the terminal (like touch test.ts).
If you want to enable creating new files through the terminal, for example, and NOT open them, try this _watcherChangeApplied(uri):
async function _watcherChangeApplied(uri) {
if (uri) {
const document = await vscode.workspace.openTextDocument(uri);
const strToAdd = "Hello World";
const wse = new vscode.WorkspaceEdit();
wse.insert(uri, new vscode.Position(0,0), strToAdd);
await vscode.workspace.applyEdit(wse);
await document.save();
}
}

VSCode Extension: How to open a file if no workspace folders are available?

I've been working on an extension that allows adding files with pre-defined content and modifying them using a custom web editor.
The custom command "Add new XXX file" looks like the following:
let disposable = vscode.commands.registerCommand('myextension.add-new-file', () => {
if(vscode.workspace.workspaceFolders?.length){
const rootPath = vscode.workspace.workspaceFolders[0].uri.fsPath ;
let counter = 0;
let filePath = '';
do{
counter++;
filePath = path.join(rootPath, `NewFile${counter}.my-ext`);
}while(fs.existsSync(filePath));
fs.writeFileSync(filePath, JSON.stringify(newFileContent), 'utf8');
const openPath = vscode.Uri.file(filePath);
vscode.commands.executeCommand('vscode.openWith', openPath, 'myextension.custom-designer');
}
});
It works OK if a folder is opened in VS Code. However, if no folder is opened, the rootPath can't be resolved. What's the solution for such a scenario? Does the 'vscode.openWith' accept the file content instead of the path to open?
You can accomplish your goal using the untitled scheme:
vscode.commands.executeCommand(
'vscode.openWith',
vscode.Uri.parse("untitled:FooBar"),
'myextension.custom-designer',
);
You can change your custom editor so that it detects when empty document gets passed to it and replaces the empty document with newFileContent.

How to open file in same VSCode window instance tab after opening folder

I tried to open folder first in new VScode window and then open file in same VScode window, but the folder is opening and the file is not opening. For reference I tried like this.
It will be great if you provide any suggestion to handle this case.
Here is my code, how i tried.
Case1: Only folder is opening.
let uri = vscode.Uri.file(apiPath);
await vscode.commands.executeCommand("vscode.openFolder", uri, {
forceNewWindow: true,
}).then(async function(){
let filePath = vscode.Uri.file(path.join(apiPath, "Abc.java"));
await vscode.commands.executeCommand("vscode.open", filePath);
})
Case2: Both folder and file is opening but in different VScode windows.
let uri = vscode.Uri.file(apiPath);
await vscode.commands.executeCommand("vscode.openFolder", uri, {
forceNewWindow: true,
});
let filePath = vscode.Uri.file(path.join(apiPath, "Abc.java"));
await vscode.commands.executeCommand("vscode.open", filePath);

Open text document in custom editor from vscode extension

I'm developing a Visual Studio Code extension that opens a webview custom editor.
Part of this is a command that prompts the user for a filename, creates the file, and then opens it.
It looks something like this:
window
.showInputBox({
prompt: "Enter name for file",
})
.then((title) => {
if (!title) {
return;
}
const fileContent = generateDefaultFileContent();
const filePath = path.join(folder.uri.path, `${title}.custom_file_format`);
workspace.fs
.writeFile(
folder.uri.with({
path: filePath,
}),
Buffer.from(fileContent, "utf8")
)
.then(() => {
workspace.openTextDocument(filePath).then((doc) =>
window.showTextDocument(doc, {
viewColumn: ViewColumn.Active,
})
);
});
});
The file gets properly created, however the call to window.showTextDocument opens the editor in the text editor and not my registered custom editor (in the example above, the custom editor will open .custom_file_format files). If you click on the newly created file in the file explorer, it will open in the custom editor.
Is there a way to get it to open the new file in the custom editor?
Turns out this can be done with...
commands.executeCommand(
"vscode.openWith",
folder.uri.with({
path: filePath,
}),
MyCustomEditor.viewType
);

How do I copy files from within a VSCode extension to the workspace?

I have certain files within the VSCode extension src folder that I would like to copy into the root of the workspace on running a certain command. Once this is working I would also like to extend this to copy other static files with specific content into other sub-folders within the workspace. I found a way to create new files here. However, I am unable to find a way to copy entire files bundled within the extension into the workspace. Looking at the MSFT documentation here, I cannot find anything that would work for my use case. Any pointers are appreciated.
I created a function copyFile that can copy file from within a VSCode extension to the workspace at the provided destination.
You can use WorkspaceEdit and FileSystem VS Code API to achieve this task as shown below.
async function copyFile(
vscode,
context,
outputChannel,
sourcePath,
destPath,
callBack
) {
try {
const wsedit = new vscode.WorkspaceEdit();
const wsPath = vscode.workspace.workspaceFolders[0].uri.fsPath;
const data = await vscode.workspace.fs.readFile(
vscode.Uri.file(context.asAbsolutePath(sourcePath))
);
const filePath = vscode.Uri.file(wsPath + destPath);
wsedit.createFile(filePath, { ignoreIfExists: true });
await vscode.workspace.fs.writeFile(filePath, data);
let isDone = await vscode.workspace.applyEdit(wsedit);
if(isDone) {
outputChannel.appendLine(`File created successfully: ${destPath}`);
callBack(null, true);
}
} catch (err) {
outputChannel.appendLine(`ERROR: ${err}`);
callBack(err, false);
}
}
Sample function call:
function activate(context) {
...
let testChannel = vscode.window.createOutputChannel("TestChannel");
// copy tasks.json file from vs code extension to the destination workspace
copyFile(vscode, context, testChannel,
'assets/tasks.json', '/.vscode/tasks.json', function(err, res) {});
...
}