Сортировка слов (не строк) в VIM - PullRequest
28 голосов
/ 25 августа 2009

Встроенная команда VIM :sort сортирует строки текста. Я хочу отсортировать слова в одну строку, например, преобразовать линию

b a d c e f

до

a b c d e f

В настоящее время я выполняю это, выбирая строку и затем используя :!tr ' ' '\n' | sort | tr '\n' ' ', но я уверен, что есть лучший, более простой и быстрый способ. Есть ли?

Обратите внимание, что я использую bash, поэтому, если для этого есть более короткая и элегантная команда bash, то это тоже хорошо.

РЕДАКТИРОВАТЬ: Мой вариант использования состоит в том, что у меня есть строка, которая говорит SOME_VARIABLE="one two three four etc", и я хочу, чтобы слова в этой переменной были отсортированы, т.е.

Конечный результат предпочтительно должен быть сопоставлен с сочетанием клавиш, поскольку это то, что мне часто нужно.

Ответы [ 6 ]

21 голосов
/ 25 августа 2009

В чистом vim вы можете сделать это:

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

Редактировать

Сделать это так, чтобы он работал в диапазоне, который меньше одной строки, немного сложнее (это позволяет либо отсортировать несколько строк по отдельности, либо отсортировать часть одной строки, в зависимости от визуального выделения):

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
14 голосов
/ 25 августа 2009

Используя отличные идеи из ваших ответов, особенно ответа Ала, я в итоге придумал следующее:

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

Это отображает кнопку F2 в режиме visual для удаления выделенного текста, разделения, сортировки и объединения, а затем повторной вставки. Когда выделение охватывает несколько строк, это отсортирует слова во всех из них и выведет одну отсортированную строку, которую я могу быстро исправить, используя gqq.

Я буду рад услышать предложения о том, как это можно улучшить.

Большое спасибо, я многому научился:)

РЕДАКТИРОВАТЬ : изменено '<C-R>"' на getreg('"') для обработки текста с символом '.

9 голосов
/ 25 августа 2009

Вот эквивалент в чистом vimscript:

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

Это не короче и не проще, но если вы часто этим занимаетесь, вы можете запустить его в диапазоне строк:

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

Или составить команду

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

Который вы можете использовать с

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

и т. Д.

6 голосов
/ 25 августа 2009
:!perl -ne '$,=" ";print sort split /\s+/'

Не уверен, требуется ли объяснение, но если да:

perl -ne ''

запускает все, что находится внутри '' для каждой строки ввода - помещая строку в переменную по умолчанию $ _.

$,=" ";

Устанавливает разделитель вывода списка в пробел. Например:

=> 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

Довольно просто.

print sort split /\s+/

Сокращенная версия:

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

($ _ в конце является переменной по умолчанию).

split - разбивает $ _ на массив, используя заданное регулярное выражение, сортирует сортировку по заданному списку, печатает - печатает его.

4 голосов
/ 25 августа 2009

Может быть, вы предпочитаете Python:

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

Визуально выделите текст и выполните эту строку.

1 голос
/ 23 декабря 2014

Мой Плагин AdvancedSorters теперь имеет команду :SortWORDs, которая делает это (среди других команд, связанных с сортировкой).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...