0

Background

  • Hi all, I'm wokring on a VScode extension for snippets. I highlighted all $name TextMate style variables like $CURRENT_YEAR and $TM_FILENAME_BASE.

    name in $name can be a integer, or a static variable.

  • I would also like to highlight variables like ${<INTEGER>:<DEFAULT>}, ${<INTEGER>:<DEFAULT>|<OPTIONAL_OPTIONS>} e.g. ${2:placeholder} and ${1:true|false}

  • Moreover, highlight transformed variables ${<STATIC_VAR>\<MATCH_REGEX>\<TRANSFORM_TARGET>\}. Example: ${TM_FILENAME/[\\.]/_/ replaces the first . with _.

  • All these example can be found in VScode documentation

  • I played Regex Golf a little, but I couldn't solve the "Balance" Chapter

Question

Is there a NEAT way to match all TextMate variables(see definition in chapter 7.2) with a Regular Expression (or other technique)?

In detail, I would want my Regex to match all $<NAME>, ${<INTEGER>:<DEFAULT>|<OPTION>}, and ${<STATIC_VAR>\<MATCH_REGEX>\<TRANSFORM_TARGET>\}

where

  • <NAME> can be digits and some specific letters like LINE_COMMENT;
  • <DEFAULT> and can be any text.
  • <STATIC_VAR> is some specific letters like LINE_COMMENT
  • <MATCH_REGEX> A regular expression, I'm not going to check if it is legal. <TRANSFORM_TARGET> The regular expression substitution output, not going to check.

My idea was to detect if there is a pair of brackets (curly parentheses). And the first word after parentheses opening is a <NAME>.

TextMate uses Oniguruma regular expression library by K. Kosako

What I've done

  • Match variable without {} with
match:"\\$(?:[0-9]+|TM_SELECTED_TEXT|<TYPED_ALL_POSSIBLE_NAMES>|TM_CURRENT_WORD|LINE_COMMENT)"
\\$(?= (?=<ALL_NAMES>)|{(?=<ALL_NAMES>):.+} )

Where <ALL_NAMES> represents all <NAME>s joined with pipe char |

Pablo LION
  • 1,294
  • 6
  • 20
  • It is not quite clear what the issue is with your "dirty" regex. What about `[$](\{)?\w+(?(1)(?:\/.*})?)` ([demo](https://regex101.com/r/N0NlPS/1))? – Wiktor Stribiżew Sep 13 '21 at 06:56
  • Yes! I just test it and it works beautifully. Before I worked with [regexr](regexr.com) and I knew nothing about "group conditional". By the way, your code did match the transformation but not the default value like `${1:OPT1|OPT2}`, please consider add a `:` like this `[$](\{)?\w+(?(1)(?:[\/:].+})?)` ([new demo](https://regex101.com/r/88xrY5/1))and answer the question so we can close it. – Pablo LION Sep 13 '21 at 07:46

2 Answers2

1

You can use a conditional construct here:

[$](\{)?\w+(?(1)(?:[\/:].*})?)

See the regex demo.

Details:

  • [$] - a $ char
  • (\{)? - an optional capturing group with ID = 1 that matches a { char
  • \w+ - one or more word chars
  • (?(1)(?:[\/:].*})?) - a conditional construct: if Group 1 matched, matches an optional sequence of a / or : and then any zero or more chars other than line break chars as many as possible up to the rightmost occurrence of a } char.
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • Sorry that I made a mistake in the code `(?(1)(?:[\/:].*})?)` because there should be at least 1 char after the `:` or `/`. I'm accepting this answer but I made a HUGE mistake while describing the question ... So I'm editing this question now. Please consider edit the answer accordingly. I'll finish it in <10min. – Pablo LION Sep 13 '21 at 07:50
  • The [conditional group](https://regex101.com/library/hP7hM9) used here did solve most of my question and it works correctly in `.tmLanguage.json` for VScode. – Pablo LION Sep 13 '21 at 07:54
  • 1
    @Pablion Yes, your question is a bit hectic.So, one char must be there after `/` or `:`? Then replace `.*` with `.+`. – Wiktor Stribiżew Sep 13 '21 at 08:07
  • Feels like I squeezed too many questions in one question. So I rewrite the question to make it clearer. Huge appreciation again. – Pablo LION Sep 13 '21 at 08:16
0

Credit to Wiktor Stribiżew

The regular expression

Test demo with explanation

[$]([{])?([0-9]+)?(?(2)|(?:TM_FILENAME))(?(1)(?(2)[:].+|[\/].+[\/].*[\/])})

What does this regex do:

  • Match all these (legal vscode snippet variables):
$1
$21
${1:default}
${1:default|option}
$TM_FILENAME
${TM_FILENAME/[\.]/_/}
${TM_FILENAME/a//}
  • And match NONE of these:
${1:}
${TM_FILENAME:a|b}
${TM_FILENAME/}
${TM_FILENAME/a/}
${TM_FILENAME//abc/}
${TM_FILENAME/a//a}

full .tmLanguage.json fomat code

"match": "[$]({)?([0-9]+)?(?(2)|(?:TM_SELECTED_TEXT|TM_CURRENT_LINE|TM_CURRENT_WORD|TM_LINE_INDEX|TM_LINE_NUMBER|TM_FILENAME_BASE|TM_FILENAME|TM_DIRECTORY|TM_FILEPATH|RELATIVE_FILEPATH|CLIPBOARD|WORKSPACE_NAME|WORKSPACE_FOLDER|CURRENT_YEAR_SHORT|CURRENT_YEAR|CURRENT_MONTH_NAME_SHORT|CURRENT_MONTH_NAME|CURRENT_MONTH|CURRENT_DATE|CURRENT_DAY_NAME_SHORT|CURRENT_DAY_NAME|CURRENT_HOUR|CURRENT_MINUTE|CURRENT_SECONDS_UNIX|CURRENT_SECOND|RANDOM|RANDOM_HEX|UUID|BLOCK_COMMENT_START|BLOCK_COMMENT_END|LINE_COMMENT))(?(1)(?(2)[:].+|[/].+[/].*[/])})",

Test in VSCode

enter image description here

Pablo LION
  • 1,294
  • 6
  • 20