Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run a system command from VSCode extension

I am trying to create a simple VSCode extension to run a set of commands when I open a folder. Basically these commands will set up our development environment. I have started off creating the boilerplace and ran through the example that VSCode provided but I am not clear how to run system commands. Appreciate any help or point me to some documentation about this topic.

like image 876
rnarasap Avatar asked Mar 24 '17 19:03

rnarasap


People also ask

How do I run an extension command in VS Code?

You can browse and install extensions from within VS Code. Bring up the Extensions view by clicking on the Extensions icon in the Activity Bar on the side of VS Code or the View: Extensions command (Ctrl+Shift+X). This will show you a list of the most popular VS Code extensions on the VS Code Marketplace.

How do you add a command to VS Code?

Go to tools, External tools in visual Studio. Click Add, name the new command then you can point to a batch file command using the browse ellipses. When you save it, you will then see the new menu item under tools.


2 Answers

Your extension environment has access to node.js libraries, so you can just use child_process or any helper libraries to execute commands:

const cp = require('child_process') cp.exec('pwd', (err, stdout, stderr) => {     console.log('stdout: ' + stdout);     console.log('stderr: ' + stderr);     if (err) {         console.log('error: ' + err);     } }); 
like image 161
Matt Bierner Avatar answered Oct 05 '22 09:10

Matt Bierner


One alternative could be to use the Terminal API which is the best option if you have the need for the process to be fully observable and controllable to/by the user.

Biggest downside: The Terminal API does not yet offer a way to introspect the processes that run inside of it.

If you actually want to run the process in the terminal, the only way to do this safely for now, would be to use a two-layer approach, where you start a wrapper process that in turn launches and observes the actual process (taken in via command line args).

Our self-made TerminalWrapper

We tried this ourselves.

  • In our first approach, the wrapper used a socket.io connection that allows for communication with and control by the extension.

  • In our second approach, we simplified and instead created the terminal using bash -c (non-interactive shell), and used a file watcher to get the results instead. Easier this way but after the process is done, the user won't be able to use the Terminal window (because its non-interactive). A lot less error-prone and does not require fulfilling socket.io dependency.

Implementation Details

  1. In our extension, we use a TerminalWrapper which runs the command inside a wrapper process, and waits for a file to contain the results.
  2. The wrapper process is here. It writes the result to a file.
  3. Usage example here:
const cwd = '.'; const command = `node -e "console.log('hi!');"`;  const { code } = await TerminalWrapper.execInTerminal(cwd, command, {}).waitForResult(); if (code) {   const processExecMsg = `${cwd}$ ${command}`;   throw new Error(`Process failed with exit code ${code} (${processExecMsg})`); } 

Biggest downside with the second approach is that we now need bash to be present, however (i) we do have a dependency checker that will warn you if you don't and explain how to get it, and (ii) using a unified shell, makes running commands a lot easier, as we now have a pretty strong unified feature set, we know we can rely on, rather than only being able to use common command execution syntax, and (iii) we can even run *.sh files without having to worry.

Introducing: VSCode Terminal API

  • VSCode API Documentation: Terminal
  • Official Terminal API Samples

All of the following imagery and excerpts are just straight up copy-and-paste'd from their official sample repository:

Create a terminal and run a command in it

context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createAndSend', () => {     const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);     terminal.sendText("echo 'Sent text immediately after creating'"); })); 

Terminal activation event

vscode.window.onDidChangeActiveTerminal(e => {     console.log(`Active terminal changed, name=${e ? e.name : 'undefined'}`); }); 

TerminalQuickPickItem

function selectTerminal(): Thenable<vscode.Terminal | undefined> {     interface TerminalQuickPickItem extends vscode.QuickPickItem {         terminal: vscode.Terminal;     }     const terminals = <vscode.Terminal[]>(<any>vscode.window).terminals;     const items: TerminalQuickPickItem[] = terminals.map(t => {         return {             label: `name: ${t.name}`,             terminal: t         };     });     return vscode.window.showQuickPick(items).then(item => {         return item ? item.terminal : undefined;     }); } 

...and a lot more!...

Terminal API Sample Commands

(<3 for the VSCode team and their hard work.)

like image 36
Domi Avatar answered Oct 05 '22 09:10

Domi