Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vim obtain string between visual selection range with Python

Tags:

python

vim

Here is some text
here is line two of text

I visually select from is to is in Vim: (brackets represent the visual selection [ ])

Here [is some text
here is] line two of text

Using Python, I can obtain the range tuples of the selection:

function! GetRange()
python << EOF

import vim

buf   = vim.current.buffer # the buffer
start = buf.mark('<')      # start selection tuple: (1,5)
end   = buf.mark('>')      # end selection tuple: (2,7)

EOF
endfunction

I source this file: :so %, select the text visually, run :<,'>call GetRange() and

now that I have (1,5) and (2,7). In Python, how can I compile the string that is the following:

is some text\nhere is

Would be nice to:

  1. Obtain this string for future manipulation
  2. then replace this selected range with the updated/manipulated string
like image 320
tester Avatar asked Aug 10 '13 20:08

tester


3 Answers

Try this:

fun! GetRange()
python << EOF

import vim

buf = vim.current.buffer
(lnum1, col1) = buf.mark('<')
(lnum2, col2) = buf.mark('>')
lines = vim.eval('getline({}, {})'.format(lnum1, lnum2))
lines[0] = lines[0][col1:]
lines[-1] = lines[-1][:col2]
print "\n".join(lines)

EOF
endfun

You can use vim.eval to get python values of vim functions and variables.

like image 52
Conner Avatar answered Nov 20 '22 18:11

Conner


This would probably work if you used pure vimscript

function! GetRange()
    let @" = substitute(@", '\n', '\\n', 'g')
endfunction

vnoremap ,r y:call GetRange()<CR>gvp

This will convert all newlines into \n in the visual selection and replace the selection with that string.

This mapping yanks the selection into the " register. Calls the function (isn't really necessary since its only one command). Then uses gv to reselect the visual selection and then pastes the quote register back onto the selected region.

Note: in vimscript all user defined functions must start with an Uppercase letter.

like image 4
FDinoff Avatar answered Nov 20 '22 16:11

FDinoff


Here's another version based on Conner's answer. I took qed's suggestion and also added a fix for when the selection is entirely within one line.

import vim

def GetRange():
    buf = vim.current.buffer
    (lnum1, col1) = buf.mark('<')
    (lnum2, col2) = buf.mark('>')
    lines = vim.eval('getline({}, {})'.format(lnum1, lnum2))
    if len(lines) == 1:
        lines[0] = lines[0][col1:col2 + 1]
    else:
        lines[0] = lines[0][col1:]
        lines[-1] = lines[-1][:col2 + 1]
    return "\n".join(lines)
like image 3
Andrew Magee Avatar answered Nov 20 '22 16:11

Andrew Magee