I have multiple .xml files in which I have a repeating string pattern. I want to replace each instance of that pattern with an integer starting with "1".
e.g.
Input:
APPLE is my favorite fruit. APPLE is red in color.
Expected Output:
1 is my favorite fruit. 2 is red in color.
For simple incrementing integers from 1, 2, 3, ... (or 0, 1, 2, ...) you can use the new snippet variables $CURSOR_NUMBER or $CURSOR_INDEX respectively.
You would still have to find a way to select all the find matches you want to replace (and so I think the extension below is better). You could use Ctrl+D to progressively select the occurrences you want or using the Find Widget:
Find: APPLE
and then Alt+Enter to select all matches and then insert a snippet like:
"Increment integer": {
"prefix": "i++",
"body": [
"${CURSOR_NUMBER}",
]
},
by typing its trigger prefix i++ and voila, all the APPLE instances are replaced by increasing integers.
I am sure there are other extensions that can do this, but using one that I wrote this is very easy: Find and Transform.
Create this keybinding (in your keybindings.json):
{
"key": "alt+y", // whatever keybinding you want
"command": "findInCurrentFile",
"args": {
// "find": "APPLE", // actually not necessary
"replace": "${matchNumber}",
"matchCase": true // match only APPLE, not Apple
}
}
That will replace whatever word your cursor is on with the matchNumber starting at 1. If you wanted to start at 0, use ${matchIndex}.
If you need to specify a more complicated regex find, you can do that too.
With Regex Text Generator you can do this.
select all the APPLE instances you want, any way you like, Ctrl+D, Selet All Occurrences, Alt+Enter from Find dialog
execute command Generate Text based on regular expression
as match expression use: .*
as generator expression use: {{=i+1}}
Related
I would love to set my 'tranformTOLowerCase' and 'tranformToUpperCase' to the same keybind, and use the when clause option that vs code has, but I can't find the right property to rely on,
I'v looked in the vsCode documentation under when contexts and didn't find a solution,
Does anyone know any different way?
I don't think there is any keybinding context key that will help to differentiate between text that is uppercase and text that is lowercase.
But you can use an extension that I wrote to do this: Find and Transform.
Create this keybinding in your keybindings.json:
{
"key": "alt+d", // whatever keybinding you want
"command": "findInCurrentFile",
"args": {
"find": "((([a-z])|([A-Z]))(.*))", // is the first letter upper of lowercase?
"replace": "${3:+${1:/upcase}}${4:+${1:/downcase}}",
"isRegex": true,
"matchCase": true, // this must be set to true to distinguish cases
"restrictFind": "selections", // works on your selection(s), one or many
}
}
As you can see, it tests the first letter. If it is lowercase a-z, then the selection is uppercased. And vice-versa, if the first letter is [A-Z], then the selection is lowercased.
If you can have digits, underscores or other non-alphabetic characters as the first character, this simple first-letter matching won't work and you would have to keep testing characters which would make for a messy regex, but could probably be done. [Let me know if you need that, I think I know how it can be done, although it is a different approach.]
The replacement:
"replace": ${3:+${1:/upcase}}${4:+${1:/downcase}}
is pretty interesting. The $n's refer to capture groups from the find regex. ${3:+${1:/upcase}} means if there is a capture group 3 (the first letter is [a-z], then upcase all of capture group 1.
And similarly if the first letter is [A-Z], so there is a capture group 4, then it and the rest of the selection, capture group 1, will be lowercased.
In a normal vscode snippet or keybinding you can not embed a capture group inside a conditional like ${3:+${1:/upcase}}. ${3:+text to add here if group 3} normally takes only text and cannot resolve variables or capture groups, much less transform their case like ${1:/upcase} inside the conditional - but the extension can.
Say for example I have a very long line of text in a single line in VS Code (let's pretend that the example given below is very long).
0xffffffffeeeeeeee02020202aaaaaaaa
At first I placed my cursor after the characters 0x.
(the cursor is denoted by the | character in the example below)
0x|ffffffffeeeeeeee02020202aaaaaaaa
Then I want to add more cursors after every N characters from the current cursor. In this case N is equal to 8 and I want to do this twice to add two more cursor like in the example below.
0x|ffffffff|eeeeeeee|02020202aaaaaaaa
So that after I press the following sequence of keys in the keyboard, in this case those sequence of keys are ,(space)0x I should be able to get these final result.
0x, 0x|ffffffff, 0x|eeeeeeee, 0x|02020202aaaaaaaa
After I deselect the cursors I should be getting this
0x, 0xffffffff, 0xeeeeeeee, 0x02020202aaaaaaaa
Is this possible to do in VS code?
There is a straightforward regex that can do what you want:
Find: ^0x|(.{8})(?!$)
But you have to enable the Find in Selection option and trigger the Select All Matches command yourself after entering it.
Or use a macro extension like multi-command and this keybinding to automate it:
{
"key": "alt+p",
"command": "extension.multiCommand.execute",
"args": {
"sequence": [
{
"command": "editor.actions.findWithArgs",
"args": {
"findInSelection": true,
"isRegex": true,
"searchString": "^0x|.{8}",
}
},
"editor.action.selectAllMatches",
"cursorRight"
]
},
}
You must select up to where you want the last cursor and then trigger the macro.
Because of a flaky implementation, you must start with the Find in Selection option disabled in the Find Widget. I haven't found a way around that.
The setting Editor > Find: Seed Search String From Selection must be set to never. Otherwise your selected text will over-ride the searchString from the macro above.
Here is the pure regex method with no extensions:
Enter ^0x|(.{8})(?!$) in your Find Widget with the regex option enabled.
^0x the first part of the string you ultimately want a cursor after.
(.{8})(?!$) select each 8-character block, but not the last - that is why there is a negative lookahead for the end of the line (?!$) - so the last 8 characters are not matched. Don't worry, there will be a cursor in front of those last 8 characters as you want. (.{8}) doesn't actually need to be in a capture group, it is just clearer to see.
Select all the text to match: 0xffffffffeeeeeeee. Stop the selection there - wherever you want the last cursor.
Enable the Find in Selection option in the Find Widget by Alt+L.
Alt+Enter to select all the find matches respecting the Find in Selection option: editor.action.selectHighlights.
Step (4) will select your matches - you should have 4 for the above string. But you don't want the matches selected you just want a cursor at the beginning of each, so do step (5):
Right arrow: this cancels each selection with a cursor at the right end of each.
Type.
You can use the extension Select By
It has a command to add a new cursor by keyboard
{
"key": "ctrl+i ctrl+alt+right", // or any other key combo
"when": "editorTextFocus",
"command": "selectby.addNewSelection",
"args": {"offset": 8}
}
Now the offset is hard coded but I will add an option to ask the user the offset.
The possibility of the context switch is already working. I have to update the README.
I'm writing some JS that requires a bunch of similar variables, numbered. I can do this just fine with multiple cursors etc, but since I'm using Emmet so much I wondered if there was a simple one line way of writing a list like:
let element1, element2, element3 ...
Is there a reason?
If you have to regularly have to apply numbers at the current cursor positions you can use the extension Regex Text Generator with a predefined setting.
"regexTextGen.predefined": {
"sequantial numbers" : {
"originalTextRegex": "(.*)",
"generatorRegex": "{{=i+1}}"
}
}
Place the cursors at the location needed (select all element and RightArrow)
execute command: Generate text based on Regular Expression
choose the sequantial numbers option
Enter and possibly adjust the offset
If you don't define a preset you can type the expressions yourself.
If you don't get an emmet answer, an extension I wrote, Find and Transform, can do this pretty easily:
{ // put this keybinding into your keybindings.json
"command": "findInCurrentFile",
"key": "alt+i",
"args": {
// "find": "element", // not necessary
"replace": "$1${matchNumber}",
"restrictFind": "line",
"isRegex": true
}
}
Just put your cursor on the word that you want matched. And then each match will be replaced by itself plus an incremented counter.
Thanks for these examples, I was using Asuka's extension Insert Numbers and it's quite helpful especially for additional sets of variables.
In this snippet defined in keybindings.json I'm trying to run a regex transform on the input result from choice dialogue - I need the same value inserted twice, in 1st position I need the part before the first semicolon and in 2nd position I want the full choice value.
I tried examples from the doc but they don't seem to be working with the choice, or am I missing something? How do I achieve this?
{
"key": "cmd+alt+ctrl+t",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"snippet": "Before semicolon: ${2/(\\w);{1,}/$1/} & Full value: ${2|1; testValue1, 2; testValue2|}"
}
},
expected output:
BeforeSemicolon: 1 & Full value: 1; testValue1
After some experimentation I have found a partial working.
It is important that the regex matches the full string. So add .* at the end.
(This should be added to the documentation)
{
"key": "cmd+alt+ctrl+t",
"command": "editor.action.insertSnippet",
"when": "editorTextFocus",
"args": {
"snippet": "Before semicolon: ${1/([^;]*).*/$1/} && Full value: ${1|123; testValue1,223; testValue2|}" }
},
And it only works for the first choice, AND you have to move the cursor inside the option text, just 1 Left Arrow is enough, this accepts the choice but keeps the field active. And then TAB. Now the transform is applied.
You have to file a bug for this.
Transform of choice variables is not supported. There is a general discussion mentioning this at https://github.com/Microsoft/vscode/pull/51621.
There are a couple of workarounds because transforms of placeholders do work as long as there is one non-transformed usage of the placeholder somewhere in the snippet. Then a transformed version elsewhere will work. This technique works for placeholder transforms but NOT choice transforms as you are trying to do. Why that difference I don't know??
Simpler workaround 1 using one placeholder:
"snippet": "Before semicolon: ${1/([^;]*).*/$1/} && Full value: ${1:1; testValue1}"
Here you would have to accept the default placeholder value (1; testValue1) or overwrite it with another value. Then on tab the transform will be applied to any other references to that same tabstop.
Complex workaround 2 usings multiple placeholders (in this case for 3 "choices":
"snippet": "Before semicolon: ${1/([^;]*).*/$1/}${2/([^;]*).*/$1/}${3/([^;]*).*/$1/} && Full value: ${1:1; testValue}${2:2; testValue}${3:3; testValue}"
Here you have to list your three (or however many you have) choices as separate placeholder tabstops. Then you will either tab to accept that placeholder you want or deletetab those you don't. It is a little tricky getting the delete/tab sequence down but once you have it, it is repetitve. You see the cursor appears to be at the beginning of the next "choice" but it is really at the end of the previous "choice".
Here is a demo of version 2 where I choose each "choice"/placeholder in turn. [I'm just using the alti keybinding here to simplify.]
Obviously if potentially have a lot of "choices" you'll probably want to use version 1. If you have a small number of "choices" that you want to be presented with and not have to type, version 2 does work pretty well.
I'm trying to replace a structure like this:
testVars.bread.componentFlour
testVars.bread.componentWater
to something like this:
testVars.dough.flour
testVars.dough.water
this happens with multiple variable names; I want to remove the component and have the first letter converted to lowercase to match CamelCase.
What I tried doing was matching testVars.bread.component(.) replacing it with testVars.dough.\l$1.
According to regex documentation, that should convert my match to lowercase. However, VSCode wants to insert \l as text.
How do I get VSCode to convert my match to lowercase?
EDIT: To clarify, this is strictly for VSCode's implementation, not a regex question itself. Matching this group in notepad++ and replacing with testVars.dough.\l\1 does exactly what I want it to do.
NEW ANSWER: !! Those case modifiers like \l and \u are being added to the find/replace functionality in vscode (both in find within one file and search across multiple files [v1.49] - see https://github.com/microsoft/vscode/pull/105101) (see https://github.com/microsoft/vscode-docs/blob/vnext/release-notes/v1_47.md#case-changing-in-regex-replace) - will be in v1.47 (find) and v1.49 (search across files).
So that your original thought to
Find : testVars.bread.component(.)
Replace : testVars.dough.\l$1
is now working.
For more on how the case modifiers work in vscode, see https://stackoverflow.com/a/62270300/836330
OLD ANSWER:
While you cannot replace with a lowercase identifier in vscode, you still have some of options.
Use a regex like (?<=testVars\.).*\.component(.*) so that testVars. is not captured - because you do not want to change its case.
Ctrl+Shift+L to select all your matches (same aseditor.action.selectHighlights).
Ctrl+Shift+P, type lowerand trigger that command (or make a keybinding for this unbound command).
Trigger your replaceAll
To speed that up you have two options. (1) Make a macro that would run steps 2, 3 and 4. Or (2) make a snippet that will transform your selections - effectively running steps 3 and 4 at once.
Snippet in your keybindings.json (not in a snippets file):
{
"key": "alt+m", // choose some keybinding
"command": "editor.action.insertSnippet",
"args": {
"snippet": "${TM_SELECTED_TEXT/.*\\.component(.*)/dough.${1:/downcase}/}"
},
},
and then Ctrl+Shift+L to select all, and alt+m to trigger the above insertSnippet.
Macro approach:
Using some macro extension, here multi-command, put this into your settings.json:
"multiCommand.commands": [
{
"command": "multiCommand.findReplaceLowercase",
"sequence": [
"editor.action.selectHighlights", {
"command": "editor.action.insertSnippet",
"args": {
"name": "replace and lowercase",
}
},
]
}
]
and a snippet, in one of your snippets files:
"replace and lowercase": { // this "label" is used in the macro
"prefix": "for",
"body": [
"${TM_SELECTED_TEXT/.*\\.component(.*)/dough.${1:/downcase}/}"
// "${TM_SELECTED_TEXT/(.*)/${1:/downcase}/}" // to downcase all selected text
],
"description": "replace selected text"
},
and a keybinding to trigger the macro:
{
"key": "alt+m", // choose some keybinding
"command": "extension.multiCommand.execute",
"args": { "command": "multiCommand.findReplaceLowercase" },
}
In the demo I copy the find regex into the find widget (or just write it there, it doesn't matter how it gets there or where focus is) and then hit alt+m (the macro keybinding) and that is it.
Obviously, this looks like a lot of work but you could keep reusing the macro and snippet, transforming to the replace result you would like the next time. And there you can use /downcase, /upcase, /capitalize and /pascalcase all you want which you can't use in the replace field of the find/search widgets.