Как получить визуально выделенный текст в VimScript - PullRequest
43 голосов
/ 07 октября 2009

Я могу получить позицию курсора с помощью getpos(), но я хочу получить выделенный текст в пределах строки, то есть '<,'>. Как это сделано?

UPDATE

Я думаю, что отредактировал ту часть, где объяснил, что хочу получить этот текст из скрипта Vim ...

Ответы [ 9 ]

75 голосов
/ 08 июня 2011

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

function! s:get_visual_selection()
    let [line_start, column_start] = getpos("'<")[1:2]
    let [line_end, column_end] = getpos("'>")[1:2]
    let lines = getline(line_start, line_end)
    if len(lines) == 0
        return ''
    endif
    let lines[-1] = lines[-1][: column_end - 2]
    let lines[0] = lines[0][column_start - 1:]
    return join(lines, "\n")
endfunction

Надеюсь, это кому-нибудь пригодится!

Обновление (май 2013 г.): На самом деле это не совсем правильно, я недавно исправил следующую ошибку в одном из опубликованных плагинов Vim:

function! s:get_visual_selection()
    " Why is this not a built-in Vim script function?!
    let [line_start, column_start] = getpos("'<")[1:2]
    let [line_end, column_end] = getpos("'>")[1:2]
    let lines = getline(line_start, line_end)
    if len(lines) == 0
        return ''
    endif
    let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)]
    let lines[0] = lines[0][column_start - 1:]
    return join(lines, "\n")
endfunction

Обновление (май 2014 г.): этот (тривиальный) код настоящим лицензируется как общественное достояние. Делай с этим что хочешь. Кредиты приветствуются, но не обязательны.

16 голосов
/ 08 октября 2009

Лучший способ, который я нашел, это вставить выборку в регистр:

function! lh#visual#selection()
  try
    let a_save = @a
    normal! gv"ay
    return @a
  finally
    let @a = a_save
  endtry
endfunction
13 голосов
/ 03 августа 2013

В Linux есть дешевая, но эффективная альтернатива программированию такой функции GetVisualSelection() самостоятельно: используйте регистр *!

Регистр * содержит содержимое самого последнего визуального выбора. См. :h x11-selection.

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

let v = @*

Кстати, * также является аккуратным маленьким помощником в интерактивном использовании. Например, в режиме вставки вы можете использовать CTRL-R *, чтобы вставить то, что вы выбрали ранее. Никакого явного дергания не происходит.

Работает только в операционных системах, поддерживающих механизм выбора X11.

7 голосов
/ 07 октября 2009

Я не совсем уверен насчет контекста, потому что getpos() действительно может принимать метки (например, '< и '>) в качестве аргументов.

Тем не менее, для того, чтобы попробовать то, о чем вы можете просить, есть также v, что похоже на '<, за исключением того, что оно всегда обновляется (то есть, пока пользователь все еще находится в визуальном режиме). Это можно использовать в сочетании с ., текущей позицией курсора, которая будет представлять конец визуального выделения.

Редактировать: я нашел их в :help line(); несколько функций, включая line() и getpos(), имеют одинаковый набор возможных аргументов.

Редактировать: Я полагаю, вы, вероятно, просто спрашиваете, как получить текст между двумя произвольными метками, а не построчно ... (т.е. это не относится конкретно к визуальному режиму). Я не думаю, что на самом деле есть способ. Да, это кажется довольно вопиющим упущением. Вы должны иметь возможность подделать его, найдя отметки с помощью getpos(), получив все строки с помощью getline(), затем отрубив первую и последнюю в соответствии с положением столбца (с учетом ситуации в зависимости от того, является ли он многострочным ). Извините, это ненастоящий ответ, но, по крайней мере, вы можете заключить его в функцию и забыть об этом.

5 голосов
/ 08 июня 2011

Я однажды написал функцию, которая может делать это, не касаясь регистров или положения курсора:

function s:F.map.getvrange(start, end)
    let [sline, scol]=a:start
    let [eline, ecol]=a:end
    let text=[]
    let ellcol=col([eline, '$'])
    let slinestr=getline(sline)
    if sline==eline
        if ecol>=ellcol
            call extend(text, [slinestr[(scol-1):], ""])
        else
            call add(text, slinestr[(scol-1):(ecol-1)])
        endif
    else
        call add(text, slinestr[(scol-1):])
        let elinestr=getline(eline)
        if (eline-sline)>1
            call extend(text, getline(sline+1, eline-1))
        endif
        if ecol<ellcol
            call add(text, elinestr[:(ecol-1)])
        else
            call extend(text, [elinestr, ""])
        endif
    endif
    return text
