Feature #13604 » readline_alternative_interface.diff
ext/readline/extconf.rb | ||
---|---|---|
readline.have_func("rl_redisplay")
|
||
readline.have_func("rl_insert_text")
|
||
readline.have_func("rl_delete_text")
|
||
readline.have_func("rl_callback_handler_install")
|
||
unless readline.have_type("rl_hook_func_t*")
|
||
# rl_hook_func_t is available since readline-4.2 (2001).
|
||
# Function is removed at readline-6.3 (2014).
|
ext/readline/readline.c | ||
---|---|---|
#include "ruby/config.h"
|
||
#include <errno.h>
|
||
#include <stdio.h>
|
||
#include <stdbool.h>
|
||
#include <string.h>
|
||
#ifdef HAVE_READLINE_READLINE_H
|
||
#include <readline/readline.h>
|
||
... | ... | |
return result;
|
||
}
|
||
#ifdef HAVE_RL_CALLBACK_HANDLER_INSTALL
|
||
#define READ_CHAR_CB "read_char_cb_block"
|
||
static ID read_char_cb_block;
|
||
static bool readline_callback_add_history;
|
||
static char * readline_callback_line;
|
||
static VALUE readline_callback_ensure(VALUE val) {
|
||
free(readline_callback_line);
|
||
readline_callback_line = NULL;
|
||
return Qnil;
|
||
}
|
||
static VALUE readline_callback_call(VALUE line) {
|
||
VALUE proc = rb_attr_get(mReadline, read_char_cb_block);
|
||
rb_funcall(proc, id_call, 1, line);
|
||
return Qnil;
|
||
}
|
||
static void readline_callback_callback(char * line) {
|
||
if (readline_callback_add_history && line) {
|
||
add_history(line);
|
||
}
|
||
readline_callback_line = line;
|
||
rb_ensure(
|
||
readline_callback_call, line ? rb_tainted_str_new2(line) : Qnil,
|
||
readline_callback_ensure, Qnil
|
||
);
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* Readline.readline(prompt = "", add_hist = false) &block -> nil
|
||
* Set up the terminal for readline I/O and display the initial expanded
|
||
* value of prompt. Save the provided block to use as a function to call when
|
||
* a complete line of input has been entered. The block should take the text
|
||
* of the line as an argument.
|
||
*
|
||
* = Example
|
||
*
|
||
* PROMPT = "rltest$ "
|
||
*
|
||
* $running = true
|
||
* $sigwinch_received = false
|
||
*
|
||
* Readline.handler_install(PROMPT, add_hist: true) do |line|
|
||
* # Can use ^D (stty eof) or `exit' to exit.
|
||
* if !line || line == "exit"
|
||
* puts unless line
|
||
* puts "exit"
|
||
* Readline.handler_remove
|
||
* $running = false
|
||
* else
|
||
* puts "input line: #{line}"
|
||
* end
|
||
* end
|
||
*
|
||
* Signal.trap('SIGWINCH') { $sigwinch_received = true }
|
||
*
|
||
* while $running do
|
||
* rs = IO.select([$stdin])
|
||
* if $sigwinch_received
|
||
* Readline.resize_terminal
|
||
* $sigwinch_received = false
|
||
* end
|
||
* Readline.read_char if r = rs[0]
|
||
* end
|
||
*
|
||
* puts "rltest: Event loop has exited"
|
||
*/
|
||
static VALUE readline_callback_handler_install(
|
||
int argc,
|
||
VALUE * argv,
|
||
VALUE self
|
||
) {
|
||
VALUE tmp, add_hist, block;
|
||
char * prompt = NULL;
|
||
rb_need_block();
|
||
if (rb_scan_args(argc, argv, "02&", &tmp, &add_hist, &block) > 0) {
|
||
prompt = RSTRING_PTR(tmp);
|
||
}
|
||
if (RTEST(add_hist)) {
|
||
readline_callback_add_history = true;
|
||
} else {
|
||
readline_callback_add_history = false;
|
||
}
|
||
rb_ivar_set(mReadline, read_char_cb_block, block);
|
||
rl_callback_handler_install(prompt, readline_callback_callback);
|
||
return Qnil;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* Readline.read_char -> nil
|
||
*
|
||
* Whenever an application determines that keyboard input is available, it
|
||
* should call read_char, which will read the next character from the current
|
||
* input source. If that character completes the line, read_char will invoke
|
||
* the handler function saved by handler_install to process the line.
|
||
* Before calling the handler function, the terminal settings are reset to
|
||
* the values they had before calling handler_install. If the handler function
|
||
* returns, the terminal settings are modified for Readline's use again. EOF
|
||
* is indicated by calling handler with a nil.
|
||
*/
|
||
static VALUE readline_callback_read_char(VALUE self) {
|
||
VALUE proc = rb_attr_get(mReadline, read_char_cb_block);
|
||
if (NIL_P(proc)) {
|
||
rb_raise(rb_eRuntimeError, "No handler installed.");
|
||
}
|
||
rl_callback_read_char();
|
||
return Qnil;
|
||
}
|
||
/*
|
||
* call-seq:
|
||
* Readline.handler_remove -> nil
|
||
*
|
||
* Restore the terminal to its initial state and remove the line handler.
|
||
* This may be called from within a callback as well as independently. If
|
||
* the handler installed by handler_install does not exit the program, this
|
||
* function should be called before the program exits to reset the terminal
|
||
* settings.
|
||
*/
|
||
static VALUE readline_callback_handler_remove(VALUE self) {
|
||
rb_ivar_set(mReadline, read_char_cb_block, Qnil);
|
||
rl_callback_handler_remove();
|
||
return Qnil;
|
||
}
|
||
#endif
|
||
#undef rb_intern
|
||
void
|
||
Init_readline(void)
|
||
... | ... | |
id_call = rb_intern("call");
|
||
completion_proc = rb_intern(COMPLETION_PROC);
|
||
completion_case_fold = rb_intern(COMPLETION_CASE_FOLD);
|
||
#ifdef HAVE_RL_CALLBACK_HANDLER_INSTALL
|
||
read_char_cb_block = rb_intern(READ_CHAR_CB);
|
||
#endif
|
||
#if defined(HAVE_RL_PRE_INPUT_HOOK)
|
||
id_pre_input_hook = rb_intern("pre_input_hook");
|
||
#endif
|
||
... | ... | |
readline_s_set_special_prefixes, 1);
|
||
rb_define_singleton_method(mReadline, "special_prefixes",
|
||
readline_s_get_special_prefixes, 0);
|
||
#ifdef HAVE_RL_CALLBACK_HANDLER_INSTALL
|
||
rb_define_singleton_method(
|
||
mReadline,
|
||
"handler_install",
|
||
readline_callback_handler_install,
|
||
-1
|
||
);
|
||
rb_define_singleton_method(
|
||
mReadline,
|
||
"read_char",
|
||
readline_callback_read_char,
|
||
0
|
||
);
|
||
rb_define_singleton_method(
|
||
mReadline,
|
||
"handler_remove",
|
||
readline_callback_handler_remove,
|
||
0
|
||
);
|
||
#endif
|
||
#if USE_INSERT_IGNORE_ESCAPE
|
||
CONST_ID(id_orig_prompt, "orig_prompt");
|
||
... | ... | |
* indent-tabs-mode: nil
|
||
* end:
|
||
*/
|
||
/* vim: set ts=8 sw=4 noexpandtab: */
|
test/readline/test_readline.rb | ||
---|---|---|
SAVED_ENV.each_with_index {|k, i| ENV[k] = @saved_env[i] }
|
||
end
|
||
def test_callback_interface
|
||
with_temp_stdio do |stdin, stdout|
|
||
stdin.write("hello\n")
|
||
stdin.close
|
||
stdout.flush
|
||
line = nil
|
||
replace_stdio(stdin.path, stdout.path) {
|
||
Readline.handler_install("> ", true) { |l| line = l if l }
|
||
6.times { Readline.read_char }
|
||
Readline.handler_remove
|
||
}
|
||
assert_equal("hello", line)
|
||
assert_equal(true, line.tainted?)
|
||
stdout.rewind
|
||
assert_equal("> ", stdout.read(2))
|
||
assert_equal(1, Readline::HISTORY.length)
|
||
assert_equal("hello", Readline::HISTORY[0])
|
||
end
|
||
end
|
||
if !/EditLine/n.match(Readline::VERSION)
|
||
def test_readline
|
||
with_temp_stdio do |stdin, stdout|
|
||
... | ... | |
return true
|
||
end
|
||
end if defined?(::Readline)
|
||
# vim: set nowrap tabstop=8 tw=0 sw=2 expandtab
|