This patch adds asynchronous functions to vimscript. If you want to perform an 
action in 700ms, simply:

let timeout_id = settimeout(700, 'echo("hello")')

To cancel the timeout before it's fired:

canceltimeout(timeout_id)

setinterval() also returns an id that can be used with canceltimeout.

The reason for this patch is simple: asynchronous functionality is needed to 
implement real-time collaborative editing in Vim. This is one of the most 
voted-for features (see http://www.vim.org/sponsor/vote_results.php).

Along with Matt Kaniaris, I founded Floobits to build real-time collaboration 
into every editor. We wrote a plugin for Vim, but we had to use hacks to get 
async behavior (abusing feedkeys or client-server). These methods had 
side-effects such as breaking leaderkeys or other shortcuts. After a lot of 
experimenting, we decided to try patching Vim.

Since Vim is character-driven, we had to munge some low-level input functions 
to get the desired behavior. We changed gui_wait_for_chars() and mch_inchar() 
so that call_timeouts() is run every ticktime milliseconds. The default 
ticktime is 100ms.

This patch isn't finished yet, but it works on unix-based OSes. If the reaction 
is positive, our intention is to change mch_inchar() (or something similar) in 
other OS-specific files. That will get async functions working for everyone.

Even if our patch isn't the best approach, we'd love to help get async 
functions in Vim. Doing so will open the door to a lot of cool plugins.

Oh, and this is the first time either myself or Matt have submitted a patch to 
Vim, so please be gentle.

Sincerely,

Geoff Greer

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.
# HG changeset patch
# User Geoff Greer <[email protected]>
# Date 1378083352 25200
# Node ID 557016400d9bab434378969e3ceba1e2329af6eb
# Parent  d17ef148ada4ada411ab7a88a3a678b6964d0fba
Add asynchronous functions to Vim: settimeout, setinterval, and canceltimeout.

diff -r d17ef148ada4 -r 557016400d9b Filelist
--- a/Filelist	Fri Aug 30 17:29:16 2013 +0200
+++ b/Filelist	Sun Sep 01 17:55:52 2013 -0700
@@ -7,6 +7,8 @@
 		src/arabic.c \
 		src/arabic.h \
 		src/ascii.h \
+		src/async.c \
+		src/async.h \
 		src/blowfish.c \
 		src/buffer.c \
 		src/charset.c \
diff -r d17ef148ada4 -r 557016400d9b src/Makefile
--- a/src/Makefile	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/Makefile	Sun Sep 01 17:55:52 2013 -0700
@@ -1424,6 +1424,7 @@
 TAGS_INCL = *.h
 
 BASIC_SRC = \
+	async.c \
 	blowfish.c \
 	buffer.c \
 	charset.c \
@@ -1513,6 +1514,7 @@
 #LINT_SRC = $(BASIC_SRC)
 
 OBJ_COMMON = \
+	objects/async.o \
 	objects/buffer.o \
 	objects/blowfish.o \
 	objects/charset.o \
@@ -2484,6 +2486,9 @@
 objects:
 	mkdir objects
 
+objects/async.o: async.c
+	$(CCC) -o $@ async.c
+
 objects/blowfish.o: blowfish.c
 	$(CCC) -o $@ blowfish.c
 
diff -r d17ef148ada4 -r 557016400d9b src/async.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/async.c	Sun Sep 01 17:55:52 2013 -0700
@@ -0,0 +1,49 @@
+#include "vim.h"
+
+#ifdef FEAT_ASYNC
+    void
+insert_timeout(timeout_T *to) {
+    timeout_T *cur = timeouts;
+    timeout_T *prev = NULL;
+
+    if (timeouts == NULL) {
+        timeouts = to;
+        return;
+    }
+    while (cur != NULL) {
+        if (cur->tm > to->tm) {
+            if (prev) {
+                prev->next = to;
+            } else {
+                timeouts = to;
+            }
+            to->next = cur;
+            return;
+        }
+        prev = cur;
+        cur = cur->next;
+    }
+}
+
+    void
+call_timeouts() {
+    struct timeval now;
+    gettimeofday(&now, NULL);
+    unsigned long tm = now.tv_sec * 1000 + now.tv_usec/1000;
+    timeout_T *tmp;
+
+    while (timeouts != NULL && timeouts->tm < tm) {
+        call_func_retnr(timeouts->cmd, 0, 0, FALSE);
+        tmp = timeouts;
+        timeouts = timeouts->next;
+        if (tmp->interval == -1) {
+            free(tmp->cmd);
+            free(tmp);
+        } else {
+            tmp->tm = tm + tmp->interval;
+            insert_timeout(tmp);
+        }
+    }
+}
+
+#endif
diff -r d17ef148ada4 -r 557016400d9b src/async.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/async.h	Sun Sep 01 17:55:52 2013 -0700
@@ -0,0 +1,5 @@
+
+EXTERN timeout_T *timeouts INIT(= NULL);
+
+void insert_timeout(timeout_T *to);
+void call_timeouts();
diff -r d17ef148ada4 -r 557016400d9b src/eval.c
--- a/src/eval.c	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/eval.c	Sun Sep 01 17:55:52 2013 -0700
@@ -674,6 +674,11 @@
 static void f_setloclist __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_setmatches __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_setpos __ARGS((typval_T *argvars, typval_T *rettv));
+#ifdef FEAT_ASYNC
+static void f_canceltimeout __ARGS((typval_T *argvars, typval_T *rettv));
+static void f_setinterval __ARGS((typval_T *argvars, typval_T *rettv));
+static void f_settimeout __ARGS((typval_T *argvars, typval_T *rettv));
+#endif
 static void f_setqflist __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_setreg __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_settabvar __ARGS((typval_T *argvars, typval_T *rettv));
@@ -7861,6 +7866,9 @@
     {"byte2line",	1, 1, f_byte2line},
     {"byteidx",		2, 2, f_byteidx},
     {"call",		2, 3, f_call},
+#ifdef FEAT_ASYNC
+    {"canceltimeout", 1, 1, f_canceltimeout},
+#endif
 #ifdef FEAT_FLOAT
     {"ceil",		1, 1, f_ceil},
 #endif
@@ -8059,6 +8067,9 @@
     {"serverlist",	0, 0, f_serverlist},
     {"setbufvar",	3, 3, f_setbufvar},
     {"setcmdpos",	1, 1, f_setcmdpos},
+#ifdef FEAT_ASYNC
+    {"setinterval",    2, 2, f_setinterval},
+#endif
     {"setline",		2, 2, f_setline},
     {"setloclist",	2, 3, f_setloclist},
     {"setmatches",	1, 1, f_setmatches},
@@ -8067,6 +8078,9 @@
     {"setreg",		2, 3, f_setreg},
     {"settabvar",	3, 3, f_settabvar},
     {"settabwinvar",	4, 4, f_settabwinvar},
+#ifdef FEAT_ASYNC
+    {"settimeout",    2, 2, f_settimeout},
+#endif
     {"setwinvar",	3, 3, f_setwinvar},
 #ifdef FEAT_CRYPT
     {"sha256",		1, 1, f_sha256},
@@ -12416,6 +12430,9 @@
 #ifdef FEAT_RELTIME
 	"reltime",
 #endif
+#ifdef FEAT_ASYNC
+    "async",
+#endif
 #ifdef FEAT_QUICKFIX
 	"quickfix",
 #endif
@@ -16589,6 +16606,93 @@
 #endif
 }
 
