Project

General

Profile

Feature #12659 ยป readline_quoting_detection_proc.patch

georgebrock (George Brocklehurst), 08/06/2016 12:55 AM

View differences:

ext/readline/extconf.rb
readline.have_var("rl_editing_mode")
readline.have_var("rl_line_buffer")
readline.have_var("rl_point")
readline.have_var("rl_char_is_quoted_p")
# workaround for native windows.
/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && readline.have_var("rl_event_hook")
/mswin|bccwin|mingw/ !~ RUBY_PLATFORM && readline.have_var("rl_catch_sigwinch")
ext/readline/readline.c
#define COMPLETION_PROC "completion_proc"
#define COMPLETION_CASE_FOLD "completion_case_fold"
static ID completion_proc, completion_case_fold;
#if defined HAVE_RL_CHAR_IS_QUOTED_P
#define QUOTING_DETECTION_PROC "quoting_detection_proc"
static ID quoting_detection_proc;
#endif
#if USE_INSERT_IGNORE_ESCAPE
static ID id_orig_prompt, id_last_prompt;
#endif
......
static char **readline_attempted_completion_function(const char *text,
int start, int end);
#ifdef HAVE_RL_CHAR_IS_QUOTED_P
int readline_char_is_quoted(char *text, int index);
long byte_index_to_char_index(VALUE str, long byte_index);
#endif
#define OutputStringValue(str) do {\
SafeStringValue(str);\
(str) = rb_str_conv_enc((str), rb_enc_get(str), rb_locale_encoding());\
......
return rb_attr_get(mReadline, completion_proc);
}
#ifdef HAVE_RL_CHAR_IS_QUOTED_P
/*
* call-seq:
* Readline.quoting_detection_proc = proc
*
* Specifies a Proc object +proc+ to determine if a character in the user's
* input is escaped. It should take the user's input and the index of the
* character in question as input, and return a boolean (true if the specified
* character is escaped).
*
* Readline will only call this proc with characters specified in
* +completer_quote_characters+, to discover if they indicate the end of a
* quoted argument, or characters specified in
* +completer_word_break_characters+, to discover if they indicate a break
* between arguments.
*
* If +completer_quote_characters+ is not set, or if the user input doesn't
* contain one of the +completer_quote_characters+ or a +\+ character,
* Readline will not attempt to use this proc at all.
*
* Raises ArgumentError if +proc+ does not respond to the call method.
*/
static VALUE
readline_s_set_quoting_detection_proc(VALUE self, VALUE proc)
{
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, quoting_detection_proc, proc);
}
#else
#define readline_s_set_quoting_detection_proc rb_f_notimplement
#endif
#ifdef HAVE_RL_CHAR_IS_QUOTED_P
/*
* call-seq:
* Readline.quoting_detection_proc -> proc
*
* Returns the quoting detection Proc object.
*/
static VALUE
readline_s_get_quoting_detection_proc(VALUE self)
{
return rb_attr_get(mReadline, quoting_detection_proc);
}
#else
#define readline_s_get_quoting_detection_proc rb_f_notimplement
#endif
/*
* call-seq:
* Readline.completion_case_fold = bool
......
return result;
}
#ifdef HAVE_RL_CHAR_IS_QUOTED_P
int
readline_char_is_quoted(char *text, int byte_index)
{
VALUE proc, result, str;
long char_index;
proc = rb_attr_get(mReadline, quoting_detection_proc);
if (NIL_P(proc)) {
return 0;
}
str = rb_locale_str_new_cstr(text);
char_index = byte_index_to_char_index(str, (long)byte_index);
if (char_index == -1) {
rb_raise(rb_eIndexError, "failed to find character at byte index");
}
result = rb_funcall(proc, rb_intern("call"), 2, str, LONG2FIX(char_index));
return result ? 1 : 0;
}
long
byte_index_to_char_index(VALUE str, long byte_index)
{
const char *ptr;
long ci, bi, len, clen;
rb_encoding *enc;
enc = rb_enc_get(str);
len = RSTRING_LEN(str);
ptr = RSTRING_PTR(str);
for (bi = 0, ci = 0; bi < len; bi += clen, ++ci) {
if (bi == byte_index) {
return ci;
}
clen = rb_enc_mbclen(ptr + bi, ptr + len, enc);
}
return -1;
}
#endif
#ifdef HAVE_RL_SET_SCREEN_SIZE
/*
* call-seq:
......
#if defined(HAVE_RL_SPECIAL_PREFIXES)
id_special_prefixes = rb_intern("special_prefixes");
#endif
#if defined HAVE_RL_CHAR_IS_QUOTED_P
quoting_detection_proc = rb_intern(QUOTING_DETECTION_PROC);
#endif
mReadline = rb_define_module("Readline");
rb_define_module_function(mReadline, "readline",
......
readline_s_set_completion_proc, 1);
rb_define_singleton_method(mReadline, "completion_proc",
readline_s_get_completion_proc, 0);
rb_define_singleton_method(mReadline, "quoting_detection_proc=",
readline_s_set_quoting_detection_proc, 1);
rb_define_singleton_method(mReadline, "quoting_detection_proc",
readline_s_get_quoting_detection_proc, 0);
rb_define_singleton_method(mReadline, "completion_case_fold=",
readline_s_set_completion_case_fold, 1);
rb_define_singleton_method(mReadline, "completion_case_fold",
......
#if defined(HAVE_RL_PRE_INPUT_HOOK)
rl_pre_input_hook = (rl_hook_func_t *)readline_pre_input_hook;
#endif
#if defined HAVE_RL_CHAR_IS_QUOTED_P
rl_char_is_quoted_p = &readline_char_is_quoted;
#endif
#ifdef HAVE_RL_CATCH_SIGNALS
rl_catch_signals = 0;
#endif
test/readline/test_readline.rb
end
end if Readline.respond_to?(:refresh_line)
def test_setting_quoting_detection_proc
return unless Readline.respond_to?(:quoting_detection_proc=)
expected = proc { |text, index| false }
Readline.quoting_detection_proc = expected
assert_equal(expected, Readline.quoting_detection_proc)
assert_raise(ArgumentError) do
Readline.quoting_detection_proc = "This does not have call method."
end
end
def test_using_quoting_detection_proc
saved_completer_quote_characters = Readline.completer_quote_characters
saved_completer_word_break_characters = Readline.completer_word_break_characters
return unless Readline.respond_to?(:quoting_detection_proc=)
passed_text = nil
line = nil
with_temp_stdio do |stdin, stdout|
replace_stdio(stdin.path, stdout.path) do
Readline.completion_proc = -> (text) do
passed_text = text
['completion']
end
Readline.completer_quote_characters = '\'"'
Readline.completer_word_break_characters = ' '
Readline.quoting_detection_proc = -> (text, index) do
index > 0 && text[index-1] == '\\'
end
stdin.write("first second\\ third\t")
stdin.flush
line = Readline.readline('> ', false)
end
end
assert_equal('second\\ third', passed_text)
assert_equal('first completion', line)
ensure
Readline.completer_quote_characters = saved_completer_quote_characters
Readline.completer_word_break_characters = saved_completer_word_break_characters
end
def test_using_quoting_detection_proc_with_multibyte_input
saved_completer_quote_characters = Readline.completer_quote_characters
saved_completer_word_break_characters = Readline.completer_word_break_characters
return unless Readline.respond_to?(:quoting_detection_proc=)
unless Encoding.find("locale") == Encoding::UTF_8
return if assert_under_utf8
skip 'this test needs UTF-8 locale'
end
passed_text = nil
escaped_char_indexes = []
line = nil
with_temp_stdio do |stdin, stdout|
replace_stdio(stdin.path, stdout.path) do
Readline.completion_proc = -> (text) do
passed_text = text
['completion']
end
Readline.completer_quote_characters = '\'"'
Readline.completer_word_break_characters = ' '
Readline.quoting_detection_proc = -> (text, index) do
escaped = index > 0 && text[index-1] == '\\'
escaped_char_indexes << index if escaped
escaped
end
stdin.write("\u3042\u3093 second\\ third\t")
stdin.flush
line = Readline.readline('> ', false)
end
end
assert_equal([10], escaped_char_indexes)
assert_equal('second\\ third', passed_text)
assert_equal("\u3042\u3093 completion", line)
ensure
Readline.completer_quote_characters = saved_completer_quote_characters
Readline.completer_word_break_characters = saved_completer_word_break_characters
end
private
def replace_stdio(stdin_path, stdout_path)
    (1-1/1)