r/vim•
bottar

Vim timer, I need help.

I need a timer in a vim script I want to improve. Vim does not support the idea of a timer in Vim script. So doing something like:

!fun callBack()
    do stuff
endfun

timer( 300, callBack )

Is not possible.

I've tried using Python's threading.Timer and signals, SIGALRM, in Python with little luck. I actually got the Timer object to work, but when the Timer thread would run it would call vim.command( "stuff" ) and it would take seconds for "stuff" to execute. I'm guessing it's a threading issue.

I've looked into using the Vim clientserver feature with Python's Timer. So if the Timer goes off the Timer thread would spawn a call to vim and use the new vim process to send commands to the already running vim process. I think this would work. But, on Fedora 11 at least, Vim clientserver is not enabled for console Vim, it only works on GVim. I use console Vim.

So, suggestions please. Know any good timer like hacks? I'm looking for millisecond precision.

Edit: Explain what I'm trying to accomplish.

There is a plugin I like, popup_it.vim but it's as annoying as it is useful. The problem with it is if your in a context it feels you need completion on it will start that completion the moment you type the first letter that puts you in that context. This is really annoying as you can be typing along at a good pace, and you don't need/want any completion, but the completion popup starts flying around and messing you up. So I want a delay. Don't start any completions until I've been idle for a given time. That's all. Very simple. But not easy to accomplish in vim.

Posted Oct 13, 2009, 7:29 PM UTC · edited

318
rson•

I think this would work. But, on Fedora 11 at least, Vim clientserver is not enabled for console Vim, it only works on GVim. I use console Vim.

Why not rebuild vim and see if it works?

3
bottarOP•

On the list of things to try. The only problem is it's not a portable solution. I don't want to build my own Vim for every machine I want this feature.

I would like to know why Fedora builds the console version without clientserver. As far as I can tell, it seems the clientserver functionality requires the use of some X libraries. If that's the case it makes sense to be able to have console Vim without the X requirements. But I don't know if that's actually the case yet.

1
gronky•

I believe GVim will run on a console, without X if the name of the program does not start with g. On RHEL systems where I work, you can run vimx to get the full featured version in a terminal.

2
bottarOP•

I just looked through the Fedora 11 vim spec file and found some interesting things. gvim is, of course, configured with --with-x while vim is configured without --with-x. So if you run just 'vim', there is no clientserver enabled. You must build vim with X to get client server.

But, you can run

gvim -v

to run the gvim binary in console mode and get the clientserver functionality.

On to the experiment!

1
bottarOP•

Using the clientserver functions with python's threading.Timer works!

If the Timer expires it runs a vim sub process that sends a remote command to the original vim's server. It's still not what I would consider a great solution, but it will do.

I had to alter my environment a little bit. I added an alias to my .bashrc

alias vim='gvim -v --servername vim'

This makes sure the 'vim' command will start a console vim up with a server instance running and listening. My initial test runs this bit of python if a Timer expires:

sname = vim.eval( 'v:servername' )
subprocess.call( ["gvim", "-v", "--servername",\
    "%s"%sname, "--remote-send", '<c-n>']  )

Which gives a basic word completion after a delay. Now I just need to adapt it to use the popup_it plugin's logic for choosing the completion type and I'm good to go. For now.

Thanks for listening and responding to my questions, it has really helped me work this out.

2
super__mario•

I really question the necessity of timer in a plain text editor. If you need a functionality like that, doesn't the task call for a real tool for the task and external program?

3
vimfan•

I agree. What are you trying to achieve with the timer? Maybe there is a better way to do it?

1
bottarOP•

'real tool'?

Are you implying a real tool is something like Eclipse, VS or similar? I'd prefer to have Vim, which is 98% of what I want in an editor and do what I can to get that last 2% perfect then wade through an immense feature list and seemingly endless menu structures of the big project manager/editor programs. Just my preference is all. I think Eclipse and the like are awesome tools, but I prefer vim. I also prefer to not rely on using the mouse as much as possible while writing code. Vim helps quite a lot in that regard.

Vim left the realm of plain text editor years ago.

1
AlecSchueler•

I think he meant that'd you continue to use vim as your editor, but give non-editing tasks to a separate, dedicated program.

1
super__mario•

No, I'm not implying that an IDE is a real tool, not even remotely. But vi/vim should follow UNIX philosophy of do one thing and do it well. VIM is the best text editor bar none, and if you want it to do something that sounds a lot like it would break the text editing paradigm, then write a specialized tool that does that and does it well.

Then either call your tool directly from VIM or invoke it from the shell.

1
bottarOP•

I understand what you mean completely. Thanks for the clarification.

2
GGLucas•

Ah, the dreaded timer. I actually went through the same thing when I created my irc client for vim (unreleased, it's too crappy for that, I'm not even using it since weechat is a ton better, I just wanted to see if it could be done, and it could be :P).

My solution looks like this:

Note that that you might have to test if the awkward way to sleep() I used is necessary, I can't really remember why I did it, but I must've had a reason not to use time.sleep() directly, it probably freezes the entire program if I just time.sleep().

class MainLoop(Thread):
    ''' Update everything in a thread. '''
    def __init__(self):
        Thread.__init__(self)
        self.running = True

    def run(self):
        ''' Run the update loop. '''
        while True:
            self.sleep(TICK_TIME)

            if self.running:
                self.update()
            else:
                break

    def stop(self):
        self.running = False

    def update(self):
        ''' Update all channels and servers '''
        vim.command("do update here !")

    def sleep(self, timeout):
        ''' Sleep for a specified amount of time. '''
        endtime = time.time() + timeout
        delay = 0.0005

        while True:
            remaining = endtime - time.time()

            if remaining <= 0:
                break

            delay = min(delay * 2, remaining, .05)
            time.sleep(delay)
2
bottarOP•

I'm curious, when you call vim.command() from the thread did that cause any problems?

I found when I did that I would get a huge delay from the time the thread called vim.command() until the command was actually executed in the foreground vim process.

1
GGLucas•

Not that I remember, seemed to work fine. The main thing I was having trouble with was updating multiple windows at the same time (vim doesn't like it if you switch windows, change contents, redraw, switch back and repeat that a couple of times - it tends to segfault). After I figured out to do this I never had any trouble with the timers.

1
arnar•

Try :help CursorHold - there might be someway of abusing that, but although precise it might not be reliable if the user has shaky hands.

1
bottarOP•

I've tried using CursorHold. I think it can be made to work, I had no luck, but I would really like to not use it if possible.

1
inkarkat•

There's another plugin like popup_it.vim, AutoComplPop: http://www.vim.org/scripts/script.php?script_id=1879

It allows you to only popup completions after typing N characters of a word, so there's no popup for short words like "it" and "the". Also, the popup choice isn't pre-selected, so you can just continue typing and ignore the popup, or use CTRL-N/P or Enter to select and accept a match. (I don't know whether popup_it.vim supports any of that.)

0
bottarOP•

popup_it behaves in the same way. The problem comes especially when using Omnicomplete as it can be slow, especially on my old slow laptop and a large code base. So after I type say 4 chars the editor will pause to lookup the completion. But if I really just wanted to keep typing that pause is a problem. Plus it's just so much nicer to not have the editor even attempt the completion unless there is a short typing pause. It feels and looks much more subtle and smooth.

It's a fuss over a rather small detail. But I really want it just right. I've been on a personal mission over the last few months to make Vim as perfect for my editing as possible. So close!

1