From 14d4980e26f434ee6093b2979420fff74ec11019 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Tue, 22 Nov 2022 15:59:03 +0100 Subject: [PATCH 1/3] Fix repainting of the client prompt after fuzzy search Signed-off-by: Azat Khuzhin --- base/base/ReplxxLineReader.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/base/base/ReplxxLineReader.cpp b/base/base/ReplxxLineReader.cpp index e0dc81af5b0..569fcf3af1f 100644 --- a/base/base/ReplxxLineReader.cpp +++ b/base/base/ReplxxLineReader.cpp @@ -420,6 +420,7 @@ ReplxxLineReader::ReplxxLineReader( auto interactive_history_search = [this](char32_t code) { openInteractiveHistorySearch(); + rx.invoke(Replxx::ACTION::CLEAR_SELF, code); return rx.invoke(Replxx::ACTION::REPAINT, code); }; rx.bind_key(Replxx::KEY::control('R'), interactive_history_search); From 59e647077d7f3537272e5c2bb361d0eff38397ef Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Tue, 22 Nov 2022 15:53:56 +0100 Subject: [PATCH 2/3] Improve fuzzy search in clickhouse-client/clickhouse-local I found few difference in sk/fzf and after this patch I think that it should behave better. So the following commands will be used by default: - fzf --read0 --tac --tiebreak=index --height=30% - sk --read0 --tac --tiebreak=-score --height=30% Those two more or less allows true fuzzy reserve search. Signed-off-by: Azat Khuzhin --- base/base/ReplxxLineReader.cpp | 30 +++++++++++++++++++++--------- base/base/ReplxxLineReader.h | 9 +++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/base/base/ReplxxLineReader.cpp b/base/base/ReplxxLineReader.cpp index 569fcf3af1f..03fffaf3feb 100644 --- a/base/base/ReplxxLineReader.cpp +++ b/base/base/ReplxxLineReader.cpp @@ -38,7 +38,7 @@ std::string getEditor() return editor; } -std::string getFuzzyFinder() +std::pair getFuzzyFinder() { const char * env_path = std::getenv("PATH"); // NOLINT(concurrency-mt-unsafe) @@ -52,14 +52,14 @@ std::string getFuzzyFinder() std::filesystem::path path(path_str); std::filesystem::path sk_bin_path = path / "sk"; if (!access(sk_bin_path.c_str(), X_OK)) - return sk_bin_path; + return {sk_bin_path, FUZZY_FINDER_SKIM}; std::filesystem::path fzf_bin_path = path / "fzf"; if (!access(fzf_bin_path.c_str(), X_OK)) - return fzf_bin_path; + return {fzf_bin_path, FUZZY_FINDER_FZF}; } - return {}; + return {"", FUZZY_FINDER_NONE}; } /// See comments in ShellCommand::executeImpl() @@ -305,11 +305,12 @@ ReplxxLineReader::ReplxxLineReader( replxx::Replxx::highlighter_callback_t highlighter_) : LineReader(history_file_path_, multiline_, std::move(extenders_), std::move(delimiters_)), highlighter(std::move(highlighter_)) , editor(getEditor()) - , fuzzy_finder(getFuzzyFinder()) { using namespace std::placeholders; using Replxx = replxx::Replxx; + std::tie(fuzzy_finder, fuzzy_finder_type) = getFuzzyFinder(); + if (!history_file_path.empty()) { history_file_fd = open(history_file_path.c_str(), O_RDWR); @@ -415,7 +416,7 @@ ReplxxLineReader::ReplxxLineReader( rx.bind_key(Replxx::KEY::meta('#'), insert_comment_action); /// interactive search in history (requires fzf/sk) - if (!fuzzy_finder.empty()) + if (fuzzy_finder_type != FUZZY_FINDER_NONE) { auto interactive_history_search = [this](char32_t code) { @@ -516,9 +517,20 @@ void ReplxxLineReader::openInteractiveHistorySearch() /// /// And also note, that fzf and skim is 95% compatible (at least option /// that is used here) - std::string fuzzy_finder_command = fmt::format( - "{} --read0 --tac --no-sort --tiebreak=index --bind=ctrl-r:toggle-sort --height=30% < {} > {}", - fuzzy_finder, history_file.getPath(), output_file.getPath()); + std::string fuzzy_finder_command = fmt::format("{} --read0 --height=30%", fuzzy_finder); + switch (fuzzy_finder_type) + { + case FUZZY_FINDER_SKIM: + fuzzy_finder_command += " --tac --tiebreak=-score"; + break; + case FUZZY_FINDER_FZF: + fuzzy_finder_command += " --tac --tiebreak=index"; + break; + case FUZZY_FINDER_NONE: + /// assertion for !fuzzy_finder.empty() is enough + break; + } + fuzzy_finder_command += fmt::format(" < {} > {}", history_file.getPath(), output_file.getPath()); char * const argv[] = {sh, sh_c, fuzzy_finder_command.data(), nullptr}; try diff --git a/base/base/ReplxxLineReader.h b/base/base/ReplxxLineReader.h index fea1405a208..9be3b3aa993 100644 --- a/base/base/ReplxxLineReader.h +++ b/base/base/ReplxxLineReader.h @@ -4,6 +4,14 @@ #include +enum FuzzyFinderType +{ + FUZZY_FINDER_NONE, + /// Use https://github.com/junegunn/fzf + FUZZY_FINDER_FZF, + /// Use https://github.com/lotabout/skim + FUZZY_FINDER_SKIM, +}; class ReplxxLineReader : public LineReader { @@ -38,4 +46,5 @@ private: std::string editor; std::string fuzzy_finder; + FuzzyFinderType fuzzy_finder_type = FUZZY_FINDER_NONE; }; From 7a9adbc2859c2469a575fff7817ae432289c68c8 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Tue, 22 Nov 2022 20:06:14 +0100 Subject: [PATCH 3/3] Escape shell arguments for fzf/skim Signed-off-by: Azat Khuzhin --- base/base/ReplxxLineReader.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/base/base/ReplxxLineReader.cpp b/base/base/ReplxxLineReader.cpp index 03fffaf3feb..b86746365b7 100644 --- a/base/base/ReplxxLineReader.cpp +++ b/base/base/ReplxxLineReader.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include /// is_any_of namespace @@ -62,6 +63,12 @@ std::pair getFuzzyFinder() return {"", FUZZY_FINDER_NONE}; } +String escapeShellArgument(std::string arg) +{ + boost::replace_all(arg, "'", "'\\''"); + return fmt::format("'{}'", arg); +} + /// See comments in ShellCommand::executeImpl() /// (for the vfork via dlsym()) int executeCommand(char * const argv[]) @@ -530,7 +537,9 @@ void ReplxxLineReader::openInteractiveHistorySearch() /// assertion for !fuzzy_finder.empty() is enough break; } - fuzzy_finder_command += fmt::format(" < {} > {}", history_file.getPath(), output_file.getPath()); + fuzzy_finder_command += fmt::format(" < {} > {}", + escapeShellArgument(history_file.getPath()), + escapeShellArgument(output_file.getPath())); char * const argv[] = {sh, sh_c, fuzzy_finder_command.data(), nullptr}; try