endfunction

Это называется так:

let [sline, scol, soff]=getpos("'<")[1:]
let [eline, ecol, eoff]=getpos("'>")[1:]
if sline>eline || (sline==eline && scol>ecol)
    let [sline, scol, eline, ecol]=[eline, ecol, sline, scol]
endif
let lchar=len(matchstr(getline(eline), '\%'.ecol.'c.'))
if lchar>1
    let ecol+=lchar-1
endif
let text=s:F.map.getvrange([sline, scol], [eline, ecol])

Обратите внимание, что в этот момент у вас будет список строк в тексте: одна из причин, по которой я написал эту функцию, заключается в способности сохранять значения NULL в файле. Если вы придерживаетесь какого-либо решения, которое возвращает текст в регистр, все значения NULL будут заменены на новые строки, и все новые строки будут также представлены как новые строки. В выходных данных функции getvrange NULL представлены как новые строки, а новые строки представлены различными элементами: между каждым элементом списка есть NL, как и при выводе getline(start, end).

Эту функцию можно использовать только для получения строк для символьного выбора (так как для этого гораздо проще, а для блоков я перебираю строки и такая функция не нужна. Есть также функции для удаления заданного диапазона (без касания регистров) и вставка текста в заданную позицию (без касания регистров или курсора).

4 голосов
/ 01 ноября 2017

Это довольно старый вопрос, но так как я могу себе представить, что многие люди столкнутся с ним в какой-то момент, вот моя модифицированная версия ответа @xolox

function! VisualSelection()
    if mode()=="v"
        let [line_start, column_start] = getpos("v")[1:2]
        let [line_end, column_end] = getpos(".")[1:2]
    else
        let [line_start, column_start] = getpos("'<")[1:2]
        let [line_end, column_end] = getpos("'>")[1:2]
    end
    if (line2byte(line_start)+column_start) > (line2byte(line_end)+column_end)
        let [line_start, column_start, line_end, column_end] =
        \   [line_end, column_end, line_start, column_start]
    end
    let lines = getline(line_start, line_end)
    if len(lines) == 0
            return ''
    endif
    let lines[-1] = lines[-1][: column_end - 1]
    let lines[0] = lines[0][column_start - 1:]
    return join(lines, "\n")
endfunction
  1. '< и '> не обновляются, когда пользователь все еще находится в визуальном режиме, поэтому в этом случае необходимо использовать . и v.
  2. Можно выбрать текст в обратном направлении в визуальном режиме, что означает, что '> предшествует '< в тексте. В этих случаях две позиции просто необходимо поменять местами.
  3. Хотя это не входит в мою версию функции, можно выбрать обратную строку, если выбор был задом наперед. Вот отрывок, который показывает, как это сделать.

Предполагается, что переменная «реверс» определена, когда метки расположены в обратном порядке:

if exists("reverse")
    let lines_r = []
    for line in lines
        call insert(lines_r, join(reverse(split(line, ".\\zs"))))
    endfor
    return join(lines_r, "\n")
else
    return join(lines, "\n")
end
2 голосов
/ 08 февраля 2015

Эта функция взята из vim-asterisk также работает в <expr> отображениях, поддерживает выборку блоков и многобайтовые столбцы.

function! GetVisualSelection()
    let mode = mode()
    let end_col = s:curswant() is s:INT.MAX ? s:INT.MAX : s:get_col_in_visual('.')
    let current_pos = [line('.'), end_col]
    let other_end_pos = [line('v'), s:get_col_in_visual('v')]
    let [begin, end] = s:sort_pos([current_pos, other_end_pos])
    if s:is_exclusive() && begin[1] !=# end[1]
        " Decrement column number for :set selection=exclusive
        let end[1] -= 1
    endif
    if mode !=# 'V' && begin ==# end
        let lines = [s:get_pos_char(begin)]
    elseif mode ==# "\<C-v>"
        let [min_c, max_c] = s:sort_num([begin[1], end[1]])
        let lines = map(range(begin[0], end[0]), '
        \   getline(v:val)[min_c - 1 : max_c - 1]
        \ ')
    elseif mode ==# 'V'
        let lines = getline(begin[0], end[0])
    else
        if begin[0] ==# end[0]
            let lines = [getline(begin[0])[begin[1]-1 : end[1]-1]]
        else
            let lines = [getline(begin[0])[begin[1]-1 :]]
            \         + (end[0] - begin[0] < 2 ? [] : getline(begin[0]+1, end[0]-1))
            \         + [getline(end[0])[: end[1]-1]]
        endif
    endif
    return join(lines, "\n") . (mode ==# 'V' ? "\n" : '')
endfunction

let s:INT = { 'MAX': 2147483647 }

" @return Number: return multibyte aware column number in Visual mode to select
function! s:get_col_in_visual(pos) abort
    let [pos, other] = [a:pos, a:pos is# '.' ? 'v' : '.']
    let c = col(pos)
    let d = s:compare_pos(s:getcoord(pos), s:getcoord(other)) > 0
    \   ? len(s:get_pos_char([line(pos), c - (s:is_exclusive() ? 1 : 0)])) - 1
    \   : 0
    return c + d
endfunction

function! s:get_multi_col(pos) abort
    let c = col(a:pos)
    return c + len(s:get_pos_char([line(a:pos), c])) - 1
endfunction

" Helper:

function! s:is_visual(mode) abort
    return a:mode =~# "[vV\<C-v>]"
endfunction

" @return Boolean
function! s:is_exclusive() abort
    return &selection is# 'exclusive'
endfunction

function! s:curswant() abort
    return winsaveview().curswant
endfunction

" @return coordinate: [Number, Number]
function! s:getcoord(expr) abort
    return getpos(a:expr)[1:2]
endfunction

"" Return character at given position with multibyte handling
" @arg [Number, Number] as coordinate or expression for position :h line()
" @return String
function! s:get_pos_char(...) abort
    let pos = get(a:, 1, '.')
    let [line, col] = type(pos) is# type('') ? s:getcoord(pos) : pos
    return matchstr(getline(line), '.', col - 1)
endfunction

" @return int index of cursor in cword
function! s:get_pos_in_cword(cword, ...) abort
    return (s:is_visual(get(a:, 1, mode(1))) || s:get_pos_char() !~# '\k') ? 0
    \   : s:count_char(searchpos(a:cword, 'bcn')[1], s:get_multi_col('.'))
endfunction

" multibyte aware
function! s:count_char(from, to) abort
    let chars = getline('.')[a:from-1:a:to-1]
    return len(split(chars, '\zs')) - 1
endfunction

" 7.4.341
" http://ftp.vim.org/vim/patches/7.4/7.4.341
if v:version > 704 || v:version == 704 && has('patch341')
    function! s:sort_num(xs) abort
        return sort(a:xs, 'n')
    endfunction
else
    function! s:_sort_num_func(x, y) abort
        return a:x - a:y
    endfunction
    function! s:sort_num(xs) abort
        return sort(a:xs, 's:_sort_num_func')
    endfunction
endif

function! s:sort_pos(pos_list) abort
    " pos_list: [ [x1, y1], [x2, y2] ]
    return sort(a:pos_list, 's:compare_pos')
endfunction

function! s:compare_pos(x, y) abort
    return max([-1, min([1,(a:x[0] == a:y[0]) ? a:x[1] - a:y[1] : a:x[0] - a:y[0]])])
endfunction
1 голос
/ 20 декабря 2014

Я думаю, вы должны использовать "resisisiter буфера обмена".

Для получения более подробной информации вы можете прочитать справку ': h clipboard-autoselect'

Если вы включите эту опцию (установите буфер обмена = без имени, автоматически выбран),
Вы можете получить выделенный текст легче, как это " let l: text = @ * «

0 голосов
/ 06 декабря 2014

Обзор:

визуально выберите, нажмите mapping, повторно выберите исходное выделение с помощью gv и скопируйте его в регистр, наконец вставьте из регистра

Вариант использования:

  1. Добавить функцию Test () к вашему vimrc:

функция! Test () диапазон
exe 'sp temp.tmp'
exe 'норма p'
EndFunction

  1. Открыть новый файл
  2. Создание сопоставления, м
    : vmap, m: норма gvy : вызов Test ()
  3. визуально выделить текст
  4. нажмите, m (выбор пропал, но «norm gv» повторно выбирает его, а «y» возвращает его в текущий регистр)
  5. Test () вызывается: файл temp.tmp открывается и «норма p» вставляется из текущего регистра, который является исходным визуальным выделением
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...