Bind a keypress to a shell command that uses the current file in visual studio code
Asked Answered
W

5

2

Is there a way to create a keybinding to execute a shell command on a file? something like:

{
    "key": "ctrl+shift+e",
    "command": "run",
    "command": "touch $file",
    "when": "editorTextFocus"
}

I don't want to use tasks as this needs to be global for the whole editor, not to a particular workspace.

Wrought answered 13/6, 2018 at 13:33 Comment(0)
A
6

[See my edit below - this is now much easier to do.]

I figured out a way to do what you want but it is a bit of a hack. As you probably know it is easy to bind a task if you can create the task in .vscode/tasks.json and use something like the following:

{
  "key": "shift+escape",
  "command": "workbench.action.tasks.runTask",
  "args": "Start server and process files"
},

It is much trickier to run a script via a keybinding without a pre-existing task. However, you can try the following which takes advantage of the "workbench.action.terminal.runSelectedText", command. But we need to get the script into a selection first so:

Use the macros extension (which is a little rough) so that we can tie together multiple commands. In your user settings:

"runCommandInTerminal": [

   {
     "command": "type",
     "args": {
        "text": "node -v"
     }
  },
  { 
    "command": "cursorMove",
    "args": {
       "to": "wrappedLineStart",
       "by": "wrappedLine",
       "value": 1,
       "select": true
    }
  },
  "workbench.action.terminal.runSelectedText",
  "editor.action.clipboardCutAction",
  //  "workbench.action.terminal.focus"
],

This is a general example of a setting "runCommandInTerminal" that you can then bind to any key chord you wish in keybindings.json, like

{
    "key": "ctrl+shift+e",
    "command": "macros.runCommandInTerminal"
},

Your example is a little harder because you want to access something like ${file} which you cannot do in the settings, only tasks.json and launch.json. Fortunately, there is a command which will get the current file: "copyFilePath". So try

"runCommandInTerminal": [

      "copyFilePath",
      {
        "command": "type",
        "args": {
          "text": "touch "
        }
      },
      "editor.action.clipboardPasteAction",
      { 
      "command": "cursorMove",
        "args": {
        "to": "wrappedLineStart",
        "by": "wrappedLine",
        "value": 1,
        "select": true
        }
      },
      "workbench.action.terminal.runSelectedText",
      "editor.action.clipboardCutAction",
      // "workbench.action.terminal.focus"
],

First get the file path, then output the first part of your script command "touch".

Next append the filepath to the end of the command.

Move the cursor to select the preceding.

Run the selection in the terminal.

Cut the selection from the editor.

This can be run with your keybinding. You will see the flash of the script being typed and cut but your editor code will be unaffected. It is best to run it from a blank line in the editor but you can run it from the beginning of a line of unrelated code if you wish (but indentation may be lost for now).

It is hacky but seems to work. I would love to know if there is an extension or another way to do this cleaner.


EDIT May, 2019:

Since my original answer (as @Jeff indicated) vscode has added the sendSequence command. And the ability to use variables with that command. So now the OP's question is much easier to accomplish:

{
  "key": "alt+x",
  "command": "workbench.action.terminal.sendSequence",
  "args": {"text": "touch ${file}"}
}

in your keybindings.json file. See variables with a sendSequnce command.

Accede answered 13/6, 2018 at 21:32 Comment(2)
Thanks for the answer but yeah this seems so ugly and hacky, there should be a better way, I opened a feature request, lets see if it happens.Wrought
Please check https://mcmap.net/q/349798/-shortcut-for-running-terminal-command-in-vs-code , I had to use \u000D at the end.Allout
R
1

with Marks answer here I could resolve my case that was run an android command with some shortcut. After that I decided to create an guide on my GitHub, hope that helps.

GitHub: LucasMMota

Run Shell Script with Keyboard Shortcuts

