It’s not entirely clear what the context is: you can talk about text in a line in a buffer or about a line stored in a VimScript variable.
note: Different interpretations of the question have led to different approaches and solutions. There are several “ old updates ” that start about halfway that were more or less outdated with the plugin mentioned above in this section. I left them because they can provide useful information for some people.
full line replacement
So, to save the text from the current line in the current buffer in the vimscript variable, you do
let words = getline('.')
And then, to cancel your order, you just do
let words = join(reverse(split(words)))
If you want to replace the current line with inverse words, you
call setline('.', words)
You can do this in one obscure line with
call setline('.', join(reverse(split(getline('.')))))
or even define a team that does this with
command! ReverseLine call setline('.', join(reverse(split(getline('.')))))
partial line selection (character)
As explained in the “old updates” section, executing common visual selection commands with characters or blocks — the first thing the OP wants to do here can be quite complicated. Ex commands, such as :substitute , will run on all lines, even if only part of the line is selected using visual type selection (as was started with unmoved v ).
I realized that after the OP commented below that reversing words in a partial line partial selection can be done quite easily with
:s/\%V.*\%V./\=join(reverse(split(submatch(0))))/
Where
\%V in RE corresponds to some part of the visual highlight. Apparently, this does not apply after the last character in the selection: excluding the final one . excludes the last character selected.\= at the beginning of a replacement indicates that it should be evaluated as a vimscript expression, with some.submatch(0) returns the entire match. This is a bit like perl $& , $1 , etc., except that it is only available when evaluating a replacement. I think this means that it can only be used in a command :substitute or when calling substitute()
So, if you want to make a replacement in a single line selection, this will work very well. You can even pass the selection using a system command using ...\=system(submatch(0)) .
multi-line character selections
This also seems to work with multi-line character selection, but you have to be careful to remove the range ( '<,'> that vim puts at the beginning of the command when exiting visual mode). You want to run the command only on the line where your visual selection begins. You will also need to use \_.* Instead of .* To match newlines.
block selection
For block elections, I do not think that there is a convenient enough way to manipulate them. I wrote a plugin that can be used to make these kinds of changes less painful, providing a way to run arbitrary Ex commands with any visual selection, as if it were the entire volume of the buffer contents.
It is available at https://github.com/intuited/visdo . There is currently no packaging, and it is not yet available on vim.org, but you can just git clone it and copy the contents (minus the README file) into your vimdir.
If you are using vim-addon-manager , just paste visdo into your vim-addons directory, and subsequently you can ActivateAddons visdo . I added a request to add it to the VAM add-ons store, so at some point you can refuse to clone and just make ActivateAddons visdo .
The plugin adds the command :VisDo , which is designed to prefix another command (similar to the way :tab or :silent ). Executing a command with the addition of VisDo will cause this command to be run in a buffer containing only the current contents of the visual selection. At the end of the command, the contents of the buffer are inserted into the visual selection of the source buffer, and the temporary buffer is deleted.
So, to complete the OP goal with VisDo, you would do (with the words that need to be reversed, and with the above ReverseLine command):
:'<,'>VisDo ReverseLine
old updates
... previous updates follow ... warning: verbose, somewhat burdened and mostly unnecessary ...
Editing the OP makes it clearer that the goal here is to be able to undo the words contained in the visual selection, and in particular the visual selection by type.
This is clearly not an easy task. The fact that vim doesn't do this kind of thing just confused me when I first started using it. I believe this is due to the fact that its roots are still very strongly related to the linear editing functions of ed and its predecessors and descendants. For example, the substitution command :'<,'>s/.../.../ will always work on whole lines, even if you are in the visual selection mode by type or scale ( ctrl v ).
Vimscript allows you to find the column number of any “label”, 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 his support. There is no direct way to get the contents of a visual highlight, and there is no way to replace the visual selection with something else. Of course, you can do both of these things using normal mode commands ( y and p ), but these clobists are logged in and are pretty dirty. But then you can save the original registers and then restore them after the paste ...
So basically you have to go to extreme lengths to do with parts of lines, which is easy to do with all lines. I suspect the best way to do this is to write a command that copies the visual selection to a new buffer, runs some other command on it, and then replaces the visual selection of the original buffer with the results, removing the temporary buffer. This approach should theoretically work both by choice and by block election, as well as for already supported line selection options. However, I have not done this yet.
This 40-line code snippet announces the ReverseCharwiseVisualWords command, which can be invoked from visual mode. It will only work if the character’s visual selection is entirely on the same line. It works by getting the entire string containing the visual selection (using getline() ), performing the parameterized conversion function ( ReverseWords ) in the selected part, and inserting the entire partially transformed string back. Looking back, I think it's probably worth going the y / p route for something more functional.
" Return 1-based column numbers for the start and end of the visual selection. function! GetVisualCols() return [getpos("'<")[2], getpos("'>")[2]] endfunction " Convert a 0-based string index to an RE atom using 1-based column index " :help /\%c function! ColAtom(index) return '\%' . string(a:index + 1) . 'c' endfunction " Replace the substring of a:str from a:start to a:end (inclusive) " with a:repl function! StrReplace(str, start, end, repl) let regexp = ColAtom(a:start) . '.*' . ColAtom(a:end + 1) return substitute(a:str, regexp, a:repl, '') endfunction " Replace the character-wise visual selection " with the result of running a:Transform on it. " Only works if the visual selection is on a single line. function! TransformCharwiseVisual(Transform) let [startCol, endCol] = GetVisualCols() " Column numbers are 1-based; string indexes are 0-based let [startIndex, endIndex] = [startCol - 1, endCol - 1] let line = getline("'<") let visualSelection = line[startIndex : endIndex] let transformed = a:Transform(visualSelection) let transformed_line = StrReplace(line, startIndex, endIndex, transformed) call setline("'<", transformed_line) endfunction function! ReverseWords(words) return join(reverse(split(a:words))) endfunction " Use -range to allow range to be passed " as by default for commands initiated from visual mode, " then ignore it. command! -range ReverseCharwiseVisualWords \ call TransformCharwiseVisual(function('ReverseWords'))
update 2
It turns out that doing things with y and p lot easier, so I thought I would post this too. Caution: I have not tested this too carefully, so there may be cases with edges.
This function replaces the TransformCharwiseVisual (and some of its dependencies) in the previous block of code. Theoretically, it should work for block selections - before the Transform function is passed to perform the corresponding actions with line separators.
function! TransformYankPasteVisual(Transform) let original_unnamed = [getreg('"'), getregtype('"')] try " Reactivate and yank the current visual selection. normal gvy let @" = a:Transform(@") normal gvp finally call call(function('setreg'), ['"'] + original_unnamed) endtry endfunction
So you can just add a second command declaration
command! -range ReverseVisualWords \ call TransformYankPasteVisual(function('ReverseWords'))
regarding gory related details
Note that the usefulness of a higher-level function similar to that used here is somewhat limited by the fact that there is no (simple or established) way to declare an inline function or code block in vimscript. This would not be such a limitation if the language were not intended to be used interactively. You can write a function that replaces its string argument with a dictionary declaration and returns a function. However, dictionary functions cannot be called using normal call syntax and must be passed to call call(dictfunct, args, {}) .
Note: The more recent update above removes the above code. See Various Sections Prior to Old Updates for a cleaner way to do this.