Peter's Blog

Redefining the Impossible

Vim Python development


I love Python and VIM and I always write the former using the latter. All the scripts I write are GUI-less tools, I don't seem to need GUIs. Something that helps me in this, and todays tip, is the runscript plugin. It does this for me:

  • Maps F11 to mark the python script in the current window as one to execute.
  • Maps F9 to open an 'output' window.
  • Maps F12 to run the script chosen by F11 and dump the output from stdout and stderr in the output window.

So the working cycle is:

  • Open or type out python script
  • Press F11 to mark script
  • Press F9 to open output window
  • Press F12 to run script and see results in output window
  • Edit script
  • Press F12
  • Edit script
  • Press F12
  • etc

I have been using this for a few years. Working like this I don't need GUI's, the output window is all I need to see the script results. I can scroll up and down it or copy text from it.

I've tried PythonWin, IDLE, Boa Constructor, Notepad, Scintilla etc and nothing compares. Because the script is not launched from a GUI environment the script is free to launch it's own GUI in wxPython for example.

The main downside of using VIM as an IDE is that I can't use a debugger: I cannot put breakpoints on lines. I do all my debugging with print statements. I could use the python command line debugger but I've never bothered to learn how.

UPDATE: after enthusing about this script I find that it does not work in text mode Vim on Gentoo linux and possibly others so I've fixed it. The problems were:

  • The path to the executable was hard-wired for windows
  • The "Output window" appears to be called "Scratch" in text mode linux version.
" Vim global plugin for running Python scripts
" Version: 1.1
" Maintainer: Frederick Young <lordfyb@hotmail.com>
" Last change: 2001 Oct 30 
"
"*******************************************************************************
"
" --Pressing the "F12" key will run the Python script in the current buffer
"  
" --Pressing the "F11" key will designate the Python script in the current
"   buffer as the script to run when pressing "F12"
"   
" --Pressing "Shift-F11" will remove the designation performed by pressing "F11"
"   and will allow pressing "F12" to run the script in the current buffer
"   
" --Typing the Ex command "Rs" will also allow you to run a Python script but
"   with the benefit of allowing you to pass up to 20 parameters to the script.
"   
"   Sample syntax...
"
"       :rs -xy 800 600
"
"   The above will run the script in the current buffer or the one designated
"   to run when pressing "F11" and pass to it the parameters "-xy 800 600"
"
" --Pressing "F9" will toggle the display of a buffer containing the output
"   produced when running the Ex command "Rs" or pressing "F12".
"
"   With some minor modifications, it should be possible to run other scripts
"   like perl, vbscript, or even executables compiled with the Ex command
"   "make".
"
"*******************************************************************************


" Change this variable to reflect the path of the Python executable on your
" system.  If your system knows how to find Python then setting this variable
" to 'python' should be enough.  Otherwise, type the complete path to the
" executable.
let s:PathToExecutable = 'python'

" Used to designate the Python script to run when pressing "F12" or typing the
" Ex command "Rs"
let s:mainfile = ""

let s:flag = 0
let @a = ""

" Map keys to function calls
if !hasmapto('<Plug>RunScript')
  nmap <unique> <silent> <F12> <Plug>ExecuteScript
endif
if !hasmapto('<Plug>SetMainScript')
  nmap <unique> <silent> <F11> <Plug>SetMainScript
endif
if !hasmapto('<Plug>ClearMainScript')
  nmap <unique> <silent> <S-F11> <Plug>ClearMainScript
endif
if !hasmapto('<Plug>ToggleOutputWindow')
  nmap <unique> <silent> <F9> <Plug>ToggleOutputWindow
endif

" The main plug-in mapping.
nmap <script> <silent> <Plug>ExecuteScript :call <SID>ExecuteScript()<CR>
nmap <script> <silent> <Plug>SetMainScript :call <SID>SetMainScript()<CR>
nmap <script> <silent> <Plug>ClearMainScript :call <SID>ClearMainScript()<CR>
nmap <script> <silent> <Plug>ToggleOutputWindow :call <SID>ToggleOutputWindow()<CR>

function s:SetMainScript()
    let s:mainfile = bufname('%')
    echo s:mainfile . ' set as the starting program.'
endfunction

function s:ClearMainScript()
    echo s:mainfile . ' is no longer the starting program.'
    let s:mainfile = ""
endfunction

function s:ExecuteScript()
    " Execute script
    if strlen(s:mainfile) > 0
        let @a = system(s:PathToExecutable . ' ' . s:mainfile)
    else
        let @a = system(s:PathToExecutable . ' ' . bufname('%'))
    endif
    "bdelete Scratch
    call s:ShowOutputInBuffer()
endfunction

function s:ToggleOutputWindow()
    if s:flag == 0
        let s:flag = 1
        " pcw
        " silent rightbelow new Scratch
        silent rightbelow new Scratch
        resize 7
        set buftype=nofile
        silent normal "aP
    elseif s:flag == 1
        let s:flag = 0
        if bufexists(Scratch) > 0
            silent! bwipeout Scratch
        endif
        let @a = ""
    endif
