Search code examples
regexvscode-snippets

When transforming a vscode snippet variable, how to substitute a group with nothing?


I'm making my own "toggle block comment" keybinding for python, using ''' instead of """ for delimiters.

This is the best I could come up with to mimic the existing keybinding (and adapt it to my liking):

      {
        "key": "alt+shift+a",
        "command": "editor.action.insertSnippet",
        "args": {
            "snippet": "${TM_SELECTED_TEXT/^('''\n?)?(.*?)(\n?''')?$/${1:?\n:'''\n}$2${3:?\n:\n'''}/s}"
        },
        "when": "editorTextFocus && editorHasSelection && editorLangId == 'python'"
      }

^('''\n?): The regex captures the opening delimiter if it exists, and places it in the first group. ${1:?\n:'''\n}: If the group was found, it substitutes it with a line break, if it doesn't, it adds the delimiter (it adds ''' and a line break). The same thing happens for the closing delimiter.

However my intention is not to substitute the delimiter if found with a line break, but with nothing, i.e. to remove the group if it is found. How could I do this?


Solution

  • In this case, instead or replacing the delimiter group with nothing, capture the content between|after|before the delimiter(s) and return the captured group (content), $1, without the delimiters. To get the toggle effect will require one or more test strings with the matching desired replacement option (add delimiter(s) OR no delimiters)

    I have the matching content group $1 be the part to keep and then add or remove what you want around it, see two solution options below**

    I am not aware of if-else if-else if-else then replacements that I could build with regex, so

    If I understand correctly, you are looking for four different cases for the desired toggle effect:

    1. If opening and closing delimiter, remove opening and closing delimiter.
    2. If opening delimiter without closing delimiter, add closing delimiter.
    3. If closing delimiter without opening delimiter, add opening delimiter.
    4. If no opening or closing delimiter, add opening and closing delimiter.

    OPTION 1:
    Each option is only true for the specific case. Here are the regex patterns and replacement patterns for the four snippets.

    1) snippet_for_matching_delimiters:

    "snippet": "${TM_SELECTED_TEXT/^'''\n?((?:.*?)(?:\n.*)*)'''\n?$/$1/s}"
    

    2) snippet_for_unmatched_opening_delimiters"

    "snippet": "${TM_SELECTED_TEXT/^('''\n?(?:.*?)(?:\n.*)*)(?!'''\n?)$/s}"
    

    3) snippet_for_unmatched_closing_delimiters:

    "snippet": "${TM_SELECTED_TEXT/^('''\n?(?:.*?)(?:\n.*)*)(?!'''\n?)$/s}"
    

    4) snippet_for_no_opening_or_closing_delimiter:

    "snippet": "${TM_SELECTED_TEXT/^(?!'''\n?)((?:.*?)(?:\n.*)*)(?!'''\n?)$/```\n$1\n```/s}"
    
    

    Regex Demo matching delimiters: https://regex101.com/r/WXobxd/3
    Regex Demo unmatched opening delimiter: https://regex101.com/r/qscBxC/2
    Regex Demo unmatched closing delimiter: https://regex101.com/r/1smRle/2
    Regex Demo no_opening_or_closing_delimiter: https://regex101.com/r/GlH7LQ/1


    OPTION 2:

    IF OPENING AND CLOSING DELIMITER: THEN REMOVE BOTH DELIMITERS
    This only matches if there are no opening or closing delimiters present.

    "snippet": "${TM_SELECTED_TEXT/^(?!'''\n?)((?:.*?)(?:\n.*)*)(?!'''\n?)$/```\n$1\n```/s}"
    

    ELSE: ADD MISSING OPENING AND/OR DELIMITER:
    This option also captures the string target text inside the delimiters when both opening and closing delimiters are present. It will return all four options with both opening and closing delimiters.

    "snippet": "${TM_SELECTED_TEXT/^(?:'''\n?)?((?:.*?)(?:\n.*?)*?)(?:\n?'''\n?)?$/```\n$1\n```/s}"
    

    Regex Demo IF-THEN: https://regex101.com/r/WXobxd/3
    Regex Demo ELSE: https://regex101.com/r/Wv7DD0/3

    I used the ECMAScript regex flavor