Search code examples
visual-studio-codekey-bindings

How to Insert Literal Text from a Keybinding, without Triggering Substitutions


I am new to VSCode, and I have a regular need to make keyboard learn sequences, which seems to not be a thing in the VSCode IDE. Following the advice of several sites, I got an extension for marcos and have been making custom keybindings as a workaround.

This has been pretty rough going because apparently, the only master list of commands is in the Keyboard Shortcuts tab, and secondly there is little to no documentation on these hundreds of individual commands. (In particular, I have not been able to find any master list of the arguments accepted by each command).

In any event, I have had some success making the custom keybindings that I need, however, one particular problem that I keep running into is that the only way that I can see to insert specific literal text is with the "type" command, however, this apparently really does try to type each character and lets the keyboard style, etc. substitutions auto-fire. I do not need or want this, and I need to be able to stop it, or find some other (practical) way to insert my literal text without any reinterpretation, substitution or other keybindings triggering (or whatever facility is doing this).

Here is my current keybinding command set:

    "EditLitPropToGet": [
        {"command": "type", "args": {"text": "get "}},
        "cursorWordRight",
        "cursorRightSelect",
        {"command": "type", "args": {"text": "() { return "}},
        "cursorEnd",
        "cursorWordStartLeft",
        {"command": "type", "args": {"text": "; }"}},
        "cursorWordRight",
        "cursorWordRight",
        "cursorWordStartLeft"
    ]

The purpose of this is, when positioned at the beginning of the first word in this line:

referenceGravity: this.surfaceGravity * (this.radius**2),

to then turn it into this line:

get referenceGravity() { this.surfaceGravity * (this.radius**2); },

This works fine up until it gets to the second type command:

        {"command": "type", "args": {"text": "() { return "}},

Before this runs the line looks like this:

get referenceGravity: this.surfaceGravity * (this.radius**2),

with the colon character selected. What the type command above is supposed to do is this:

get referenceGravity() { return  this.surfaceGravity * (this.radius**2),

But what actually happens is this:

get referenceGravity() { return }) this.surfaceGravity * (this.radius**2),

Clearly, this is because it is seeing the type text as keyboard input and is triggering the style closures for "()" and "{}".

How can I just insert literal text?

To be clear, I am not looking to also have to define a custom snippet everytime I want to do this, or to have to pre-load the paste-buffer, I need these keybindings to be self-contained.

Nor do I want to blindly en-masse disable all extensions, stylings, etc at the beginning, if that means that I have to blindly enable them all at the end. I am willing to try something like that, but if I already have something disabled, it shouldn't then re-enable it because it doesn't know what was previously enabled/disabled. If it's just disabling/enabling one specific setting, that's fine, I can live with correcting that as needed.


Just to update this, I have accepted the answer by @Mark because it gave a solution for this specific case. However, as to the general question: "How can I insert literal text?", I believe that I finally understand what folks have been telling: You can use insertSnippet to insert literal text, you just have to escape things that might be seen as Textmate/RegEx commands ("$", and "`" with "\"). So this should work:

    "EditLitPropToGet": [
        {"command": "editor.action.insertSnippet", 
            "args": {"snippet": "get "}},
        "cursorWordRight",
        "cursorRightSelect",
        {"command": "editor.action.insertSnippet", 
            "args": {"snippet": "() { return "}},
        "cursorEnd",
        "cursorWordStartLeft",
        {"command": "editor.action.insertSnippet", 
            "args": {"snippet": "; }"}},
        "cursorWordRight",
        "cursorWordRight",
        "cursorWordStartLeft"
    ]

Solution

  • Just to show you how the snippet transform version, this keybinding:

    {
      "key": "ctrl+k ctrl+1",             // or whatever you want
      "command": "editor.action.insertSnippet",
        "args": {
          "snippet": "get ${TM_SELECTED_TEXT/^(\\w*):(.*),$/$1() {$2/}; },"
      }
    },
    

    produces your desired output. You do have to select the line first, but that could be automated inside a macro very easily (or just bind the cursorLineEndSelect command. That macro would have just two commands: cursorLineEndSelect and insert the above snippet.


    Example macro using multi-command, I don't know the syntax for ctf0.macros which does look more powerful:

     "multiCommand.commands": [
    
         {
          "command": "multiCommand.yourMacroNameHere",
          "interval": 2000,
          "sequence": [
            "cursorEnd",
            "cursorHomeSelect",
            {
              "command": "editor.action.insertSnippet",
              "args": {
                "snippet": "get ${TM_SELECTED_TEXT/^\\s*(\\w*):(.*),$/$1() {$2/}; },"
              }
            },
            "cursorDown"
          ]
        },
    

    The interval is unecessary in your case, just there to watch it work -it can be commented out. The cursor can be anywhere on the line containing the line you want to transform. It doesn't need to be pre-selected, the macro will do that for you.

    I would reload vscode (through the command palette) after making these changes just in case.

    Again, the demo looks slow but that is just to demonstrate the individual steps with the interval option. Otherwise it is instantaneous.

    snippet transform


    If you want to use your macro, then use the insertSnippet command in place of that one type command:

        // {"command": "type", "args": {"text": "() { return "}},
        {
          "command": "editor.action.insertSnippet",
          "args": {
            "snippet": "() { return }"
          }
        },
    

    that does not seem to trigger the auto-close braces. Snippets do require escaping more characters than a type command does though - but not in your case.