Search code examples
visual-studio-codecustomizationvscode-snippets

Custom snippets with a specific behavior with a keybinding in VS Code


I am trying to insert a snippet %>% with a keybinding Ctrl+Shift+M in VS Code.

Example code:

data %>% select()

I am able to achieve this (not totally happy) with this setting in keybindings.json

// Place your key bindings in this file to override the defaults
[
    {
        "key": "Ctrl+Shift+M",
        "command": "editor.action.insertSnippet",
        "when": "editorTextFocus",
        "args": {
          "snippet": " %>% "
        }
      }
]

In RStudio IDE whether I have a space after data or not, with the Ctrl+Shift+M keybinding, it only inserts a single space between data and %>%. See this:

enter image description here

However in the setting I have created in keybindings.json in VS Code, I am not able to limit space between data and %>% to only a single space. With my setting I will have two spaces in between. See this:

enter image description here

I read this, and this but couldn't help. How could I possibly limit the space to only a single space?


Solution

  • Generally it is tricky to do what you want because vscode snippets just insert whether the cursor is - but you cannot delete something before the cursor. It would be easy to do if you selected that prior word, like data in your case, but you probably don't want to do that.

    But using a conditional replacement it is possible:

    {
      "key": "Ctrl+Shift+M",
      "command": "editor.action.insertSnippet",
      "when": "editorTextFocus",
      "args": {
        "snippet": "${TM_CURRENT_WORD/(.*)/${1:? %>% :%>% }/}"
      }
    },
    

    This snippet looks at the word under the cursor when you trigger it. If there is a word, like when the cursor is right after a word that counts, there will be a capture group 1. If there is no word, like if you entered a space first and a space is considered a word apparently for the purposes of TM_CURRENT_WORD, there will be no capture group 1.

    The replacement text is ${1:? %>% :%>% } which is a conditional replacement which says: if there is a capture group 1, the case with no space, insert %>% with one leading and trailing space. Else when capture group 1 so there already is a leading space, insert %>% with no leading space but one trailing space.

    Demo:

    demo of snippet with/without spaces

    The only case where it doesn't work as you wish is case 4 in the demo which is hopefully not a common use case for you - going back into text to insert your snippet. It adds the extra space because with the cursor right before a word that is considered a TM_CURRENT_WORD and falls into capture group 1. Otherwise I think the only solution to this use case would be an extension or require you to select that preceding word first.