From cfa658b899b44a8a0f4c7f3dd9811ae0d44ddd3a Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Mon, 15 May 2023 21:10:02 +1000 Subject: [PATCH] Add to cancel current operation The current implementation only does this during regex operations, but should be extensible to other operations that might take a long time by regularly calling EventManager::handle_urgent_events(). --- src/client.cc | 8 +++++++- src/event_manager.cc | 8 ++++++++ src/event_manager.hh | 2 ++ src/exception.hh | 5 +++++ src/main.cc | 13 +++++++++---- src/normal.cc | 2 +- src/selectors.cc | 37 ++++++++++++++++++++++++------------- 7 files changed, 56 insertions(+), 19 deletions(-) diff --git a/src/client.cc b/src/client.cc index f022c699..33285b95 100644 --- a/src/client.cc +++ b/src/client.cc @@ -53,6 +53,12 @@ Client::Client(std::unique_ptr&& ui, killpg(getpgrp(), SIGINT); set_signal_handler(SIGINT, prev_handler); } + else if (key == ctrl('g')) + { + m_pending_keys.clear(); + print_status({"operation cancelled", context().faces()["Error"]}); + throw cancel{}; + } else if (key.modifiers & Key::Modifiers::Resize) { m_window->set_dimensions(key.coord()); @@ -110,7 +116,7 @@ bool Client::process_pending_inputs() catch (Kakoune::runtime_error& error) { write_to_debug_buffer(format("Error: {}", error.what())); - context().print_status({error.what().str(), context().faces()["Error"] }); + context().print_status({error.what().str(), context().faces()["Error"]}); context().hooks().run_hook(Hook::RuntimeError, error.what(), context()); } } diff --git a/src/event_manager.cc b/src/event_manager.cc index 891780b2..60c16eae 100644 --- a/src/event_manager.cc +++ b/src/event_manager.cc @@ -161,6 +161,13 @@ void EventManager::force_signal(int fd) m_has_forced_fd = true; } +void EventManager::handle_urgent_events() +{ + if (has_instance()) + instance().handle_next_events(EventMode::Urgent, nullptr, false); +} + + SignalHandler set_signal_handler(int signum, SignalHandler handler) { struct sigaction new_action, old_action; @@ -171,4 +178,5 @@ SignalHandler set_signal_handler(int signum, SignalHandler handler) sigaction(signum, &new_action, &old_action); return old_action.sa_handler; } + } diff --git a/src/event_manager.hh b/src/event_manager.hh index edf52e3e..68f6dee3 100644 --- a/src/event_manager.hh +++ b/src/event_manager.hh @@ -96,6 +96,8 @@ public: // on next handle_next_events call. void force_signal(int fd); + static void handle_urgent_events(); + private: friend class FDWatcher; friend class Timer; diff --git a/src/exception.hh b/src/exception.hh index 0ff3d825..28f41029 100644 --- a/src/exception.hh +++ b/src/exception.hh @@ -29,6 +29,11 @@ struct failure : runtime_error using runtime_error::runtime_error; }; +struct cancel : runtime_error +{ + cancel() : runtime_error("cancellation requested") {} +}; + struct logic_error : exception { }; diff --git a/src/main.cc b/src/main.cc index 609fea47..e49a1d0d 100644 --- a/src/main.cc +++ b/src/main.cc @@ -894,12 +894,17 @@ int run_server(StringView session, StringView server_init, // Loop so that eventual inputs happening during the processing are handled as // well, avoiding unneeded redraws. bool allow_blocking = not client_manager.has_pending_inputs(); - while (event_manager.handle_next_events(EventMode::Normal, nullptr, allow_blocking)) + try { - if (client_manager.process_pending_inputs()) - break; - allow_blocking = false; + while (event_manager.handle_next_events(EventMode::Normal, nullptr, allow_blocking)) + { + if (client_manager.process_pending_inputs()) + break; + allow_blocking = false; + } } + catch (const cancel&) {} + client_manager.process_pending_inputs(); client_manager.clear_client_trash(); diff --git a/src/normal.cc b/src/normal.cc index 4c6e0203..f1123ca7 100644 --- a/src/normal.cc +++ b/src/normal.cc @@ -1159,7 +1159,7 @@ void keep(Context& context, NormalParams params) const auto flags = match_flags(is_bol(begin.coord()), false, is_bow(buffer, begin.coord()), is_eow(buffer, end.coord())); - if (regex_search(begin, end, begin, end, regex, flags) == matching) + if (regex_search(begin, end, begin, end, regex, flags, EventManager::handle_urgent_events) == matching) keep.push_back(sel); } if (keep.empty()) diff --git a/src/selectors.cc b/src/selectors.cc index cf631e72..e3cba87d 100644 --- a/src/selectors.cc +++ b/src/selectors.cc @@ -2,6 +2,7 @@ #include "buffer_utils.hh" #include "context.hh" +#include "event_manager.hh" #include "flags.hh" #include "option_types.hh" #include "regex.hh" @@ -287,16 +288,19 @@ find_opening(Iterator pos, const Container& container, // When on the token of a non-nestable block, we want to consider it opening if (nestable and backward_regex_search(container.begin(), pos, - container.begin(), container.end(), res, closing) and + container.begin(), container.end(), res, closing, + RegexExecFlags::None, EventManager::handle_urgent_events) and res[0].second == pos) pos = res[0].first; - using RegexIt = RegexIterator; - for (auto&& match : RegexIt{container.begin(), pos, container.begin(), container.end(), opening}) + using RegexIt = RegexIterator; + for (auto&& match : RegexIt{container.begin(), pos, container.begin(), container.end(), opening, + RegexExecFlags::None, EventManager::handle_urgent_events}) { if (nestable) { - for (auto m [[maybe_unused]] : RegexIt{match[0].second, pos, container.begin(), container.end(), closing}) + for (auto m [[maybe_unused]] : RegexIt{match[0].second, pos, container.begin(), container.end(), closing, + RegexExecFlags::None, EventManager::handle_urgent_events}) ++level; } @@ -314,12 +318,13 @@ find_closing(Iterator pos, const Container& container, const Regex& opening, const Regex& closing, int level, bool nestable) { - using RegexIt = RegexIterator; - for (auto match : RegexIt{pos, container.end(), container.begin(), container.end(), closing}) + for (auto match : RegexIterator{pos, container.end(), container.begin(), container.end(), closing, + RegexExecFlags::None, EventManager::handle_urgent_events}) { if (nestable) { - for (auto m [[maybe_unused]] : RegexIt{pos, match[0].first, container.begin(), container.end(), opening}) + for (auto m [[maybe_unused]] : RegexIterator{pos, match[0].first, container.begin(), container.end(), opening, + RegexExecFlags::None, EventManager::handle_urgent_events}) ++level; } @@ -356,7 +361,8 @@ find_surrounding(const Container& container, Iterator pos, return {}; } else if (MatchResults res; - regex_search(pos, container.end(), container.begin(), container.end(), res, opening) and + regex_search(pos, container.end(), container.begin(), container.end(), res, opening, + RegexExecFlags::None, EventManager::handle_urgent_events) and res[0].first == pos) // Skip opening match if pos lies on it last = empty(res[0]) ? std::next(res[0].second) : res[0].second; @@ -868,11 +874,13 @@ static bool find_next(const Buffer& buffer, const BufferIterator& pos, { if (pos != buffer.end() and regex_search(pos, buffer.end(), buffer.begin(), buffer.end(), - matches, ex, match_flags(buffer, pos, buffer.end()))) + matches, ex, match_flags(buffer, pos, buffer.end()), + EventManager::handle_urgent_events)) return true; wrapped = true; return regex_search(buffer.begin(), buffer.end(), buffer.begin(), buffer.end(), - matches, ex, match_flags(buffer, buffer.begin(), buffer.end())); + matches, ex, match_flags(buffer, buffer.begin(), buffer.end()), + EventManager::handle_urgent_events); } static bool find_prev(const Buffer& buffer, const BufferIterator& pos, @@ -883,13 +891,15 @@ static bool find_prev(const Buffer& buffer, const BufferIterator& pos, backward_regex_search(buffer.begin(), pos, buffer.begin(), buffer.end(), matches, ex, match_flags(buffer, buffer.begin(), pos) | - RegexExecFlags::NotInitialNull)) + RegexExecFlags::NotInitialNull, + EventManager::handle_urgent_events)) return true; wrapped = true; return backward_regex_search(buffer.begin(), buffer.end(), buffer.begin(), buffer.end(), matches, ex, match_flags(buffer, buffer.begin(), buffer.end()) | - RegexExecFlags::NotInitialNull); + RegexExecFlags::NotInitialNull, + EventManager::handle_urgent_events); } template @@ -935,7 +945,8 @@ Vector select_matches(const Buffer& buffer, ConstArrayView auto sel_beg = buffer.iterator_at(sel.min()); auto sel_end = utf8::next(buffer.iterator_at(sel.max()), buffer.end()); - for (auto&& match : RegexIterator{sel_beg, sel_end, vm, match_flags(buffer, sel_beg, sel_end)}) + for (auto&& match : RegexIterator{sel_beg, sel_end, vm, match_flags(buffer, sel_beg, sel_end), + EventManager::handle_urgent_events}) { auto capture = match[capture_idx]; if (not capture.matched or capture.first == sel_end)