diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5c80392bb..c9e36aa0c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -112,6 +112,7 @@ Interactive improvements - ``diff`` will now colourise output, if supported (#7308). - The command-not-found handling has been simplified. When it can't find a command, fish now just executes a function called ``fish_command_not_found`` instead of firing an event, making it easier to replace and reason about. Shims for backwards-compatibility have been added (#7293). - Control-C no longer occasionally prints an "unknown command" error (#7145). +- History search is now case-insensitive unless the search string contains an uppercase character (#7273). New or improved bindings diff --git a/doc_src/index.rst b/doc_src/index.rst index 0a51559fe..317f57190 100644 --- a/doc_src/index.rst +++ b/doc_src/index.rst @@ -1718,7 +1718,7 @@ After a command has been executed, it is remembered in the history list. Any dup By pressing :kbd:`Alt`\ +\ :kbd:`↑` and :kbd:`Alt`\ +\ :kbd:`↓`, a history search is also performed, but instead of searching for a complete commandline, each commandline is broken into separate elements just like it would be before execution, and the history is searched for an element matching that under the cursor. -History searches can be aborted by pressing the escape key. +History searches are case-insensitive unless the search string contains an uppercase character, and they can be aborted by pressing the escape key. Prefixing the commandline with a space will prevent the entire line from being stored in the history. diff --git a/src/reader.cpp b/src/reader.cpp index ed7903337..d6c7d9b9c 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -398,11 +398,15 @@ class reader_history_search_t { matches_ = {text}; match_index_ = 0; mode_ = mode; + history_search_flags_t flags = history_search_no_dedup; + // Make the search case-insensitive unless we have an uppercase character. + wcstring low = wcstolower(text); + if (low == text) flags |= history_search_ignore_case; // We can skip dedup in history_search_t because we do it ourselves in skips_. search_ = history_search_t( *hist, text, by_prefix() ? history_search_type_t::prefix : history_search_type_t::contains, - history_search_no_dedup); + flags); } /// Reset to inactive search. @@ -935,7 +939,7 @@ void reader_data_t::paint_layout(const wchar_t *reason) { if (!conf.in_silent_mode && !data.history_search_text.empty()) { const wcstring &needle = data.history_search_text; const wcstring &haystack = cmd_line->text(); - size_t match_pos = haystack.find(needle); + size_t match_pos = ifind(haystack,needle); if (match_pos != wcstring::npos) { for (size_t i = 0; i < needle.size(); i++) { colors.at(match_pos + i).background = highlight_role_t::search_match; diff --git a/tests/pexpects/history.py b/tests/pexpects/history.py index 551e0c226..c1aecb7be 100644 --- a/tests/pexpects/history.py +++ b/tests/pexpects/history.py @@ -132,3 +132,13 @@ expect_re("count again 1\r\n") # Verify that the $history var has the expected content. sendline("echo history2=$history\[2\]") expect_re("history2=echo count AGAIN .*\r\n") + +# Verify that history search is case-insensitive by default +sendline("echo term") +expect_str("term") +sendline("echo TERM") +expect_str("TERM") +sendline("echo banana") +expect_str("banana") +send("ter\x1b[A") # up-arrow +expect_re("echo TERM")