Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VSCode Advanced Snippet Transform

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};"
    ]
  },
}
like image 617
jabacchetta Avatar asked Mar 03 '23 19:03

jabacchetta


2 Answers

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.

like image 127
Wiktor Stribiżew Avatar answered Mar 11 '23 04:03

Wiktor Stribiżew


@Wiktor will add the correct answer later but I wanted to say something about those conditionals in vscode's variable transforms that is too long for a comment.

It would be very nice is if you could do something like this:

"${TM_FILEPATH/.*\\(.+)\\((index\\.js)|(.*))/${3:?$1:$2}}/g}",

a conditional if group 3 isn't empty insert group 1 (the parent directory) else insert group 2 (the filename). But that doesn't work although the regex is fine (albeit simplified re: path separators and the file extension here).

Unfortunately, while a "plain" conditional does work very nicely:

${3:?There is an index.js file:There is not an index.js file}

it appears that using regex groups within the conditional text replacement is not supported. This would seem to follow from the grammar link (see snippet grammar]1. Looking at this part about conditionals:

'${' int ':?' if ':' else '}' 

I would say captured groups within the conditional if/else portions are not supported. It does not explicitly seem to allow capture groups - only if/else plain text.

like image 32
Mark Avatar answered Mar 11 '23 03:03

Mark