You've already (rightly) accepted an answer that provides a much simpler method of achieving your goal, but I thought I'd address a few of the other issues raised by your question.
The problems with your :let
command
To debug your macro, a first step might be to try recording a working macro, and then comparing this with the macro created by your let
command. I've run your command, and recorded a series of steps into the "r
register, so lets take a look at the differences by running the :registers
command:
"q \/^$ N j <S-V> \/^$ n k :sort
"r /^$^MNjVnk:sort^M
Okay, so first off, the "q
register begins with a backslash, and has another one midway though. You were correct in your guess that these are not required, but as you discovered, there are further problems with your code.
The "q
register contains several spaces. Some of these should in fact be <CR>
characters (displayed in the output with the notation ^M
meaning <Ctrl-M>
), and others are simply not required at all.
The "q
register contains the string <S-V>
, whereas it should simply contain an upper-case V
.
The "q
register contains both a second instance of the search command and an instance of the jump to next match motion, n
. Only one of these is required.
What went wrong
I think these all stem from a few misunderstandings about a). how macros are stored and/or b). how control characters are included in strings.
A macro contains a series of keystrokes, and nothing else. You don't separate operations with space characters: if a space character is included that equates to a press of the Space on your keyboard.
Similarly, if you want to run a command-line command, you need to include a <CR>
character to emulate pressing Return.
The <S-V>
notation is not required to insert upper-case characters into a string: V
is already an upper-case character. Furthermore, in Vimscript, single quotes '
denote a "literal" string, which contains exactly the characters included (with the sole exception that two quotes stand for one quote). Double-quotes "
on the other hand, allow you to include control characters by using the <angle-bracket>
notation and prepending the opening bracket with a backslash.
Thus, this sets the variable v
to contain the the string "<S-V>"
:
:let v = '<S-V>'
Whereas this is a long-winded way of setting the variable v
to contain the string "V"
:
:let v = "\<S-V>"
How to fix it
So to create the correct :let
command you have two options:
Enter the following into your vimrc
: let @q = ''
and then, having recorded the working macro into a register "q
, paste the (current) contents of that register in between the single quotes by pressing e.g. "qP. The downside of this is that, while it works, it's a bit icky to include literal control characters in your vimrc
, so perhaps a better solution is to:
Rewrite the :let
command to use a double quoted string:
let @q = "/^$\<CR>NjVnk:sort\<CR>"
How to improve it
Firstly, there is a better way of searching backwards than first searching forwards and then going back with N
, which is to use ?
instead of /
.
Also, there is a better way of moving to the line after or before a blank line than running the command j
or k
, which is to use a "search offset". At the end of your search command, add +1
to place the cursor on the line after the match, or -1
to place the cursor on the line before the match. Because we are searching backwards, we need to first end the regular expression with ?
. (When searching forwards, you'd use /
.)
You can repeat a search by omitting the regular expression entirely, but you can still change the search offsets or other flags.
Thus:
let @q = "?^$?+1\<CR>V//-1\<CR>:sort\<CR>"
Finally, you specifically asked about how to handle the start and end of file. You can do so by updating your regular expression.
When searching upwards, instead of searching for a blank lines we'll search for either:
- The start of the file, or,
- The first character on the line following a blank line.
We can no longer use an line-offset, but instead we need to leave the cursor on the end of the match by ending our search with the e
offset to leave the cursor on the final character of the match.
And when searching downwards, we'll search for either:
- The end of the file, or,
- The last character on the line preceding a blank line.
To search for the start of the file you can use \%^
, and to search for the end of the file you can use \%$
.
To search for A
or B
you can use the notation A\|B
in your regular expression.
So:
let @q = "?\\%^\\|^$\\n.?e\<CR>V/\\%$\\|.\\n^$/\<CR>:sort\<CR>"