0

I've recently refactored vim-colors-solarized and I've stumbled on a code like this

let s:foo = s:bar " used in multiple places
" ... many lines
let s:goo = s:boo " used in multiple places

How can I inline all usages of such variables automatically easily. Of course I can write a function to do that. But maybe something like g/let s:."=\s*s:.*/normal dw"adWdW"vdW" | exe "g/@v/s/@a/@v/g" | d in which I mean find all lines matching a pattern, yank variable name to be inlined into register a it's value into register v. Then inside of this global command execute another global command on all lines after the match substituting variable with its value and removing initially matched line.

3
  • 2
    Write the function (or a macro). Trying to cram it into one messy line is bound to be error prone and run into edge cases.
    – D. Ben Knoble
    Jan 4 '19 at 22:35
  • In other words, I’d argue that your way is one of the more complex, in that it is hard to understand at first glance. A function or macro can be simpler, broken down into pieces, &c. If by simplest you mean something specific, please clarify.
    – D. Ben Knoble
    Jan 4 '19 at 22:36
  • You see the problem is that I have a policy of either doing the best thing I possibly can or nothing at all. In this case If I would write a function it would definitely not be done in vimscript and it would be able to do inlining of any variable in vimscript. And to do that I would have to start with a parser. To much work for such a specific and rare case. Jan 5 '19 at 7:17
1

After expanding on the other answer, I thought I should also share my own personal solution for these renaming/refactoring type problems.

nnoremap * :'<,'>s/\C\<<C-r><C-w>\>//g<C-f>$F/i
vnoremap * y:'<,'>s/\C\<<C-r>"\>//g<C-f>$F/i
nnoremap & :%s/\C\<<C-r><C-w>\>//g<C-f>$F/i
vnoremap & y:%s/\C\<<C-r>"\>//g<C-f>$F/i

This provides two handy dual-mode key mappings. To replace all instances of foo in a document, while your cursor is over an instance of foo enter:

&barEnter

To replace all instances of not-strictly-word foo-bar simply make a visual selection and enter &barEnter

If you want to limit your renaming to a single section (e.g. a function or class), define the area you want to operate in with a visual selection first—I generally use VVY (which technically yanks the selected area, but serves our purpose)—then much as above, while hovering or visually selecting a word, enter:

*barEnter

How it works

Using the remapping for * as it is the more complex, pressing it will cause an ex command line to appear, which will look something like this:

:'<,'>s/\C\<foo\>/XXX/g

Where XXX represents where your cursor will be, allowing you to type a replacement and press enter immediately.

Caveats

The \< and \> match the beginning and ending of words respectively, this can sometimes be undesirable (e.g. if you are replacing foo() with bar(1)) and in those cases it is necessary to remove one of the anchors.

:'<,'>s/\C\<foo()/XXX/g

Some people may already use the * key binding, whose default action serves a similar purpose (finding the next instance of the word under the cursor), obviously you can bind to something else—and let me know if you find a reliable cross-platform way to bind Shift-Alt-* or some such, otherwise <leader>* doesn't do anything terribly useful.

Bonus

I have found that this binding can work (to a limited degree) in some Vim emulators, e.g. VsVim for Visual Studio—if it defined in simpler terms

nnoremap * :'<,'>s/\C\<<c-r><c-w>\>/
nnoremap & :%s/\C\<<c-r><c-w>\>/

It can also be modified (on the fly, or as part of the binding) to use everybody's favorite Vim plugin author's magic :S search, and therefore replace foo with bar, Foo with Bar, and FOO with BAR. As someone else put it, "We do not deserve Tim Pope." https://stackoverflow.com/a/23144847/912236

Improvements

If anyone would like to write a more complex version that automatically dropped the begin/end of word anchors, when the search string didn't begin/end with a word: that would be great.

There are other instance: class.foo vs var foo, and # comment foo where differentiation might be handy, and there are times when it might be nice to have a copy of the original text duplicated so you can make a tiny edit. I generally find these cases to be less common/annoying that you might think. If you want that kind of smart refactoring, use an IDE.

1
  • @DBK, thanks for the em dashes, I get lazy without my Mac and forget Alt-0150/Alt-0151 can make en/em dashes on Windows. I should really make an AHK macro to emulate the Mac method. Mar 10 at 9:22
0

I normally use search and cgn combination for inlining and repeating with .

gn visually selects the next match of a search (think of it as the graphic version of next)

c changes; i.e. erases to the point designated by the follow keystroke(s).

. (dot) was something I only recently learned after 25 years of Vi/Vim. It repeats the last action you performed, though I suspect the exact definition is more complex than that.

If you put all this together, it turns out that you can replace all instances of foo with bar, by entering:

/fooEntercgnbarEsc..... (one dot per replacement or just hold it until done).

Certainly an interesting and useful joining of 4 or so normally separate functions to serve 1 purpose.

This method could be used to automatically replace words as Orwellophile's answer does, just substitute * for the search portion. e.g.: *cgnbarEsc if your cursor was over the word foo.

1
  • 2
    Welcome to Vi and Vim! Your answer was flagged for being too short. I think your answer is actually ok, but it could be improved by explaining how to use cgn for this particular use case in detail. What would you search for? What would you replace it with? Which keystrokes would you use? Thanks for contributing and I hope you have a great time here!
    – filbranden
    Jun 12 '20 at 21:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.