In this example I set a keyboard shortcut to run android reload on my react native project.

    1. Install the macros extension to execute multiple commands.
    1. After installed go to Preferences>Settings (or cmd+,)
    1. Find Macros Configuration and use to edit in User Settings, or simply add the following in your user settings:

    {

    //... other configs you could have
    
    "macros": {
        "runCommandInTerminal": [
            "editor.action.insertLineAfter", // go to new line below
            {
                "command": "type",
                "args": {
                    "text": "adb shell input text \"RR\" " // exec cmd to reload android device (could be any else)
                }
            },
            // select text typed above
            {
                "command": "cursorMove",
                "args": {
                    "to": "wrappedLineStart",
                    "by": "wrappedLine",
                    "value": 1,
                    "select": true
                }
            },
            "workbench.action.terminal.runSelectedText", //run command
            "editor.action.clipboardCutAction", // remove cmd typed
            //"editor.action.deleteLines", //couldn't use. When uncommented, command doesn't work
        ],
    }
    

    }

    1. Now that we have the command set, we need to set a trigger shortcut. So go to Preferences>Keyboard Shortcuts (or cmd+k), open keybindings.json and edit like this:

    {

    "key": "alt+s",

    "command":"macros.runCommandInTerminal"

    }

    1. Save your User Settings customizations and keybindings.json, respectively

Bind file format and indenting with save shortcut

keybindings.json:

{
        "key": "cmd+s",
        "command": "macros.saveContentAndIndent",
        "when": "editorTextFocus && !editorReadonly"
    },

User Settings:

"macros": {
        "saveContentAndIndent": [
            "editor.action.formatDocument", // format
            "workbench.action.files.save", // as I used Save shorcut, I add the save bind again.
        ],
        //....
}
Romeyn answered 25/6, 2018 at 17:33 Comment(2)
I can't understand step 3 because the formating is confusing, can you put the stuff in steps 3 and 4 in a code box and pretty print them?Wrought
Done @LeonardoSantagadaRomeyn
R
1

UPDATE 2:
VS Code now provides variables for integrated terminal commands. https://code.visualstudio.com/updates/v1_32#_variable-support-in-send-sequence-command

If you'd like to do more powerful tasks such as running them in the background (instead of just sending text to the current terminal) get the Command Runner or macro-commander extension

Original:
I created an extension macro-commander to solve this with a non-hacky solution. It is an improved version of the macros extension that runs the commands in order (synchronously) and lets you inject the filepath/folderpath into the command. Commands can be run in the user's VS Code terminal or a hidden background terminal.

UPDATE 1:
As of today (months later) I realized, much to my dismay, there was an existing extension that can probably do what you (and I) wanted. It is Command Runner written by edonet. If I had known about it I probably would've never made my extension.

Command Runner doesn't have as much generic possibilities (multiple commands, user input, or javascript) but it has a better UX and is probably the extension you (the reader) should try first. However, if Command Runner can't do what you're looking for, then macro-commander probably can, albeit with less elegance.

Whenever I decide to do a full update to macro-commander I'll talk with edonet and see if we can combine/add the functionality into his extension, in which case I'll update the macro-commander README to redirect to his/her extension.

Rotz answered 16/5, 2019 at 20:16 Comment(1)
Excellent, particularly like the running of commands in a background terminal! Exactly what I was looking for :)Vasculum
M
0

Command Runner quickly and easily solved this for me.

Mort answered 27/9 at 14:44 Comment(0)
S
-1

Simple answer - add this to your keybindings.json:

{
    "key": "ctrl+shift+e",
    "command": "workbench.action.terminal.sendSequence",
    "args": {
        "text": "touch ${relativeFile}\u000D"
    }
    "when": "editorTextFocus"
}

Explanation: when you press "ctrl+shift+e", what follows after "text" in "args" is sent to the terminal. \u000D is the code for a carriage return ("ENTER key"), which effectively "presses enter" after sending the text to the terminal. Without \u000D, the command will be sent to the terminal but will not run, instead it will wait for you to focus the terminal and press enter.

Sources: 1 2 3 4 5

Singlecross answered 1/8 at 18:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.