How do I search and replace based on a list?

12 views
Skip to first unread message

Luciano ES

unread,
Aug 30, 2021, 7:53:30 AM8/30/21
to v...@vim.org
Greetings.

I've been using Vim for a long time, but never got around to learning programming in it. I've always found it difficult. I wonder if someone here is willing to help me with a little project.

I sometimes highlight a line or paragraph and run some search and replace on it:

:'<,'>s/some string/another string/g

Then I may do it again with another pair of strings. Then another pair.

Some time later, I may have to do it again. There may be new string pairs, but most of the pairs will have been applied before. So at least they are recorded in the command history for some time. But sometimes that history is lost/overwritten or it's just annoying to scroll until I find the exact desired pair.

So I would like to create a list of string pairs, ideally in a separate file and with some kind of separator such as '|' between the strings. Then I would highlight the line or paragraph and run the macro or script and have all pair replacements applied automatically.

The replacing part would be quite easy for me to accomplish if I only wanted to apply one pair of strings. But I want to keep a list and I have no idea about how to open and parse the list file and iterate over each pair. I know a little programming and I could certainly achieve that in other languages, but I am really not fluent in Vimish. Can someone please help me with that?

TIA

--
Luciano ES
>>

Tim Chase

unread,
Aug 30, 2021, 9:30:06 AM8/30/21
to Luciano ES, vim...@googlegroups.com, v...@vim.org
On 2021-08-28 03:05, Luciano ES wrote:
> So I would like to create a list of string pairs, ideally in a
> separate file and with some kind of separator such as '|' between
> the strings. Then I would highlight the line or paragraph and run
> the macro or script and have all pair replacements applied
> automatically.

If you're willing to keep it in a vim-script rather than an external
file, and you can identify your search-strings generically with a
regex, it's amazingly beautiful:

:%s/generic regex/\=get({'from1': 'to1', 'from2': 'to2', 'from3':
'to3'}, submatch(0), submatch(0))/g

where you only need to update that from→to dict.

If each is only a single word, that generic regex can be as simple as

:%s/\w\+/\=get({…}, submatch(0), submatch(0))/g

That dictionary doesn't *have* to be inlined, so you can create it
and maintain it dynamically:

:let b:mymap={'from1': 'to1', 'from2': 'to2', 'from3': 'to3'}

and then use

:%s/\w\+/\=get(b:mymap, submatch(0), submatch(0))/g

allowing you to dynamically update it

:let b:mymap['tim']='luciano'

and re-run the command to pick up the new substitution(s)

:%s/\w\+/\=get(b:mymap, submatch(0), submatch(0))/g

It can even be made into a mapping in your vimrc if you want easy
access to it rather than relying on your preserved command-history.

It also has the advantage that each item only gets substituted once.
For an example, imagine doing

%s/wasp/horsefly/g
%s/fly/gnat/g

you'd end up with a bunch of "horsegnats".

However, if you want, you can transform a pipe-delimited list
(assuming no crazy meta-chars) into commands that can then be
executed:

:%s@\(.*\)|\(.*\)@:%s/\1/\2/g

This mogrifies it into a list of :s commands. You can then yank them
all

:%y


At this point, you can either

:q!

to close that buffer discarding those modifications, or you can keep
them around and directly modify the list of substitute commands
directly rather than using an intermediate pipe-delimited form that
you need to transform every time.

Once you've yanked them into the scratch/unnamed register, you can
execute them as a macro

@"

However, beware the replacing-already-replaced-things issue mentioned
above.

Hope this helps,

-Tim


For more, you can read up at

:help get()
:help sub-replace-special
:help @

Reply all
Reply to author
Forward
0 new messages