endfunction

" Ex command which take 0 or more ( up to 20 ) parameters
command -nargs=* Rs call s:CLExecuteScript(<f-args>)

function s:CLExecuteScript(...)
    let index = 1
    let params = ""
    " Assemble the paramters to pass to the Python script
    while index <= a:0
        execute 'let params = params . " " . a:' . index
        let index = index + 1
    endwhile
    " Execute script (with parameters if provided)
    if strlen(s:mainfile) > 0
        let @a = system(s:PathToExecutable . ' ' . s:mainfile . params)
    else
        let @a = system(s:PathToExecutable . ' ' . bufname('%') . params)
    endif
    "bdelete Scratch
    call s:ShowOutputInBuffer()
endfunction

" Display a buffer containing the contents of register "a"
function s:ShowOutputInBuffer()
    if s:flag == 1
        if bufexists("Scratch") > 0
            silent! bwipeout Scratch
        endif
        silent botright new Scratch
        resize 7
        set buftype=nofile
        silent normal "aP
    endif
endfunction

12 Comments

kpd Says:

over 4 years ago

This might help - python debug from within vim:

http://debris.demon.nl/vimdb/

Peter Says:

over 4 years ago

This is interesting, a python debugger for vim.

I tried installing it but it seems to have problems:

  • when I run a script with Vdb it runs to completion and there is no way of killing it (output is fed through a pager but the 'q' for quit option does not work.
  • The Vdb command just runs my script, it doesn't drop into a debugger that allows me to step through the code, as the comments in vimdb.py would suggest.
  • output does not appear to be sent to a buffer like runscript so I cannot scrutinise it as thoroughly: the pager does not go back.
  • breakpoints don't work, it doesn't stop.

My problems may be with using it on windows, with Vim6.3 and Python 2.4.

However, this is an interesting concept. It would be nice to blend it's functionality with runscript, if that is possible. If anything, this is the first real vim python coding I have seen and it has some interesting techniques.

I got it to load as follows (there are no installation instructions on the website):

  • Unzip files into c:\bin\vim\vimdb
  • Edit _vimrc and add the following:
    python << EOF
    import sys
    
    sys.path.append( 'c:\\bin\\vim\\vimdb')
    from vimdb import *
    EOF
    

Peter

Michael Says:

over 3 years ago

The script is nice. However when I use it on more than one script at a time, you get swap file name conflicts on "output window".

Peter Says:

over 3 years ago

I didn't write this, I found it on www.vim.org. I only use one script at a time...

You could try running multiple instances of vim with different swap directories.

As for debugging python from vim, I have this.

Peter

Anonymous Says:

over 2 years ago

Hey,

Have you ever seen PIDA?

It looks awesome. pida.berlios.de

Bill Mill Says:

over 2 years ago

Also, learn to love the pdb module. Instead of a print statement, put "import pdb; pdb.set_trace()" where you would put the print statement. Then, when the code hits that line, the interpreter will drop you into an interactive debugger which is quite nice once you get used to it.

Really, I should bind that line to a shortcut, maybe ctrl-alt-d or something.

Ludvig Ericson Says:

over 2 years ago

The path to vimdb has changed; debris.demon.nl/projects/vimdb/

Bill Says:

about 1 year ago

Peter, thanks for posting this script and thanks for the good comments from everyone.

This is an excellent and very useful script. I made a few changes for my own preferences. I didn't like having the cursor end up in the output window when you opened it and when you ran the script. I also wanted the code to be saved before executing with F12.

I added "winc k" (without quotes) as the last statement in the s:ToggleOutputWindow() and s:ShowOutputInBuffer() functions.

I also added "wall" (without quotes) as the first statement in the s:CLExecuteScript(...) function.

Bill Says:

about 1 year ago

Small problem with this script on my Linux box. In the ToggleOutputWindow function it has:

if bufexists(Scratch) > 0

I changed this to:

if bufexists("Scratch") > 0

Without the quotes whenever I pressed F9 to toggle I was getting an error. Works great.

I also added "winc k" (without the quotes) as the last line in ToggleOutputWindow to keep the cursor in the edit windows.

And I added "wall" (without the quotes) as the first line in ExecuteScript to save the code before executing.

Thanks for the tips from everyone. And thanks Peter for posting this on your blog.

Keith Says:

about 1 year ago

Thanks - this is just what I've been looking for. I use Vim everywhere.

Keith

macween Says:

about 1 year ago

wow, it look so great!!! I will try it...

Phloam Says:

about 1 year ago

Fixed a bug that stopped me from hiding the output buffer once I had opened it.

Line 101 is currently: if bufexists(Scratch) > 0

Whereas it should be: if bufexists("Scratch") > 0

Phloam

Have Your Say

I welcome constructive comments or questions but I reserve the right to delete any comments that displease me.

Who are you?

(Optional) If you enter an email address here I might email you back. Your email address will not be sold to spammers or shown anywhere

What do you have to say?