Wednesday, April 29, 2009

Vim Sugar

Chris Sutter writes:

I have a script and mapping in my ~/.vimrc:


function! CheckForShebang()

   if (match( getline(1) , '^\#!') == 0)

       map <F5> :!./%<CR>

   else

       unmap <F%>

   end

endfunction

map <F5> :call CheckForShebang()



This way, if i'm editing a script with a shebang at the top and i hit <F5>, it maps <F5> to run the script (if not, <F5> is unmapped, so that it doesn't keep checking if I hit it again). I always found myself mapping <F5> by hand to run the current script while i was editting/debugging and I'd frequently lose track of whether I had mapped it or not. You could, of course, replace <F5> with your favorite run/compile/debug/etc key, it just happens to be my standard (from my QBasic days hehe).

Thanks for the tip Chris!

14 comments:

Buster Dog said...

You are missing the end of the if else construction. spaces added to fake out html filter.

function! CheckForShebang()
if (match( getline(1) , '^\#!') == 0)
map < F5 > :!./%< CR >
else
unmap < F% >
end
endfunction
map < F5 > :call CheckForShebang()

w0m said...

I like.
I randomly hit my "run" key whenever I stop to think; this will prevent it from throwing errors at me when i am editing documentation/etc. (I replaced the unamp with an echo as I oft do multiple file types in one session.)

ekse said...

In the current form, you need to press F5, Enter and F5 again to execute the script.I made a little modification so that when you press on F5 for the first time, the script is executed :

function! CheckForShebang()

if (match( getline(1) , '^\#!') == 0)

map < F5 > :!./%< CR >
:!./%
else

unmap < F% >
endif
endfunction

map < F5 > :call CheckForShebang()< CR >

Caio Romão said...

Hi,

Just to let you know that when reading your blog through googlereader, the block you posted as code appears with a white font and white background - quite impossible to read :)

Travis Whitton said...

@Buster Dog

oops! thanks for catching that. fixed.

graywh said...

:map < expr > < F5 > CheckForShebang()

is probably the easiest way to do the original mapping.

Derek Wyatt said...

You could use an event to do the mapping a bit better and localize the mapping to the buffer as well.


au BufEnter * call CheckForShebang()


and in CheckForShebang() change the "map" to "map <buffer>" to make the mapping local to that buffer (actually "nmap <silent> <buffer>" is probably what you really want). You also can get rid of the "unmap" because it's buffer-local and you're not screwing around with any global mapping that you have to unmap later.

And you can go further and map the "concept" of "use F5 to run the file in the buffer" to do different things depending on the file type.

Let's say you want to do that, so the function name is no longer very appropriate, so call it, MapF5ToExecuteFileInBuffer() instead. Now you can have that function do the following:

- map #! files to just execute them straight if on Unix (with a possible automatic chmod u+x) or run them through the appropriate interpreter if you're on Windows (assuming the files don't execute nicely on Windows as it is)

- have Rakefiles run through rake (instead of ruby)

- have vimscripts run through a separate copy of VIM

- etc...

All of this is done magically by hitting the same key for all things. You get the same functionality in a more reusable concept that maps to any file type but does the same logical operation.

Enjoy :)

ekse said...

@derek I tried doing the modifications you suggested but I keep getting errors. Could you post the modified function ? That would be great.

Derek Wyatt said...

I hadn't actually written before, but I've actually tested what's below and it works for me:

function! MapF5ToExecuteFileInBuffer()
    if match(getline(1), '^\#!') == 0
        nmap <silent> <buffer> <F5> :!./%<cr>
    elseif " some other interesting condition here
         " do a different type of mapping
    endif
endfunction

augroup derek_buffer_mapping
    au! BufEnter * call MapF5ToExecuteFileInBuffer()
augroup END

Now if you edit a file and put a #! at the top then the next time you enter the buffer it will do the mapping.

Agreed... "the next time you enter the buffer!?!?", I hear you scream. Yup, that sucks :) Ok... a better event to attach this to is probably "BufWritePre" instead of "BufEnter". Or do both... but the "BufWritePre" or "BufWriteCmd" or "BufWritePost"... all of those should be just fine. The idea is that you can't execute it until you've written it.

ekse said...

@derek Thanks it works just fine!

mossiso said...

Great tip! I'll use this one daily.

punktyras said...

Yes fixed - now dark grey text on black background.... :)

w0m said...

Just as google mailing list mentioned it i figured i'd append here...
In the elseif of the function above; do something like...

:map < F5 > :make %< \|!./% << CR >

to also compile with the same keypress (I'm sure this needs some fudging also)

lennox said...

Hey, I changed your script a little so that it also works if the script is lacking executable permissions:

function! CheckForShebang()
if (match( getline(1) , '^\#!') == 0)
let b:interpreter = getline(1)[2:]
map <F5> :exec "!".b:interpreter." %"<CR>
else
unmap <F5>
end
endfunction