+#ifdef FEAT_ASYNC
+static int timeout_id = 0;
+
+    static void
+set_timeout(argvars, rettv, interval)
+    typval_T    *argvars;
+    typval_T    *rettv;
+    int interval;
+{
+    long i = get_tv_number(&argvars[0]);
+    char_u *cmd = get_tv_string(&argvars[1]);
+    struct timeval now;
+    rettv->v_type = VAR_NUMBER;
+
+    if (i < 0) {
+        rettv->vval.v_number = -1;
+        EMSG2(_(e_invarg2), "Interval cannot be negative.");
+        return;
+    }
+
+    gettimeofday(&now, NULL);
+    timeout_T *to = malloc(sizeof(timeout_T));
+    to->id = timeout_id++;
+    rettv->vval.v_number = to->id;
+    to->tm = now.tv_sec * 1000 + now.tv_usec/1000 + i;
+    to->cmd = (char_u*)strdup((char*)cmd);
+    to->interval = interval ? i : -1;
+    to->next = NULL;
+
+    insert_timeout(to);
+}
+
+    static void
+f_setinterval(argvars, rettv)
+    typval_T    *argvars;
+    typval_T    *rettv;
+{
+    set_timeout(argvars, rettv, TRUE);
+}
+
+    static void
+f_settimeout(argvars, rettv)
+    typval_T    *argvars;
+    typval_T    *rettv;
+{
+    set_timeout(argvars, rettv, FALSE);
+}
+
+    static void
+f_canceltimeout(argvars, rettv)
+    typval_T    *argvars;
+    typval_T    *rettv;
+{
+    long id = get_tv_number(&argvars[0]);
+    if (id < 0) {
+        rettv->vval.v_number = -1;
+        EMSG2(_(e_invarg2), "Timeout id cannot be negative.");
+        return;
+    }
+
+    timeout_T *tmp = timeouts;
+    timeout_T *prev = NULL;
+    timeout_T *next;
+    while (tmp != NULL) {
+        next = tmp->next;
+        if (tmp->id == id) {
+            if (prev) {
+                prev->next = next;
+            } else {
+                timeouts = next;
+            }
+            free(tmp->cmd);
+            free(tmp);
+            rettv->vval.v_number = 0;
+            rettv->v_type = VAR_NUMBER;
+            return;
+        } else {
+            prev = tmp;
+        }
+        tmp = next;
+    }
+    rettv->vval.v_number = 1;
+    rettv->v_type = VAR_NUMBER;
+    EMSG2(_(e_invarg2), "Timeout id not found.");
+}
+#endif
+
 /*
  * "setpos()" function
  */
