handle multiline commands in history search output

Fixes #31
This commit is contained in:
Kurtis Rader 2016-10-16 20:33:33 -07:00
parent bff6a6e66a
commit a26f68d63f
5 changed files with 29 additions and 13 deletions

View file

@ -2,7 +2,7 @@
\subsection history-synopsis Synopsis
\fish{synopsis}
history search [ --show-time ] [ --case-sensitive ] [ --exact | --prefix | --contains ] [ --max=n ] [ "search string"... ]
history search [ --show-time ] [ --case-sensitive ] [ --exact | --prefix | --contains ] [ --max=n ] [ --null ] [ "search string"... ]
history delete [ --show-time ] [ --case-sensitive ] [ --exact | --prefix | --contains ] "search string"...
history merge
history save
@ -42,6 +42,8 @@ These flags can appear before or immediately after one of the sub-commands liste
- `-t` or `--show-time` prepends each history entry with the date and time the entry was recorded . By default it uses the strftime format `# %c%n`. You can specify another format; e.g., `--show-time='%Y-%m-%d %H:%M:%S '` or `--show-time='%a%I%p'`. The short option, `-t` doesn't accept a stftime format string; it only uses the default format. Any strftime format is allowed, including `%s` to get the raw UNIX seconds since the epoch. Note that `--with-time` is also allowed but is deprecated and will be removed at a future date.
- `-z` or `--null` causes history entries written by the search operations to be terminated by a NUL character rather than a newline. This allows the output to be processed by `read -z` to correctly handle multiline history entries.
- `-<number>` `-n <number>` or `--max=<number>` limits the matched history items to the first "n" matching entries. This is only valid for `history search`.
- `-h` or `--help` display help for this command.

View file

@ -36,6 +36,7 @@ function history --description "display or manipulate interactive command histor
set -l show_time
set -l max_count
set -l case_sensitive
set -l null
# Check for a recognized subcommand as the first argument.
if set -q argv[1]
@ -82,6 +83,8 @@ function history --description "display or manipulate interactive command histor
set search_mode --contains
case -e --exact
set search_mode --exact
case -z --null
set null --null
case -n --max
if string match -- '-n?*' $argv[1]
or string match -- '--max=*' $argv[1]
@ -128,9 +131,9 @@ function history --description "display or manipulate interactive command histor
set -l pager less
set -q PAGER
and set pager $PAGER
builtin history search $search_mode $show_time $max_count $case_sensitive -- $argv | eval $pager
builtin history search $search_mode $show_time $max_count $case_sensitive $null -- $argv | eval $pager
else
builtin history search $search_mode $show_time $max_count $case_sensitive -- $argv
builtin history search $search_mode $show_time $max_count $case_sensitive $null -- $argv
end
case delete # interactively delete history
@ -150,7 +153,10 @@ function history --description "display or manipulate interactive command histor
# TODO: Fix this so that requesting history entries with a timestamp works:
# set -l found_items (builtin history search $search_mode $show_time -- $argv)
set -l found_items (builtin history search $search_mode $case_sensitive -- $argv)
set -l found_items
builtin history search $search_mode $case_sensitive --null -- $argv | while read -lz x
set found_items $found_items $x
end
if set -q found_items[1]
set -l found_items_count (count $found_items)
for i in (seq $found_items_count)

View file

@ -2853,7 +2853,7 @@ static bool set_hist_cmd(wchar_t *const cmd, hist_cmd_t *hist_cmd, hist_cmd_t su
}
#define CHECK_FOR_UNEXPECTED_HIST_ARGS(hist_cmd) \
if (history_search_type_defined || show_time_format) { \
if (history_search_type_defined || show_time_format || null_terminate) { \
streams.err.append_format(_(L"%ls: you cannot use any options with the %ls command\n"), \
cmd, hist_cmd_to_string(hist_cmd).c_str()); \
status = STATUS_BUILTIN_ERROR; \
@ -2876,10 +2876,11 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
bool history_search_type_defined = false;
const wchar_t *show_time_format = NULL;
bool case_sensitive = false;
bool null_terminate = false;
// TODO: Remove the long options that correspond to subcommands (e.g., '--delete') on or after
// 2017-10 (which will be a full year after these flags have been deprecated).
const wchar_t *short_options = L":Cmn:epcht";
const wchar_t *short_options = L":Cmn:epchtz";
const struct woption long_options[] = {{L"prefix", no_argument, NULL, 'p'},
{L"contains", no_argument, NULL, 'c'},
{L"help", no_argument, NULL, 'h'},
@ -2887,6 +2888,7 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
{L"with-time", optional_argument, NULL, 't'},
{L"exact", no_argument, NULL, 'e'},
{L"max", required_argument, NULL, 'n'},
{L"null", no_argument, 0, 'z'},
{L"case-sensitive", no_argument, 0, 'C'},
{L"delete", no_argument, NULL, 1},
{L"search", no_argument, NULL, 2},
@ -2967,6 +2969,10 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
}
break;
}
case 'z': {
null_terminate = true;
break;
}
case 'h': {
builtin_print_help(parser, streams, cmd, streams.out);
return STATUS_BUILTIN_OK;
@ -3021,7 +3027,7 @@ static int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **ar
switch (hist_cmd) {
case HIST_SEARCH: {
if (!history->search(search_type, args, show_time_format, max_items, case_sensitive,
streams)) {
null_terminate, streams)) {
status = STATUS_BUILTIN_ERROR;
}
break;

View file

@ -1436,7 +1436,7 @@ void history_t::save(void) {
// Formats a single history record, including a trailing newline. Returns true
// if bytes were written to the output stream and false otherwise.
static bool format_history_record(const history_item_t &item, const wchar_t *show_time_format,
io_streams_t &streams) {
bool null_terminate, io_streams_t &streams) {
if (show_time_format) {
const time_t seconds = item.timestamp();
struct tm timestamp;
@ -1449,18 +1449,19 @@ static bool format_history_record(const history_item_t &item, const wchar_t *sho
streams.out.append(timestamp_string);
}
streams.out.append(item.str());
streams.out.append(L"\n");
streams.out.append(null_terminate ? L'\0' : L'\n');
return true;
}
bool history_t::search(history_search_type_t search_type, wcstring_list_t search_args,
const wchar_t *show_time_format, long max_items, bool case_sensitive,
io_streams_t &streams) {
bool null_terminate, io_streams_t &streams) {
// scoped_lock locker(lock);
if (search_args.empty()) {
// Start at one because zero is the current command.
for (int i = 1; !this->item_at_index(i).empty() && max_items; ++i, --max_items) {
if (!format_history_record(this->item_at_index(i), show_time_format, streams)) {
if (!format_history_record(this->item_at_index(i), show_time_format, null_terminate,
streams)) {
return false;
}
}
@ -1477,7 +1478,8 @@ bool history_t::search(history_search_type_t search_type, wcstring_list_t search
history_search_t searcher =
history_search_t(*this, search_string, search_type, case_sensitive);
while (searcher.go_backwards()) {
if (!format_history_record(searcher.current_item(), show_time_format, streams)) {
if (!format_history_record(searcher.current_item(), show_time_format, null_terminate,
streams)) {
return false;
}
if (--max_items == 0) return true;

View file

@ -230,7 +230,7 @@ class history_t {
// Searches history.
bool search(history_search_type_t search_type, wcstring_list_t search_args,
const wchar_t *show_time_format, long max_items, bool case_sensitive,
io_streams_t &streams);
bool null_terminate, io_streams_t &streams);
// Enable / disable automatic saving. Main thread only!
void disable_automatic_saving();