Den 2014-10-02 16:33, Tim Chase skrev: > 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 >
[Back from a rather bad cold...] Thanks for the reply! I had tried the `:g/something/t.|s/foo/bar/ge` trick, but it does what I want only when the `:g` pattern and the `:s` pattern are the same; otherwise it copies all lines which match the `:g` pattern, whether the `:s` does anything to that line or not, the whole point of my function being to avoid just that and copy only lines which are actually affected by the `:s` -- i.e where the `:s` pattern matches, so there is an ecological niche for my function anyway. I guess the only way to preserve marks on the original line is to always copy it, apply the `:s` to the copy and then delete the copy again if it is still identical to the original line, which seems terribly wasteful even if it makes the function simpler: ```VimL " Copy each line in range, apply :subst to the copy " and delete it again if it was unaltered! " :[range]GS[!] s/{search}/{replacement}/[flags] " without ! the copy is pasted with p (below original) " with ! the copy is pasted with P (above the original) func! s:copy_on_subst(subst, bang) range let p = empty(a:bang) ? 'p' : 'P' " insert copy below or above? let curline = a:firstline let lastline = a:lastline while curline <= lastline " make a copy of the current line, and end up on the copy exe ':norm ' . curline . 'Gyy' . p " apply the :subst (or whatever) to the copy exe a:subst " if the copy and the original are still identical " i.e. if the :subst didn't match anything if getline(curline) == getline(curline+1) " the two interesting lines are always the one at the " current line number and the one below, regardless of which " of them is the copy or the original! " " delete the copy if it was unaltered exe ':norm dd' " jump to the next line let curline+=1 else " if the copy line was altered by the :subst let lastline+=1 " our range just got one line longer let curline+= " jump past the original/copy line endif endwhile endfunc :com! -nargs=1 -bang -range GS :<line1>,<line2>call <SID>copy_on_subst(<f-args>, '<bang>') ``` -- -- 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+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.