Sorting words (not lines) in VIM

后端 未结 6 508
长情又很酷
长情又很酷 2020-12-13 02:44

The built-in VIM :sort command sorts lines of text. I want to sort words in a single line, e.g. transform the line

b a d c e f

相关标签:
6条回答
  • 2020-12-13 03:02

    Using great ideas from your answers, especially Al's answer, I eventually came up with the following:

    :vnoremap <F2> d:execute 'normal i' . join(sort(split(getreg('"'))), ' ')<CR>
    

    This maps the F2 button in visual mode to delete the selected text, split, sort and join it and then re-insert it. When the selection spans multiple lines this will sort the words in all of them and output one sorted line, which I can quickly fix using gqq.

    I'll be glad to hear suggestions on how this can be further improved.

    Many thanks, I've learned a lot :)

    EDIT: Changed '<C-R>"' to getreg('"') to handle text with the char ' in it.

    0 讨论(0)
  • 2020-12-13 03:03

    Maybe you preffer Python:

    !python -c "import sys; print ' '.join(sorted(sys.stdin.read().split()))"
    

    Visual select text, and execute this line.

    0 讨论(0)
  • 2020-12-13 03:05
    :!perl -ne '$,=" ";print sort split /\s+/'
    

    Not sure if it requires explanation, but if yes:

    perl -ne ''
    

    runs whatever is within '' for every line in input - putting the line in default variable $_.

    $,=" ";
    

    Sets list output separator to space. For example:

    => perl -e 'print 1,2,3'
    123
    
    => perl -e '$,=" ";print 1,2,3'
    1 2 3
    
    => perl -e '$,=", ";print 1,2,3'
    1, 2, 3
    

    Pretty simple.

    print sort split /\s+/
    

    Is shortened version of:

    print( sort( split( /\s+/, $_ ) ) )
    

    ($_ at the end is default variable).

    split - splits $_ to array using given regexp, sort sorts given list, print - prints it.

    0 讨论(0)
  • 2020-12-13 03:18

    My AdvancedSorters plugin now has a :SortWORDs command that does this (among other sorting-related commands).

    0 讨论(0)
  • 2020-12-13 03:21

    In pure vim, you could do this:

    call setline('.', join(sort(split(getline('.'), ' ')), " "))
    

    Edit

    To do this so that it works over a range that is less than one line is a little more complicated (this allows either sorting multiple lines individually or sorting part of one line, depending on the visual selection):

    command! -nargs=0 -range SortWords call SortWords()
    " Add a mapping, go to your string, then press vi",s
    " vi" selects everything inside the quotation
    " ,s calls the sorting algorithm
    vmap ,s :SortWords<CR>
    " Normal mode one: ,s to select the string and sort it
    nmap ,s vi",s
    function! SortWords()
        " Get the visual mark points
        let StartPosition = getpos("'<")
        let EndPosition = getpos("'>")
    
        if StartPosition[0] != EndPosition[0]
            echoerr "Range spans multiple buffers"
        elseif StartPosition[1] != EndPosition[1]
            " This is a multiple line range, probably easiest to work line wise
    
            " This could be made a lot more complicated and sort the whole
            " lot, but that would require thoughts on how many
            " words/characters on each line, so that can be an exercise for
            " the reader!
            for LineNum in range(StartPosition[1], EndPosition[1])
                call setline(LineNum, join(sort(split(getline('.'), ' ')), " "))
            endfor
        else
            " Single line range, sort words
            let CurrentLine = getline(StartPosition[1])
    
            " Split the line into the prefix, the selected bit and the suffix
    
            " The start bit
            if StartPosition[2] > 1
                let StartOfLine = CurrentLine[:StartPosition[2]-2]
            else
                let StartOfLine = ""
            endif
            " The end bit
            if EndPosition[2] < len(CurrentLine)
                let EndOfLine = CurrentLine[EndPosition[2]:]
            else
                let EndOfLine = ""
            endif
            " The middle bit
            let BitToSort = CurrentLine[StartPosition[2]-1:EndPosition[2]-1]
    
            " Move spaces at the start of the section to variable StartOfLine
            while BitToSort[0] == ' '
                let BitToSort = BitToSort[1:]
                let StartOfLine .= ' '
            endwhile
            " Move spaces at the end of the section to variable EndOfLine
            while BitToSort[len(BitToSort)-1] == ' '
                let BitToSort = BitToSort[:len(BitToSort)-2]
                let EndOfLine = ' ' . EndOfLine
            endwhile
    
            " Sort the middle bit
            let Sorted = join(sort(split(BitToSort, ' ')), ' ')
            " Reform the line
            let NewLine = StartOfLine . Sorted . EndOfLine
            " Write it out
            call setline(StartPosition[1], NewLine)
        endif
    endfunction
    
    0 讨论(0)
  • 2020-12-13 03:25

    Here's the equivalent in pure vimscript:

     :call setline('.',join(sort(split(getline('.'),' ')),' '))
    

    It's no shorter or simpler, but if this is something you do often, you can run it across a range of lines:

     :%call setline('.',join(sort(split(getline('.'),' ')),' '))
    

    Or make a command

     :command -nargs=0 -range SortLine <line1>,<line2>call setline('.',join(sort(split(getline('.'),' ')),' '))
    

    Which you can use with

    :SortLine
    :'<,'>SortLine
    :%SortLine
    

    etc etc

    0 讨论(0)
提交回复
热议问题