This is my string:
"this is my sentence"
I would like to have this output:
"sentence my is this"
I would like to select a few words on a line (in a buffer) and reverse it word by word.
Can anyone help me?
Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.
Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.
Lost your password? Please enter your email address. You will receive a link and will create a new password via email.
Please briefly explain why you feel this question should be reported.
Please briefly explain why you feel this answer should be reported.
Please briefly explain why you feel this user should be reported.
It’s not totally clear what the context is here: you could be talking about text in a line in a buffer or about a string stored in a VimScript variable.
note: Different interpretations of the question led to various approaches and solutions.
There are some “old updates” that start about halfway through that have been rendered more or less obsolete by a plugin mentioned just above that section. I’ve left them in because they may provide useful info for some people.
full line replacement
So to store the text from the current line in the current buffer in a vimscript variable, you do
And then to reverse their order, you just do
If you want to replace the current line with the reversed words, you do
You can do it in one somewhat inscrutable line with
or even define a command that does that with
partial-line (character-wise) selections
As explained down in the “old updates” section, running general commands on a character- or block-wise visual selection — the former being what the OP wants to do here — can be pretty complicated. Ex commands like
:substitutewill be run on entire lines even if only part of the line is selected using a character-wise visual select (as initiated with an unshifted v).I realized after the OP commented below that reversing the words in a partial-line character-wise selection can be accomplished fairly easily with
wherein
\%Vwithin the RE matches some part of the visual selection. Apparently this does not extend after the last character in the selection: leaving out the final.will exclude the last selected character.\=at the beginning of the replacement indicates that it is to be evaluated as a vimscript expression, with some differences.submatch(0)returns the entire match. This works a bit like perl’s$&,$1, etc., except that it is only available when evaluating the replacement. I think this means that it can only be used in a:substitutecommand or in a call tosubstitute()So if you want to do a substitution on a single-line selection, this will work quite well. You can even pipe the selection through a system command using
...\=system(submatch(0)).multiple-line character-wise selections
This seems to also work on a multiple-line character-wise selection, but you have to be careful to delete the range (the
'<,'>that vim puts at the beginning of a command when coming from visual mode). You want to run the command on just the line where your visual selection starts. You’ll also have to use\_.*instead of.*in order to match across newlines.block-wise selections
For block-wise selections, I don’t think there’s a reasonably convenient way to manipulate them. I have written a plugin that can be used to make these sorts of edits less painful, by providing a way to run arbitrary Ex commands on any visual selection as though it were the entire buffer contents.
It is available at https://github.com/intuited/visdo. Currently there’s no packaging, and it is not yet available on vim.org, but you can just
git cloneit and copy the contents (minus the README file) into your vimdir.If you use vim-addon-manager, just clone visdo in your vim-addons directory and you’ll subsequently be able to
ActivateAddons visdo. I’ve put in a request to have it added to the VAM addons repository, so at some point you will be able to dispense with the cloning and just doActivateAddons visdo.The plugin adds a
:VisDocommand that is meant to be prefixed to another command (similarly to the way that:tabor:silentwork). Running a command withVisDoprepended will cause that command to run on a buffer containing only the current contents of the visual selection. After the command completes, the buffer’s contents are pasted into the original buffer’s visual selection, and the temp buffer is deleted.So to complete the OP’s goal with VisDo, you would do (with the words to be reversed selected, and with the above-defined ReverseLine command available):
old updates
…previous updates follow … warning: verbose, somewhat obselete, and mostly unnecessary…
The OP’s edit makes it more clear that the goal here is to be able to reverse the words contained in a visual selection, and specifically a character-wise visual selection.
This is decidedly not a simple task. The fact that vim does not make this sort of thing easy really confused me when I first started using it. I guess this is because its roots are still very much in the line-oriented editing functionality of
edand its predecessors and descendants. For example, the substitute command:'<,'>s/.../.../will always work on entire lines even if you are in character-wise or block-wise (ctrlv) visual selection mode.Vimscript does make it possible to find the column number of any ‘mark’, including the beginning of the visual selection (
'<) and the end of the visual selection ('>). That is, as far as I can tell, the limit of its support. There is no direct way to get the contents of the visual selection, and there is no way to replace the visual selection with something else. Of course, you can do both of those things using normal-mode commands (yandp), but this clobbers registers and is kind of messy. But then you can save the initial registers and then restore them after the paste…So basically you have to go to sort of extreme lengths to do with parts of lines what can easily done with entire lines. I suspect that the best way to do this is to write a command that copies the visual selection into a new buffer, runs some other command on it, and then replaces the original buffer’s visual selection with the results, deleting the temp buffer. This approach should theoretically work for both character-wise and block-wise selections, as well as for the already-supported linewise selections. However, I haven’t done that yet.
This 40-line code chunk declares a command
ReverseCharwiseVisualWordswhich can be called from visual mode. It will only work if the character-wise visual selection is entirely on a single line. It works by getting the entire line containing the visual selection (usinggetline()) running a parameterized transformation function (ReverseWords) on the selected part of it, and pasting the whole partly-transformed line back. In retrospect, I think it’s probably worth going they/proute for anything more featureful.update 2
It turns out that doing things with
yandpis a lot simpler, so I thought I’d post that too. Caveat: I didn’t test this all too thoroughly, so there may be edge cases.This function replaces
TransformCharwiseVisual(and some of its dependencies) in the previous code block. It should theoretically work for block-wise selections too — it’s up to the passedTransformfunction to do appropriate things with line delimiters.So then you can just add a second command declaration
tangentially related gory detail
Note that the utility of a higher-level function like the ones used here is somewhat limited by the fact that there is no (easy or established) way to declare an inline function or block of code in vimscript. This wouldn’t be such a limitation if the language weren’t meant to be used interactively. You could write a function which substitutes its string argument into a dictionary function declaration and returns the function. However, dictionary functions cannot be called using the normal invocation syntax and have to be passed to
call call(dictfunct, args, {}).note: A more recent update, given above, obsoletes the above code. See the various sections preceding old updates for a cleaner way to do this.