Feature #3616
closedIRB + readline incorrectly counts non-printing characters in prompt
Description
=begin
When setting a prompt for IRB that contains terminal control characters:
IRB.conf[:PROMPT][:COLOR] = {
:PROMPT_I => "\e[0;1;32m>>> \e[0m",
...
}
IRB.conf[:PROMPT_MODE] = :COLOR
IRB includes "\e[...m" in the prompt length calculation, causing readline
functions like beginning-of-line to move to the incorrect place on the line:
>>> 012345678901234567890
^ caret
<Press \C-a>
>>> 012345678901234567890
^ caret
Fortunately, GNU Readline offers a couple of constants to mark that a string
should be non-printing:
# readline.h
/* Definitions available for use by readline clients. */
#define RL_PROMPT_START_IGNORE '\001'
#define RL_PROMPT_END_IGNORE '\002'
Bash handles this issue by mapping \[' and
]' to RL_PROMPT_START_IGNORE and
RL_PROMPT_END_IGNORE inside of `PS*' prompt variables. It would be nice to offer
the same feature in IRB, or simply to surround all terminal escape sequences in
the IRB prompt with RL_PROMPT_*_IGNORE.
Note that this bug does not occur with libedit.
=end
Updated by nobu (Nobuyoshi Nakada) over 13 years ago
=begin
Hi,
At Mon, 26 Jul 2010 14:56:14 +0900,
Sung Pae wrote in [ruby-core:31484]:
Bash handles this issue by mapping
\[' and
]' to RL_PROMPT_START_IGNORE and
RL_PROMPT_END_IGNORE inside of `PS*' prompt variables. It would be nice to offer
the same feature in IRB, or simply to surround all terminal escape sequences in
the IRB prompt with RL_PROMPT_*_IGNORE.
Even if it is implemented, you'll have to write as:
:PROMPT_I => "\\[\e[0;1;32m\\]>>> \\[\e[0m\\]"
It doesn't seem nice.
--
Nobu Nakada
=end
Updated by guns (Sung Pae) over 13 years ago
=begin
Even if it is implemented, you'll have to write as:
:PROMPT_I => "\\[\e[0;1;32m\\]>>> \\[\e[0m\\]"
It doesn't seem nice.
Yes, I agree. That is very ugly. I think it's appropriate to simply surround all
terminal escape sequences with the special RL values. Also, \001 and \002 do not
seem to affect libedit at all.
It is relatively simple to patch, so if the maintainer does not wish to bother,
I can supply a patch later in the week after my own deadlines are past.
guns
=end
Updated by nobu (Nobuyoshi Nakada) over 13 years ago
=begin
Hi,
At Tue, 27 Jul 2010 08:34:08 +0900,
Sung Pae wrote in [ruby-core:31509]:
Yes, I agree. That is very ugly. I think it's appropriate to simply surround all
terminal escape sequences with the special RL values. Also, \001 and \002 do not
seem to affect libedit at all.
OK. I'll yield this ticket to the maintainer of ext/readline.
diff --git a/ext/readline/readline.c b/ext/readline/readline.c
index 3ecea94..9b154bc 100644
--- a/ext/readline/readline.c
+++ b/ext/readline/readline.c
@@ -43,10 +43,20 @@
static VALUE mReadline;
#define EDIT_LINE_LIBRARY_VERSION "EditLine wrapper"
+#ifndef USE_INSERT_IGNORE_ESCAPE
+# ifndef HAVE_EDITLINE_READLINE_H
+# define USE_INSERT_IGNORE_ESCAPE 1
+# else
+# define USE_INSERT_IGNORE_ESCAPE 0
+# endif
+#endif
#define COMPLETION_PROC "completion_proc"
#define COMPLETION_CASE_FOLD "completion_case_fold"
static ID completion_proc, completion_case_fold;
+#if USE_INSERT_IGNORE_ESCAPE
+static ID id_orig_prompt, id_last_prompt;
+#endif
#ifndef HAVE_RL_FILENAME_COMPLETION_FUNCTION
define rl_filename_completion_function filename_completion_function¶
@@ -145,6 +155,81 @@ readline_event(void)
}
#endif
+#if USE_INSERT_IGNORE_ESCAPE
+static VALUE
+insert_ignore_escape(VALUE self, VALUE prompt)
+{
- VALUE last_prompt, orig_prompt = rb_attr_get(self, id_orig_prompt);
- int ignoring = 0;
- const char *s0, *s, *e;
- long len;
- static const char ignore_code[2] = {RL_PROMPT_START_IGNORE, RL_PROMPT_END_IGNORE};
- prompt = rb_str_new_shared(prompt);
- last_prompt = rb_attr_get(self, id_last_prompt);
- if (orig_prompt == prompt) return last_prompt;
- len = RSTRING_LEN(prompt);
- if (NIL_P(last_prompt)) {
- last_prompt = rb_str_tmp_new(len);
- }
- s = s0 = RSTRING_PTR(prompt);
- e = s0 + len;
- rb_str_set_len(last_prompt, 0);
- while (s < e && *s) {
- switch (*s) {
- case RL_PROMPT_START_IGNORE:
-
ignoring = -1;
-
rb_str_cat(last_prompt, s0, ++s - s0);
-
s0 = s;
-
break;
- case RL_PROMPT_END_IGNORE:
-
ignoring = 0;
-
rb_str_cat(last_prompt, s0, ++s - s0);
-
s0 = s;
-
break;
- case '\033':
-
if (++s < e && *s == '[') {
-
rb_str_cat(last_prompt, s0, s - s0 - 1);
-
s0 = s - 1;
-
while (++s < e && *s) {
-
if (ISALPHA(*s)) {
-
if (!ignoring) {
-
ignoring = 1;
-
rb_str_cat(last_prompt, ignore_code+0, 1);
-
}
-
rb_str_cat(last_prompt, s0, ++s - s0);
-
s0 = s;
-
break;
-
}
-
else if (!('0' <= *s && *s <= '9' || *s == ';')) {
-
break;
-
}
-
}
-
}
-
break;
- default:
-
if (ignoring > 0) {
-
ignoring = 0;
-
rb_str_cat(last_prompt, ignore_code+1, 1);
-
}
-
s++;
-
break;
- }
- }
- if (ignoring > 0) {
- ignoring = 0;
- rb_str_cat(last_prompt, ignore_code+1, 1);
- }
- rb_str_cat(last_prompt, s0, s - s0);
- rb_ivar_set(self, id_orig_prompt, prompt);
- rb_ivar_set(self, id_last_prompt, last_prompt);
- return last_prompt;
+}
+#endif
static VALUE
readline_get(VALUE prompt)
{
@@ -248,6 +333,10 @@ readline_readline(int argc, VALUE *argv, VALUE self)
rb_secure(4);
if (rb_scan_args(argc, argv, "02", &tmp, &add_hist) > 0) {
OutputStringValue(tmp);
+#if USE_INSERT_IGNORE_ESCAPE
- tmp = insert_ignore_escape(self, tmp);
- rb_str_locktmp(tmp);
+#endif
prompt = RSTRING_PTR(tmp);
}
@@ -257,6 +346,11 @@ readline_readline(int argc, VALUE argv, VALUE self)
rl_prep_terminal(1);
#endif
buff = (char)rb_protect(readline_get, (VALUE)prompt, &status);
+#if USE_INSERT_IGNORE_ESCAPE
- if (prompt) {
- rb_str_unlocktmp(tmp);
- }
+#endif
if (status) {
#if defined HAVE_RL_CLEANUP_AFTER_SIGNAL
/* restore terminal mode and signal handler*/
@@ -1381,6 +1475,11 @@ Init_readline()
rb_define_singleton_method(mReadline, "refresh_line",
readline_s_refresh_line, 0);
+#if USE_INSERT_IGNORE_ESCAPE
- CONST_ID(id_orig_prompt, "orig_prompt");
- CONST_ID(id_last_prompt, "last_prompt");
+#endif - history = rb_obj_alloc(rb_cObject);
rb_extend_object(history, rb_mEnumerable);
rb_define_singleton_method(history,"to_s", hist_to_s, 0);
--
Nobu Nakada
=end
Updated by nobu (Nobuyoshi Nakada) over 13 years ago
- Category set to ext
- Status changed from Open to Assigned
- Assignee set to kouji (Kouji Takao)
=begin
=end
Updated by kouji (Kouji Takao) over 13 years ago
- Status changed from Assigned to Closed
- % Done changed from 0 to 100
=begin
This issue was solved with changeset r30496.
Sung, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.
=end