diff -r d17ef148ada4 -r 557016400d9b src/feature.h
--- a/src/feature.h	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/feature.h	Sun Sep 01 17:55:52 2013 -0700
@@ -467,6 +467,13 @@
 #endif
 
 /*
+ * +async		settimeout and setinterval functions.
+ */
+#if defined(FEAT_NORMAL)
+# define FEAT_ASYNC
+#endif
+
+/*
  * +diff		Displaying diffs in a nice way.
  *			Requires +windows and +autocmd.
  */
diff -r d17ef148ada4 -r 557016400d9b src/gui.c
--- a/src/gui.c	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/gui.c	Sun Sep 01 17:55:52 2013 -0700
@@ -2898,18 +2898,31 @@
     gui_mch_start_blink();
 
     retval = FAIL;
+
+    int i = 0;
+    while (i < p_ut) {
+#ifdef FEAT_ASYNC
+		retval = gui_mch_wait_for_chars(p_tt);
+		call_timeouts();
+		i += p_tt;
+#else
+		retval = gui_mch_wait_for_chars(p_ut);
+		i += p_ut;
+#endif
+		if (retval == OK) {
+			break;
+		}
+    }
+
+#ifdef FEAT_AUTOCMD
     /*
      * We may want to trigger the CursorHold event.  First wait for
      * 'updatetime' and if nothing is typed within that time put the
      * K_CURSORHOLD key in the input buffer.
      */
-    if (gui_mch_wait_for_chars(p_ut) == OK)
-	retval = OK;
-#ifdef FEAT_AUTOCMD
-    else if (trigger_cursorhold())
+    if (retval == FAIL && trigger_cursorhold())
     {
 	char_u	buf[3];
-
 	/* Put K_CURSORHOLD in the input buffer. */
 	buf[0] = CSI;
 	buf[1] = KS_EXTRA;
@@ -2920,13 +2933,6 @@
     }
 #endif
 
-    if (retval == FAIL)
-    {
-	/* Blocking wait. */
-	before_blocking();
-	retval = gui_mch_wait_for_chars(-1L);
-    }
-
     gui_mch_stop_blink();
     return retval;
 }
diff -r d17ef148ada4 -r 557016400d9b src/option.c
--- a/src/option.c	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/option.c	Sun Sep 01 17:55:52 2013 -0700
@@ -2590,6 +2590,11 @@
 			    (char_u *)NULL, PV_NONE,
 #endif
 			    {(char_u *)"", (char_u *)0L} SCRIPTID_INIT},
