Add <c-g> 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().
This commit is contained in:
Maxime Coste 2023-05-15 21:10:02 +10:00
parent e140df8f08
commit cfa658b899
7 changed files with 56 additions and 19 deletions

View File

@ -53,6 +53,12 @@ Client::Client(std::unique_ptr<UserInterface>&& ui,
killpg(getpgrp(), SIGINT); killpg(getpgrp(), SIGINT);
set_signal_handler(SIGINT, prev_handler); 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) else if (key.modifiers & Key::Modifiers::Resize)
{ {
m_window->set_dimensions(key.coord()); m_window->set_dimensions(key.coord());
@ -110,7 +116,7 @@ bool Client::process_pending_inputs()
catch (Kakoune::runtime_error& error) catch (Kakoune::runtime_error& error)
{ {
write_to_debug_buffer(format("Error: {}", error.what())); 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()); context().hooks().run_hook(Hook::RuntimeError, error.what(), context());
} }
} }

View File

@ -161,6 +161,13 @@ void EventManager::force_signal(int fd)
m_has_forced_fd = true; 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) SignalHandler set_signal_handler(int signum, SignalHandler handler)
{ {
struct sigaction new_action, old_action; struct sigaction new_action, old_action;
@ -171,4 +178,5 @@ SignalHandler set_signal_handler(int signum, SignalHandler handler)
sigaction(signum, &new_action, &old_action); sigaction(signum, &new_action, &old_action);
return old_action.sa_handler; return old_action.sa_handler;
} }
} }

View File

@ -96,6 +96,8 @@ public:
// on next handle_next_events call. // on next handle_next_events call.
void force_signal(int fd); void force_signal(int fd);
static void handle_urgent_events();
private: private:
friend class FDWatcher; friend class FDWatcher;
friend class Timer; friend class Timer;

View File

@ -29,6 +29,11 @@ struct failure : runtime_error
using runtime_error::runtime_error; using runtime_error::runtime_error;
}; };
struct cancel : runtime_error
{
cancel() : runtime_error("cancellation requested") {}
};
struct logic_error : exception struct logic_error : exception
{ {
}; };

View File

@ -894,12 +894,17 @@ int run_server(StringView session, StringView server_init,
// Loop so that eventual inputs happening during the processing are handled as // Loop so that eventual inputs happening during the processing are handled as
// well, avoiding unneeded redraws. // well, avoiding unneeded redraws.
bool allow_blocking = not client_manager.has_pending_inputs(); bool allow_blocking = not client_manager.has_pending_inputs();
try
{
while (event_manager.handle_next_events(EventMode::Normal, nullptr, allow_blocking)) while (event_manager.handle_next_events(EventMode::Normal, nullptr, allow_blocking))
{ {
if (client_manager.process_pending_inputs()) if (client_manager.process_pending_inputs())
break; break;
allow_blocking = false; allow_blocking = false;
} }
}
catch (const cancel&) {}
client_manager.process_pending_inputs(); client_manager.process_pending_inputs();
client_manager.clear_client_trash(); client_manager.clear_client_trash();

View File

@ -1159,7 +1159,7 @@ void keep(Context& context, NormalParams params)
const auto flags = match_flags(is_bol(begin.coord()), false, const auto flags = match_flags(is_bol(begin.coord()), false,
is_bow(buffer, begin.coord()), is_bow(buffer, begin.coord()),
is_eow(buffer, end.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); keep.push_back(sel);
} }
if (keep.empty()) if (keep.empty())

View File

@ -2,6 +2,7 @@
#include "buffer_utils.hh" #include "buffer_utils.hh"
#include "context.hh" #include "context.hh"
#include "event_manager.hh"
#include "flags.hh" #include "flags.hh"
#include "option_types.hh" #include "option_types.hh"
#include "regex.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 // When on the token of a non-nestable block, we want to consider it opening
if (nestable and if (nestable and
backward_regex_search(container.begin(), pos, 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) res[0].second == pos)
pos = res[0].first; pos = res[0].first;
using RegexIt = RegexIterator<Iterator, RegexMode::Backward>; using RegexIt = RegexIterator<Iterator, RegexMode::Backward, const Regex, void (*)()>;
for (auto&& match : RegexIt{container.begin(), pos, container.begin(), container.end(), opening}) for (auto&& match : RegexIt{container.begin(), pos, container.begin(), container.end(), opening,
RegexExecFlags::None, EventManager::handle_urgent_events})
{ {
if (nestable) 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; ++level;
} }
@ -314,12 +318,13 @@ find_closing(Iterator pos, const Container& container,
const Regex& opening, const Regex& closing, const Regex& opening, const Regex& closing,
int level, bool nestable) int level, bool nestable)
{ {
using RegexIt = RegexIterator<Iterator, RegexMode::Forward>; for (auto match : RegexIterator{pos, container.end(), container.begin(), container.end(), closing,
for (auto match : RegexIt{pos, container.end(), container.begin(), container.end(), closing}) RegexExecFlags::None, EventManager::handle_urgent_events})
{ {
if (nestable) 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; ++level;
} }
@ -356,7 +361,8 @@ find_surrounding(const Container& container, Iterator pos,
return {}; return {};
} }
else if (MatchResults<Iterator> res; else if (MatchResults<Iterator> 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 res[0].first == pos) // Skip opening match if pos lies on it
last = empty(res[0]) ? std::next(res[0].second) : res[0].second; 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 if (pos != buffer.end() and
regex_search(pos, buffer.end(), buffer.begin(), buffer.end(), 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; return true;
wrapped = true; wrapped = true;
return regex_search(buffer.begin(), buffer.end(), buffer.begin(), buffer.end(), 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, 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(), backward_regex_search(buffer.begin(), pos, buffer.begin(), buffer.end(),
matches, ex, matches, ex,
match_flags(buffer, buffer.begin(), pos) | match_flags(buffer, buffer.begin(), pos) |
RegexExecFlags::NotInitialNull)) RegexExecFlags::NotInitialNull,
EventManager::handle_urgent_events))
return true; return true;
wrapped = true; wrapped = true;
return backward_regex_search(buffer.begin(), buffer.end(), buffer.begin(), buffer.end(), return backward_regex_search(buffer.begin(), buffer.end(), buffer.begin(), buffer.end(),
matches, ex, matches, ex,
match_flags(buffer, buffer.begin(), buffer.end()) | match_flags(buffer, buffer.begin(), buffer.end()) |
RegexExecFlags::NotInitialNull); RegexExecFlags::NotInitialNull,
EventManager::handle_urgent_events);
} }
template<RegexMode mode> template<RegexMode mode>
@ -935,7 +945,8 @@ Vector<Selection> select_matches(const Buffer& buffer, ConstArrayView<Selection>
auto sel_beg = buffer.iterator_at(sel.min()); auto sel_beg = buffer.iterator_at(sel.min());
auto sel_end = utf8::next(buffer.iterator_at(sel.max()), buffer.end()); 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]; auto capture = match[capture_idx];
if (not capture.matched or capture.first == sel_end) if (not capture.matched or capture.first == sel_end)