4

Considerthis text with shopping and todo list

First thing in the morning
- call Bob
- buy carrots
- buy bread
- text Charlie
- buy butter
- email Alice
only then start the day

What I am looking for a is a vim command to be applied from the first line that starts with a minus, to the last one that does. Something of the short of

:/^\s*-/,!/^\s*-/!sort -u

which will produce

First thing in the morning
- buy bread
- buy butter
- buy carrots
- call Bob
- email Alice
- text Charlie
only then start the day
2

Here are two functions that you could use. Both takes as argument a pattern to search and a command to execute on the consecutive lines matching the pattern.

You can call them with:

call RangeFromPattern("^\s*-", "!sort -u")

The first one goes to the first line of the buffer, searches for the first line matching the pattern, then goes to the last line of the buffer, searches for the last line matching the pattern and then apply the command to the range firstline,lastline.

Note that if you buffer contains several sections like this:

1. First line
2. - matching pattern
3. - matching pattern
4. not matching pattern
5. - matching pattern
6. - matching pattern
7. Last line

The command will be applied to lines 2 to 6 (including the line 4 which doesn't match the pattern)

function! RangeFromPattern(pattern, command)
    " Save cursor position
    let saveCursor = getcurpos()

    " Get the first line matching the pattern
    call cursor(1, 1)
    let firstLine = search(a:pattern, "c")

    " Get the last line matching the pattern
    call cursor('$', 1)
    let lastLine = search(a:pattern, "b")

    " Restore the pattern position
    call setpos('.', saveCursor)

    " Execute the command
    execute firstLine . "," . lastLine . a:command
endfunction

Now to avoid the problem of the previous function here is another version which will start the search on the current line, keep the number of the first matching line, increment this number while the next lines matches the pattern and finally apply the command to this set of lines. On the previous example I gave, if you call the function with your cursor on line 5, the command will be applied on range 5 to 6.

function! RangeFromPattern2(pattern, command)
    " Save cursor position
    let saveCursor = getcurpos()

    " Get the first line matching the pattern
    let firstLine = search(a:pattern, "c")

    " Get the contiguous lines matching the pattern
    let prevLine = firstLine
    while (search(a:pattern) == prevLine + 1)
        let prevLine += 1
    endwhile

    " Restore the pattern position
    call setpos('.', saveCursor)

    " Execute the command
    execute firstLine . "," . prevLine . a:command
endfunction

An example of the function on your example:

enter image description here

2

Assuming the lines that start with a minus will be contiguous, I would use visual mode (assuming vim). Highlight the minus lines, then

:sort

will get you there. The whole sequence, assuming a file with that format, starting at the first character of the file:

jVGk:sort
1

Based on this answer on SO

:/^\s*-/,/^only/-1 !sort -u

provided the cursor is not placed anywhere on the lines starting with -

And, I don't know why

:/^\s*-/,/^\s*[^-]/-1 !sort -u

works only if cursor is positioned on the line immediately before start of search pattern

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.