+#ifdef FEAT_ASYNC
+    {"ticktime",  "tt",   P_NUM|P_VI_DEF,
+			    (char_u *)&p_tt, PV_NONE,
+			    {(char_u *)100L, (char_u *)0L} SCRIPTID_INIT},
+#endif
     {"tildeop",	    "top",  P_BOOL|P_VI_DEF|P_VIM,
 			    (char_u *)&p_to, PV_NONE,
 			    {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT},
diff -r d17ef148ada4 -r 557016400d9b src/option.h
--- a/src/option.h	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/option.h	Sun Sep 01 17:55:52 2013 -0700
@@ -795,6 +795,9 @@
 #ifdef FEAT_INS_EXPAND
 EXTERN char_u	*p_tsr;		/* 'thesaurus' */
 #endif
+#ifdef FEAT_ASYNC
+EXTERN long	p_tt;		/* 'ticktime' */
+#endif
 EXTERN int	p_ttimeout;	/* 'ttimeout' */
 EXTERN long	p_ttm;		/* 'ttimeoutlen' */
 EXTERN int	p_tbi;		/* 'ttybuiltin' */
diff -r d17ef148ada4 -r 557016400d9b src/os_unix.c
--- a/src/os_unix.c	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/os_unix.c	Sun Sep 01 17:55:52 2013 -0700
@@ -379,6 +379,7 @@
     int		tb_change_cnt;
 {
     int		len;
+    int 	retval = FAIL;
 
 #ifdef FEAT_NETBEANS_INTG
     /* Process the queued netbeans messages. */
@@ -393,7 +394,8 @@
     if (wtime >= 0)
     {
 	while (WaitForChar(wtime) == 0)		/* no character available */
-	{
+	{	
+		call_timeouts();
 	    if (!do_resize)	/* return if not interrupted by resize */
 		return 0;
 	    handle_resize();
@@ -410,7 +412,22 @@
 	 * flush all the swap files to disk.
 	 * Also done when interrupted by SIGWINCH.
 	 */
-	if (WaitForChar(p_ut) == 0)
+
+#ifdef FEAT_ASYNC
+	int t = 0;
+	while (t < p_ut) {
+		retval = WaitForChar(p_tt);
+		call_timeouts();
+		t += p_tt;
+		if (retval == OK) {
+			break;
+		}
+	}
+#else
+	retval = WaitForChar(p_ut);
+#endif
+
+	if (retval == FAIL)
 	{
 #ifdef FEAT_AUTOCMD
 	    if (trigger_cursorhold() && maxlen >= 3
@@ -440,12 +457,29 @@
 	 * We want to be interrupted by the winch signal
 	 * or by an event on the monitored file descriptors.
 	 */
+
+	
+	#ifdef FEAT_ASYNC
+	while (TRUE) {
+		retval = WaitForChar(p_tt);
+		call_timeouts();
+		if (retval == OK) {
+			break;
+		}
+	    if (do_resize) {
+    	    /* interrupted by SIGWINCH signal */
+    		handle_resize();
+    	    return 0;
+	    }
+	}
+	#else
 	if (WaitForChar(-1L) == 0)
 	{
 	    if (do_resize)	    /* interrupted by SIGWINCH signal */
 		handle_resize();
 	    return 0;
 	}
+	#endif
 #endif
 
 	/* If input was put directly in typeahead buffer bail out here. */
diff -r d17ef148ada4 -r 557016400d9b src/structs.h
--- a/src/structs.h	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/structs.h	Sun Sep 01 17:55:52 2013 -0700
@@ -2540,3 +2540,17 @@
   UINT32_T state[8];
   char_u   buffer[64];
 } context_sha256_T;
+
+#ifdef FEAT_ASYNC
+/*
+ * Used for async settimeout/interval.
+ */
+struct timeout_T {
+    int id;                     /* timeout/interval id */
+    int interval;               /* interval period if interval, otherwise -1 */
+    unsigned long tm;           /* time to fire (epoch milliseconds) */
+    char_u *cmd;                /* vim command to run */
+    struct timeout_T *next;     /* pointer to next timeout in linked list */
+};
+typedef struct timeout_T timeout_T;
+#endif
diff -r d17ef148ada4 -r 557016400d9b src/version.c
--- a/src/version.c	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/version.c	Sun Sep 01 17:55:52 2013 -0700
@@ -77,6 +77,11 @@
 #else
 	"-arabic",
 #endif
+#ifdef FEAT_ASYNC
+	"+async",
+#else
+	"-async",
+#endif
 #ifdef FEAT_AUTOCMD
 	"+autocmd",
 #else
diff -r d17ef148ada4 -r 557016400d9b src/vim.h
--- a/src/vim.h	Fri Aug 30 17:29:16 2013 +0200
+++ b/src/vim.h	Sun Sep 01 17:55:52 2013 -0700
@@ -2123,6 +2123,10 @@
 # endif
 #endif
 
+#ifdef FEAT_ASYNC
+# include "async.h"
+#endif
+
 #if defined(FEAT_BROWSE) && defined(GTK_CHECK_VERSION)
 # if GTK_CHECK_VERSION(2,4,0)
 #  define USE_FILE_CHOOSER

Raspunde prin e-mail lui