How to effectively search and replace in Vim by first "testing" or "preview" the search part? - regex

Sometimes I want to search and replace in Vim using the s/search_for/replace_with/options format, but the search_for part becomes a complicated regex that I can't get right the first time.
I have set incsearch hlsearch in my .vimrc so Vim will start highlighting as I type when I am searching using the /search_for format. This is useful to first "test"/"preview" my regex. Then once I get the regex I want, I apply to the s/ to search and replace.
But there is two big limitation to this approach:
It's a hassle to copy and paste the regex I created in / mode to s/ mode.
I can't preview with matched groups in regex (ie ( and )) or use the magic mode \v while in /.
So how do you guys on SO try to do complicated regex search and replace in Vim?

Test your regex in search mode with /, then use s//new_value/. When you pass nothing to the search portion of s, it takes the most recent search.
As #Sam Brink also says, you can use <C-r>/ to paste the contents of the search register, so s/<C-r>//new_value/ works too. This may be more convenient when you have a complicated search expression.

As already noted, you can practice the search part with /your-regex-here/. When that is working correctly, you can use s//replacement/ to use the latest search.
Once you've done that once, you can use & to repeat the last s/// command, even if you've done different searches since then. You can also use :&g to do the substitute globally on the current line. And you could use :.,$&g to do the search on all matches between here (.) and the end of the file ($), amongst a legion of other possibilities.
You also, of course, have undo if the operation didn't work as you expected.

As the others have noted I typically use s//replacement/ to do my replacements but you can also use <C-r>/ to paste what is in the search register. So you can use s/<C-r>//replacement/ where the <C-r>/ will paste your search and you can do any last minute changes you want.
<C-r> inserts the contents of a register where the cursor is
The / register holds the most recent search term
:registers will display the contents of every register so you can see whats available.

Since Neovim 0.1.7 there is the Incremental (“live”) :substitute function. (so this only works in Neovim!)
To enable it set the incommand option:
:set inccommand=split
It was announced here: https://neovim.io/news/2016/11/

Related

How do I join two regular expressions into one in Notepad++?

