Make command line editing (use of readline) be per UI

Due to the way that readline's API works (based on globals), we can
only have one instance of readline in a process.  So the goal of this
patch is to only allow editing in the main UI, and make sure that only
one UI calls into readline.  Some MI paths touch readline variables
currently, which is bad as that is changing variables that matter for
the main console UI.  This patch fixes those.

This actually fixes a nasty bug -- starting gdb in MI mode ("gdb
-i=mi"), and then doing "set editing on" crashes GDB, because MI is
not prepared to use readline:

 set editing on
 &"set editing on\n"
 =cmd-param-changed,param="editing",value="on"
 ^done
 (gdb)
 p 1
 readline: readline_callback_read_char() called with no handler!
 Aborted (core dumped)

The fix for that was to add an interp_proc method to query the
interpreter whether it actually supports editing.  New test included.

gdb/ChangeLog:
2016-06-21  Pedro Alves  <palves@redhat.com>

	PR mi/20034
	* cli/cli-interp.c: Include cli-interp.h and event-top.h.
	(cli_interpreter_resume): Pass 1 to gdb_setup_readline.  Set the
	UI's input_handler here.
	(cli_interpreter_supports_command_editing): New function.
	(cli_interp_procs): Install it.
	* cli/cli-interp.h: New file.
	* event-top.c (async_command_editing_p): Rename to ...
	(set_editing_cmd_var): ... this.
	(change_line_handler): Add parameter 'editing', and use it.  Bail
	early if the interpreter doesn't support editing.  Don't touch
	readline state if editing is off.
	(gdb_rl_callback_handler_remove, gdb_rl_callback_handler_install)
	(gdb_rl_callback_handler_reinstall): Assert the current UI is the
	main UI.
	(display_gdb_prompt): Don't call gdb_rl_callback_handler_remove if
	not using readline.  Check whether the current UI is using command
	editing instead of checking the async_command_editing_p global.
	(set_async_editing_command): Delete.
	(gdb_setup_readline): Add 'editing' parameter.  Only allow editing
	on the main UI.  Don't touch readline state if editing is off.
	(gdb_disable_readline): Don't touch readline state if editing is
	off.
	* event-top.h (gdb_setup_readline): Add 'int' parameter.
	(set_async_editing_command): Delete declaration.
	(change_line_handler, command_line_handler): Declare.
	(async_command_editing_p): Rename to ...
	(set_editing_cmd_var): ... this.
	* infrun.c (reinstall_readline_callback_handler_cleanup): Check
	whether the current UI has editing enabled rather than checking
	the async_command_editing_p global.
	* interps.c (interp_supports_command_editing): New function.
	* interps.h (interp_supports_command_editing_ftype): New typedef.
	(struct interp_procs) <supports_command_editing_proc>: New field.
	(interp_supports_command_editing): Declare.
	* mi/mi-interp.c (mi_interpreter_resume): Pass 0 to
	gdb_setup_readline.  Don't clear the async_command_editing_p
	global.  Update comments.
	* top.c (gdb_readline_wrapper_line, gdb_readline_wrapper): Check
	whether the current UI has editing enabled rather than checking
	the async_command_editing_p global.  Don't touch readline state if
	editing is off.
	(undo_terminal_modifications_before_exit): Switch to the main UI.
	Unconditionally call gdb_disable_readline.
	(set_editing): New function.
	(show_async_command_editing_p): Rename to ...
	(show_editing): ... this.  Show the state of the current UI.
	(_initialize_top): Adjust.
	* top.h (struct ui) <command_editing>: New field.
	* tui/tui-interp.c: Include cli/cli-interp.h.
	(tui_resume): Pass 1 to gdb_setup_readline.  Set the UI's
	input_handler.
	(tui_interp_procs): Install
	cli_interpreter_supports_command_editing.
	* tui/tui-io.c (tui_getc): Check whether the current UI has
	editing enabled rather than checking the async_command_editing_p
	global.

gdb/testsuite/ChangeLog:
2016-06-21  Pedro Alves  <palves@redhat.com>

	PR mi/20034
	* gdb.mi/mi-editing.exp: New file.
This commit is contained in:
Pedro Alves
2016-06-21 01:11:48 +01:00
parent b6dcde571e
commit 3c216924d6
14 changed files with 261 additions and 71 deletions

View File

@@ -793,7 +793,7 @@ gdb_readline_wrapper_line (char *line)
we're handling an asynchronous target event and running in the
background, just before returning to the event loop to process
further input (or more target events). */
if (async_command_editing_p)
if (current_ui->command_editing)
gdb_rl_callback_handler_remove ();
}
@@ -813,7 +813,8 @@ gdb_readline_wrapper_cleanup (void *arg)
struct gdb_readline_wrapper_cleanup *cleanup
= (struct gdb_readline_wrapper_cleanup *) arg;
rl_already_prompted = cleanup->already_prompted_orig;
if (ui->command_editing)
rl_already_prompted = cleanup->already_prompted_orig;
gdb_assert (ui->input_handler == gdb_readline_wrapper_line);
ui->input_handler = cleanup->handler_orig;
@@ -851,7 +852,10 @@ gdb_readline_wrapper (const char *prompt)
cleanup->handler_orig = ui->input_handler;
ui->input_handler = gdb_readline_wrapper_line;
cleanup->already_prompted_orig = rl_already_prompted;
if (ui->command_editing)
cleanup->already_prompted_orig = rl_already_prompted;
else
cleanup->already_prompted_orig = 0;
cleanup->target_is_async_orig = target_is_async_p ();
@@ -863,7 +867,8 @@ gdb_readline_wrapper (const char *prompt)
/* Display our prompt and prevent double prompt display. */
display_gdb_prompt (prompt);
rl_already_prompted = 1;
if (ui->command_editing)
rl_already_prompted = 1;
if (after_char_processing_hook)
(*after_char_processing_hook) ();
@@ -1420,12 +1425,18 @@ quit_confirm (void)
static void
undo_terminal_modifications_before_exit (void)
{
struct ui *saved_top_level = current_ui;
target_terminal_ours ();
current_ui = main_ui;
#if defined(TUI)
tui_disable ();
#endif
if (async_command_editing_p)
gdb_disable_readline ();
gdb_disable_readline ();
current_ui = saved_top_level;
}
@@ -1739,13 +1750,24 @@ show_prompt (struct ui_file *file, int from_tty,
fprintf_filtered (file, _("Gdb's prompt is \"%s\".\n"), value);
}
/* "set editing" command. */
static void
show_async_command_editing_p (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
set_editing (char *args, int from_tty, struct cmd_list_element *c)
{
change_line_handler (set_editing_cmd_var);
/* Update the control variable so that MI's =cmd-param-changed event
shows the correct value. */
set_editing_cmd_var = current_ui->command_editing;
}
static void
show_editing (struct ui_file *file, int from_tty,
struct cmd_list_element *c, const char *value)
{
fprintf_filtered (file, _("Editing of command lines as "
"they are typed is %s.\n"),
value);
current_ui->command_editing ? _("on") : _("off"));
}
static void
@@ -1836,14 +1858,14 @@ used inside of user-defined commands that should not be repeated when\n\
hitting return."));
add_setshow_boolean_cmd ("editing", class_support,
&async_command_editing_p, _("\
&set_editing_cmd_var, _("\
Set editing of command lines as they are typed."), _("\
Show editing of command lines as they are typed."), _("\
Use \"on\" to enable the editing, and \"off\" to disable it.\n\
Without an argument, command line editing is enabled. To edit, use\n\
EMACS-like or VI-like commands like control-P or ESC."),
set_async_editing_command,
show_async_command_editing_p,
set_editing,
show_editing,
&setlist, &showlist);
add_setshow_boolean_cmd ("save", no_class, &write_history_p, _("\