From e99be0e6d28fad96efd2b2be23fa38e7559e80e1 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Tue, 26 Mar 2019 22:51:09 +0100 Subject: patch 8.1.1056: no eval function for Ruby Problem: No eval function for Ruby. Solution: Add rubyeval(). (Ozaki Kiichi, closes #4152) --- runtime/doc/eval.txt | 12 ++ runtime/doc/if_ruby.txt | 14 +- src/evalfunc.c | 21 +++ src/if_ruby.c | 422 +++++++++++++++++++++++++++++++++------------- src/proto/if_ruby.pro | 1 + src/testdir/test_ruby.vim | 144 ++++++++-------- src/version.c | 2 + 7 files changed, 432 insertions(+), 184 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 1294481da..db63a2299 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2521,6 +2521,7 @@ repeat({expr}, {count}) String repeat {expr} {count} times resolve({filename}) String get filename a shortcut points to reverse({list}) List reverse {list} in-place round({expr}) Float round off {expr} +rubyeval({expr}) any evaluate |Ruby| expression screenattr({row}, {col}) Number attribute at screen position screenchar({row}, {col}) Number character at screen position screencol() Number current cursor column @@ -7432,6 +7433,17 @@ round({expr}) *round()* < -5.0 {only available when compiled with the |+float| feature} +rubyeval({expr}) *rubyeval()* + Evaluate Ruby expression {expr} and return its result + converted to Vim data structures. + Numbers, floats and strings are returned as they are (strings + are copied though). + Arrays are represented as Vim |List| type. + Hashes are represented as Vim |Dictionary| type. + Other objects are represented as strings resulted from their + "Object#to_s" method. + {only available when compiled with the |+ruby| feature} + screenattr({row}, {col}) *screenattr()* Like |screenchar()|, but return the attribute. This is a rather arbitrary number that can only be used to compare to the diff --git a/runtime/doc/if_ruby.txt b/runtime/doc/if_ruby.txt index a98b6209e..e2e774215 100644 --- a/runtime/doc/if_ruby.txt +++ b/runtime/doc/if_ruby.txt @@ -11,7 +11,8 @@ The Ruby Interface to Vim *ruby* *Ruby* 3. Vim::Buffer objects |ruby-buffer| 4. Vim::Window objects |ruby-window| 5. Global variables |ruby-globals| -6. Dynamic loading |ruby-dynamic| +6. rubyeval() Vim function |ruby-rubyeval| +7. Dynamic loading |ruby-dynamic| {Vi does not have any of these commands} *E266* *E267* *E268* *E269* *E270* *E271* *E272* *E273* @@ -198,7 +199,16 @@ $curwin The current window object. $curbuf The current buffer object. ============================================================================== -6. Dynamic loading *ruby-dynamic* +6. rubyeval() Vim function *ruby-rubyeval* + +To facilitate bi-directional interface, you can use |rubyeval()| function to +evaluate Ruby expressions and pass their values to Vim script. + +The Ruby value "true", "false" and "nil" are converted to v:true, v:false and +v:null, respectively. + +============================================================================== +7. Dynamic loading *ruby-dynamic* On MS-Windows and Unix the Ruby library can be loaded dynamically. The |:version| output then includes |+ruby/dyn|. diff --git a/src/evalfunc.c b/src/evalfunc.c index 733d45aa5..c1a32f3c2 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -338,6 +338,9 @@ static void f_reverse(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT static void f_round(typval_T *argvars, typval_T *rettv); #endif +#ifdef FEAT_RUBY +static void f_rubyeval(typval_T *argvars, typval_T *rettv); +#endif static void f_screenattr(typval_T *argvars, typval_T *rettv); static void f_screenchar(typval_T *argvars, typval_T *rettv); static void f_screencol(typval_T *argvars, typval_T *rettv); @@ -828,6 +831,9 @@ static struct fst {"reverse", 1, 1, f_reverse}, #ifdef FEAT_FLOAT {"round", 1, 1, f_round}, +#endif +#ifdef FEAT_RUBY + {"rubyeval", 1, 1, f_rubyeval}, #endif {"screenattr", 2, 2, f_screenattr}, {"screenchar", 2, 2, f_screenchar}, @@ -10351,6 +10357,21 @@ f_round(typval_T *argvars, typval_T *rettv) } #endif +#ifdef FEAT_RUBY +/* + * "rubyeval()" function + */ + static void +f_rubyeval(typval_T *argvars, typval_T *rettv) +{ + char_u *str; + char_u buf[NUMBUFLEN]; + + str = tv_get_string_buf(&argvars[0], buf); + do_rubyeval(str, rettv); +} +#endif + /* * "screenattr()" function */ diff --git a/src/if_ruby.c b/src/if_ruby.c index 543ee8ac2..63294f4e5 100644 --- a/src/if_ruby.c +++ b/src/if_ruby.c @@ -205,6 +205,7 @@ static int ensure_ruby_initialized(void); static void error_print(int); static void ruby_io_init(void); static void ruby_vim_init(void); +static int ruby_convert_to_vim_value(VALUE val, typval_T *rettv); #if defined(RUBY19_OR_LATER) || defined(RUBY_INIT_STACK) # if defined(__ia64) && !defined(ruby_init_stack) @@ -259,6 +260,7 @@ static void ruby_vim_init(void); # endif # define rb_global_variable dll_rb_global_variable # define rb_hash_aset dll_rb_hash_aset +# define rb_hash_foreach dll_rb_hash_foreach # define rb_hash_new dll_rb_hash_new # define rb_inspect dll_rb_inspect # define rb_int2inum dll_rb_int2inum @@ -275,6 +277,7 @@ static void ruby_vim_init(void); # endif # define rb_num2uint dll_rb_num2uint # endif +# define rb_num2dbl dll_rb_num2dbl # define rb_lastline_get dll_rb_lastline_get # define rb_lastline_set dll_rb_lastline_set # define rb_protect dll_rb_protect @@ -409,6 +412,7 @@ static VALUE (*dll_rb_funcall2) (VALUE, ID, int, const VALUE*); # endif static void (*dll_rb_global_variable) (VALUE*); static VALUE (*dll_rb_hash_aset) (VALUE, VALUE, VALUE); +static VALUE (*dll_rb_hash_foreach) (VALUE, int (*)(VALUE, VALUE, VALUE), VALUE); static VALUE (*dll_rb_hash_new) (void); static VALUE (*dll_rb_inspect) (VALUE); static VALUE (*dll_rb_int2inum) (long); @@ -418,6 +422,7 @@ static long (*dll_rb_fix2int) (VALUE); static long (*dll_rb_num2int) (VALUE); static unsigned long (*dll_rb_num2uint) (VALUE); # endif +static double (*dll_rb_num2dbl) (VALUE); static VALUE (*dll_rb_lastline_get) (void); static void (*dll_rb_lastline_set) (VALUE); static VALUE (*dll_rb_protect) (VALUE (*)(VALUE), VALUE, int*); @@ -501,42 +506,50 @@ static void (*dll_rb_gc_writebarrier_unprotect)(VALUE obj); # if defined(RUBY19_OR_LATER) && !defined(PROTO) # if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 22 -long rb_num2long_stub(VALUE x) + long +rb_num2long_stub(VALUE x) # else -SIGNED_VALUE rb_num2long_stub(VALUE x) + SIGNED_VALUE +rb_num2long_stub(VALUE x) # endif { return dll_rb_num2long(x); } # if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 26 -VALUE rb_int2big_stub(intptr_t x) + VALUE +rb_int2big_stub(intptr_t x) # else -VALUE rb_int2big_stub(SIGNED_VALUE x) + VALUE +rb_int2big_stub(SIGNED_VALUE x) # endif { return dll_rb_int2big(x); } # if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 19 \ && VIM_SIZEOF_INT < VIM_SIZEOF_LONG -long rb_fix2int_stub(VALUE x) + long +rb_fix2int_stub(VALUE x) { return dll_rb_fix2int(x); } -long rb_num2int_stub(VALUE x) + long +rb_num2int_stub(VALUE x) { return dll_rb_num2int(x); } # endif # if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 20 -VALUE + VALUE rb_float_new_in_heap(double d) { return dll_rb_float_new(d); } # if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 22 -unsigned long rb_num2ulong(VALUE x) + unsigned long +rb_num2ulong(VALUE x) # else -VALUE rb_num2ulong(VALUE x) + VALUE +rb_num2ulong(VALUE x) # endif { return (long)RSHIFT((SIGNED_VALUE)(x),1); @@ -547,12 +560,14 @@ VALUE rb_num2ulong(VALUE x) /* Do not generate a prototype here, VALUE isn't always defined. */ # if defined(USE_RGENGC) && USE_RGENGC && !defined(PROTO) # if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER == 21 -void rb_gc_writebarrier_unprotect_promoted_stub(VALUE obj) + void +rb_gc_writebarrier_unprotect_promoted_stub(VALUE obj) { dll_rb_gc_writebarrier_unprotect_promoted(obj); } # else -void rb_gc_writebarrier_unprotect_stub(VALUE obj) + void +rb_gc_writebarrier_unprotect_stub(VALUE obj) { dll_rb_gc_writebarrier_unprotect(obj); } @@ -560,7 +575,8 @@ void rb_gc_writebarrier_unprotect_stub(VALUE obj) # endif # if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 26 -void rb_ary_detransient_stub(VALUE x) + void +rb_ary_detransient_stub(VALUE x) { dll_rb_ary_detransient(x); } @@ -629,6 +645,7 @@ static struct # endif {"rb_global_variable", (RUBY_PROC*)&dll_rb_global_variable}, {"rb_hash_aset", (RUBY_PROC*)&dll_rb_hash_aset}, + {"rb_hash_foreach", (RUBY_PROC*)&dll_rb_hash_foreach}, {"rb_hash_new", (RUBY_PROC*)&dll_rb_hash_new}, {"rb_inspect", (RUBY_PROC*)&dll_rb_inspect}, {"rb_int2inum", (RUBY_PROC*)&dll_rb_int2inum}, @@ -638,6 +655,7 @@ static struct {"rb_num2int", (RUBY_PROC*)&dll_rb_num2int}, {"rb_num2uint", (RUBY_PROC*)&dll_rb_num2uint}, # endif + {"rb_num2dbl", (RUBY_PROC*)&dll_rb_num2dbl}, {"rb_lastline_get", (RUBY_PROC*)&dll_rb_lastline_get}, {"rb_lastline_set", (RUBY_PROC*)&dll_rb_lastline_set}, {"rb_protect", (RUBY_PROC*)&dll_rb_protect}, @@ -789,7 +807,8 @@ ruby_end(void) #endif } -void ex_ruby(exarg_T *eap) + void +ex_ruby(exarg_T *eap) { int state; char *script = NULL; @@ -860,7 +879,8 @@ eval_enc_string_protect(const char *str, int *state) return rb_eval_string_protect(str, state); } -void ex_rubydo(exarg_T *eap) + void +ex_rubydo(exarg_T *eap) { int state; linenr_T i; @@ -906,13 +926,15 @@ void ex_rubydo(exarg_T *eap) } } -static VALUE rb_load_wrap(VALUE file_to_load) + static VALUE +rb_load_wrap(VALUE file_to_load) { rb_load(file_to_load, 0); return Qnil; } -void ex_rubyfile(exarg_T *eap) + void +ex_rubyfile(exarg_T *eap) { int state; @@ -925,7 +947,8 @@ void ex_rubyfile(exarg_T *eap) } } -void ruby_buffer_free(buf_T *buf) + void +ruby_buffer_free(buf_T *buf) { if (buf->b_ruby_ref) { @@ -934,7 +957,8 @@ void ruby_buffer_free(buf_T *buf) } } -void ruby_window_free(win_T *win) + void +ruby_window_free(win_T *win) { if (win->w_ruby_ref) { @@ -943,7 +967,8 @@ void ruby_window_free(win_T *win) } } -static int ensure_ruby_initialized(void) + static int +ensure_ruby_initialized(void) { if (!ruby_initialized) { @@ -993,7 +1018,8 @@ static int ensure_ruby_initialized(void) return ruby_initialized; } -static void error_print(int state) + static void +error_print(int state) { #if !defined(DYNAMIC_RUBY) && !defined(RUBY19_OR_LATER) RUBYEXTERN VALUE ruby_errinfo; @@ -1018,66 +1044,67 @@ static void error_print(int state) switch (state) { - case TAG_RETURN: - emsg(_("E267: unexpected return")); - break; - case TAG_NEXT: - emsg(_("E268: unexpected next")); - break; - case TAG_BREAK: - emsg(_("E269: unexpected break")); - break; - case TAG_REDO: - emsg(_("E270: unexpected redo")); - break; - case TAG_RETRY: - emsg(_("E271: retry outside of rescue clause")); - break; - case TAG_RAISE: - case TAG_FATAL: + case TAG_RETURN: + emsg(_("E267: unexpected return")); + break; + case TAG_NEXT: + emsg(_("E268: unexpected next")); + break; + case TAG_BREAK: + emsg(_("E269: unexpected break")); + break; + case TAG_REDO: + emsg(_("E270: unexpected redo")); + break; + case TAG_RETRY: + emsg(_("E271: retry outside of rescue clause")); + break; + case TAG_RAISE: + case TAG_FATAL: #ifdef RUBY19_OR_LATER - error = rb_errinfo(); + error = rb_errinfo(); #else - error = ruby_errinfo; + error = ruby_errinfo; #endif - eclass = CLASS_OF(error); - einfo = rb_obj_as_string(error); - if (eclass == rb_eRuntimeError && RSTRING_LEN(einfo) == 0) - { - emsg(_("E272: unhandled exception")); - } - else - { - VALUE epath; - char *p; - - epath = rb_class_path(eclass); - vim_snprintf(buff, BUFSIZ, "%s: %s", - RSTRING_PTR(epath), RSTRING_PTR(einfo)); - p = strchr(buff, '\n'); - if (p) *p = '\0'; - emsg(buff); - } + eclass = CLASS_OF(error); + einfo = rb_obj_as_string(error); + if (eclass == rb_eRuntimeError && RSTRING_LEN(einfo) == 0) + { + emsg(_("E272: unhandled exception")); + } + else + { + VALUE epath; + char *p; + + epath = rb_class_path(eclass); + vim_snprintf(buff, BUFSIZ, "%s: %s", + RSTRING_PTR(epath), RSTRING_PTR(einfo)); + p = strchr(buff, '\n'); + if (p) *p = '\0'; + emsg(buff); + } - attr = syn_name2attr((char_u *)"Error"); + attr = syn_name2attr((char_u *)"Error"); # ifdef RUBY21_OR_LATER - bt = rb_funcallv(error, rb_intern("backtrace"), 0, 0); - for (i = 0; i < RARRAY_LEN(bt); i++) - msg_attr(RSTRING_PTR(RARRAY_AREF(bt, i)), attr); + bt = rb_funcallv(error, rb_intern("backtrace"), 0, 0); + for (i = 0; i < RARRAY_LEN(bt); i++) + msg_attr(RSTRING_PTR(RARRAY_AREF(bt, i)), attr); # else - bt = rb_funcall2(error, rb_intern("backtrace"), 0, 0); - for (i = 0; i < RARRAY_LEN(bt); i++) - msg_attr(RSTRING_PTR(RARRAY_PTR(bt)[i]), attr); + bt = rb_funcall2(error, rb_intern("backtrace"), 0, 0); + for (i = 0; i < RARRAY_LEN(bt); i++) + msg_attr(RSTRING_PTR(RARRAY_PTR(bt)[i]), attr); # endif - break; - default: - vim_snprintf(buff, BUFSIZ, _("E273: unknown longjmp status %d"), state); - emsg(buff); - break; + break; + default: + vim_snprintf(buff, BUFSIZ, _("E273: unknown longjmp status %d"), state); + emsg(buff); + break; } } -static VALUE vim_message(VALUE self UNUSED, VALUE str) + static VALUE +vim_message(VALUE self UNUSED, VALUE str) { char *buff, *p; @@ -1098,21 +1125,24 @@ static VALUE vim_message(VALUE self UNUSED, VALUE str) return Qnil; } -static VALUE vim_set_option(VALUE self UNUSED, VALUE str) + static VALUE +vim_set_option(VALUE self UNUSED, VALUE str) { do_set((char_u *)StringValuePtr(str), 0); update_screen(NOT_VALID); return Qnil; } -static VALUE vim_command(VALUE self UNUSED, VALUE str) + static VALUE +vim_command(VALUE self UNUSED, VALUE str) { do_cmdline_cmd((char_u *)StringValuePtr(str)); return Qnil; } #ifdef FEAT_EVAL -static VALUE vim_to_ruby(typval_T *tv) + static VALUE +vim_to_ruby(typval_T *tv) { VALUE result = Qnil; @@ -1188,7 +1218,8 @@ static VALUE vim_to_ruby(typval_T *tv) } #endif -static VALUE vim_evaluate(VALUE self UNUSED, VALUE str) + static VALUE +vim_evaluate(VALUE self UNUSED, VALUE str) { #ifdef FEAT_EVAL typval_T *tv; @@ -1221,13 +1252,15 @@ static const rb_data_type_t buffer_type = { # endif }; -static size_t buffer_dsize(const void *buf UNUSED) + static size_t +buffer_dsize(const void *buf UNUSED) { return sizeof(buf_T); } #endif -static VALUE buffer_new(buf_T *buf) + static VALUE +buffer_new(buf_T *buf) { if (buf->b_ruby_ref) { @@ -1246,7 +1279,8 @@ static VALUE buffer_new(buf_T *buf) } } -static buf_T *get_buf(VALUE obj) + static buf_T * +get_buf(VALUE obj) { buf_T *buf; @@ -1260,7 +1294,8 @@ static buf_T *get_buf(VALUE obj) return buf; } -static VALUE vim_blob(VALUE self UNUSED, VALUE str) + static VALUE +vim_blob(VALUE self UNUSED, VALUE str) { VALUE result = rb_str_new("0z", 2); char buf[4]; @@ -1273,12 +1308,14 @@ static VALUE vim_blob(VALUE self UNUSED, VALUE str) return result; } -static VALUE buffer_s_current(void) + static VALUE +buffer_s_current(void) { return buffer_new(curbuf); } -static VALUE buffer_s_count(void) + static VALUE +buffer_s_count(void) { buf_T *b; int n = 0; @@ -1294,7 +1331,8 @@ static VALUE buffer_s_count(void) return INT2NUM(n); } -static VALUE buffer_s_aref(VALUE self UNUSED, VALUE num) + static VALUE +buffer_s_aref(VALUE self UNUSED, VALUE num) { buf_T *b; int n = NUM2INT(num); @@ -1314,35 +1352,40 @@ static VALUE buffer_s_aref(VALUE self UNUSED, VALUE num) return Qnil; } -static VALUE buffer_name(VALUE self) + static VALUE +buffer_name(VALUE self) { buf_T *buf = get_buf(self); return buf->b_ffname ? rb_str_new2((char *)buf->b_ffname) : Qnil; } -static VALUE buffer_number(VALUE self) + static VALUE +buffer_number(VALUE self) { buf_T *buf = get_buf(self); return INT2NUM(buf->b_fnum); } -static VALUE buffer_count(VALUE self) + static VALUE +buffer_count(VALUE self) { buf_T *buf = get_buf(self); return INT2NUM(buf->b_ml.ml_line_count); } -static VALUE get_buffer_line(buf_T *buf, linenr_T n) + static VALUE +get_buffer_line(buf_T *buf, linenr_T n) { if (n <= 0 || n > buf->b_ml.ml_line_count) rb_raise(rb_eIndexError, "line number %ld out of range", (long)n); return vim_str2rb_enc_str((char *)ml_get_buf(buf, n, FALSE)); } -static VALUE buffer_aref(VALUE self, VALUE num) + static VALUE +buffer_aref(VALUE self, VALUE num) { buf_T *buf = get_buf(self); @@ -1351,7 +1394,8 @@ static VALUE buffer_aref(VALUE self, VALUE num) return Qnil; /* For stop warning */ } -static VALUE set_buffer_line(buf_T *buf, linenr_T n, VALUE str) + static VALUE +set_buffer_line(buf_T *buf, linenr_T n, VALUE str) { char *line = StringValuePtr(str); aco_save_T aco; @@ -1383,7 +1427,8 @@ static VALUE set_buffer_line(buf_T *buf, linenr_T n, VALUE str) return str; } -static VALUE buffer_aset(VALUE self, VALUE num, VALUE str) + static VALUE +buffer_aset(VALUE self, VALUE num, VALUE str) { buf_T *buf = get_buf(self); @@ -1392,7 +1437,8 @@ static VALUE buffer_aset(VALUE self, VALUE num, VALUE str) return str; } -static VALUE buffer_delete(VALUE self, VALUE num) + static VALUE +buffer_delete(VALUE self, VALUE num) { buf_T *buf = get_buf(self); long n = NUM2LONG(num); @@ -1427,7 +1473,8 @@ static VALUE buffer_delete(VALUE self, VALUE num) return Qnil; } -static VALUE buffer_append(VALUE self, VALUE num, VALUE str) + static VALUE +buffer_append(VALUE self, VALUE num, VALUE str) { buf_T *buf = get_buf(self); char *line = StringValuePtr(str); @@ -1479,13 +1526,15 @@ static const rb_data_type_t window_type = { # endif }; -static size_t window_dsize(const void *win UNUSED) + static size_t +window_dsize(const void *win UNUSED) { return sizeof(win_T); } #endif -static VALUE window_new(win_T *win) + static VALUE +window_new(win_T *win) { if (win->w_ruby_ref) { @@ -1504,7 +1553,8 @@ static VALUE window_new(win_T *win) } } -static win_T *get_win(VALUE obj) + static win_T * +get_win(VALUE obj) { win_T *win; @@ -1518,7 +1568,8 @@ static win_T *get_win(VALUE obj) return win; } -static VALUE window_s_current(void) + static VALUE +window_s_current(void) { return window_new(curwin); } @@ -1527,24 +1578,26 @@ static VALUE window_s_current(void) * Added line manipulation functions * SegPhault - 03/07/05 */ -static VALUE line_s_current(void) + static VALUE +line_s_current(void) { return get_buffer_line(curbuf, curwin->w_cursor.lnum); } -static VALUE set_current_line(VALUE self UNUSED, VALUE str) + static VALUE +set_current_line(VALUE self UNUSED, VALUE str) { return set_buffer_line(curbuf, curwin->w_cursor.lnum, str); } -static VALUE current_line_number(void) + static VALUE +current_line_number(void) { return INT2FIX((int)curwin->w_cursor.lnum); } - - -static VALUE window_s_count(void) + static VALUE +window_s_count(void) { win_T *w; int n = 0; @@ -1554,7 +1607,8 @@ static VALUE window_s_count(void) return INT2NUM(n); } -static VALUE window_s_aref(VALUE self UNUSED, VALUE num) + static VALUE +window_s_aref(VALUE self UNUSED, VALUE num) { win_T *w; int n = NUM2INT(num); @@ -1565,21 +1619,24 @@ static VALUE window_s_aref(VALUE self UNUSED, VALUE num) return Qnil; } -static VALUE window_buffer(VALUE self) + static VALUE +window_buffer(VALUE self) { win_T *win = get_win(self); return buffer_new(win->w_buffer); } -static VALUE window_height(VALUE self) + static VALUE +window_height(VALUE self) { win_T *win = get_win(self); return INT2NUM(win->w_height); } -static VALUE window_set_height(VALUE self, VALUE height) + static VALUE +window_set_height(VALUE self, VALUE height) { win_T *win = get_win(self); win_T *savewin = curwin; @@ -1590,12 +1647,14 @@ static VALUE window_set_height(VALUE self, VALUE height) return height; } -static VALUE window_width(VALUE self UNUSED) + static VALUE +window_width(VALUE self UNUSED) { return INT2NUM(get_win(self)->w_width); } -static VALUE window_set_width(VALUE self UNUSED, VALUE width) + static VALUE +window_set_width(VALUE self UNUSED, VALUE width) { win_T *win = get_win(self); win_T *savewin = curwin; @@ -1606,14 +1665,16 @@ static VALUE window_set_width(VALUE self UNUSED, VALUE width) return width; } -static VALUE window_cursor(VALUE self) + static VALUE +window_cursor(VALUE self) { win_T *win = get_win(self); return rb_assoc_new(INT2NUM(win->w_cursor.lnum), INT2NUM(win->w_cursor.col)); } -static VALUE window_set_cursor(VALUE self, VALUE pos) + static VALUE +window_set_cursor(VALUE self, VALUE pos) { VALUE lnum, col; win_T *win = get_win(self); @@ -1631,12 +1692,14 @@ static VALUE window_set_cursor(VALUE self, VALUE pos) return Qnil; } -static VALUE f_nop(VALUE self UNUSED) + static VALUE +f_nop(VALUE self UNUSED) { return Qnil; } -static VALUE f_p(int argc, VALUE *argv, VALUE self UNUSED) + static VALUE +f_p(int argc, VALUE *argv, VALUE self UNUSED) { int i; VALUE str = rb_str_new("", 0); @@ -1656,7 +1719,8 @@ static VALUE f_p(int argc, VALUE *argv, VALUE self UNUSED) return ret; } -static void ruby_io_init(void) + static void +ruby_io_init(void) { #ifndef DYNAMIC_RUBY RUBYEXTERN VALUE rb_stdout; @@ -1672,7 +1736,8 @@ static void ruby_io_init(void) rb_define_global_function("p", f_p, -1); } -static void ruby_vim_init(void) + static void +ruby_vim_init(void) { objtbl = rb_hash_new(); rb_global_variable(&objtbl); @@ -1736,8 +1801,139 @@ static void ruby_vim_init(void) rb_define_virtual_variable("$curwin", window_s_current, 0); } -void vim_ruby_init(void *stack_start) + void +vim_ruby_init(void *stack_start) { /* should get machine stack start address early in main function */ ruby_stack_start = stack_start; } + + static int +convert_hash2dict(VALUE key, VALUE val, VALUE arg) +{ + dict_T *d = (dict_T *)arg; + dictitem_T *di; + + di = dictitem_alloc((char_u *)RSTRING_PTR(RSTRING(rb_obj_as_string(key)))); + if (di == NULL || ruby_convert_to_vim_value(val, &di->di_tv) != OK + || dict_add(d, di) != OK) + { + d->dv_hashtab.ht_error = TRUE; + return ST_STOP; + } + return ST_CONTINUE; +} + + static int +ruby_convert_to_vim_value(VALUE val, typval_T *rettv) +{ + switch (TYPE(val)) + { + case T_NIL: + rettv->v_type = VAR_SPECIAL; + rettv->vval.v_number = VVAL_NULL; + break; + case T_TRUE: + rettv->v_type = VAR_SPECIAL; + rettv->vval.v_number = VVAL_TRUE; + break; + case T_FALSE: + rettv->v_type = VAR_SPECIAL; + rettv->vval.v_number = VVAL_FALSE; + break; + case T_BIGNUM: + case T_FIXNUM: + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = (varnumber_T)NUM2LONG(val); + break; +#ifdef FEAT_FLOAT + case T_FLOAT: + rettv->v_type = VAR_FLOAT; + rettv->vval.v_float = (float_T)NUM2DBL(val); + break; +#endif + default: + val = rb_obj_as_string(val); + // FALLTHROUGH + case T_STRING: + { + VALUE str = (VALUE)RSTRING(val); + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = vim_strnsave((char_u *)RSTRING_PTR(str), + (int)RSTRING_LEN(str)); + } + break; + case T_ARRAY: + { + list_T *l; + long i; + typval_T v; + + l = list_alloc(); + if (l == NULL) + return FAIL; + + for (i = 0; i < RARRAY_LEN(val); ++i) + { + if (ruby_convert_to_vim_value((VALUE)RARRAY_PTR(val)[i], + &v) != OK) + { + list_unref(l); + return FAIL; + } + list_append_tv(l, &v); + clear_tv(&v); + } + + rettv->v_type = VAR_LIST; + rettv->vval.v_list = l; + ++l->lv_refcount; + } + break; + case T_HASH: + { + dict_T *d; + + d = dict_alloc(); + if (d == NULL) + return FAIL; + + rb_hash_foreach(val, convert_hash2dict, (VALUE)d); + if (d->dv_hashtab.ht_error) + { + dict_unref(d); + return FAIL; + } + + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = d; + ++d->dv_refcount; + } + break; + } + return OK; +} + + void +do_rubyeval(char_u *str, typval_T *rettv) +{ + int retval = FAIL; + + if (ensure_ruby_initialized()) + { + int state; + VALUE obj; + + obj = rb_eval_string_protect((const char *)str, &state); + if (state) + error_print(state); + else + retval = ruby_convert_to_vim_value(obj, rettv); + } + if (retval == FAIL) + { + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = 0; + } +} diff --git a/src/proto/if_ruby.pro b/src/proto/if_ruby.pro index 496859ce5..cc7c7e6c1 100644 --- a/src/proto/if_ruby.pro +++ b/src/proto/if_ruby.pro @@ -7,4 +7,5 @@ void ex_rubyfile(exarg_T *eap); void ruby_buffer_free(buf_T *buf); void ruby_window_free(win_T *win); void vim_ruby_init(void *stack_start); +void do_rubyeval(char_u *str, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_ruby.vim b/src/testdir/test_ruby.vim index fe2afa625..3c9df3a05 100644 --- a/src/testdir/test_ruby.vim +++ b/src/testdir/test_ruby.vim @@ -4,13 +4,6 @@ if !has('ruby') finish end -" Helper function as there is no builtin rubyeval() function similar -" to perleval, luaevel() or pyeval(). -func RubyEval(ruby_expr) - let s = split(execute('ruby print ' . a:ruby_expr), "\n") - return (len(s) == 0) ? '' : s[-1] -endfunc - func Test_ruby_change_buffer() call setline(line('$'), ['1 line 1']) ruby Vim.command("normal /^1\n") @@ -49,12 +42,12 @@ func Test_set_cursor() normal gg rubydo $curwin.cursor = [1, 5] call assert_equal([1, 6], [line('.'), col('.')]) - call assert_equal('[1, 5]', RubyEval('$curwin.cursor')) + call assert_equal([1, 5], rubyeval('$curwin.cursor')) " Check that movement after setting cursor position keeps current column. normal j call assert_equal([2, 6], [line('.'), col('.')]) - call assert_equal('[2, 5]', RubyEval('$curwin.cursor')) + call assert_equal([2, 5], rubyeval('$curwin.cursor')) call assert_fails('ruby $curwin.cursor = [1]', \ 'ArgumentError: array length must be 2') @@ -65,25 +58,25 @@ endfunc func Test_buffer_count() new call setline(1, ['one', 'two', 'three']) - call assert_equal('3', RubyEval('$curbuf.count')) - call assert_equal('3', RubyEval('$curbuf.length')) + call assert_equal(3, rubyeval('$curbuf.count')) + call assert_equal(3, rubyeval('$curbuf.length')) bwipe! endfunc " Test buffer.name (buffer name) func Test_buffer_name() new Xfoo - call assert_equal(expand('%:p'), RubyEval('$curbuf.name')) + call assert_equal(expand('%:p'), rubyeval('$curbuf.name')) bwipe - call assert_equal('', RubyEval('$curbuf.name')) + call assert_equal(v:null, rubyeval('$curbuf.name')) endfunc " Test buffer.number (number of the buffer). func Test_buffer_number() new - call assert_equal(string(bufnr('%')), RubyEval('$curbuf.number')) + call assert_equal(bufnr('%'), rubyeval('$curbuf.number')) new - call assert_equal(string(bufnr('%')), RubyEval('$curbuf.number')) + call assert_equal(bufnr('%'), rubyeval('$curbuf.number')) %bwipe endfunc @@ -124,7 +117,7 @@ func Test_buffer_line() new call setline(1, ['one', 'two', 'three']) 2 - call assert_equal('two', RubyEval('$curbuf.line')) + call assert_equal('two', rubyeval('$curbuf.line')) ruby $curbuf.line = 'TWO' call assert_equal(['one', 'TWO', 'three'], getline(1, '$')) @@ -137,7 +130,7 @@ func Test_buffer_line_number() new call setline(1, ['one', 'two', 'three']) 2 - call assert_equal('2', RubyEval('$curbuf.line_number')) + call assert_equal(2, rubyeval('$curbuf.line_number')) bwipe! endfunc @@ -145,8 +138,8 @@ endfunc func Test_buffer_get() new call setline(1, ['one', 'two']) - call assert_equal('one', RubyEval('$curbuf[1]')) - call assert_equal('two', RubyEval('$curbuf[2]')) + call assert_equal('one', rubyeval('$curbuf[1]')) + call assert_equal('two', rubyeval('$curbuf[2]')) call assert_fails('ruby $curbuf[0]', \ 'IndexError: line number 0 out of range') @@ -178,7 +171,7 @@ func Test_window_height() call assert_equal(2, winheight(0)) " Test getting window height - call assert_equal('2', RubyEval('$curwin.height')) + call assert_equal(2, rubyeval('$curwin.height')) bwipe endfunc @@ -192,7 +185,7 @@ func Test_window_width() call assert_equal(2, winwidth(0)) " Test getting window width - call assert_equal('2', RubyEval('$curwin.width')) + call assert_equal(2, rubyeval('$curwin.width')) bwipe endfunc @@ -207,10 +200,10 @@ func Test_window_buffer() ruby $b1 = $curwin.buffer ruby $w1 = $curwin - call assert_equal(RubyEval('$b1'), RubyEval('$w1.buffer')) - call assert_equal(RubyEval('$b2'), RubyEval('$w2.buffer')) - call assert_equal(string(bufnr('Xfoo1')), RubyEval('$w1.buffer.number')) - call assert_equal(string(bufnr('Xfoo2')), RubyEval('$w2.buffer.number')) + call assert_equal(rubyeval('$b1'), rubyeval('$w1.buffer')) + call assert_equal(rubyeval('$b2'), rubyeval('$w2.buffer')) + call assert_equal(bufnr('Xfoo1'), rubyeval('$w1.buffer.number')) + call assert_equal(bufnr('Xfoo2'), rubyeval('$w2.buffer.number')) ruby $b1, $w1, $b2, $w2 = nil %bwipe @@ -218,8 +211,8 @@ endfunc " Test Vim::Window.current (get current window object) func Test_Vim_window_current() - let cw = RubyEval('$curwin') - call assert_equal(cw, RubyEval('Vim::Window.current')) + let cw = rubyeval('$curwin') + call assert_equal(cw, rubyeval('Vim::Window.current')) call assert_match('^#$', cw) endfunc @@ -228,27 +221,27 @@ func Test_Vim_window_count() new Xfoo1 new Xfoo2 split - call assert_equal('4', RubyEval('Vim::Window.count')) + call assert_equal(4, rubyeval('Vim::Window.count')) %bwipe - call assert_equal('1', RubyEval('Vim::Window.count')) + call assert_equal(1, rubyeval('Vim::Window.count')) endfunc " Test Vim::Window[n] (get window object of window n) func Test_Vim_window_get() new Xfoo1 new Xfoo2 - call assert_match('Xfoo2$', RubyEval('Vim::Window[0].buffer.name')) + call assert_match('Xfoo2$', rubyeval('Vim::Window[0].buffer.name')) wincmd j - call assert_match('Xfoo1$', RubyEval('Vim::Window[1].buffer.name')) + call assert_match('Xfoo1$', rubyeval('Vim::Window[1].buffer.name')) wincmd j - call assert_equal('', RubyEval('Vim::Window[2].buffer.name')) + call assert_equal(v:null, rubyeval('Vim::Window[2].buffer.name')) %bwipe endfunc " Test Vim::Buffer.current (return the buffer object of current buffer) func Test_Vim_buffer_current() - let cb = RubyEval('$curbuf') - call assert_equal(cb, RubyEval('Vim::Buffer.current')) + let cb = rubyeval('$curbuf') + call assert_equal(cb, rubyeval('Vim::Buffer.current')) call assert_match('^#$', cb) endfunc @@ -256,9 +249,9 @@ endfunc func Test_Vim_buffer_count() new Xfoo1 new Xfoo2 - call assert_equal('3', RubyEval('Vim::Buffer.count')) + call assert_equal(3, rubyeval('Vim::Buffer.count')) %bwipe - call assert_equal('1', RubyEval('Vim::Buffer.count')) + call assert_equal(1, rubyeval('Vim::Buffer.count')) endfunc " Test Vim::buffer[n] (return the buffer object of buffer number n) @@ -267,9 +260,9 @@ func Test_Vim_buffer_get() new Xfoo2 " Index of Vim::Buffer[n] goes from 0 to the number of buffers. - call assert_equal('', RubyEval('Vim::Buffer[0].name')) - call assert_match('Xfoo1$', RubyEval('Vim::Buffer[1].name')) - call assert_match('Xfoo2$', RubyEval('Vim::Buffer[2].name')) + call assert_equal(v:null, rubyeval('Vim::Buffer[0].name')) + call assert_match('Xfoo1$', rubyeval('Vim::Buffer[1].name')) + call assert_match('Xfoo2$', rubyeval('Vim::Buffer[2].name')) call assert_fails('ruby print Vim::Buffer[3].name', \ "NoMethodError: undefined method `name' for nil:NilClass") %bwipe @@ -295,43 +288,43 @@ func Test_Vim_set_option() endfunc func Test_Vim_evaluate() - call assert_equal('123', RubyEval('Vim::evaluate("123")')) + call assert_equal(123, rubyeval('Vim::evaluate("123")')) " Vim::evaluate("123").class gives Integer or Fixnum depending " on versions of Ruby. - call assert_match('^Integer\|Fixnum$', RubyEval('Vim::evaluate("123").class')) + call assert_match('^Integer\|Fixnum$', rubyeval('Vim::evaluate("123").class')) - call assert_equal('1.23', RubyEval('Vim::evaluate("1.23")')) - call assert_equal('Float', RubyEval('Vim::evaluate("1.23").class')) + call assert_equal(1.23, rubyeval('Vim::evaluate("1.23")')) + call assert_equal('Float', rubyeval('Vim::evaluate("1.23").class')) - call assert_equal('foo', RubyEval('Vim::evaluate("\"foo\"")')) - call assert_equal('String', RubyEval('Vim::evaluate("\"foo\"").class')) + call assert_equal('foo', rubyeval('Vim::evaluate("\"foo\"")')) + call assert_equal('String', rubyeval('Vim::evaluate("\"foo\"").class')) - call assert_equal('["\x01\xAB"]', RubyEval('Vim::evaluate("0z01ab").unpack("M")')) - call assert_equal('String', RubyEval('Vim::evaluate("0z01ab").class')) + call assert_equal(["\x01\xAB"], rubyeval('Vim::evaluate("0z01ab").unpack("M")')) + call assert_equal('String', rubyeval('Vim::evaluate("0z01ab").class')) - call assert_equal('[1, 2]', RubyEval('Vim::evaluate("[1, 2]")')) - call assert_equal('Array', RubyEval('Vim::evaluate("[1, 2]").class')) + call assert_equal([1, 2], rubyeval('Vim::evaluate("[1, 2]")')) + call assert_equal('Array', rubyeval('Vim::evaluate("[1, 2]").class')) - call assert_equal('{"1"=>2}', RubyEval('Vim::evaluate("{1:2}")')) - call assert_equal('Hash', RubyEval('Vim::evaluate("{1:2}").class')) + call assert_equal({'1': 2}, rubyeval('Vim::evaluate("{1:2}")')) + call assert_equal('Hash', rubyeval('Vim::evaluate("{1:2}").class')) - call assert_equal('', RubyEval('Vim::evaluate("v:null")')) - call assert_equal('NilClass', RubyEval('Vim::evaluate("v:null").class')) + call assert_equal(v:null, rubyeval('Vim::evaluate("v:null")')) + call assert_equal('NilClass', rubyeval('Vim::evaluate("v:null").class')) - call assert_equal('', RubyEval('Vim::evaluate("v:none")')) - call assert_equal('NilClass', RubyEval('Vim::evaluate("v:none").class')) + call assert_equal(v:null, rubyeval('Vim::evaluate("v:none")')) + call assert_equal('NilClass', rubyeval('Vim::evaluate("v:none").class')) - call assert_equal('true', RubyEval('Vim::evaluate("v:true")')) - call assert_equal('TrueClass', RubyEval('Vim::evaluate("v:true").class')) - call assert_equal('false', RubyEval('Vim::evaluate("v:false")')) - call assert_equal('FalseClass',RubyEval('Vim::evaluate("v:false").class')) + call assert_equal(v:true, rubyeval('Vim::evaluate("v:true")')) + call assert_equal('TrueClass', rubyeval('Vim::evaluate("v:true").class')) + call assert_equal(v:false, rubyeval('Vim::evaluate("v:false")')) + call assert_equal('FalseClass',rubyeval('Vim::evaluate("v:false").class')) endfunc func Test_Vim_blob() - call assert_equal('0z', RubyEval('Vim::blob("")')) - call assert_equal('0z31326162', RubyEval('Vim::blob("12ab")')) - call assert_equal('0z00010203', RubyEval('Vim::blob("\x00\x01\x02\x03")')) - call assert_equal('0z8081FEFF', RubyEval('Vim::blob("\x80\x81\xfe\xff")')) + call assert_equal('0z', rubyeval('Vim::blob("")')) + call assert_equal('0z31326162', rubyeval('Vim::blob("12ab")')) + call assert_equal('0z00010203', rubyeval('Vim::blob("\x00\x01\x02\x03")')) + call assert_equal('0z8081FEFF', rubyeval('Vim::blob("\x80\x81\xfe\xff")')) endfunc func Test_Vim_evaluate_list() @@ -364,9 +357,22 @@ func Test_Vim_message() endfunc func Test_print() - ruby print "Hello World!" - let messages = split(execute('message'), "\n") - call assert_equal('Hello World!', messages[-1]) + func RubyPrint(expr) + return trim(execute('ruby print ' . a:expr)) + endfunc + + call assert_equal('123', RubyPrint('123')) + call assert_equal('1.23', RubyPrint('1.23')) + call assert_equal('Hello World!', RubyPrint('"Hello World!"')) + call assert_equal('[1, 2]', RubyPrint('[1, 2]')) + call assert_equal('{"k1"=>"v1", "k2"=>"v2"}', RubyPrint('({"k1" => "v1", "k2" => "v2"})')) + call assert_equal('true', RubyPrint('true')) + call assert_equal('false', RubyPrint('false')) + call assert_equal('', RubyPrint('nil')) + call assert_match('Vim', RubyPrint('Vim')) + call assert_match('Module', RubyPrint('Vim.class')) + + delfunc RubyPrint endfunc func Test_p() @@ -376,13 +382,13 @@ func Test_p() " Check return values of p method - call assert_equal('123', RubyEval('p(123)')) - call assert_equal('[1, 2, 3]', RubyEval('p(1, 2, 3)')) + call assert_equal(123, rubyeval('p(123)')) + call assert_equal([1, 2, 3], rubyeval('p(1, 2, 3)')) " Avoid the "message maintainer" line. let $LANG = '' messages clear - call assert_equal('true', RubyEval('p() == nil')) + call assert_equal(v:true, rubyeval('p() == nil')) let messages = split(execute('message'), "\n") call assert_equal(0, len(messages)) diff --git a/src/version.c b/src/version.c index e4e927926..c23e834bd 100644 --- a/src/version.c +++ b/src/version.c @@ -775,6 +775,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1056, /**/ 1055, /**/ -- cgit v1.2.1