Project

General

Profile

Feature #13604 » readline_alternative_interface.diff

Patch - graywolf (Gray Wolf), 05/27/2017 10:00 AM

View differences:

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
(1-1/3)