Quantcast
Viewing all articles
Browse latest Browse all 15

Execute Selected Lines of (Optionally) Marked Up Python Code in a Vim Buffer

Site Section: 

Keywords: 

There are a number of solutions for executing Python code in your active buffer in Vim. All of these expect the buffer lines to be well-formatted Python code, with correct indentation. Many times, however, I am working on program or other documentation (in, for example reStructuredTex or Markdown format), and the code fragments that I want to execute have extra indentation or line leaders.

For example, a reStructuredText buffer might look like:

        How to Wuzzle the Wookie
        -------------------------

        The :func:`foo` function can be used to wuzzle the wookie by::

            >>> import bar
            >>> fuzzlewuzzle = bar.foo("the wookie")
            >>> for fuzz in fuzzlewuzzle:
            ...     if fuzz is bar.buzz():
            ...          bar.wuzzle(fuzz)

While a Markdown buffer might have:

        ## How to Wuzzle the Wookie

        The ``foo`` function can be used to to wuzzle the wookie by:

            import bar
            fuzzlewuzzle = bar.foo("the wookie")
            for fuzz in fuzzlewuzzle:
                if fuzz is bar.buzz():
                    bar.wuzzle(fuzz)

Simply passing the code lines in the above text to be evaluated in Python will not do: the extra decorators and indents required for embedding the code in the main document need to be dealt with.

Furthermore, many of the solutions that I could find (a) require Vim to have been built with Python enabled, and (b) use Vim's internal Python to evaluate the lines. The first seems like an unnecessary constraint, considering that native VimScript can handle the pre-processing quite well, while the second is problematical if there are external packages in your default system Python that the internal Vim Python does not have or if you prefer using the default system Python for whatever other reason. Here I describe a pure-VimScript solution that handles the execution of optionally-marked-up visually-selected line ranges in your default system Python, and either shows the results in the command window or inserts the results in the current buffer.

Installation and Usage

Include the following code in your "~/.vimrc" or otherwise source it into your current Vim session. Then select some lines of Python code and type ":EvalPy" to evaluate the lines in the default Python session and show the results in the command window, or ":EvalPy!" to evaluate the lines in the default Python session and insert the results in the current buffer.

" Execute currently selected visual range as Python.  Lines are pre-processed
" to remove extra indentation, leaders, or decorators that might be in place
" due to the line range being part of a code block in a markup-language
" document (such as ReStructured Text, Markdown, etc.)
" Usage: Select a range of line in the buffer and then call ':EvalPy' to
" execute those lines in the default system Python and show the results in the
" command window. Using the 'bang' operator (':EvalPy!') will execute the
" lines and insert the results in the current buffer.
function! <SID>EvaluateCurrentRangeAsMarkedUpPython(insert_results) range
    "" get lines
    let [lnum1, col1] = getpos("'<")[1:2]
    let [lnum2, col2] = getpos("'>")[1:2]
    let lines = getline(lnum1, lnum2)
    " let lines[-1] = lines[-1][: col2 - 2]
    " let lines[0] = lines[0][col1 - 1:]

    "" remove blank rows
    let rows = []
    for line in lines
        let row = substitute(line, '^\s*\(.\{-}\)\s*$', '\1', '')
        if len(row) > 0
            call add(rows, line)
        endif
    endfor
    let lines = rows

    if len(lines) == 0
        return
    endif
    let leader = matchstr(lines[0], '^\s*\(>>>\|\.\.\.\)\{0,1}\s*')
    let leader_len = len(leader)
    let code_lines = []
    for line in lines
        let code_line = strpart(line, leader_len)
        call add(code_lines, code_line)
    endfor
    let code = join(code_lines, "\n")
    if empty(a:insert_results)
        redir => result
        silent execute "!python -c " . shellescape(code)
        redir END
        let rows = split(result, '\n')[1:]
        let result = join(rows, "\n")
        echo result
    else
        let endpos = getpos("'>")
        call setpos('.', endpos)
        execute "r !python -c " . shellescape(code)
    endif
endfunction
command! -bang -range EvalPy :call s:EvaluateCurrentRangeAsMarkedUpPython("<bang>")


Viewing all articles
Browse latest Browse all 15

Trending Articles