Tony,
Thanks for the feedback. I've attached a new patch with some basic comments and
non-ANSI C function definitions.
I'd really like to get feedback on the changes to gui_wait_for_chars() and
mch_inchar(). That's where the meat of this patch is.
Sincerely,
Geoff
On Monday, September 2, 2013 5:00:00 PM UTC-7, Tony Mechelynck wrote:
> On 02/09/13 03:42, Geoff Greer wrote:
>
> > 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
>
> >
>
> Your patch is not in the approved coding style (see :help style-examples)
>
>
>
> Wrong:
>
>
>
> void
>
> insert_timeouts(timeout_T *to) {
>
> timeout_T *cur = timeouts
>
> timeout_T *prev = NULL
>
>
>
>
>
> OK:
>
> /*
>
> * Explanation of what the function is used for
>
> * and of how to call it
>
> */
>
> void
>
> insert_timeouts(to)
>
> timeout_T *to; /* short comment about to */
>
> {
>
> timeout_T *cur = timeouts; /* short comment about cur */
>
> timeout_T *prev = NULL; /* short comment about prev */
>
>
>
> NOTE: Don't use ANSI style function declarations. A few people still
>
> have to
>
> use a compiler that doesn't support it.
>
>
>
>
>
>
>
>
>
> Best regards,
>
> Tony.
>
> --
>
> Consensus Terrorism:
>
> The process that decides in-office attitudes and behavior.
>
> -- Douglas Coupland, "Generation X: Tales for an Accelerated
>
> Culture"
--
--
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.
diff -r d17ef148ada4 Filelist
--- a/Filelist Fri Aug 30 17:29:16 2013 +0200
+++ b/Filelist Mon Sep 02 18:20:17 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 src/Makefile
--- a/src/Makefile Fri Aug 30 17:29:16 2013 +0200
+++ b/src/Makefile Mon Sep 02 18:20:17 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 src/async.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/async.c Mon Sep 02 18:20:17 2013 -0700
@@ -0,0 +1,60 @@
+#include "vim.h"
+
+#ifdef FEAT_ASYNC
+
+/*
+ * Insert a new timeout into the timeout linked list.
+ * This is called by set_timeout() in eval.c
+ */
+ void
+insert_timeout(to)
+ timeout_T *to; /* timeout to insert */
+{
+ 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;
+ }
+}
+
+/*
+ * Execute timeouts that are due.
+ * This is called every ticktime milliseconds by low-level input functions.
+ */
+ 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 src/async.h
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/async.h Mon Sep 02 18:20:17 2013 -0700
@@ -0,0 +1,5 @@
+
+EXTERN timeout_T *timeouts INIT(= NULL);
+
+void insert_timeout(timeout_T *to);
+void call_timeouts();
diff -r d17ef148ada4 src/eval.c
--- a/src/eval.c Fri Aug 30 17:29:16 2013 +0200
+++ b/src/eval.c Mon Sep 02 18:20:17 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 src/feature.h
--- a/src/feature.h Fri Aug 30 17:29:16 2013 +0200
+++ b/src/feature.h Mon Sep 02 18:20:17 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 src/gui.c
--- a/src/gui.c Fri Aug 30 17:29:16 2013 +0200
+++ b/src/gui.c Mon Sep 02 18:20:17 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 src/option.c
--- a/src/option.c Fri Aug 30 17:29:16 2013 +0200
+++ b/src/option.c Mon Sep 02 18:20:17 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 src/option.h
--- a/src/option.h Fri Aug 30 17:29:16 2013 +0200
+++ b/src/option.h Mon Sep 02 18:20:17 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 src/os_unix.c
--- a/src/os_unix.c Fri Aug 30 17:29:16 2013 +0200
+++ b/src/os_unix.c Mon Sep 02 18:20:17 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 src/structs.h
--- a/src/structs.h Fri Aug 30 17:29:16 2013 +0200
+++ b/src/structs.h Mon Sep 02 18:20:17 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 src/version.c
--- a/src/version.c Fri Aug 30 17:29:16 2013 +0200
+++ b/src/version.c Mon Sep 02 18:20:17 2013 -0700
@@ -77,6 +77,11 @@
#else
"-arabic",
#endif
+#ifdef FEAT_ASYNC
+ "+async",
+#else
+ "-async",
+#endif
#ifdef FEAT_AUTOCMD
"+autocmd",
#else
diff -r d17ef148ada4 src/vim.h
--- a/src/vim.h Fri Aug 30 17:29:16 2013 +0200
+++ b/src/vim.h Mon Sep 02 18:20:17 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