0

I have this string

mark:: string1, string2, string3

I want it to be

mark:: xxstring1xx, xxstring2xx, xxstring3xx

The point is, I don't know how many times the matched string repeated. Sometimes there are 10 strings in the line, sometimes there is none. So far I have come up with this matching pattern mark:: ((.*)(, )+)*, but I'm unable to find a way to substitute individual matched string.

If possible I would like to have this output:

mark:: xxstring1xx
mark:: xxstring2xx
mark:: xxstring3xx

But if it's not possible it's fine to have the one-line solution

Ooker
  • 1,969
  • 4
  • 28
  • 58

2 Answers2

1

You can use

(\G(?!\A)\s*,\s*|mark::\s*)([^\s,](?:[^,]*[^\s,])?)

And replace with $1xx$2xx.

See the regex demo. Details:

  • (\G(?!\A)\s*,\s*|mark::\s*) - Group 1 ($1):
    • \G(?!\A)\s*,\s* - end of the previous successful match and then a comma enclosed with zero or more whitespaces
    • | - or
    • mark::\s* - mark:: and zero or more whitespaces
  • ([^\s,](?:[^,]*[^\s,])?) - Group 2 ($2):
    • [^\s,] - a char other than whitespace and comma
    • (?:[^,]*[^\s,])? - an optional sequence of zero or more non-commas and then a char other than a whitespace and a comma.

In Visual Studio Code file search and replace feature, you can use a Rust regex compliant regex:

(mark::(?:\s*(?:,\s*)?xx\w*xx)*\s*(?:,\s*)?)([^\s,](?:[^,]*[^\s,])?)

Replace with the same $1xx$2xx replacement pattern. Caveat: you need to hit the replace button as many times as there are matches.

See this regex demo showing the replacement stages.

Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • I put this into VScode and it says invalid escape. Do you know why? – Ooker Jun 22 '22 at 10:30
  • @Ooker Yes, because it is not compliant with neither ECMAScript nor Rust regex flavors. Where are you using it? In the Search and replace in files (magnifying glass) or in-document search and replace? – Wiktor Stribiżew Jun 22 '22 at 10:34
  • in all files (Ctrl+Shift+H). So VScode's regex is ECMAScript or Rust flavor? – Ooker Jun 22 '22 at 10:36
  • So, you are using the "magnifying glass", and you cannot use the `(\G(?!\A)\s*,\s*|mark::\s*)([^\s,](?:[^,]*[^\s,])?)` here because it is not compliant with Rust regex flavor. Use Notepad++ instead (Ctrl + Shift + F, and choose *Replace in Files*). – Wiktor Stribiżew Jun 22 '22 at 10:38
  • @Ooker I added a VSCode compliant solution. – Wiktor Stribiżew Jun 22 '22 at 10:55
1

By using snippets you can make use of their ability to use conditionals.

IF you can select the line first, this is quite easy. Use this keybinding in your keybindings.json:

{
  "key": "alt+w",            // whatever keybinding you want
  "command": "editor.action.insertSnippet",
  "args": {
    "snippet": "${TM_SELECTED_TEXT/(mark::\\s*)|([^,]+)(, )?/$1${2:+xx}$2${2:+xx}$3/g}"
  }
}

The find is simple: (mark::\\s*)|([^,]+)(, )?

replace: $1${2:+xx}$2${2:+xx}$3 Capture group 1 followed by xx if there is a group 2 ${2:+xx} : conditional, followed by group 2, followed by another conditional.

Demo:

snippet demo 1


If you have a bunch of these lines in a file and you want to transform them all at once, then follow these steps:

  1. In the Find widget, Find: (mark::\s*)(.*)$ with the regex option enabled.
  2. Alt+Enter to select all matches.
  3. Trigger your snippet keybinding from above.

Demo:

snippet transform demo 2


For your other version with separate lines for each entry, use this in the keybinding:

{
  "key": "alt+w",
  "command": "editor.action.insertSnippet",
  "args": {
    // single line version 
    // "snippet": "${TM_SELECTED_TEXT/(mark::\\s*)|([^,]+)(, )?/$1${2:+xx}$2${2:+xx}$3/g}"
    
    // each on its own line
    "snippet": "${TM_SELECTED_TEXT/(mark::\\s*)|([^,]+)(, )?/${2:+mark:: }${2:+xx}$2${2:+xx}${3:+\n}/g}"
  }
}

snippet demo 3

Mark
  • 143,421
  • 24
  • 428
  • 436