Vim: инвертировать строку (по словам)

Это моя строка:

"this is my sentence"

Я хотел бы иметь этот вывод:

"sentence my is this"

Я хотел бы выбрать несколько слов на строке (в буфере) и перевернуть ее слово за словом.

может кто-нибудь помочь мне?

4 ответов


не совсем понятно, какой здесь контекст: вы можете говорить о тексте в строке в буфере или о строке, хранящейся в переменной VimScript.

Примечание: различные интерпретации вопроса привели к различным подходам и решениям.
Есть некоторые "старые обновления", которые начинаются примерно на полпути, которые были более или менее устаревшими плагином, упомянутым выше этого раздела. Я оставила их, потому что они могут предоставить полезную информацию для некоторых людей.

полная замена строки

Итак, чтобы сохранить текст из текущей строки в текущем буфере в переменной vimscript, вы делаете

let words = getline('.')

и затем, чтобы изменить их порядок, вы просто делаете

let words = join(reverse(split(words)))

если вы хотите заменить текущую строку обратными словами, вы делаете

call setline('.', words)

вы можете сделать это в несколько неисповедимы линии с

call setline('.', join(reverse(split(getline('.')))))

или даже определить команду, которая делает то, что с

command! ReverseLine call setline('.', join(reverse(split(getline('.')))))

частичная линия (символьная) выборки

как объяснено в разделе "старые обновления", выполнение общих команд для визуального выбора символов или блоков - первый из которых является тем, что OP хочет сделать здесь-может быть довольно сложным. Бывшей команды, как :substitute будет выполняться по всем строкам, даже если выбрана только часть строки с помощью характер-мудрый визуального выбора (как это было сделано с несмещенные v).

я понял после того, как OP прокомментировал ниже, что реверсирование слов в выборе символов с частичной строкой может быть выполнено довольно легко с

:s/\%V.*\%V./\=join(reverse(split(submatch(0))))/

при этом

  • \%V в пределах RE соответствует некоторой части визуального выбора. По-видимому, это не распространяется после последнего символа в выборе: опуская окончательный . исключит последний выбранный символ.
  • \= в начале замены указывает, что он должен быть оценен как выражение vimscript, с некоторыми различия.
  • submatch(0) возвращает весь матч. Это работает немного как perl $&, , etc., за исключением того, что он доступен только при оценке замены. Я думаю, это означает, что его можно использовать только в :substitute команда или в вызове substitute()

поэтому, если вы хотите сделать замену на однострочный выбор, это будет работать довольно хорошо. Вы даже можете передать выбор через системную команду, используя ...\=system(submatch(0)).

многострочный символьный выбор

это, похоже, также работает с многострочным символьным выбором, но вы должны быть осторожны, чтобы удалить диапазон ('<,'> что vim ставит в начале команда при поступлении из визуального режима). Вы хотите запустить команду только в той строке, где начинается визуальный выбор. Вам также придется использовать \_.* вместо .* для того, чтобы соответствовать по новым линиям.

блок-мудрый выбор

для выбора блоков я не думаю, что есть разумно удобный способ манипулировать ими. Я написал плагин, который можно использовать, чтобы сделать такие изменения менее болезненными, предоставляя способ запуска произвольного Ex команды на любом визуальном выборе, как если бы это было все содержимое буфера.

он доступен по адресу https://github.com/intuited/visdo. В настоящее время нет упаковки, и она еще не доступна на vim.org, но вы можете просто git clone it и скопируйте содержимое (минус файл README) в ваш vimdir.

если вы используете vim-addon-manager, просто клонировать visdo в каталоге Vim-аддонов, и вы впоследствии сможете ActivateAddons visdo. Я отправил запрос на его добавление в репозиторий vam addons, поэтому в какой-то момент Вы сможете обойтись без клонирования и просто сделать ActivateAddons visdo.

