diff src/if_ruby.c @ 7:3fc0f57ecb91 v7.0001

updated for version 7.0001
author vimboss
date Sun, 13 Jun 2004 20:20:40 +0000
parents
children ddada568db54
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/if_ruby.c
@@ -0,0 +1,868 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * VIM - Vi IMproved	by Bram Moolenaar
+ * Ruby interface by Shugo Maeda.
+ *
+ * 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 <stdio.h>
+#include <string.h>
+
+#ifdef _WIN32
+# if !defined(DYNAMIC_RUBY_VER) || (DYNAMIC_RUBY_VER < 18)
+#   define NT
+# endif
+# ifndef DYNAMIC_RUBY
+#  define IMPORT /* For static dll usage __declspec(dllimport) */
+#  define RUBYEXTERN __declspec(dllimport)
+# endif
+#endif
+#ifndef RUBYEXTERN
+# define RUBYEXTERN extern
+#endif
+
+/*
+ * This is tricky.  In ruby.h there is (inline) function rb_class_of()
+ * definition.  This function use these variables.  But we want function to
+ * use dll_* variables.
+ */
+#ifdef DYNAMIC_RUBY
+# define rb_cFalseClass		(*dll_rb_cFalseClass)
+# define rb_cFixnum		(*dll_rb_cFixnum)
+# define rb_cNilClass		(*dll_rb_cNilClass)
+# define rb_cSymbol		(*dll_rb_cSymbol)
+# define rb_cTrueClass		(*dll_rb_cTrueClass)
+# if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 18
+/*
+ * On ver 1.8, all Ruby functions are exported with "__declspce(dllimport)"
+ * in ruby.h.  But it cause trouble for these variables, because it is
+ * defined in this file.  When defined this RUBY_EXPORT it modified to
+ * "extern" and be able to avoid this problem.
+ */
+#  define RUBY_EXPORT
+# endif
+#endif
+
+#include <ruby.h>
+
+#undef EXTERN
+#undef _
+
+/* T_DATA defined both by Ruby and Mac header files, hack around it... */
+#ifdef FEAT_GUI_MAC
+# define __OPENTRANSPORT__
+# define __OPENTRANSPORTPROTOCOL__
+# define __OPENTRANSPORTPROVIDERS__
+#endif
+
+#include "vim.h"
+#include "version.h"
+
+#if defined(PROTO) && !defined(FEAT_RUBY)
+/* Define these to be able to generate the function prototypes. */
+# define VALUE int
+# define RUBY_DATA_FUNC int
+#endif
+
+static int ruby_initialized = 0;
+static VALUE objtbl;
+
+static VALUE mVIM;
+static VALUE cBuffer;
+static VALUE cVimWindow;
+static VALUE eDeletedBufferError;
+static VALUE eDeletedWindowError;
+
+static int ensure_ruby_initialized(void);
+static void error_print(int);
+static void ruby_io_init(void);
+static void ruby_vim_init(void);
+
+#if defined(DYNAMIC_RUBY) || defined(PROTO)
+#ifdef PROTO
+# define HINSTANCE int		/* for generating prototypes */
+#endif
+
+/*
+ * Wrapper defines
+ */
+#define rb_assoc_new			dll_rb_assoc_new
+#define rb_cObject			(*dll_rb_cObject)
+#define rb_check_type			dll_rb_check_type
+#define rb_class_path			dll_rb_class_path
+#define rb_data_object_alloc		dll_rb_data_object_alloc
+#define rb_define_class_under		dll_rb_define_class_under
+#define rb_define_const			dll_rb_define_const
+#define rb_define_global_function	dll_rb_define_global_function
+#define rb_define_method		dll_rb_define_method
+#define rb_define_module		dll_rb_define_module
+#define rb_define_module_function	dll_rb_define_module_function
+#define rb_define_singleton_method	dll_rb_define_singleton_method
+#define rb_define_virtual_variable	dll_rb_define_virtual_variable
+#define rb_stdout			(*dll_rb_stdout)
+#define rb_eArgError			(*dll_rb_eArgError)
+#define rb_eIndexError			(*dll_rb_eIndexError)
+#define rb_eRuntimeError		(*dll_rb_eRuntimeError)
+#define rb_eStandardError		(*dll_rb_eStandardError)
+#define rb_eval_string_protect		dll_rb_eval_string_protect
+#define rb_global_variable		dll_rb_global_variable
+#define rb_hash_aset			dll_rb_hash_aset
+#define rb_hash_new			dll_rb_hash_new
+#define rb_inspect			dll_rb_inspect
+#define rb_int2inum			dll_rb_int2inum
+#define rb_lastline_get			dll_rb_lastline_get
+#define rb_lastline_set			dll_rb_lastline_set
+#define rb_load_protect			dll_rb_load_protect
+#define rb_num2long			dll_rb_num2long
+#define rb_num2ulong			dll_rb_num2ulong
+#define rb_obj_alloc			dll_rb_obj_alloc
+#define rb_obj_as_string		dll_rb_obj_as_string
+#define rb_obj_id			dll_rb_obj_id
+#define rb_raise			dll_rb_raise
+#define rb_str2cstr			dll_rb_str2cstr
+#define rb_str_cat			dll_rb_str_cat
+#define rb_str_concat			dll_rb_str_concat
+#define rb_str_new			dll_rb_str_new
+#define rb_str_new2			dll_rb_str_new2
+#define ruby_errinfo			(*dll_ruby_errinfo)
+#define ruby_init			dll_ruby_init
+#define ruby_init_loadpath		dll_ruby_init_loadpath
+#if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 18
+# define rb_w32_snprintf		dll_rb_w32_snprintf
+#endif
+
+/*
+ * Pointers for dynamic link
+ */
+static VALUE (*dll_rb_assoc_new) (VALUE, VALUE);
+static VALUE *dll_rb_cFalseClass;
+static VALUE *dll_rb_cFixnum;
+static VALUE *dll_rb_cNilClass;
+static VALUE *dll_rb_cObject;
+static VALUE *dll_rb_cSymbol;
+static VALUE *dll_rb_cTrueClass;
+static void (*dll_rb_check_type) (VALUE,int);
+static VALUE (*dll_rb_class_path) (VALUE);
+static VALUE (*dll_rb_data_object_alloc) (VALUE, void*, RUBY_DATA_FUNC, RUBY_DATA_FUNC);
+static VALUE (*dll_rb_define_class_under) (VALUE, const char*, VALUE);
+static void (*dll_rb_define_const) (VALUE,const char*,VALUE);
+static void (*dll_rb_define_global_function) (const char*,VALUE(*)(),int);
+static void (*dll_rb_define_method) (VALUE,const char*,VALUE(*)(),int);
+static VALUE (*dll_rb_define_module) (const char*);
+static void (*dll_rb_define_module_function) (VALUE,const char*,VALUE(*)(),int);
+static void (*dll_rb_define_singleton_method) (VALUE,const char*,VALUE(*)(),int);
+static void (*dll_rb_define_virtual_variable) (const char*,VALUE(*)(),void(*)());
+static VALUE *dll_rb_stdout;
+static VALUE *dll_rb_eArgError;
+static VALUE *dll_rb_eIndexError;
+static VALUE *dll_rb_eRuntimeError;
+static VALUE *dll_rb_eStandardError;
+static VALUE (*dll_rb_eval_string_protect) (const char*, int*);
+static void (*dll_rb_global_variable) (VALUE*);
+static VALUE (*dll_rb_hash_aset) (VALUE, VALUE, VALUE);
+static VALUE (*dll_rb_hash_new) (void);
+static VALUE (*dll_rb_inspect) (VALUE);
+static VALUE (*dll_rb_int2inum) (long);
+static VALUE (*dll_rb_int2inum) (long);
+static VALUE (*dll_rb_lastline_get) (void);
+static void (*dll_rb_lastline_set) (VALUE);
+static void (*dll_rb_load_protect) (VALUE, int, int*);
+static long (*dll_rb_num2long) (VALUE);
+static unsigned long (*dll_rb_num2ulong) (VALUE);
+static VALUE (*dll_rb_obj_alloc) (VALUE);
+static VALUE (*dll_rb_obj_as_string) (VALUE);
+static VALUE (*dll_rb_obj_id) (VALUE);
+static void (*dll_rb_raise) (VALUE, const char*, ...);
+static char *(*dll_rb_str2cstr) (VALUE,int*);
+static VALUE (*dll_rb_str_cat) (VALUE, const char*, long);
+static VALUE (*dll_rb_str_concat) (VALUE, VALUE);
+static VALUE (*dll_rb_str_new) (const char*, long);
+static VALUE (*dll_rb_str_new2) (const char*);
+static VALUE *dll_ruby_errinfo;
+static void (*dll_ruby_init) (void);
+static void (*dll_ruby_init_loadpath) (void);
+#if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 18
+static int (*dll_rb_w32_snprintf)(char*, size_t, const char*, ...);
+#endif
+
+static HINSTANCE hinstRuby = 0; /* Instance of ruby.dll */
+
+/*
+ * Table of name to function pointer of python.
+ */
+#define RUBY_PROC FARPROC
+static struct
+{
+    char *name;
+    RUBY_PROC *ptr;
+} ruby_funcname_table[] =
+{
+    {"rb_assoc_new", (RUBY_PROC*)&dll_rb_assoc_new},
+    {"rb_cFalseClass", (RUBY_PROC*)&dll_rb_cFalseClass},
+    {"rb_cFixnum", (RUBY_PROC*)&dll_rb_cFixnum},
+    {"rb_cNilClass", (RUBY_PROC*)&dll_rb_cNilClass},
+    {"rb_cObject", (RUBY_PROC*)&dll_rb_cObject},
+    {"rb_cSymbol", (RUBY_PROC*)&dll_rb_cSymbol},
+    {"rb_cTrueClass", (RUBY_PROC*)&dll_rb_cTrueClass},
+    {"rb_check_type", (RUBY_PROC*)&dll_rb_check_type},
+    {"rb_class_path", (RUBY_PROC*)&dll_rb_class_path},
+    {"rb_data_object_alloc", (RUBY_PROC*)&dll_rb_data_object_alloc},
+    {"rb_define_class_under", (RUBY_PROC*)&dll_rb_define_class_under},
+    {"rb_define_const", (RUBY_PROC*)&dll_rb_define_const},
+    {"rb_define_global_function", (RUBY_PROC*)&dll_rb_define_global_function},
+    {"rb_define_method", (RUBY_PROC*)&dll_rb_define_method},
+    {"rb_define_module", (RUBY_PROC*)&dll_rb_define_module},
+    {"rb_define_module_function", (RUBY_PROC*)&dll_rb_define_module_function},
+    {"rb_define_singleton_method", (RUBY_PROC*)&dll_rb_define_singleton_method},
+    {"rb_define_virtual_variable", (RUBY_PROC*)&dll_rb_define_virtual_variable},
+    {"rb_stdout", (RUBY_PROC*)&dll_rb_stdout},
+    {"rb_eArgError", (RUBY_PROC*)&dll_rb_eArgError},
+    {"rb_eIndexError", (RUBY_PROC*)&dll_rb_eIndexError},
+    {"rb_eRuntimeError", (RUBY_PROC*)&dll_rb_eRuntimeError},
+    {"rb_eStandardError", (RUBY_PROC*)&dll_rb_eStandardError},
+    {"rb_eval_string_protect", (RUBY_PROC*)&dll_rb_eval_string_protect},
+    {"rb_global_variable", (RUBY_PROC*)&dll_rb_global_variable},
+    {"rb_hash_aset", (RUBY_PROC*)&dll_rb_hash_aset},
+    {"rb_hash_new", (RUBY_PROC*)&dll_rb_hash_new},
+    {"rb_inspect", (RUBY_PROC*)&dll_rb_inspect},
+    {"rb_int2inum", (RUBY_PROC*)&dll_rb_int2inum},
+    {"rb_lastline_get", (RUBY_PROC*)&dll_rb_lastline_get},
+    {"rb_lastline_set", (RUBY_PROC*)&dll_rb_lastline_set},
+    {"rb_load_protect", (RUBY_PROC*)&dll_rb_load_protect},
+    {"rb_num2long", (RUBY_PROC*)&dll_rb_num2long},
+    {"rb_num2ulong", (RUBY_PROC*)&dll_rb_num2ulong},
+    {"rb_obj_alloc", (RUBY_PROC*)&dll_rb_obj_alloc},
+    {"rb_obj_as_string", (RUBY_PROC*)&dll_rb_obj_as_string},
+    {"rb_obj_id", (RUBY_PROC*)&dll_rb_obj_id},
+    {"rb_raise", (RUBY_PROC*)&dll_rb_raise},
+    {"rb_str2cstr", (RUBY_PROC*)&dll_rb_str2cstr},
+    {"rb_str_cat", (RUBY_PROC*)&dll_rb_str_cat},
+    {"rb_str_concat", (RUBY_PROC*)&dll_rb_str_concat},
+    {"rb_str_new", (RUBY_PROC*)&dll_rb_str_new},
+    {"rb_str_new2", (RUBY_PROC*)&dll_rb_str_new2},
+    {"ruby_errinfo", (RUBY_PROC*)&dll_ruby_errinfo},
+    {"ruby_init", (RUBY_PROC*)&dll_ruby_init},
+    {"ruby_init_loadpath", (RUBY_PROC*)&dll_ruby_init_loadpath},
+#if defined(DYNAMIC_RUBY_VER) && DYNAMIC_RUBY_VER >= 18
+    {"rb_w32_snprintf", (RUBY_PROC*)&dll_rb_w32_snprintf},
+#endif
+    {"", NULL},
+};
+
+/*
+ * Free ruby.dll
+ */
+    static void
+end_dynamic_ruby()
+{
+    if (hinstRuby)
+    {
+	FreeLibrary(hinstRuby);
+	hinstRuby = 0;
+    }
+}
+
+/*
+ * Load library and get all pointers.
+ * Parameter 'libname' provides name of DLL.
+ * Return OK or FAIL.
+ */
+    static int
+ruby_runtime_link_init(char *libname, int verbose)
+{
+    int i;
+
+    if (hinstRuby)
+	return OK;
+    hinstRuby = LoadLibrary(libname);
+    if (!hinstRuby)
+    {
+	if (verbose)
+	    EMSG2(_(e_loadlib), libname);
+	return FAIL;
+    }
+
+    for (i = 0; ruby_funcname_table[i].ptr; ++i)
+    {
+	if (!(*ruby_funcname_table[i].ptr = GetProcAddress(hinstRuby,
+			ruby_funcname_table[i].name)))
+	{
+	    FreeLibrary(hinstRuby);
+	    hinstRuby = 0;
+	    if (verbose)
+		EMSG2(_(e_loadfunc), ruby_funcname_table[i].name);
+	    return FAIL;
+	}
+    }
+    return OK;
+}
+
+/*
+ * If ruby is enabled (there is installed ruby on Windows system) return TRUE,
+ * else FALSE.
+ */
+    int
+ruby_enabled(verbose)
+    int		verbose;
+{
+    return ruby_runtime_link_init(DYNAMIC_RUBY_DLL, verbose) == OK;
+}
+#endif /* defined(DYNAMIC_RUBY) || defined(PROTO) */
+
+    void
+ruby_end()
+{
+#ifdef DYNAMIC_RUBY
+    end_dynamic_ruby();
+#endif
+}
+
+void ex_ruby(exarg_T *eap)
+{
+    int state;
+    char *script = NULL;
+
+    script = script_get(eap, eap->arg);
+    if (!eap->skip && ensure_ruby_initialized())
+    {
+	if (script == NULL)
+	    rb_eval_string_protect((char *)eap->arg, &state);
+	else
+	    rb_eval_string_protect(script, &state);
+	if (state)
+	    error_print(state);
+    }
+    vim_free(script);
+}
+
+void ex_rubydo(exarg_T *eap)
+{
+    int state;
+    linenr_T i;
+
+    if (ensure_ruby_initialized())
+    {
+	if (u_save(eap->line1 - 1, eap->line2 + 1) != OK)
+	    return;
+	for (i = eap->line1; i <= eap->line2; i++) {
+	    VALUE line, oldline;
+
+	    line = oldline = rb_str_new2(ml_get(i));
+	    rb_lastline_set(line);
+	    rb_eval_string_protect((char *) eap->arg, &state);
+	    if (state) {
+		error_print(state);
+		break;
+	    }
+	    line = rb_lastline_get();
+	    if (!NIL_P(line)) {
+		if (TYPE(line) != T_STRING) {
+		    EMSG("E265: $_ must be an instance of String");
+		    return;
+		}
+		ml_replace(i, (char_u *) STR2CSTR(line), 1);
+		changed();
+#ifdef SYNTAX_HL
+		syn_changed(i); /* recompute syntax hl. for this line */
+#endif
+	    }
+	}
+	check_cursor();
+	update_curbuf(NOT_VALID);
+    }
+}
+
+void ex_rubyfile(exarg_T *eap)
+{
+    int state;
+
+    if (ensure_ruby_initialized())
+    {
+	rb_load_protect(rb_str_new2((char *) eap->arg), 0, &state);
+	if (state) error_print(state);
+    }
+}
+
+void ruby_buffer_free(buf_T *buf)
+{
+    if (buf->ruby_ref) {
+	rb_hash_aset(objtbl, rb_obj_id((VALUE) buf->ruby_ref), Qnil);
+	RDATA(buf->ruby_ref)->data = NULL;
+    }
+}
+
+void ruby_window_free(win_T *win)
+{
+    if (win->ruby_ref) {
+	rb_hash_aset(objtbl, rb_obj_id((VALUE) win->ruby_ref), Qnil);
+	RDATA(win->ruby_ref)->data = NULL;
+    }
+}
+
+static int ensure_ruby_initialized(void)
+{
+    if (!ruby_initialized)
+    {
+#ifdef DYNAMIC_RUBY
+	if (ruby_enabled(TRUE))
+	{
+#endif
+	    ruby_init();
+	    ruby_init_loadpath();
+	    ruby_io_init();
+	    ruby_vim_init();
+	    ruby_initialized = 1;
+#ifdef DYNAMIC_RUBY
+	}
+	else
+	{
+	    EMSG(_("E266: Sorry, this command is disabled, the Ruby library could not be loaded."));
+	    return 0;
+	}
+#endif
+    }
+    return ruby_initialized;
+}
+
+static void error_print(int state)
+{
+#ifndef DYNAMIC_RUBY
+    RUBYEXTERN VALUE ruby_errinfo;
+#endif
+    VALUE eclass;
+    VALUE einfo;
+    char buff[BUFSIZ];
+
+#define TAG_RETURN	0x1
+#define TAG_BREAK	0x2
+#define TAG_NEXT	0x3
+#define TAG_RETRY	0x4
+#define TAG_REDO	0x5
+#define TAG_RAISE	0x6
+#define TAG_THROW	0x7
+#define TAG_FATAL	0x8
+#define TAG_MASK	0xf
+
+    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:
+	eclass = CLASS_OF(ruby_errinfo);
+	einfo = rb_obj_as_string(ruby_errinfo);
+	if (eclass == rb_eRuntimeError && RSTRING(einfo)->len == 0) {
+	    EMSG("E272: unhandled exception");
+	}
+	else {
+	    VALUE epath;
+	    char *p;
+
+	    epath = rb_class_path(eclass);
+	    snprintf(buff, BUFSIZ, "%s: %s",
+		     RSTRING(epath)->ptr, RSTRING(einfo)->ptr);
+	    p = strchr(buff, '\n');
+	    if (p) *p = '\0';
+	    EMSG(buff);
+	}
+	break;
+    default:
+	snprintf(buff, BUFSIZ, _("E273: unknown longjmp status %d"), state);
+	EMSG(buff);
+	break;
+    }
+}
+
+static VALUE vim_message(VALUE self, VALUE str)
+{
+    char *buff, *p;
+
+    str = rb_obj_as_string(str);
+    buff = ALLOCA_N(char, RSTRING(str)->len);
+    strcpy(buff, RSTRING(str)->ptr);
+    p = strchr(buff, '\n');
+    if (p) *p = '\0';
+    MSG(buff);
+    return Qnil;
+}
+
+static VALUE vim_set_option(VALUE self, VALUE str)
+{
+    do_set((char_u *)STR2CSTR(str), 0);
+    update_screen(NOT_VALID);
+    return Qnil;
+}
+
+static VALUE vim_command(VALUE self, VALUE str)
+{
+    do_cmdline_cmd((char_u *)STR2CSTR(str));
+    return Qnil;
+}
+
+static VALUE vim_evaluate(VALUE self, VALUE str)
+{
+#ifdef FEAT_EVAL
+    char_u *value = eval_to_string((char_u *)STR2CSTR(str), NULL);
+
+    if (value)
+    {
+	VALUE val = rb_str_new2(value);
+	vim_free(value);
+	return val;
+    }
+    else
+#endif
+	return Qnil;
+}
+
+static VALUE buffer_new(buf_T *buf)
+{
+    if (buf->ruby_ref) {
+	return (VALUE) buf->ruby_ref;
+    }
+    else {
+	VALUE obj = Data_Wrap_Struct(cBuffer, 0, 0, buf);
+	buf->ruby_ref = (void *) obj;
+	rb_hash_aset(objtbl, rb_obj_id(obj), obj);
+	return obj;
+    }
+}
+
+static buf_T *get_buf(VALUE obj)
+{
+    buf_T *buf;
+
+    Data_Get_Struct(obj, buf_T, buf);
+    if (buf == NULL)
+	rb_raise(eDeletedBufferError, "attempt to refer to deleted buffer");
+    return buf;
+}
+
+static VALUE buffer_s_current()
+{
+    return buffer_new(curbuf);
+}
+
+static VALUE buffer_s_count()
+{
+    buf_T *b;
+    int n = 0;
+
+    for (b = firstbuf; b; b = b->b_next) n++;
+    return INT2NUM(n);
+}
+
+static VALUE buffer_s_aref(VALUE self, VALUE num)
+{
+    buf_T *b;
+    int n = NUM2INT(num);
+
+    for (b = firstbuf; b; b = b->b_next, --n) {
+	if (n == 0)
+	    return buffer_new(b);
+    }
+    return Qnil;
+}
+
+static VALUE buffer_name(VALUE self)
+{
+    buf_T *buf = get_buf(self);
+
+    return buf->b_ffname ? rb_str_new2(buf->b_ffname) : Qnil;
+}
+
+static VALUE buffer_number(VALUE self)
+{
+    buf_T *buf = get_buf(self);
+
+    return INT2NUM(buf->b_fnum);
+}
+
+static VALUE buffer_count(VALUE self)
+{
+    buf_T *buf = get_buf(self);
+
+    return INT2NUM(buf->b_ml.ml_line_count);
+}
+
+static VALUE buffer_aref(VALUE self, VALUE num)
+{
+    buf_T *buf = get_buf(self);
+    long n = NUM2LONG(num);
+
+    if (n > 0 && n <= buf->b_ml.ml_line_count) {
+	char *line = ml_get_buf(buf, n, FALSE);
+	return line ? rb_str_new2(line) : Qnil;
+    }
+    else {
+	rb_raise(rb_eIndexError, "index %d out of buffer", n);
+	return Qnil; /* For stop warning */
+    }
+}
+
+static VALUE buffer_aset(VALUE self, VALUE num, VALUE str)
+{
+    buf_T *buf = get_buf(self);
+    buf_T *savebuf = curbuf;
+    char *line = STR2CSTR(str);
+    long n = NUM2LONG(num);
+
+    if (n > 0 && n <= buf->b_ml.ml_line_count && line != NULL) {
+	curbuf = buf;
+	if (u_savesub(n) == OK) {
+	    ml_replace(n, (char_u *) line, TRUE);
+	    changed();
+#ifdef SYNTAX_HL
+	    syn_changed(n); /* recompute syntax hl. for this line */
+#endif
+	}
+	curbuf = savebuf;
+	update_curbuf(NOT_VALID);
+    }
+    else {
+	rb_raise(rb_eIndexError, "index %d out of buffer", n);
+	return Qnil; /* For stop warning */
+    }
+    return str;
+}
+
+static VALUE buffer_delete(VALUE self, VALUE num)
+{
+    buf_T *buf = get_buf(self);
+    buf_T *savebuf = curbuf;
+    long n = NUM2LONG(num);
+
+    if (n > 0 && n <= buf->b_ml.ml_line_count) {
+	curbuf = buf;
+	if (u_savedel(n, 1) == OK) {
+	    mark_adjust(n, n, MAXLNUM, -1);
+	    ml_delete(n, 0);
+	    changed();
+	}
+	curbuf = savebuf;
+	update_curbuf(NOT_VALID);
+    }
+    else {
+	rb_raise(rb_eIndexError, "index %d out of buffer", n);
+    }
+    return Qnil;
+}
+
+static VALUE buffer_append(VALUE self, VALUE num, VALUE str)
+{
+    buf_T *buf = get_buf(self);
+    buf_T *savebuf = curbuf;
+    char *line = STR2CSTR(str);
+    long n = NUM2LONG(num);
+
+    if (n >= 0 && n <= buf->b_ml.ml_line_count && line != NULL) {
+	curbuf = buf;
+	if (u_inssub(n + 1) == OK) {
+	    mark_adjust(n + 1, MAXLNUM, 1L, 0L);
+	    ml_append(n, (char_u *) line, (colnr_T) 0, FALSE);
+	    changed();
+	}
+	curbuf = savebuf;
+	update_curbuf(NOT_VALID);
+    }
+    else {
+	rb_raise(rb_eIndexError, "index %d out of buffer", n);
+    }
+    return str;
+}
+
+static VALUE window_new(win_T *win)
+{
+    if (win->ruby_ref) {
+	return (VALUE) win->ruby_ref;
+    }
+    else {
+	VALUE obj = Data_Wrap_Struct(cVimWindow, 0, 0, win);
+	win->ruby_ref = (void *) obj;
+	rb_hash_aset(objtbl, rb_obj_id(obj), obj);
+	return obj;
+    }
+}
+
+static win_T *get_win(VALUE obj)
+{
+    win_T *win;
+
+    Data_Get_Struct(obj, win_T, win);
+    if (win == NULL)
+	rb_raise(eDeletedWindowError, "attempt to refer to deleted window");
+    return win;
+}
+
+static VALUE window_s_current()
+{
+    return window_new(curwin);
+}
+
+static VALUE window_s_count()
+{
+#ifdef FEAT_WINDOWS
+    win_T	*w;
+    int n = 0;
+
+    for (w = firstwin; w; w = w->w_next)
+	n++;
+    return INT2NUM(n);
+#else
+    return INT2NUM(1);
+#endif
+}
+
+static VALUE window_s_aref(VALUE self, VALUE num)
+{
+    win_T *w;
+    int n = NUM2INT(num);
+
+#ifndef FEAT_WINDOWS
+    w = curwin;
+#else
+    for (w = firstwin; w != NULL; w = w->w_next, --n)
+#endif
+	if (n == 0)
+	    return window_new(w);
+    return Qnil;
+}
+
+static VALUE window_buffer(VALUE self)
+{
+    win_T *win = get_win(self);
+
+    return buffer_new(win->w_buffer);
+}
+
+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)
+{
+    win_T *win = get_win(self);
+    win_T *savewin = curwin;
+
+    curwin = win;
+    win_setheight(NUM2INT(height));
+    curwin = savewin;
+    return height;
+}
+
+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)
+{
+    VALUE lnum, col;
+    win_T *win = get_win(self);
+
+    Check_Type(pos, T_ARRAY);
+    if (RARRAY(pos)->len != 2)
+	rb_raise(rb_eArgError, "array length must be 2");
+    lnum = RARRAY(pos)->ptr[0];
+    col = RARRAY(pos)->ptr[1];
+    win->w_cursor.lnum = NUM2LONG(lnum);
+    win->w_cursor.col = NUM2UINT(col);
+    check_cursor();		    /* put cursor on an existing line */
+    update_screen(NOT_VALID);
+    return Qnil;
+}
+
+static VALUE f_p(int argc, VALUE *argv, VALUE self)
+{
+    int i;
+    VALUE str = rb_str_new("", 0);
+
+    for (i = 0; i < argc; i++) {
+	if (i > 0) rb_str_cat(str, ", ", 2);
+	rb_str_concat(str, rb_inspect(argv[i]));
+    }
+    MSG(RSTRING(str)->ptr);
+    return Qnil;
+}
+
+static void ruby_io_init(void)
+{
+#ifndef DYNAMIC_RUBY
+    RUBYEXTERN VALUE rb_stdout;
+#endif
+
+    rb_stdout = rb_obj_alloc(rb_cObject);
+    rb_define_singleton_method(rb_stdout, "write", vim_message, 1);
+    rb_define_global_function("p", f_p, -1);
+}
+
+static void ruby_vim_init(void)
+{
+    objtbl = rb_hash_new();
+    rb_global_variable(&objtbl);
+
+    mVIM = rb_define_module("VIM");
+    rb_define_const(mVIM, "VERSION_MAJOR", INT2NUM(VIM_VERSION_MAJOR));
+    rb_define_const(mVIM, "VERSION_MINOR", INT2NUM(VIM_VERSION_MINOR));
+    rb_define_const(mVIM, "VERSION_BUILD", INT2NUM(VIM_VERSION_BUILD));
+    rb_define_const(mVIM, "VERSION_PATCHLEVEL", INT2NUM(VIM_VERSION_PATCHLEVEL));
+    rb_define_const(mVIM, "VERSION_SHORT", rb_str_new2(VIM_VERSION_SHORT));
+    rb_define_const(mVIM, "VERSION_MEDIUM", rb_str_new2(VIM_VERSION_MEDIUM));
+    rb_define_const(mVIM, "VERSION_LONG", rb_str_new2(VIM_VERSION_LONG));
+    rb_define_const(mVIM, "VERSION_LONG_DATE", rb_str_new2(VIM_VERSION_LONG_DATE));
+    rb_define_module_function(mVIM, "message", vim_message, 1);
+    rb_define_module_function(mVIM, "set_option", vim_set_option, 1);
+    rb_define_module_function(mVIM, "command", vim_command, 1);
+    rb_define_module_function(mVIM, "evaluate", vim_evaluate, 1);
+
+    eDeletedBufferError = rb_define_class_under(mVIM, "DeletedBufferError",
+						rb_eStandardError);
+    eDeletedWindowError = rb_define_class_under(mVIM, "DeletedWindowError",
+						rb_eStandardError);
+
+    cBuffer = rb_define_class_under(mVIM, "Buffer", rb_cObject);
+    rb_define_singleton_method(cBuffer, "current", buffer_s_current, 0);
+    rb_define_singleton_method(cBuffer, "count", buffer_s_count, 0);
+    rb_define_singleton_method(cBuffer, "[]", buffer_s_aref, 1);
+    rb_define_method(cBuffer, "name", buffer_name, 0);
+    rb_define_method(cBuffer, "number", buffer_number, 0);
+    rb_define_method(cBuffer, "count", buffer_count, 0);
+    rb_define_method(cBuffer, "length", buffer_count, 0);
+    rb_define_method(cBuffer, "[]", buffer_aref, 1);
+    rb_define_method(cBuffer, "[]=", buffer_aset, 2);
+    rb_define_method(cBuffer, "delete", buffer_delete, 1);
+    rb_define_method(cBuffer, "append", buffer_append, 2);
+
+    cVimWindow = rb_define_class_under(mVIM, "Window", rb_cObject);
+    rb_define_singleton_method(cVimWindow, "current", window_s_current, 0);
+    rb_define_singleton_method(cVimWindow, "count", window_s_count, 0);
+    rb_define_singleton_method(cVimWindow, "[]", window_s_aref, 1);
+    rb_define_method(cVimWindow, "buffer", window_buffer, 0);
+    rb_define_method(cVimWindow, "height", window_height, 0);
+    rb_define_method(cVimWindow, "height=", window_set_height, 1);
+    rb_define_method(cVimWindow, "cursor", window_cursor, 0);
+    rb_define_method(cVimWindow, "cursor=", window_set_cursor, 1);
+
+    rb_define_virtual_variable("$curbuf", buffer_s_current, 0);
+    rb_define_virtual_variable("$curwin", window_s_current, 0);
+}