vscode snippet - transform and replace filename - regex

my filename is
some-fancy-ui.component.html
I want to use a vscode snippet to transform it to
SOME_FANCY_UI
So basically
apply upcase to each character
Replace all - with _
Remove .component.html
Currently I have
'${TM_FILENAME/(.)(-)(.)/${1:/upcase}${2:/_}${3:/upcase}/g}'
which gives me this
'SETUP-PRINTER-SERVER-LIST.COMPONENT.HTML'
The docs doesn't explain how to apply replace in combination with their transforms on regex groups.

If the chunks you need to upper are separated with - or . you may use
"Filename to UPPER_SNAKE_CASE": {
"prefix": "usc_",
"body": [
"${TM_FILENAME/\\.component\\.html$|(^|[-.])([^-.]+)/${1:+_}${2:/upcase}/g}"
],
"description": "Convert filename to UPPER_SNAKE_CASE dropping .component.html at the end"
}
You may check the regex workings here.
\.component\.html$ - matches .component.html at the end of the string
| - or
(^|[-.]) capture start of string or - / . into Group 1
([^-.]+) capture any 1+ chars other than - and . into Group 2.
The ${1:+_}${2:/upcase} replacement means:
${1:+ - if Group 1 is not empty,
_ - replace with _
} - end of the first group handling
${2:/upcase} - put the uppered Group 2 value back.

Here is a pretty simple alternation regex:
"upcaseSnake": {
"prefix": "rf1",
"body": [
"${TM_FILENAME_BASE/(\\..*)|(-)|(.)/${2:+_}${3:/upcase}/g}",
"${TM_FILENAME/(\\..*)|(-)|(.)/${2:+_}${3:/upcase}/g}"
],
"description": "upcase and snake the filename"
},
Either version works.
(\\..*)|(-)|(.) alternation of three capture groups is conceptually simple. The order of the groups is important, and it is also what makes the regex so simple.
(\\..*) everything after and including the first dot . in the filename goes into group 1 which will not be used in the transform.
(-) group 2, if there is a group 2, replace it with an underscore ${2:+_}.
(.) group 3, all other characters go into group 3 which will be upcased ${3:/upcase}.
See regex101 demo.

Related

Change type of enclosure brackets with special conditions

