Thursday, October 2, 2014

Re: make substitution on a copy (kindof) of matching lines

On 2014-10-02 16:17, BPJ wrote:
> The other day I felt the need for a command/function which did
> a :substitute on a *copy* of each line matching its search pattern
> and inserted that copy below the original, unmodified line. I soon
> realized that it would be much easier to first make a copy of each
> (unmodified) line, then execute the :substitute on the original
> line, and lastly insert the unmodified copy above the original line
> if the original line had been modified, as determined by comparing
> the possibly modified original line to the always unmodified copy.
>
> I soon found that I also had a use case for getting the modified
> line above the unmodified one, so I needed a way to tell the
> command/function to do that -- obviously a bang on the command and
> an extra argument on the function.
[snip]
> Originally I intended the command to behave similarly to :global,
> defaulting to operating on the whole buffer unless an explicit
> range was given, but I soon found that I sometimes wanted to find
> eligible lines using :global itself and a pattern different from
> the substitution pattern, and doing this with -nargs=% ended in
> disaster, as the range given to :global was invisible to my
> command, so my command operated on the whole file anyway (I should
> have realized that to begin with, I realize! :-) The solution was
> to use -range instead of -range=% and use an empty pattern on
> the :AS argument if I use :g and want to use the same pattern on :s
>
> I now have the following questions:
>
> * (How) can I make this simpler? (Obviously)
>
> * (How) can I restore the original :g-like default behavior and
> still be able to use an actual :g with a separate pattern when I
> want to? N.B. that the -nargs=1 is important to me: I don't want
> to have to do an extra level of escaping in the :s expression!

These two can be combined into one answer. For your initial case,
I'd use

:g/^/t.|s/foo/bar/ge

(the "e" flag suppresses the error in the event the line doesn't
contain "foo")

which can of course be limited by range:

:'<,'>g/^/t.|s/foo/bar/ge

or to a subset of lines containing "baz"

:g/baz/t.|s/foo/bar/ge


And if you want to change the destination to the line *above* rather
than the line below, just tell it where to copy the line to:

:g/^/t-2|s/foo/bar/g

You could even gather the modified lines at the bottom of your file:

:g/^/t$|s/foo/bar/g

> * The fact that the function actually modifies the original line
> 'breaks' marks in terms of the desired apparent behavior, where the
> modification is made on a copy. How can I avoid that?

The "t." command should leave the original line untouched, so it
shouldn't mess with marks/folds.

> * Obviously one could have any expression which modifies the line
> as argument, but I can't think up an example. Anyone who can?
>
> * Has someone had this idea before, or am I the lone loonie?

While not *frequently*, I do use this fairly regularly.

> * There is something builtin which does this, isn't there? ;-)

All the parts are in the box, you just have to assemble them in the
right order. :-)

-tim



--
--
You received this message from the "vim_use" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

---
You received this message because you are subscribed to the Google Groups "vim_use" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vim_use+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment