Hi, 2013/10/11 Fri 10:13:34 UTC+9 Ken Takata wrote: > Your patch still have some coding style errors and I fixed them. > Please check the attached patch. > > Additionally, there are still some issues I think: > > 1. Copyright information is missing in the timers.c. > 2. MCH_MONOTONIC_TIME is defined in os_macosx.m and os_unix.c, but it should > be > defined in header files because the definition is used in timers.c. > 3. Do we still need to support MSVC6? (Note that 7.4.044 is a fix for VC6.) > If it is needed, we can't use "long long". We should define a 64-bit type. > Similar (but not the same) thing is written in the todo.txt: > > > On 64 bit MS-Windows "long" is only 32 bits, but we sometimes need to store > > a > > 64 bits value. Change all number options to use nropt_T and define it to > > the > > right type.
I have updated the patch: * Couldn't compile with MSVC. * Add has("timers"). * Update documents. * Fix a warning. * etc. Regards, Ken Takata -- -- 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 vim_dev+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
diff --git a/Filelist b/Filelist --- a/Filelist +++ b/Filelist @@ -69,6 +69,7 @@ src/term.c \ src/term.h \ src/termlib.c \ + src/timers.c \ src/ui.c \ src/undo.c \ src/version.c \ @@ -94,6 +95,7 @@ src/testdir/python_after/*.py \ src/testdir/python_before/*.py \ src/proto.h \ + src/proto/timers.pro \ src/proto/blowfish.pro \ src/proto/buffer.pro \ src/proto/charset.pro \ diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2013 Aug 24 +*eval.txt* For Vim version 7.4. Last change: 2013 Sep 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1715,6 +1715,7 @@ byteidx( {expr}, {nr}) Number byte index of {nr}'th char in {expr} call( {func}, {arglist} [, {dict}]) any call {func} with arguments {arglist} +canceltimeout( {nr}) Number cancel the timeout or interval ceil( {expr}) Float round {expr} up changenr() Number current change number char2nr( {expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr} @@ -1926,6 +1927,8 @@ serverlist() String get a list of available servers setbufvar( {expr}, {varname}, {val}) set {varname} in buffer {expr} to {val} setcmdpos( {pos}) Number set cursor position in command-line +setinterval( {nr}, {string}) Number evaluate the expression {string} every + {nr} milliseconds setline( {lnum}, {line}) Number set line {lnum} to {line} setloclist( {nr}, {list}[, {action}]) Number modify location list using {list} @@ -1936,6 +1939,8 @@ settabvar( {nr}, {varname}, {val}) set {varname} in tab page {nr} to {val} settabwinvar( {tabnr}, {winnr}, {varname}, {val}) set {varname} in window {winnr} in tab page {tabnr} to {val} +settimeout( {nr}, {string}) Number evaluate the expression {string} after + {nr} milliseconds setwinvar( {nr}, {varname}, {val}) set {varname} in window {nr} to {val} sha256( {string}) String SHA256 checksum of {string} shellescape( {string} [, {special}]) @@ -2281,6 +2286,15 @@ {dict} is for functions with the "dict" attribute. It will be used to set the local variable "self". |Dictionary-function| +canceltimeout({nr}) *canceltimeout()* + Cancel the timeout or interval with id, {nr}, preventing it + from every firing. + Also see |settimeout()| and |setinterval()|. + {only available when compiled with the |+timers| feature} + Examples: > + let timeout_id = settimeout(5000, 'echo(2)') + echo canceltimeout(timeout_id) + ceil({expr}) *ceil()* Return the smallest integral value greater than or equal to {expr} as a |Float| (round up). @@ -5187,6 +5201,23 @@ Returns 0 when successful, 1 when not editing the command line. +setinterval({nr}, {string}) *setinterval()* *E881* + Immediately returns an interval id and evaluate the expression, + {string}, every {nr} milliseconds. Intervals do not pile up. + The timer's resolution defaults to 100ms and can be changed by + setting |ticktime|. Intervals can be canceled by calling + |canceltimeout({interval_id})|. + NOTE: Vim is single-threaded and all expressions are run + within the main thread. Therefore, expressions should return + control flow within a short amount of time. + Also see |settimeout()| and |canceltimeout()|. + {only available when compiled with the |+timers| feature} + Examples: > + :call setinterval(1000, "call echo(2)") +< 2 + 2 + 2 + setline({lnum}, {text}) *setline()* Set line {lnum} of the current buffer to {text}. To insert lines use |append()|. @@ -5356,6 +5387,21 @@ :call settabwinvar(3, 2, "myvar", "foobar") < This function is not available in the |sandbox|. +settimeout({nr}, {string}) *settimeout()* + Immediately returns an interval id and evaluate the expression, + {string}, once after {nr} milliseconds. The timer's + resolution defaults to 100ms and can be changed by setting + |ticktime|. Timeouts can be canceled by calling + |canceltimeout({interval_id})|. + NOTE: Vim is single-threaded and all expressions are run + within the main thread. Therefore, expressions should return + control flow within a short amount of time. + Also see |setinterval()| and |canceltimeout()|. + {only available when compiled with the |+timers| feature} + Examples: > + :call settimeout(1000, "call echo(2)") +< 2 + setwinvar({nr}, {varname}, {val}) *setwinvar()* Like |settabwinvar()| for the current tab page. Examples: > @@ -6479,6 +6525,7 @@ textobjects Compiled with support for |text-objects|. tgetent Compiled with tgetent support, able to use a termcap or terminfo file. +timers Compiled with support for timer funtions. title Compiled with window title support |'title'|. toolbar Compiled with support for |gui-toolbar|. unix Unix version of Vim. diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1544,6 +1544,7 @@ |:tearoff| :te[aroff] tear-off a menu |:tfirst| :tf[irst] jump to first matching tag |:throw| :th[row] throw an exception +|:timers| :ti[mers] show all pending timers |:tjump| :tj[ump] like ":tselect", but jump directly when there is only one match |:tlast| :tl[ast] jump to last matching tag diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -7253,6 +7253,17 @@ uses another default. Backticks cannot be used in this option for security reasons. + *'ticktime'* *'tt'* +'ticktime' 'tt' number (default 100) + global + {not in Vi} + This is the resolution for all timers set with |setinterval()| and + |settimeout()|. Decreasing this number will make the timers + more accurate but will also make Vim less efficient. To globally + disable all timers for debugging, set |ticktime| to -1. + Also see |setinterval()|, |settimeout()|, and |canceltimeout()|. + {only available when compiled with the |+timers| feature} + *'tildeop'* *'top'* *'notildeop'* *'notop'* 'tildeop' 'top' boolean (default off) global diff --git a/runtime/doc/tags b/runtime/doc/tags --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1014,6 +1014,7 @@ 'tf' options.txt /*'tf'* 'tgst' options.txt /*'tgst'* 'thesaurus' options.txt /*'thesaurus'* +'ticktime' options.txt /*'ticktime'* 'tildeop' options.txt /*'tildeop'* 'timeout' options.txt /*'timeout'* 'timeoutlen' options.txt /*'timeoutlen'* @@ -1032,6 +1033,7 @@ 'ts' options.txt /*'ts'* 'tsl' options.txt /*'tsl'* 'tsr' options.txt /*'tsr'* +'tt' options.txt /*'tt'* 'ttimeout' options.txt /*'ttimeout'* 'ttimeoutlen' options.txt /*'ttimeoutlen'* 'ttm' options.txt /*'ttm'* @@ -4296,6 +4298,7 @@ E879 syntax.txt /*E879* E88 windows.txt /*E88* E880 if_pyth.txt /*E880* +E881 eval.txt /*E881* E89 message.txt /*E89* E90 message.txt /*E90* E91 options.txt /*E91* @@ -4987,6 +4990,7 @@ c_digraph cmdline.txt /*c_digraph* c_wildchar cmdline.txt /*c_wildchar* call() eval.txt /*call()* +canceltimeout() eval.txt /*canceltimeout()* carriage-return intro.txt /*carriage-return* case change.txt /*case* catch-all eval.txt /*catch-all* @@ -7653,6 +7657,7 @@ set-spc-auto spell.txt /*set-spc-auto* setbufvar() eval.txt /*setbufvar()* setcmdpos() eval.txt /*setcmdpos()* +setinterval() eval.txt /*setinterval()* setline() eval.txt /*setline()* setloclist() eval.txt /*setloclist()* setmatches() eval.txt /*setmatches()* @@ -7661,6 +7666,7 @@ setreg() eval.txt /*setreg()* settabvar() eval.txt /*settabvar()* settabwinvar() eval.txt /*settabwinvar()* +settimeout() eval.txt /*settimeout()* setting-guifont gui.txt /*setting-guifont* setting-guitablabel tabpage.txt /*setting-guitablabel* setting-tabline tabpage.txt /*setting-tabline* diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -412,6 +412,7 @@ N *+termresponse* support for |t_RV| and |v:termresponse| N *+textobjects* |text-objects| selection *+tgetent* non-Unix only: able to use external termcap + *+timers* timer functions N *+title* Setting the window 'title' and 'icon' N *+toolbar* |gui-toolbar| N *+user_commands* User-defined commands. |user-commands| diff --git a/src/Make_bc3.mak b/src/Make_bc3.mak --- a/src/Make_bc3.mak +++ b/src/Make_bc3.mak @@ -91,6 +91,7 @@ syntax.obj \ tag.obj \ term.obj \ + timers.obj \ ui.obj \ undo.obj \ window.obj diff --git a/src/Make_bc5.mak b/src/Make_bc5.mak --- a/src/Make_bc5.mak +++ b/src/Make_bc5.mak @@ -617,6 +617,7 @@ $(OBJDIR)\syntax.obj \ $(OBJDIR)\tag.obj \ $(OBJDIR)\term.obj \ + $(OBJDIR)\timers.obj \ $(OBJDIR)\ui.obj \ $(OBJDIR)\undo.obj \ $(OBJDIR)\version.obj \ diff --git a/src/Make_cyg.mak b/src/Make_cyg.mak --- a/src/Make_cyg.mak +++ b/src/Make_cyg.mak @@ -568,6 +568,7 @@ $(OUTDIR)/syntax.o \ $(OUTDIR)/tag.o \ $(OUTDIR)/term.o \ + $(OUTDIR)/timers.o \ $(OUTDIR)/ui.o \ $(OUTDIR)/undo.o \ $(OUTDIR)/version.o \ diff --git a/src/Make_dice.mak b/src/Make_dice.mak --- a/src/Make_dice.mak +++ b/src/Make_dice.mak @@ -67,6 +67,7 @@ syntax.c \ tag.c \ term.c \ + timers.c \ ui.c \ undo.c \ window.c \ @@ -113,6 +114,7 @@ o/syntax.o \ o/tag.o \ o/term.o \ + o/timers.o \ o/ui.o \ o/undo.o \ o/window.o \ @@ -226,6 +228,8 @@ o/termlib.o: termlib.c $(SYMS) +o/timers.o: timers.c $(SYMS) + o/ui.o: ui.c $(SYMS) o/undo.o: undo.c $(SYMS) diff --git a/src/Make_djg.mak b/src/Make_djg.mak --- a/src/Make_djg.mak +++ b/src/Make_djg.mak @@ -60,6 +60,7 @@ obj/syntax.o \ obj/tag.o \ obj/term.o \ + obj/timers.o \ obj/ui.o \ obj/undo.o \ obj/window.o \ diff --git a/src/Make_ivc.mak b/src/Make_ivc.mak --- a/src/Make_ivc.mak +++ b/src/Make_ivc.mak @@ -253,6 +253,7 @@ "$(INTDIR)/syntax.obj" \ "$(INTDIR)/tag.obj" \ "$(INTDIR)/term.obj" \ + "$(INTDIR)/timers.obj" \ "$(INTDIR)/ui.obj" \ "$(INTDIR)/undo.obj" \ "$(INTDIR)/version.obj" \ @@ -649,6 +650,10 @@ # End Source File # Begin Source File +SOURCE=.\timers.c +# End Source File +# Begin Source File + SOURCE=.\ui.c # End Source File # Begin Source File diff --git a/src/Make_manx.mak b/src/Make_manx.mak --- a/src/Make_manx.mak +++ b/src/Make_manx.mak @@ -75,6 +75,7 @@ syntax.c \ tag.c \ term.c \ + timers.c \ ui.c \ undo.c \ window.c \ @@ -123,6 +124,7 @@ obj/syntax.o \ obj/tag.o \ obj/term.o \ + obj/timers.o \ obj/ui.o \ obj/undo.o \ obj/window.o \ @@ -170,6 +172,7 @@ proto/tag.pro \ proto/term.pro \ proto/termlib.pro \ + proto/timers.pro \ proto/ui.pro \ proto/undo.pro \ proto/window.pro @@ -348,6 +351,9 @@ obj/termlib.o: termlib.c $(CCSYM) $@ termlib.c +obj/timers.o: timers.c + $(CCSYM) $@ timers.c + obj/ui.o: ui.c $(CCSYM) $@ ui.c diff --git a/src/Make_ming.mak b/src/Make_ming.mak --- a/src/Make_ming.mak +++ b/src/Make_ming.mak @@ -546,6 +546,7 @@ $(OUTDIR)/syntax.o \ $(OUTDIR)/tag.o \ $(OUTDIR)/term.o \ + $(OUTDIR)/timers.o \ $(OUTDIR)/ui.o \ $(OUTDIR)/undo.o \ $(OUTDIR)/version.o \ diff --git a/src/Make_morph.mak b/src/Make_morph.mak --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -65,6 +65,7 @@ syntax.c \ tag.c \ term.c \ + timers.c \ ui.c \ undo.c \ version.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -569,6 +569,7 @@ $(OUTDIR)\syntax.obj \ $(OUTDIR)\tag.obj \ $(OUTDIR)\term.obj \ + $(OUTDIR)\timers.obj \ $(OUTDIR)\ui.obj \ $(OUTDIR)\undo.obj \ $(OUTDIR)\window.obj \ @@ -1192,6 +1193,8 @@ $(OUTDIR)/term.obj: $(OUTDIR) term.c $(INCL) +$(OUTDIR)/timers.obj: $(OUTDIR) timers.c $(INCL) + $(OUTDIR)/ui.obj: $(OUTDIR) ui.c $(INCL) $(OUTDIR)/undo.obj: $(OUTDIR) undo.c $(INCL) diff --git a/src/Make_os2.mak b/src/Make_os2.mak --- a/src/Make_os2.mak +++ b/src/Make_os2.mak @@ -79,6 +79,7 @@ syntax.o \ tag.o \ term.o \ + timers.o \ ui.o \ undo.o \ window.o \ diff --git a/src/Make_sas.mak b/src/Make_sas.mak --- a/src/Make_sas.mak +++ b/src/Make_sas.mak @@ -177,6 +177,7 @@ syntax.o \ tag.o \ term.o \ + timers.o \ ui.o \ undo.o \ window.o \ @@ -225,6 +226,7 @@ proto/tag.pro \ proto/term.pro \ proto/termlib.pro \ + proto/timers.pro \ proto/ui.pro \ proto/undo.pro \ proto/window.pro @@ -368,6 +370,8 @@ proto/term.pro: term.c termlib.o: termlib.c proto/termlib.pro: termlib.c +timers.o: timers.c +proto/timers.pro: timers.c ui.o: ui.c proto/ui.pro: ui.c undo.o: undo.c diff --git a/src/Make_vms.mms b/src/Make_vms.mms --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -308,7 +308,7 @@ ex_docmd.c ex_eval.c ex_getln.c if_xcmdsrv.c fileio.c fold.c getchar.c \ hardcopy.c hashtab.c main.c mark.c menu.c mbyte.c memfile.c memline.c message.c misc1.c \ misc2.c move.c normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c sha256.c\ - spell.c syntax.c tag.c term.c termlib.c ui.c undo.c version.c screen.c \ + spell.c syntax.c tag.c term.c termlib.c timers.c ui.c undo.c version.c screen.c \ window.c os_unix.c os_vms.c pathdef.c \ $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) $(SNIFF_SRC) \ $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) @@ -319,7 +319,7 @@ menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \ move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj quickfix.obj \ regexp.obj search.obj sha256.obj spell.obj syntax.obj tag.obj term.obj termlib.obj \ - ui.obj undo.obj screen.obj version.obj window.obj os_unix.obj \ + timers.obj ui.obj undo.obj screen.obj version.obj window.obj os_unix.obj \ os_vms.obj pathdef.obj if_mzsch.obj\ $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) $(SNIFF_OBJ) \ $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) diff --git a/src/Makefile b/src/Makefile --- a/src/Makefile +++ b/src/Makefile @@ -1468,6 +1468,7 @@ syntax.c \ tag.c \ term.c \ + timers.c \ ui.c \ undo.c \ version.c \ @@ -1557,6 +1558,7 @@ $(SNIFF_OBJ) \ objects/tag.o \ objects/term.o \ + objects/timers.o \ objects/ui.o \ objects/undo.o \ objects/version.o \ @@ -1630,6 +1632,7 @@ tag.pro \ term.pro \ termlib.pro \ + timers.pro \ ui.pro \ undo.pro \ version.pro \ @@ -2723,6 +2726,9 @@ objects/term.o: term.c $(CCC) -o $@ term.c +objects/timers.o: timers.c + $(CCC) -o $@ timers.c + objects/ui.o: ui.c $(CCC) -o $@ ui.c @@ -3004,6 +3010,9 @@ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \ arabic.h +objects/timers.o: timers.c vim.h auto/config.h feature.h os_unix.h os_mac.h ascii.h \ + keymap.h term.h macros.h option.h structs.h regexp.h gui.h ex_cmds.h \ + proto.h proto/timers.pro globals.h farsi.h arabic.h auto/osdef.h objects/ui.o: ui.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h ascii.h \ keymap.h term.h macros.h option.h structs.h regexp.h gui.h gui_beval.h \ proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h arabic.h diff --git a/src/auto/configure b/src/auto/configure --- a/src/auto/configure +++ b/src/auto/configure @@ -11090,6 +11090,57 @@ fi + +for ac_func in clock_gettime +do : + ac_fn_c_check_func "$LINENO" "clock_gettime" "ac_cv_func_clock_gettime" +if test "x$ac_cv_func_clock_gettime" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_CLOCK_GETTIME 1 +_ACEOF +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for clock_gettime in -lrt" >&5 +$as_echo_n "checking for clock_gettime in -lrt... " >&6; } +if ${ac_cv_lib_rt_clock_gettime+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrt $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char clock_gettime (); +int +main () +{ +return clock_gettime (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_rt_clock_gettime=yes +else + ac_cv_lib_rt_clock_gettime=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_clock_gettime" >&5 +$as_echo "$ac_cv_lib_rt_clock_gettime" >&6; } +if test "x$ac_cv_lib_rt_clock_gettime" = xyes; then : + LIBS="$LIBS -lrt" +fi +fi +done + for ac_func in bcmp fchdir fchown fsync getcwd getpseudotty \ getpwent getpwnam getpwuid getrlimit gettimeofday getwd lstat memcmp \ memset mkdtemp nanosleep opendir putenv qsort readlink select setenv \ diff --git a/src/configure.in b/src/configure.in --- a/src/configure.in +++ b/src/configure.in @@ -3263,6 +3263,13 @@ AC_DEFINE(BAD_GETCWD) fi +AC_CHECK_FUNCS(clock_gettime, [], [ + AC_CHECK_LIB(rt, clock_gettime, [ + LIBS="$LIBS -lrt" + ]) +]) + + dnl Check for functions in one big call, to reduce the size of configure. dnl Can only be used for functions that do not require any include. AC_CHECK_FUNCS(bcmp fchdir fchown fsync getcwd getpseudotty \ diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -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_TIMERS +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)); @@ -7862,6 +7867,9 @@ {"byte2line", 1, 1, f_byte2line}, {"byteidx", 2, 2, f_byteidx}, {"call", 2, 3, f_call}, +#ifdef FEAT_TIMERS + {"canceltimeout", 1, 1, f_canceltimeout}, +#endif #ifdef FEAT_FLOAT {"ceil", 1, 1, f_ceil}, #endif @@ -8060,6 +8068,9 @@ {"serverlist", 0, 0, f_serverlist}, {"setbufvar", 3, 3, f_setbufvar}, {"setcmdpos", 1, 1, f_setcmdpos}, +#ifdef FEAT_TIMERS + {"setinterval", 2, 2, f_setinterval}, +#endif {"setline", 2, 2, f_setline}, {"setloclist", 2, 3, f_setloclist}, {"setmatches", 1, 1, f_setmatches}, @@ -8068,6 +8079,9 @@ {"setreg", 2, 3, f_setreg}, {"settabvar", 3, 3, f_settabvar}, {"settabwinvar", 4, 4, f_settabwinvar}, +#ifdef FEAT_TIMERS + {"settimeout", 2, 2, f_settimeout}, +#endif {"setwinvar", 3, 3, f_setwinvar}, #ifdef FEAT_CRYPT {"sha256", 1, 1, f_sha256}, @@ -12486,9 +12500,15 @@ #ifdef FEAT_TEXTOBJ "textobjects", #endif +#ifdef FEAT_TIMERS + "timers", +#endif #ifdef HAVE_TGETENT "tgetent", #endif +#ifdef FEAT_TIMERS + "timers", +#endif #ifdef FEAT_TITLE "title", #endif @@ -16599,6 +16619,119 @@ #endif } +#ifdef FEAT_TIMERS +static int timeout_id = 0; + + static void +set_timeout(argvars, rettv, interval) + typval_T *argvars; + typval_T *rettv; + int interval; +{ + timeout_T *to; + long i = get_tv_number(&argvars[0]); + char_u *cmd = get_tv_string(&argvars[1]); + + rettv->v_type = VAR_NUMBER; + + if (i < 0) + { + rettv->vval.v_number = -1; + EMSG2(_(e_invarg2), "Interval cannot be negative."); + return; + } + + to = malloc(sizeof(timeout_T)); + to->id = timeout_id++; + to->tm = get_monotonic_time() + i; + if (sourcing_name) + { + to->sourcing_name = (char_u*)strdup((char *)sourcing_name); + to->sourcing_lnum = sourcing_lnum; + } + else + { + to->sourcing_name = (char_u*)strdup((char*)cmd); + to->sourcing_lnum = 0; + } + + rettv->vval.v_number = to->id; + to->cmd = (char_u*)strdup((char*)cmd); + to->interval = interval ? i : -1; + to->next = NULL; + + insert_timeout(to); +} + +/* + * "setinterval()" function + */ + static void +f_setinterval(argvars, rettv) + typval_T *argvars; + typval_T *rettv; +{ + set_timeout(argvars, rettv, TRUE); +} + +/* + * "settimeout()" function + */ + static void +f_settimeout(argvars, rettv) + typval_T *argvars; + typval_T *rettv; +{ + set_timeout(argvars, rettv, FALSE); +} + +/* + * "canceltimeout()" function + */ + static void +f_canceltimeout(argvars, rettv) + typval_T *argvars; + typval_T *rettv; +{ + timeout_T *tmp = timeouts; + timeout_T *prev = NULL; + timeout_T *next; + long id = get_tv_number(&argvars[0]); + + if (id < 0) + { + rettv->vval.v_number = -1; + EMSG2(_(e_invarg2), "Timeout id cannot be negative."); + return; + } + + 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 --git a/src/ex_cmds.h b/src/ex_cmds.h --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -979,6 +979,8 @@ RANGE|NOTADR|BANG|TRLBAR|ZEROR), EX(CMD_throw, "throw", ex_throw, EXTRA|NEEDARG|SBOXOK|CMDWIN), +EX(CMD_timers, "timers", ex_timers, + SBOXOK|CMDWIN), EX(CMD_tjump, "tjump", ex_tag, BANG|TRLBAR|WORD1), EX(CMD_tlast, "tlast", ex_tag, diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -470,6 +470,12 @@ # define ex_nbstart ex_ni #endif +#ifdef FEAT_TIMERS +static void ex_timers __ARGS((exarg_T *eap)); +#else +# define ex_timers ex_ni +#endif + #ifndef FEAT_EVAL # define ex_debug ex_ni # define ex_breakadd ex_ni @@ -11508,3 +11514,54 @@ ml_clearmarked(); /* clear rest of the marks */ } #endif + +#ifdef FEAT_TIMERS + + static void +ex_timers(eap) + exarg_T *eap; +{ + int msg_save = msg_scroll; + timeout_T *tm; + char num_str[20]; + unsigned long long now = get_monotonic_time(); + + msg_start(); + MSG_PUTS_TITLE(_("\n--- Timers ---\n")); + msg_puts((char_u *)"ID Next (ms) Interval (ms) Source Command\n"); + for (tm = timeouts; tm != NULL; tm = tm->next) + { + sprintf(num_str, "%i", tm->id); + msg_puts((char_u *)num_str); + msg_col = 10; + + sprintf(num_str, "%llu", tm->tm - now); + msg_puts((char_u *)num_str); + msg_col = 24; + + if (tm->interval != -1) + { + sprintf(num_str, "%i", tm->interval); + msg_puts((char_u *)num_str); + } + msg_col = 40; + + msg_puts_long_attr(tm->sourcing_name, 30); + if (tm->sourcing_lnum) + { + sprintf(num_str, ":%li", tm->sourcing_lnum); + msg_puts_long_attr((char_u *)num_str, 5); + } + + msg_col = 75; + MSG_PUTS_TITLE(tm->cmd); + msg_putchar('\n'); + + msg_col = 0; + } + + msg_clr_eos(); + msg_end(); + msg_scroll = msg_save; +} +#endif diff --git a/src/feature.h b/src/feature.h --- a/src/feature.h +++ b/src/feature.h @@ -467,6 +467,14 @@ #endif /* + * +timers settimeout and setinterval functions. + */ +#if defined(FEAT_NORMAL) && (defined(MSWIN) || defined(MACOS_X) || \ + (defined(HAVE_GETTIMEOFDAY) && defined(HAVE_SYS_TIME_H))) +# define FEAT_TIMERS +#endif + +/* * +diff Displaying diffs in a nice way. * Requires +windows and +autocmd. */ diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1626,3 +1626,10 @@ #ifdef FEAT_ARABIC # include "arabic.h" #endif + +#ifdef FEAT_TIMERS +/* + * For keeping track of timeouts. + */ +EXTERN timeout_T *timeouts INIT(= NULL); +#endif diff --git a/src/gui.c b/src/gui.c --- a/src/gui.c +++ b/src/gui.c @@ -2862,7 +2862,9 @@ gui_wait_for_chars(wtime) long wtime; { - int retval; + int retval; + long i = 0; + long time_to_wait; #ifdef FEAT_MENU /* @@ -2887,7 +2889,19 @@ /* Blink when waiting for a character. Probably only does something * for showmatch() */ gui_mch_start_blink(); - retval = gui_mch_wait_for_chars(wtime); + while (i < wtime) + { +#ifdef FEAT_TIMERS + time_to_wait = call_timeouts(wtime - i); + i += time_to_wait; + retval = gui_mch_wait_for_chars(time_to_wait); +#else + retval = gui_mch_wait_for_chars(wtime); + i += wtime; +#endif + if (retval == OK) + break; + } gui_mch_stop_blink(); return retval; } @@ -2898,18 +2912,29 @@ gui_mch_start_blink(); retval = FAIL; + + while (i < p_ut) { +#ifdef FEAT_TIMERS + time_to_wait = call_timeouts(p_ut - i); + i += time_to_wait; + retval = gui_mch_wait_for_chars(time_to_wait); +#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 +2945,6 @@ } #endif - if (retval == FAIL) - { - /* Blocking wait. */ - before_blocking(); - retval = gui_mch_wait_for_chars(-1L); - } - gui_mch_stop_blink(); return retval; } diff --git a/src/main.aap b/src/main.aap --- a/src/main.aap +++ b/src/main.aap @@ -334,6 +334,7 @@ syntax.c tag.c term.c + timers.c ui.c undo.c window.c diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -2590,6 +2590,13 @@ (char_u *)NULL, PV_NONE, #endif {(char_u *)"", (char_u *)0L} SCRIPTID_INIT}, + {"ticktime", "tt", P_NUM|P_VI_DEF, +#ifdef FEAT_TIMERS + (char_u *)&p_tt, PV_NONE, +#else + (char_u *)NULL, PV_NONE, +#endif + {(char_u *)20L, (char_u *)0L} SCRIPTID_INIT}, {"tildeop", "top", P_BOOL|P_VI_DEF|P_VIM, (char_u *)&p_to, PV_NONE, {(char_u *)FALSE, (char_u *)0L} SCRIPTID_INIT}, diff --git a/src/option.h b/src/option.h --- a/src/option.h +++ b/src/option.h @@ -795,6 +795,9 @@ #ifdef FEAT_INS_EXPAND EXTERN char_u *p_tsr; /* 'thesaurus' */ #endif +#ifdef FEAT_TIMERS +EXTERN long p_tt; /* 'ticktime' */ +#endif EXTERN int p_ttimeout; /* 'ttimeout' */ EXTERN long p_ttm; /* 'ttimeoutlen' */ EXTERN int p_tbi; /* 'ttybuiltin' */ diff --git a/src/os_macosx.m b/src/os_macosx.m --- a/src/os_macosx.m +++ b/src/os_macosx.m @@ -23,6 +23,22 @@ #include "vim.h" #import <Cocoa/Cocoa.h> +#include <mach/mach.h> +#include <mach/mach_time.h> + + +#define MCH_MONOTONIC_TIME + unsigned long long +mch_monotonic_time(void) +{ + mach_timebase_info_data_t info; + + if (mach_timebase_info(&info) != KERN_SUCCESS) + abort(); + + return (mach_absolute_time() * info.numer / info.denom) / 1000000; +} + /* * Clipboard support for the console. diff --git a/src/os_unix.c b/src/os_unix.c --- a/src/os_unix.c +++ b/src/os_unix.c @@ -892,6 +892,17 @@ } #endif +#if !defined(MACOS_X_UNIX) +# define MCH_MONOTONIC_TIME + unsigned long long +mch_monotonic_time(void) +{ + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} +#endif + #ifdef SET_SIG_ALARM /* * signal function for alarm(). @@ -5057,9 +5068,13 @@ #ifdef FEAT_NETBEANS_INTG int nb_fd = netbeans_filedesc(); #endif -#if defined(FEAT_XCLIPBOARD) || defined(USE_XSMP) || defined(FEAT_MZSCHEME) +#if defined(FEAT_XCLIPBOARD) || defined(USE_XSMP) || defined(FEAT_MZSCHEME) \ + || defined(FEAT_TIMERS) static int busy = FALSE; +# ifdef FEAT_TIMERS + unsigned long long now; +# endif /* May retry getting characters after an event was handled. */ # define MAY_LOOP @@ -5072,18 +5087,24 @@ if (msec > 0 && ( # ifdef FEAT_XCLIPBOARD xterm_Shell != (Widget)0 -# if defined(USE_XSMP) || defined(FEAT_MZSCHEME) +# if defined(USE_XSMP) || defined(FEAT_MZSCHEME) || defined(FEAT_TIMERS) || # endif # endif # ifdef USE_XSMP xsmp_icefd != -1 -# ifdef FEAT_MZSCHEME +# if defined(FEAT_MZSCHEME) || defined(FEAT_TIMERS) || # endif # endif # ifdef FEAT_MZSCHEME (mzthreads_allowed() && p_mzq > 0) +# ifdef FEAT_TIMERS + || +# endif +# endif +# ifdef FEAT_TIMERS + TRUE # endif )) gettimeofday(&start_tv, NULL); @@ -5130,6 +5151,9 @@ mzquantum_used = TRUE; } # endif +# ifdef FEAT_TIMERS + towait = call_timeouts(msec); +# endif fds[0].fd = fd; fds[0].events = POLLIN; nfd = 1; @@ -5257,6 +5281,9 @@ mzquantum_used = TRUE; } # endif +# ifdef FEAT_TIMERS + towait = call_timeouts(msec); +# endif # ifdef __EMX__ /* don't check for incoming chars if not in raw mode, because select() * always returns TRUE then (in some version of emx.dll) */ @@ -5369,6 +5396,10 @@ /* loop if MzThreads must be scheduled and timeout occurred */ finished = FALSE; # endif +# ifdef FEAT_TIMERS + if (ret == 0 && msec > p_tt) + finished = FALSE; +# endif # ifdef FEAT_SNIFF if (ret < 0 ) diff --git a/src/os_win32.c b/src/os_win32.c --- a/src/os_win32.c +++ b/src/os_win32.c @@ -29,6 +29,7 @@ #include <sys/types.h> #include <signal.h> #include <limits.h> +#include <math.h> /* cproto fails on missing include files */ #ifndef PROTO @@ -624,6 +625,52 @@ } } + unsigned long long +mch_monotonic_time(void) +{ + static ULONGLONG (*GetTickCount64) (void) = NULL; + static ULONGLONG (CALLBACK *_GetTickCount64)(void); + static int has_getickcount64 = -1; + ULONGLONG result; + OSVERSIONINFO ovi; + + ovi.dwOSVersionInfoSize = sizeof(ovi); + GetVersionEx(&ovi); + + if (has_getickcount64 == -1) + { + /* GetTickCount64() was added to Windows Vista */ + if (ovi.dwMajorVersion >= 6) + { + HINSTANCE hKernel32 = GetModuleHandleW(L"KERNEL32"); + *(FARPROC*)&_GetTickCount64 = GetProcAddress(hKernel32, + "GetTickCount64"); + has_getickcount64 = (_GetTickCount64 != NULL); + } + else + has_getickcount64 = 0; + } + + if (has_getickcount64) + { + result = _GetTickCount64(); + } + else + { + static DWORD last_ticks = 0; + static DWORD n_overflow = 0; + DWORD ticks; + + ticks = GetTickCount(); + if (ticks < last_ticks) + n_overflow++; + last_ticks = ticks; + + result = (unsigned long long)ldexp(n_overflow, 32); + result += ticks; + } + return result; +} /* * Return TRUE when running on Windows 95 (or 98 or ME). * Only to be used after mch_init(). diff --git a/src/os_win32.h b/src/os_win32.h --- a/src/os_win32.h +++ b/src/os_win32.h @@ -56,6 +56,7 @@ #define HAVE_ST_MODE /* have stat.st_mode */ #define FEAT_SHORTCUT /* resolve shortcuts */ +#define MCH_MONOTONIC_TIME /* for timers */ #if (!defined(__BORLANDC__) || __BORLANDC__ >= 0x550) \ && (!defined(_MSC_VER) || _MSC_VER > 1020) diff --git a/src/proto.h b/src/proto.h --- a/src/proto.h +++ b/src/proto.h @@ -289,6 +289,10 @@ void clip_mch_set_selection(VimClipboard *cbd); #endif +#ifdef FEAT_TIMERS +# include "timers.pro" +#endif + #ifdef __BORLANDC__ # define _PROTO_H #endif diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -6,6 +6,7 @@ long_u mch_total_mem __ARGS((int special)); void mch_delay __ARGS((long msec, int ignoreinput)); int mch_stackcheck __ARGS((char *p)); +unsigned long long mch_monotonic_time __ARGS((void)); void mch_startjmp __ARGS((void)); void mch_endjmp __ARGS((void)); void mch_didjmp __ARGS((void)); diff --git a/src/proto/os_win32.pro b/src/proto/os_win32.pro --- a/src/proto/os_win32.pro +++ b/src/proto/os_win32.pro @@ -55,4 +55,5 @@ void used_file_arg __ARGS((char *name, int literal, int full_path, int diff_mode)); void set_alist_count __ARGS((void)); void fix_arg_enc __ARGS((void)); +unsigned long long mch_monotonic_time __ARGS((void)); /* vim: set ft=c : */ diff --git a/src/proto/timers.pro b/src/proto/timers.pro new file mode 100644 --- /dev/null +++ b/src/proto/timers.pro @@ -0,0 +1,5 @@ +/* timers.c */ +unsigned long long get_monotonic_time __ARGS((void)); +void insert_timeout __ARGS((timeout_T *to)); +long call_timeouts __ARGS((long max_wait)); +/* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -2540,3 +2540,19 @@ UINT32_T state[8]; char_u buffer[64]; } context_sha256_T; + +#ifdef FEAT_TIMERS +/* + * Used for +timers settimeout/interval. + */ +typedef struct timeout_S timeout_T; +struct timeout_S { + int id; /* timeout/interval id */ + int interval; /* interval period if interval, otherwise -1 */ + unsigned long long tm; /* time to fire (epoch milliseconds) */ + char_u *cmd; /* vim command to run */ + char_u *sourcing_name; + linenr_T sourcing_lnum; + timeout_T *next; /* pointer to next timeout in linked list */ +}; +#endif diff --git a/src/timers.c b/src/timers.c new file mode 100644 --- /dev/null +++ b/src/timers.c @@ -0,0 +1,152 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +#include "vim.h" + +#ifdef FEAT_TIMERS + +/* + * Avoid recursive calls to call_timeouts + */ +static int calling_timeouts = FALSE; + +/* + * Return monotonic time, if available. Fall back to gettimeofday otherwise. + */ + unsigned long long +get_monotonic_time(void) +{ + unsigned long long tm; +# ifdef MCH_MONOTONIC_TIME + tm = mch_monotonic_time(); +# else + struct timeval now; + gettimeofday(&now, NULL); + tm = now.tv_sec * 1000 + now.tv_usec / 1000; +# endif + return tm; +} + + +/* + * 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; + } + prev->next = to; + to->next = NULL; +} + +/* + * Execute timeouts that are due. + * Return the amount of time before call_timeouts() should be run again. + */ + long +call_timeouts(max_to_wait) + long max_to_wait; +{ + unsigned long long now = get_monotonic_time(); + unsigned long long towait = p_tt; + timeout_T *tmp; + int retval; + + if (calling_timeouts) + { + return max_to_wait; + } + + calling_timeouts = TRUE; + + while (timeouts != NULL && timeouts->tm < now) + { + retval = do_cmdline_cmd(timeouts->cmd); + tmp = timeouts; + timeouts = timeouts->next; + if (tmp->interval == -1 || retval == FAIL || did_throw || did_emsg) + { + if (got_int) + { + if (tmp->sourcing_lnum) + { + EMSG(_("E881: An interval was canceled because of an interrupt")); + EMSG3(_("%s:%s"), tmp->sourcing_name, tmp->sourcing_lnum); + } + else + { + EMSG(_("E881: An interval was canceled because of an interrupt")); + EMSG2(_("%s"), tmp->sourcing_name); + } + } + free(tmp->cmd); + free(tmp->sourcing_name); + free(tmp); + } + else + { + tmp->tm = now + tmp->interval; + insert_timeout(tmp); + } + } + + calling_timeouts = FALSE; + + /* if there is not a timer, change towait so that it will get called */ + if (timeouts != NULL && max_to_wait != 0) + { + now = get_monotonic_time(); + if (now > timeouts->tm) + return p_tt; + + towait = timeouts->tm - now; + + /* don't wake up every 1 ms ... limit to p_tt */ + if (towait < p_tt) + towait = p_tt; + + /* don't overshoot the wait time */ + if (max_to_wait > 0 && towait > max_to_wait) + towait = max_to_wait; + + return (long)towait; + } + + return max_to_wait; +} + +#endif diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -617,6 +617,11 @@ #else "-textobjects", #endif +#ifdef FEAT_TIMERS + "+timers", +#else + "-timers", +#endif #ifdef FEAT_TITLE "+title", #else