Search code examples
javascriptregexvisual-studio-codecode-snippetsvscode-snippets

VS Code Snippet transform: if (regex matches X) { use transform A } else if (regex matches Y) { use transform B }


I'm trying to create a snippet that creates a class name based on the file path. If the file is named index.js, I'd like the class name to take the folder name. Otherwise, use the file name.

I've got a transform (shown below) that is currently working if the file is named index.js (it correctly inserts the folder name).

How would I expand on this (assuming it's even possible) to also work for the second case?

I did notice from the VSCode documentation that there are some basic if/else formats that you can use, which will only insert the given text if a capture group exists. I have been able to get those working with some simple examples. But not sure if these can be used in some way to accomplish what I'm trying to do.

Current snippet:

{
  "mySnippet": {
    "prefix": "cls",
    "body": [
        "class ${TM_FILEPATH/[\\/\\w$]+\\/(?!index)|\\/index.js//g} {}",
        "export default ${TM_FILEPATH/[\\/\\w$]+\\/(?!index)|\\/index.js//g};"
    ]
  },
}

Solution

  • I suggest using

    ${TM_FILEPATH/.*[\\/\\\\]([^\\/\\\\]+)[\\/\\\\]index\\.js$|.*[\\/\\\\](.*)/$1$2/}
    

    If you do not want to include the file extension to the output use

    ${TM_FILEPATH/.*[\\/\\\\]([^\\/\\\\]+)[\\/\\\\]index\\.js$|.*[\\/\\\\](.*?)(?:\\.[^.]*)$/$1$2/}
    

    The regex #1 will work as shown here or regex #2 here, see its graph:

    enter image description here

    The point here is to use two alternatives separated with | alternation operator that will match the whole string while capturing the parts you need, making sure the more specific (with the known file name) alternative comes first, and the more generic one (that will match any file name, will come last. The replacement pattern will be two backreferences, $1$2, since only one will actually contain some text after a match has been found.

    Regex details

    Note the backslashes are doubled because the pattern is passed as a string literal, and / chars must be escaped because the string literal contains a "stringified" regex literal.

    • .*[\/\\]([^\/\\]+)[\/\\]index\.js$:
      • .* - any 0+ chars other than line break chars, as many as possible
      • [\/\\] - a / or \
      • ([^\/\\]+) - Capturing group 1: one or more (+) chars other than / and \ ([^...] is a negated character class)
      • [\/\\] - a / or \
      • index\.js - an index.js substring
      • $ - end of string
    • | - or
    • .*[\/\\](.*):
      • .* - any 0+ chars other than line break chars, as many as possible
      • [\/\\] - a / or \
      • (.*) - Capturing group 2: any 0+ chars other than line break chars, as many as possible
      • (.*?)(?:\.[^.]*)?$ - will capture into Group 2 any 0 or more chars other than line break chars, as few as possible, and then will try to match an optional sequence of . and 0+ non-dot chars up to the end of the string ($).

    So, the full code snippet will look like

    {
      "mySnippet": {
        "prefix": "cls",
        "body": [
          "class ${TM_FILEPATH/.*[\\/\\\\]([^\\/\\\\]+)[\\/\\\\]index\\.js$|.*[\\/\\\\](.*?)(?:\\.[^.]*)$/$1$2/} {}",
          "export default ${TM_FILEPATH/.*[\\/\\\\]([^\\/\\\\]+)[\\/\\\\]index\\.js$|.*[\\/\\\\](.*?)(?:\\.[^.]*)$/$1$2/};"
        ]
      }
    }
    

    Feel free to adjust it as you want.