diff --git a/ext/readline/extconf.rb b/ext/readline/extconf.rb index b8e9e0f..3a854d6 100644 --- a/ext/readline/extconf.rb +++ b/ext/readline/extconf.rb @@ -83,6 +83,7 @@ have_readline_var("rl_point") /mswin|bccwin|mingw/ !~ RUBY_PLATFORM && have_readline_var("rl_event_hook") /mswin|bccwin|mingw/ !~ RUBY_PLATFORM && have_readline_var("rl_catch_sigwinch") /mswin|bccwin|mingw/ !~ RUBY_PLATFORM && have_readline_var("rl_catch_signals") +have_readline_var("rl_pre_input_hook") have_readline_func("rl_cleanup_after_signal") have_readline_func("rl_free_line_state") have_readline_func("rl_clear_signals") @@ -93,6 +94,8 @@ have_readline_func("rl_emacs_editing_mode") have_readline_func("replace_history_entry") have_readline_func("remove_history") have_readline_func("clear_history") +have_readline_func("rl_redisplay") +have_readline_func("rl_insert_text") have_readline_macro("RL_PROMPT_START_IGNORE") have_readline_macro("RL_PROMPT_END_IGNORE") create_makefile("readline") diff --git a/ext/readline/readline.c b/ext/readline/readline.c index 423e585..be6aaa1 100644 --- a/ext/readline/readline.c +++ b/ext/readline/readline.c @@ -61,6 +61,9 @@ static ID completion_proc, completion_case_fold; #if USE_INSERT_IGNORE_ESCAPE static ID id_orig_prompt, id_last_prompt; #endif +#if defined(HAVE_RL_PRE_INPUT_HOOK) +static ID id_pre_input_hook_proc; +#endif #ifndef HAVE_RL_FILENAME_COMPLETION_FUNCTION # define rl_filename_completion_function filename_completion_function @@ -468,6 +471,63 @@ readline_s_set_output(VALUE self, VALUE output) return output; } +#if defined(HAVE_RL_PRE_INPUT_HOOK) +static VALUE +readline_s_set_pre_input_hook_proc(VALUE self, VALUE proc) +{ + rb_secure(4); + if (!NIL_P(proc) && !rb_respond_to(proc, rb_intern("call"))) + rb_raise(rb_eArgError, "argument must respond to `call'"); + return rb_ivar_set(mReadline, id_pre_input_hook_proc, proc); +} + +static VALUE +readline_s_get_pre_input_hook_proc(VALUE self) +{ + rb_secure(4); + return rb_attr_get(mReadline, id_pre_input_hook_proc); +} + +static int +readline_pre_input_hook(void) +{ + VALUE proc; + + proc = rb_attr_get(mReadline, id_pre_input_hook_proc); + if (!NIL_P(proc)) + rb_funcall(proc, rb_intern("call"), 0); + return 0; +} +#else +#define readline_s_set_pre_input_hook_proc rb_f_notimplement +#define readline_s_get_pre_input_hook_proc rb_f_notimplement +#endif + +#if defined(HAVE_RL_INSERT_TEXT) +static VALUE +readline_s_insert_text(VALUE self, VALUE str) +{ + rb_secure(4); + OutputStringValue(str); + rl_insert_text(RSTRING_PTR(str)); + return self; +} +#else +#define readline_s_insert_text rb_f_notimplement +#endif + +#if defined(HAVE_RL_REDISPLAY) +static VALUE +readline_s_redisplay(VALUE self) +{ + rb_secure(4); + rl_redisplay(); + return self; +} +#else +#define readline_s_redisplay rb_f_notimplement +#endif + /* * call-seq: * Readline.completion_proc = proc @@ -1531,6 +1591,9 @@ Init_readline() completion_proc = rb_intern(COMPLETION_PROC); completion_case_fold = rb_intern(COMPLETION_CASE_FOLD); +#if defined(HAVE_RL_PRE_INPUT_HOOK) + id_pre_input_hook_proc = rb_intern("pre_input_hook_proc"); +#endif mReadline = rb_define_module("Readline"); rb_define_module_function(mReadline, "readline", @@ -1589,6 +1652,14 @@ Init_readline() readline_s_get_filename_quote_characters, 0); rb_define_singleton_method(mReadline, "refresh_line", readline_s_refresh_line, 0); + rb_define_singleton_method(mReadline, "pre_input_hook_proc=", + readline_s_set_pre_input_hook_proc, 1); + rb_define_singleton_method(mReadline, "pre_input_hook_proc", + readline_s_get_pre_input_hook_proc, 0); + rb_define_singleton_method(mReadline, "insert_text", + readline_s_insert_text, 1); + rb_define_singleton_method(mReadline, "redisplay", + readline_s_redisplay, 0); #if USE_INSERT_IGNORE_ESCAPE CONST_ID(id_orig_prompt, "orig_prompt"); @@ -1672,6 +1743,15 @@ Init_readline() rb_define_const(mReadline, "VERSION", version); rl_attempted_completion_function = readline_attempted_completion_function; +#if defined(HAVE_RL_PRE_INPUT_HOOK) + rl_pre_input_hook = readline_pre_input_hook; +#endif +#if defined HAVE_RL_GETC_FUNCTION + rl_getc_function = readline_getc; + id_getbyte = rb_intern_const("getbyte"); +#elif defined HAVE_RL_EVENT_HOOK + rl_event_hook = readline_event; +#endif #ifdef HAVE_RL_CATCH_SIGNALS rl_catch_signals = 0; #endif diff --git a/test/readline/test_readline.rb b/test/readline/test_readline.rb index dcb83e9..30c6c64 100644 --- a/test/readline/test_readline.rb +++ b/test/readline/test_readline.rb @@ -70,6 +70,10 @@ class TestReadline < Test::Unit::TestCase ["point"], ["set_screen_size", 1, 1], ["get_screen_size"], + ["pre_input_hook_proc=", proc {}], + ["pre_input_hook_proc"], + ["insert_text", ""], + ["redisplay"], ] method_args.each do |method_name, *args| assert_raise(SecurityError, NotImplementedError, @@ -324,6 +328,53 @@ class TestReadline < Test::Unit::TestCase end end + def test_pre_input_hook_proc + begin + pr = proc {} + assert_equal(Readline.pre_input_hook_proc = pr, pr) + assert_equal(Readline.pre_input_hook_proc, pr) + assert_nil(Readline.pre_input_hook_proc = nil) + rescue NotImplementedError + end + end + + def test_insert_text + begin + str = "test_insert_text" + assert_equal(Readline.insert_text(str), Readline) + assert_equal(Readline.line_buffer, str) + assert_equal(Readline.line_buffer.encoding, + get_default_internal_encoding) + rescue NotImplementedError + end + end + + def test_modify_text_in_pre_input_hook + begin + stdin = Tempfile.new("readline_redisplay_stdin") + stdout = Tempfile.new("readline_redisplay_stdout") + stdin.write("world\n") + stdin.close + Readline.pre_input_hook_proc = proc do + assert_equal(Readline.line_buffer, "") + Readline.insert_text("hello ") + Readline.redisplay + end + replace_stdio(stdin.path, stdout.path) do + line = Readline.readline("> ") + assert_equal(line, "hello world") + end + assert_equal(stdout.read, "> ") + stdout.close + #rescue NotImplementedError + ensure + begin + Readline.pre_input_hook_proc = nil + rescue NotImplementedError + end + end + end + private def replace_stdio(stdin_path, stdout_path)