diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 696a5938f7..8cd835d5be 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,12 @@ +2015-06-26 Patrick Palka + + * NEWS: Mention the new option "history remove-duplicates". + * top.c (history_remove_duplicates): New static variable. + (show_history_remove_duplicates): New static function. + (gdb_add_history): Conditionally remove duplicate history + entries. + (init_main): Add "history remove-duplicates" option. + 2015-06-26 Patrick Palka * tui/tui-win.c (focus_completer): New static function. diff --git a/gdb/NEWS b/gdb/NEWS index 3ec58518a2..6d29004581 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -145,6 +145,10 @@ show max-completions to avoid generating large completion lists, the computation of which can cause the debugger to become temporarily unresponsive. +set history remove-duplicates +show history remove-duplicates + Control the removal of duplicate history entries. + maint set symbol-cache-size maint show symbol-cache-size Control the size of the symbol cache. diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index 6d86750852..e0beb7f0e3 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,8 @@ +2015-06-26 Patrick Palka + + * gdb.texinfo (Command History): Document the new option + "history remove-duplicates". + 2015-06-19 Doug Evans * stabs.texinfo (ELF Linker Relocation): Mention Sun stabs is no diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index c9a532a366..20a95632a7 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -22644,6 +22644,21 @@ to 256 if this variable is not set. Non-numeric values of @env{GDBHISTSIZE} are ignored. If @var{size} is @code{unlimited} or if @env{GDBHISTSIZE} is either a negative number or the empty string, then the number of commands @value{GDBN} keeps in the history list is unlimited. + +@cindex remove duplicate history +@kindex set history remove-duplicates +@item set history remove-duplicates @var{count} +@itemx set history remove-duplicates unlimited +Control the removal of duplicate history entries in the command history list. +If @var{count} is non-zero, @value{GDBN} will look back at the last @var{count} +history entries and remove the first entry that is a duplicate of the current +entry being added to the command history list. If @var{count} is +@code{unlimited} then this lookbehind is unbounded. If @var{count} is 0, then +removal of duplicate history entries is disabled. + +Only history entries added during the current session are considered for +removal. This option is set to 0 by default. + @end table History expansion assigns special meaning to the character @kbd{!}. diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index ce792be6f8..af7f131ac5 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2015-06-26 Patrick Palka + + * gdb.base/history-duplicates.exp: New test. + 2015-06-26 Patrick Palka * gdb.base/completion.exp: Test the completion of the "focus" diff --git a/gdb/testsuite/gdb.base/history-duplicates.exp b/gdb/testsuite/gdb.base/history-duplicates.exp new file mode 100644 index 0000000000..11bb1ed9ca --- /dev/null +++ b/gdb/testsuite/gdb.base/history-duplicates.exp @@ -0,0 +1,117 @@ +# Copyright 2015 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# This file is part of the gdb testsuite. + +# Test the operation of the "history remove-duplicates" option. + + +# Check that the previous history entry is ENTRY. + +proc check_prev_history_entry { entry { test_suffix "" } } { + set test_name "history entry is $entry" + if { $test_suffix != "" } { + append test_name " $test_suffix" + } + + # Send ^P followed by ^L. + send_gdb "\x10\x0c" + + gdb_expect { + -re $entry { + pass $test_name + } + timeout { + fail $test_name + } + } +} + +# Foreach element ELT in THINGS, run the command "print $ELT", making sure that +# each invocation of "print" has a unique test name. + +proc run_print_on_each_thing { things } { + set index 0 + + foreach thing $things { + gdb_test "print $thing" "" "printing $thing (item #$index)" + incr index + } +} + +# By default the option is set to 0. +gdb_exit +gdb_start +gdb_test "show history remove-duplicates" "is 0\\." + +# Test the "unlimited" setting. +with_test_prefix "remove-duplicates=unlimited" { + gdb_exit + gdb_start + gdb_test "set history remove-duplicates unlimited" + + run_print_on_each_thing { 0 1 2 1 1 2 3 3 4 1 2 3 4 } + + check_prev_history_entry "print 4" + check_prev_history_entry "print 3" + check_prev_history_entry "print 2" + check_prev_history_entry "print 1" + check_prev_history_entry "print 0" +} + + +# Test the "1" setting. +with_test_prefix "remove-duplicates=1" { + gdb_exit + gdb_start + gdb_test "set history remove-duplicates 1" + + run_print_on_each_thing { 0 1 0 2 2 1 } + + check_prev_history_entry "print 1" + check_prev_history_entry "print 2" + check_prev_history_entry "print 0" + check_prev_history_entry "print 1" "(again)" + check_prev_history_entry "print 0" "(again)" +} + + +# Test the "0" setting. +with_test_prefix "remove-duplicates=0" { + gdb_exit + gdb_start + gdb_test "set history remove-duplicates 0" + + run_print_on_each_thing { 0 0 1 1 } + + check_prev_history_entry "print 1" + check_prev_history_entry "print 1" "(again)" + check_prev_history_entry "print 0" + check_prev_history_entry "print 0" "(again)" +} + + +# Test the "2" setting. +with_test_prefix "remove-duplicates=2" { + gdb_exit + gdb_start + gdb_test "set history remove-duplicates 2" + + run_print_on_each_thing { 1 2 0 2 0 } + + check_prev_history_entry "print 0" + check_prev_history_entry "print 2" + check_prev_history_entry "print 1" +} diff --git a/gdb/top.c b/gdb/top.c index 77fe096119..01fddd2c95 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -698,6 +698,20 @@ show_history_size (struct ui_file *file, int from_tty, value); } +/* Variable associated with the "history remove-duplicates" option. + The value -1 means unlimited. */ +static int history_remove_duplicates = 0; + +static void +show_history_remove_duplicates (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, + _("The number of history entries to look back at for " + "duplicates is %s.\n"), + value); +} + static char *history_filename; static void show_history_filename (struct ui_file *file, int from_tty, @@ -897,8 +911,43 @@ static int command_count = 0; void gdb_add_history (const char *command) { - add_history (command); command_count++; + + if (history_remove_duplicates != 0) + { + int lookbehind; + int lookbehind_threshold; + + /* The lookbehind threshold for finding a duplicate history entry is + bounded by command_count because we can't meaningfully delete + history entries that are already stored in the history file since + the history file is appended to. */ + if (history_remove_duplicates == -1 + || history_remove_duplicates > command_count) + lookbehind_threshold = command_count; + else + lookbehind_threshold = history_remove_duplicates; + + using_history (); + for (lookbehind = 0; lookbehind < lookbehind_threshold; lookbehind++) + { + HIST_ENTRY *temp = previous_history (); + + if (temp == NULL) + break; + + if (strcmp (temp->line, command) == 0) + { + HIST_ENTRY *prev = remove_history (where_history ()); + command_count--; + free_history_entry (prev); + break; + } + } + using_history (); + } + + add_history (command); } /* Safely append new history entries to the history file in a corruption-free @@ -1880,6 +1929,21 @@ variable \"GDBHISTSIZE\", or to 256 if this variable is not set."), show_history_size, &sethistlist, &showhistlist); + add_setshow_zuinteger_unlimited_cmd ("remove-duplicates", no_class, + &history_remove_duplicates, _("\ +Set how far back in history to look for and remove duplicate entries."), _("\ +Show how far back in history to look for and remove duplicate entries."), _("\ +If set to a nonzero value N, GDB will look back at the last N history entries\n\ +and remove the first history entry that is a duplicate of the most recent\n\ +entry, each time a new history entry is added.\n\ +If set to \"unlimited\", this lookbehind is unbounded.\n\ +Only history entries added during this session are considered for removal.\n\ +If set to 0, removal of duplicate history entries is disabled.\n\ +By default this option is set to 0."), + NULL, + show_history_remove_duplicates, + &sethistlist, &showhistlist); + add_setshow_filename_cmd ("filename", no_class, &history_filename, _("\ Set the filename in which to record the command history"), _("\ Show the filename in which to record the command history"), _("\