What I'm trying to achieve is changing square brackets [] to curly/brace brackets {}.
There are two conditions, some start with [", the others end with "]
There will not be any occurrences where both exist in same string. Haven't run across any yet.
BEFORE:
[Strained breathing]
["Wanna Give My Love"
by The Sons of Rainier]
[Mavrick blows a fart]
["Hallelujah"
by The Sons of Rainer]
[Victor over the phone]
[The Korgi's "Everybody's
Got To Learn Sometime"]
[Lola chuckles]
["It's Good"
by Jack Hammer]
[Uno Hype's "Leave"]
Here's what I would like as the end results
AFTER:
[Strained breathing]
{"Wanna Give My Love"
by The Sons of Rainier playing}
[Mavrick blows a fart]
{"Hallelujah"
by The Sons of Rainer}
[Victor over the phone]
{The Korgi's "Everybody's
Got To Learn Sometime"}
[Lola chuckles]
{"It's Good"
by Jack Hammer}
{Uno Hype's "Leave"}
Here are my attempts:
Find: (?=\[")([\S\s]+?)\]
Replace: \{$1\}
Find: (?=\[[A-Z])([\S\s]+?)\"]
Replace: \{$1\}
Find: \["([A-Z][\S\s]+?)\]
Replace: \{$1\}
So frustrated that my light blub is still so dim in regards to regex.
Thanks in Advance
You could use this regex:
\[("[^]]+|[^]]+")\]
which matches a [ followed by either
a " and some number of non-] characters; or
some number of non-] characters followed by a "
and then followed by a ], and replace it with {\1}.
Regex demo on regex101
You can use
\[([^]["]*"[^][]*)]
Explanation
\[ Match [
( Capture group 1
[^]["]* Optionally match any char except ] [ "
" Then match a single "
[^][]* Optionally match any char except ] [
) Close group 1
] Match ]
Regex demo
In the replacement use {\1}

Matching pattern repeats for unknown times. How to replace each matched string?

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
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:
If you have a bunch of these lines in a file and you want to transform them all at once, then follow these steps:
In the Find widget, Find: (mark::\s*)(.*)$ with the regex option enabled.
Alt+Enter to select all matches.
Trigger your snippet keybinding from above.
Demo:
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}"
}
}
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.

How to apply more than on transform on visual studio code snippets?

Hopefully you have some experience with visual studio code snippet writing if you have opened this and you can help me.
I am trying to get better at writing visual studio code snippets.
This is one I have at the moment:
"Styled Template": {
"prefix": "sty",
"body": [
"import styled from \"styled-components\";",
"",
"const colors = (props) => props.theme.colors.${TM_FILENAME_BASE/(.*)/${1:/downcase}/};",
"",
"export const Container = styled.div`",
" display: flex;",
" width: 100%;",
" height:100%;",
"`;",
"$2"
],
"description": "Styled Template"
},
As you can see above I am using the filename base contant in my snippet and I am transforming the text to be downcase but I also need to transform it with another regex so replace the text '.styled' in the name with nothing ''.
Is it possible to add 2 transforms on the same element? I am struggling to find a way at the moment.
You can use
${TM_FILENAME_BASE/^(?:(.*?)(?:\.styled))?(.*)$/${1:/downcase}${2:/downcase}/}
See the regex demo
Pattern details
^ - start of string
(?:(.*?)(?:\.styled))? - an optional occurrence of:
(.*?) - Group 1: any zero or more chars other than line break chars, as few as possible
(?:\.styled) - a .styled substring
(.*) - Group 2: any zero or more chars other than line break chars, as many as possible
$ - end of string.
So, in this case, the part before .styled is captured into Group 1 and the part after it is captured in Group 2. The replacement is a concatenation of these two groups (with /downcase applied to both).

Conditionals and regex doubts with grok filter in logstash

I'm taking my first steps with elastic-stack with a practical approach, trying to make it work with an appliacation in my enviroment. I'm having difficulties understanding from scratch how to write grok filters. I would like to have one like this one working, so from that one, I can work the rest of them.
I've taken some udemy courses, I'm reading this "Elastic Stack 6.0", I'm reading the documentation, but I can't find a way to make this work as intended.
So far, the only grok filter I'm using that actually works, is as simple as (/etc/logstash/config.d/beats.conf)
input {
beats {
port => 5044
}
}
filter {
grok {
match => { 'message' => "%{DATE:date} %{TIME:time} %
{LOGLEVEL:loglevel}"
}
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
}
}
This is one of the log entries I'll need to work with, but there are many with different forms. I just need to have this one sorted out so I can adapt the filters to the rest.
2019-02-05 19:13:04,394 INFO [qtp1286783232-574:http://localhost:8080/service/soap/AuthRequest] [name=admin#example.com;oip=172.16.1.69;ua=zclient/8.8.9_GA_3019;soapId=3bde7ed0;] SoapEngine - handler exception: authentication failed for [admin], invalid password
I'd like to have this info, only when there is a "soapId" and when the field next to "INFO" starts with "qtq":
date: 2019-02-05
time: 19:13:04,394
loglevel: INFO
identifier: qtp1286783232-574
soap: http://localhost:8080/service/soap/AuthRequest
Which could also end in things like "GetInfoRequest" or "NoOpRequest"
account: admin#example.com
oip: 172.16.1.69
client: zclient/8.8.9_GA_3019
soapid: 3bde7ed0
error: true (if either "invalid password" or "authentication failed" are found in the line)
If the conditions are not met, then I will apply other filters (which hopefully I will be able to write adapting this one as a base).
You can't have false in the output if you have invalid password in the input. You can only match what is there in the string.
I think you may use
%{DATE:date} %{TIME:time} %{LOGLEVEL:loglevel} *\[(?<identifier>qtp[^\]\[:]*):(?<soap>[^\]\[]*)]\s*\[name=(?<account>[^;]+);oip=(?<oip>[0-9.]+);ua=(?<client>[^;]+);soapId=(?<soapId>[^;]+);].*?(?:(?<error>authentication failed).*)?$
Here are the details of the added patterns:
* - 0+ spaces
\[ - a [ char
(?<identifier>qtp[^\]\[:]*) - Named group "identifier": qtp and then 0+ chars other than :, ] and [
: - a colon
(?<soap>[^\]\[]*) - Named group "soap": 0+ chars other than ] and [
]\s*\[name= - a ], then 0+ whitespaces and [name= substring
(?<account>[^;]+) - Named group "account": 1+ chars other than ;
;oip= - a literal substring
(?<oip>[0-9.]+) - Named group "oip": 1+ digits and/or dots
;ua= - a literal substring
(?<client>[^;]+) - Named group "client": 1+ chars other than ;
;soapId= - a literal substring
(?<soapId>[^;]+) - Named group "soapId": 1+ chars other than ;
;] - a literal substring
.*? - any 0+ chars other than line break chars, as few as possible
(?:(?<error>authentication failed).*)? - an optional group matching 1 or 0 occurrences of
Named group "error": authentication failed substring
.* - all the rest of the line
$ - end of input.

How can I do multiple replace using a shared backreference?

I have a need to do some data-transformation for data load compatibility. The nested key:value pairs need to be flattened and have their group id prepended to each piece of child data.
I've been trying to understand the page at
Repeating a Capturing Group vs. Capturing a Repeated Group but can't seem to wrap my head around it.
My expression so far:
"(?'group'[\w]+)": {\n((\s*"(?'key'[^"]+)": "(?'value'[^"]+)"(?:,\n)?)+)\n},?
Working sample: https://regex101.com/r/Wobej7/1
I'm aware that using 1 or more intermediate steps would simplify the process but at this point I want to know if it's even possible.
Source Data Example:
"g1": {
"k1": "v1",
"k2": "v2",
"k3": "v3"
},
"g2": {
"k4": "v4",
"k5": "v5",
"k6": "v6"
},
"g3": {
"k7": "v7",
"k8": "v8",
"k9": "v9"
}
Desired transformation:
{"g1","k1","v1"},
{"g1","k2","v2"},
{"g1","k3","v3"},
{"g2","k4","v4"},
{"g2","k5","v5"},
{"g2","k6","v6"},
{"g3","k7","v7"},
{"g3","k8","v8"},
{"g3","k9","v9"}
TL; DR
Step 1
Search for:
("[^"]+"):\s*{[^}]*},?\K
Replace with \1
Live demo
Step 2
Search for:
(?:"[^"]+":\s*{|\G(?!\A))\s*("[^"]+"):\s*((?1))(?=[^}]*},?((?1)))(?|(,)|\s*}(,?).*\R*)
Replace with:
{\3,\1,\2}\4\n
Live demo
Whole philosophy
This is not going to be a one-liner regex solution for different reasons. The most important one is we can neither store a part of a match for later referring nor are able to do infinite lookbehinds in PCRE. But fortunately most of similar problems could be done in two steps.
Very first step should be moving group name to end of {...} block. This way we can have group name each time we want to transform our matches into a single line output.
("[^"]+"):\s*{[^}]*},?\K
( Start of capturing group #1
"[^"]+" Match a group name
) End of CG #1
:\s*{ Group name should precede bunch of other characters
[^}]*},? We have to go further up to end of block
\K Throw away every thing matched so far
We have our group name held in first capturing group and have to replace whole match with it:
\1
Now a block like this:
"g1": {
.
.
.
},
Appears like this one:
"g1": {
.
.
.
},"g1"
Next step is to match key:value pairs of each block beside capturing recent added group name at the end of block.
(?:"[^"]+":\s*{|\G(?!\A))\s*("[^"]+"):\s*((?1))(?=[^}]*},?((?1)))(?|(,)|\s*}(,?).*\R*)
(?: Start of a non-capturing group
"[^"]+" Try to match a group name
:\s*{ A group name should come after bunch of other characters
| Or
\G(?!\A) Continue from previous match
) End of NCG
\s*("[^"]+"):\s*((?1)) Then try to match and capture a key:value pair
(?=[^}]*},?((?1))) Simultaneously match and capture group name at the end of block
(?|(,)|\s*}(,?).*\R*) Match remaining characters such as commas, brace or newlines
This way in each single successful try of regex engine we have four captured data that their order is the key:
{\3,\1,\2}\4\n
\3 Group name (that one added at the end of block)
\1 Key
\2 Value
\4 Comma (may be there or may not)