I've been searching a lot in the web and in here but I can't find a solution to this.
I have to make two replacements in all registry paths saved in a text file as follows:
replace all asterisc with: [#42]
replace all single backslashes with two.
I already have two expressions that do this right:
1st case:
Find: (\*) - Replace: \[#42\]
2nd case:
Find: ([^\\])(\\)([^\\]) - Replace: $1$2\\$3
Now, all I want is to join them together into just one expression so that I can do run this in one time only.
I'm using Notepad++ 6.5.1 in Windows 7 (64 bits).
Example line in which I want this to work (I include backslashes but i don't know if they will appear right in the html):
HKLM\SOFTWARE\Classes\*\shellex\ContextMenuHandlers\
I already tried separating it with a pipe, like I do in Jscript (WSH), but it doesn't work here. I also tried a lot of other things but none worked.
Any help?
Thanks!
Edit: I have put all the backslashes right, but the page html seem to be "eating" some of them!
Edit2: Someone reedited my text to include an accent that doesn't remove the backslashes, so the expressions went wrong again. But I got it and fixed it. ;-)
Sorry, but this was my first post here. :)
As everyone else already mentioned this is not possible.
But, you can achieve what you want in Notepad++ by using a Macro.
Go to "Macro" > "Start Recording" menu, apply those two search and replace regular expressions, press "Stop Recording", then "Save Current Recorded Macro", there give it a name, assign a shortcut, and you are done. You now can reuse the same replacements whenever you want with one shortcut.
Since your replacement strings are totally different and use data that come not from any capture (i.e. [#42]), you can't.
Keep in mind that replacement strings are only masks, and can not contain any conditional content.

Regexp-replace: Multiple replacements within a match

I'm converting our MVC3 project to use T4MVC. And I would like to replace java-script includes to work with T4MVC as well. So I need to replace
"~/Scripts/DataTables/TableTools/TableTools.min.js"
"~/Scripts/jquery-ui-1.8.24.min.js"
Into
Scripts.DataTables.TableTools.TableTools_min_js
Scripts.jquery_ui_1_8_24_min_js
I'm using Notepad++ as a regexp tool at the moment, and it is using POSIX regexps.
I can find script name and replace it with these regexps:
Find: \("~/Scripts/(.*)"\)
Replace with \(Scripts.\1\)
But I can't figure out how do I replace dots and dashes in the file names into underscores and replace forward slashes into dots.
I can check that js-filename have dot or dash in a name with this
\("~/Scripts/(?=\.*)(?=\-*).*"\)
But how do I replace groups within a group?
Need to have non-greedy replacement within group, and have these replacements going in an order, so forward slashes converted into a dot will not be converted to underscore afterwards.
This is a non-critical problem, I've already done all the replacements manually, but I thought I'm good with regexp, so this problem bugs me!!
p.s. preferred tool is Notepad++, but any POSIX regexp solution would do -)
p.p.s. Here you can get a sample of stuff to be replaced
And here is the the target text
I would just use a site like RegexHero
You can past the code into the target string box, then place (?<=(~/Script).*)[.-](?=(.*"[)]")) into the Regular Expression box, with _ in the Replacement String box.
Once the replace is done, click on Final String at the bottom, and select Move to target string and start a new expression.
From there, Paste (?<=(<script).*)("~/)(?=(.*[)]" ))|(?<=(Url.).*)(")(?=(.*(\)" ))) into the Regular Expression box and leave the Replacement String box empty.
Once the replace is done, click on Final String at the bottom, and select Move to target string and start a new expression.
From there paste (?<=(Script).*)[/](?=(.*[)]")) into the Regular Expression box and . into the Replacement String box.
After that, the Final String box will have what you are looking for. I'm not sure the upper limits of how much text you can parse, but it could be broken up if that's an issue. I'm sure there might be better ways to do it, but this tends to be the way I go about things like this. One reason I like this site, is because I don't have to install anything, so I can do it anywhere quickly.
Edit 1: Per the comments, I have moved step 3 to Step 5 and added new steps 3 and 4. I had to do it this way, because new Step 5 would have replaced the / in "~/Scripts with a ., breaking the removal of "~/. I also had to change Step 5's code to account for the changed beginning of Script
Here is a vanilla Notepad++ solution, but it's certainly not the most elegant one. I managed to do the transformation with several passes over the file.
First pass
Replace . and - with _.
Find: ("~/Scripts[^"]*?)[.-]
Replace With: \1_
Unfortunately, I could not find a way to match only the . or -, because it would require a lookbehind, which is apparently not supported by Notepad++. Due to this, every time you execute the replacement only the first . or - in a script name will be replaced (because matches cannot overlap). Hence, you have to run this replacement multiple times until no more replacements are done (in your example input, that would be 8 times).
Second pass
Replace / with ..
Find: ("~/Scripts[^"]*?)/
Replace with: \1.
This is basically the same thing as the first pass, just with different characters (you will have to this 3 times for the example file). Doing the passes in this order ensures that no slashes will end up as underscores.
Third pass
Remove the surrounding characters.
Find: "~/(Scripts[^"]*?)"
Replace with: \1
This will now match all the script names that are still surrounded by "~/ and ", capturing what is in between and just outputting that.
Note that by including those surrounding characters in the find patterns of the first two passes, you can avoid converting the . in strings that are already of the new format.
As I said this is not the most convenient way to do it. Especially, since passes one and two have to be executed manually multiple times. But it would still save a lot of time for large files, and I cannot think of a way to get all of them - only in the correct strings - in one pass, without lookbehind capabilities. Of course, I would very much welcome suggestions to improve this solution :). I hope I could at least give you (and anyone with a similar problem) a starting point.
If, as your question indicates, you'd like to use N++ then use N++ Python Script. Setup the script and assign a shortcut key, then you have a single pass solution requiring only to open, modify, and save... can't get much simpler than that.
I think part of the problem is that N++ is not a regex tool and the use of a dedicated regex tool
, or even a search/replace solution, is sometimes warranted. You may be better off, both in speed and in time value using a tool made for text processing vs editing.
[Script Edit]:: Altered to match the modified in/out expectations.
# Substitute & Replace within matched group.
from Npp import *
import re
def repl(m):
return "(Scripts." + re.sub( "[-.]", "_", m.group(1) ).replace( "/", "." ) + ")"
editor.pyreplace( '(?:[(].*?Scripts.)(.*?)(?:"?[)])', repl )
Install:: Plugins -> Plugin Manager -> Python Script
New Script:: Plugins -> Python Script -> script-name.py
Select target tab.
Run:: Plugins -> Python Script -> Scripts -> script-name
[Edit: An extended one-liner PythonScript command]
Having need for the new regex module for Python (that I hope replaces re) I played around and compiled it for use with the N++ PythonScript plugin and decided to test it on your sample set.
Two commands on the console ended up with the correct results in the editor.
import regex as re
editor.setText( (re.compile( r'(?<=.*Content[(].*)((?<omit>["~]+?([~])[/]|["])|(?<toUnderscore>[-.]+)|(?<toDot>[/]+))+(?=.*[)]".*)' ) ).sub(lambda m: {'omit':'','toDot':'.','toUnderscore':'_'}[[ key for key, value in m.groupdict().items() if value != None ][0]], editor.getText() ) )
Very sweet!
What else is really cool about using regex instead of re was that I was able to build the expression in Expresso and use it as is! Which allows for a verbose explanation of it, just by copy-paste of the r'' string portion into Expresso.
The abbreviated text of which is::
Match a prefix but exclude it from the capture. [.*Content[(].*]
[1]: A numbered capture group. [(?<omit>["~]+?([~])[/]|["])|(?<toUnderscore>[-.]+)|(?<toDot>[/]+)], one or more repetitions
Select from 3 alternatives
[omit]: A named capture group. [["~]+?([~])[/]|["]]
Select from 2 alternatives
["~]+?([~])[/]
Any character in this class: ["]
[toUnderscore]: A named capture group. [[-.]+]
[toDot]: A named capture group. [[/]+]
Match a suffix but exclude it from the capture. [.*[)]".*]
The command breakdown is fairly nifty, we are telling Scintilla to set the full buffer contents to the results of a compiled regex substitution command by essentially using a 'switch' off of the name of the group that isn't empty.
Hopefully Dave (the PythonScript Author) will add the regex module to the ExtraPythonLibs part of the project.
Alternatively you could use a script that would do it and avoid copy pasting and the rest of the manual labor altogether. Consider using the following script:
$_.gsub!(%r{(?:"~/)?Scripts/([a-z0-9./-]+)"?}i) do |i|
'Scripts.' + $1.split('/').map { |i| i.gsub(/[.-]/, '_') }.join('.')
end
And run it like this:
$ ruby -pi.bak script.rb *.ext
All the files with extension .ext will be edited in-place and the original files will be saved with .ext.bak extension. If you use revision control (and you should) then you can easily review changes with some visual diff tool, correct them if necessary and commit them afterwards.

VIM - how to paste search register without special characters?

In insert mode, Ctrlr/ (see :h "/) will paste the contents of the search register. However, if you search for a word with * and paste the search register, it will put in the special word boundary characters \< and \>. Or, if you're lucky, you might get '\Vsearch. I think this is silly, but I'm sure VIM has its reasons for making my life harder. How would I go about getting the contents of the search register without special vim magic characters?
The search register may contain no (simple /foo search), few (\<cword\> search from * command), or a lot (^\%(foo\|bar\)\w\+!\?) "special characters". For the first two, there's usually a single match, for the last, there can be many matches.
Initially I've used a mapping that filtered away the special regexp atoms, but then I've written a more general PatternComplete plugin (just published), that provides insert-mode and command-line mappings to insert the match(es). It also allows you to enter a regexp and then insert all matches.
Why would you want the search register to contain anything else than exactly the previous search pattern? #/ contains the pattern, not the match.
If you do /\Vfoo*bar it's normal and expected that you get \Vfoo*bar in the / register. Same for /foo\*bar where you'd get foo\*bar… The point of the search register is to be reused for further searches and substitutions: if it contained foo*bar instead of \Vfoo*bar, you couldn't do n or N or :s//baz which would make everyone's life a lot harder.
Since there is no verynomagic option you can set in your ~/.vimrc I guess that you could do a substitution on the content of the register before pasting it.
fee faa foo*bar baz bam
/\Vfoo*bar<CR>
:reg /<CR>
\Vfoo*bar
:let #a = substitute(#\, '\\V', '', '')<CR>
"ap
foo*bar
Maybe you could create a mapping that does the substitution and the pasting in one go.
But I have a hunch that what you really want is to reuse the matched text, not the search pattern. If I'm right, see this tip for a possible solution.

How to recall search pattern when writing replace regex pattern in Vim?

Here's the scenario: I've got a big file filled with all sorts of eclectic rubbish that I want to regex. I fiddle around and come up with a perfect search pattern by using the / command and seeing what it highlights.
Now I want to use that pattern to replace with. So, I start typing :%s/ and I cannot recall what the pattern was. Is there some magical keyboard command that will pull in my last search pattern here? If I'm writing a particularly complex regex, I have even opened up a new MacVim window, typed the regex from the first window into a buffer there, then typed it back into the Vim window when writing the replace pattern. There has got to be a better way of doing so.
Found my own answer after having written up the question: Vim tips lists it as substitute last search and can be done in one of two ways.
Using an empty search:
:%s//replace/g
By pressing Ctrl + r then / to recall the material from the search buffer (/ - you can use any other named buffer by substituting the / for the letter representing that buffer).
There is a / register that contains the current search pattern, so you can insert the search pattern into whatever you are typing (either in insert mode or on the command line) with <CTRL-R>/
Another option: after entering the pattern with <CTRL-R>/, edit the command line with <CTRL-F>.
:help cmdwin

How can I match the beginning of a line in dreamweaver with regex?

Problem:
^.+ matches only the first line of the source code in dreamweaver. I need it to match each line so that I can wrap each full line in P tags. I have 500 files to do this in.
I know ^ should match the beginning of a line and I also know that multi-line mode must be enabled for it to work on each line and not just at the beginning of the file. I also know dreamweaver uses javascript source code.
Is lack of multi-line mode the problem? Is there any way to turn it on in dreamweaver? I tried using /m at the beginning search to enable multi-line mode, but that didn't work either.
I'm open to any solution for my current problem, even if it involves a different program. However, a fix for dreamweaver is ideal, 2nd place is a way to do this in notepad++, 3rd place is a way do to this in python or something (I only know javascript, you'll have to spell it out exactly in another language).
Thank you,
robert
p.s.
I found I can "select all > right click > selection > indent" to add two spaces to the beginning of each line in dreamweaver. This allows me to find the beginning of each line with / {2,}/. I really don't want to select all > indent on all 500 files, but i'm about to start since I've already spent a few hours bludgeoning dreamweaver.
Don't use Dreamweaver for this - use Notepad++ (since you are familiar with it) at its regular expression support is superior.
If you are comfortable with a more robust scripting language (Python, Ruby, Perl, etc.) then that would be an ever better way to do it.
The way that I might do this in DW would not involve using the find-replace tool's "Regular Expression" option, but instead using just plain old matching on a CrLf.
In the Find portion, since you can't directly enter a CrLf, you'll have to copy one to your clipboard beforehand and paste it in where needed.
In the Replace portion, replace with:
</p>[CrLf]
<p>
Again, be sure to paste in a proper "[CrLf]". This will work on all but the very first and very last lines of your document, so I know this isn't a 100% solution. There are probably better solutions using other tools that someone else can recommend!
Good luck!
-Mike
I had a flash of insight right after posting. (isn't that the way of it?)
Dreamweaver can find the end of each line with \r\n so instead of trying to work forward, i should have just worked backwards.
search: (.+)(\r\n)
replace: <p>$1</p>$2
[\w\W]* matches anything, including a newline. Its greedy, so it fact it matches everything.