плагин добавляет :VisDo команда, которая должна иметь префикс к другой команде (аналогично тому, как :tab или :silent работы). Выполнение команды с помощью VisDo prepended вызовет выполнение этой команды в буфере, содержащем только текущее содержимое визуального выделения. После завершения команды содержимое буфера вставляется в визуальный выбор исходного буфера, а временный буфер удаляется.

таким образом, чтобы завершить цель OP с VisDo, вы бы сделали (со словами, которые будут отменены, и с помощью указанной выше команды ReverseLine):

:'<,'>VisDo ReverseLine


старые обновления

...предыдущие обновления следуют ... предупреждение: многословный, несколько obselete, и в основном ненужным...

в Редактирование OP делает более ясным, что цель здесь состоит в том, чтобы иметь возможность отменить слова, содержащиеся в визуальном выборе, и в частности персонаж-мудрый визуальное выделение.

это определенно не простая задача. Тот факт, что vim не делает такие вещи легкими, действительно смутил меня, когда я впервые начал использовать его. Я думаю, это потому, что его корни все еще очень много в линейно-ориентированной функциональности редактирования ed и его предшественников и потомков. Например, команда substitute :'<,'>s/.../.../ всегда будет работать на целых линиях, даже если вы находитесь в характере или блоке (ctrlv) режим визуального выбора.

Vimscript позволяет найти номер столбца любой "метки", включая начало визуального выделения ('<) и конец визуального выбора ('>). Это, насколько я могу судить, предел его поддержки. Нет прямого способа получить содержимое визуального выбора, и нет никакого способа заменить визуальный выбор чем-то другим. Конечно, вы можете сделать обе эти вещи, используя команды нормального режима (y и p), но это clobbers регистрируется и является своего рода беспорядочным. Но затем вы можете сохранить начальные регистры, а затем восстановить их после вставки...

таким образом, в основном вы должны пойти на крайние длины, чтобы сделать с частями линий то, что можно легко сделать с целыми линиями. Я подозреваю, что лучший способ сделать это-написать команду, которая копирует визуальный выбор в новый буфер, запускает на нем другую команду, а затем заменяет визуальный выбор исходного буфера результатами, удаляя временный буфер. Этот подход теоретически должен работать как для символьных, так и для блочных выборок, а также для уже поддерживаемых линейных выборок. Однако я этого еще не сделал.

этот 40-строчный фрагмент кода объявляет команду ReverseCharwiseVisualWords который можно назвать из визуального режима. Он будет работать только в том случае, если визуальный выбор символов полностью находится на одной строке. Он работает, получая всю строку, содержащую визуальный выбор (используя getline()) запуск параметризованной функции преобразования (ReverseWords) на выбранной его части, и вставляя всю частично преобразованную строку обратно. Оглядываясь назад, я думаю, что, вероятно, стоит пойти y/p маршрут для чего-нибудь более характерного.

" 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'))

обновление 2

оказывается, что делать вещи с y и p намного проще, поэтому я подумал, что тоже отправлю это. Предостережение: я не проверял это слишком тщательно, поэтому могут быть крайние случаи.

эта функция заменяет TransformCharwiseVisual (и некоторые из его зависимостей) в предыдущем блоке кода. Теоретически он должен работать и для блочных выборок-это зависит от пройденного


возможно:

:s/\v(.*) (.*) (.*) (.*)/   /

конечно, вам, вероятно, нужно быть более конкретным в первой части, чтобы найти это конкретное предложение. Как правило, вы можете ссылаться на группы совпадений как \number с \0, являющимся целым совпадением.


вот способ сделать, позвонив Руби. После выбора строки, которую вы хотите отменить, вы можете сделать это в командном режиме, чтобы заменить ее:

!ruby -e 'puts ARGF.read.strip.split(/\b/).reverse.join'

Я сам нашел решение благодаря вашим ответам и много попыток:)

это работает:

function! Test()
exe 'normal ' . 'gv"ay'
let r = join(reverse(split(getreg('a'))))
let @a = r
exe 'normal ' . 'gv"ap'
endfunction

не думал, что у меня есть возможность написать такую функцию:)