Search code examples
code-snippetsvscode-snippets

Nested regex replacement in VS Code Snippet


I've made a mistake in making a lot of bold readings in restructured text (rst) files where I should've used a different heading style.

I need to replace:

**some heading**

with:

some heading
............

I'm wanting to do this with VS Code snippets, where I highlight the text in question, and then run the snippet to make the replacement.

To do this, I think I need to make two replacements:

  1. Take selected text, strip leading and trailing **
  2. Take the result of 1, and then essentially replace anything (.) with literal dot (.): s/././g

I cannot figure out how to make nested replacements in VS Code snippets -- is this even possible?

This is what I have right now:

    "BoldToSubHeading": {
        "prefix": ["boldtosubheading"],
        "body": [
            "${TM_SELECTED_TEXT/\\*\\*(.*)\\*\\*/${1}/}",
            "${TM_SELECTED_TEXT/././g}",
        ],
        "description": "Change bold text into subheading."
    }

If the input text is:

**foobar**

I get four too many dots -- because the asterisks are included in the last replacement.

foobar
..........

Textmate docs, which the Snippet code seems to be based on, indicates:

For nested replacements, use named captures as variables are inherited.

However, I've tried the "${TM_SELECTED_TEXT/(?<guts>.*)/${guts}/g}" with no luck.

Any suggestions to solve my replacement problem? Are nested replacements possible with snippets?

Requirements are:

  1. Highlighted text will contain double asterisk at front and back
  2. Text between the double asterisks (let's call it 'guts') can contain anything -- spaces, numbers, quotes, etc
  3. The result should be 'guts', plus a new line, plus a number of dots equal to length of guts.

Solution

  • I thought this would be easier with one snippet - perhaps with a conditional replacement - but I couldn't figure it out in a reasonable time. But it is pretty easy to do in steps.

    Using a macro extension like multi-command, put this into your settings:

    "multiCommand.commands": [
    
      {
        "command": "multiCommand.refactorHeadings",
        "interval": 250,   // remove this line and will be instantaneous
        "sequence": [
    
          {
            "command": "editor.action.insertSnippet",
            "args": {
              "snippet": "${TM_SELECTED_TEXT/\\*\\*(.*)\\*\\*/$1/}",
            }
          },
           "editor.action.copyLinesDownAction",
            "cursorLineStartSelect",
          {
            "command": "editor.action.insertSnippet",
            "args": {
              "snippet": "${TM_SELECTED_TEXT/././g}",
            }
          } 
        ]
      }
    ],
    

    and a keybinding (in keybindings.json) to trigger it:

    {
      "key": "alt+c",    // whatever keybinding you like
      "command": "extension.multiCommand.execute",
      "args": { "command": "multiCommand.refactorHeadings" },
    
            // replace markdown with your langId
      // "when": "editorTextFocus && editorHasSelection && editorLangId == markdown"
    },
    

    refactor headings with a macro

    The demo shows selecting all such occurrences of your simple find regex,Ctrl+Shift+L will do that, and triggering the macro. It is showed slowed down just for demonstration purposes.