Separate events between normal and urgent ones

Run urgent ones while executing %sh blocks.

Fixes #236
This commit is contained in:
Maxime Coste 2014-11-25 01:00:18 +00:00
parent 0272da65c0
commit 49931fbf05
13 changed files with 140 additions and 76 deletions

View File

@ -105,7 +105,10 @@ Buffer* create_fifo_buffer(String name, int fd, bool scroll)
ValueId fifo_watcher_id = s_fifo_watcher_id; ValueId fifo_watcher_id = s_fifo_watcher_id;
std::unique_ptr<FDWatcher, decltype(watcher_deleter)> watcher( std::unique_ptr<FDWatcher, decltype(watcher_deleter)> watcher(
new FDWatcher(fd, [buffer, scroll, fifo_watcher_id](FDWatcher& watcher) { new FDWatcher(fd, [buffer, scroll, fifo_watcher_id](FDWatcher& watcher, EventMode mode) {
if (mode != EventMode::Normal)
return;
constexpr size_t buffer_size = 2048; constexpr size_t buffer_size = 2048;
// if we read data slower than it arrives in the fifo, limiting the // if we read data slower than it arrives in the fifo, limiting the
// iteration number allows us to go back go back to the event loop and // iteration number allows us to go back go back to the event loop and

View File

@ -7,8 +7,12 @@
#include "file.hh" #include "file.hh"
#include "remote.hh" #include "remote.hh"
#include "client_manager.hh" #include "client_manager.hh"
#include "event_manager.hh"
#include "window.hh" #include "window.hh"
#include <signal.h>
#include <unistd.h>
namespace Kakoune namespace Kakoune
{ {
@ -34,14 +38,44 @@ Client::~Client()
m_window->options().unregister_watcher(*this); m_window->options().unregister_watcher(*this);
} }
void Client::handle_available_input() void Client::handle_available_input(EventMode mode)
{ {
while (m_ui->is_key_available()) if (mode == EventMode::Normal)
{ {
m_input_handler.handle_key(m_ui->get_key()); try
m_input_handler.clear_mode_trash(); {
for (auto& key : m_pending_keys)
{
m_input_handler.handle_key(key);
m_input_handler.clear_mode_trash();
}
m_pending_keys.clear();
while (m_ui->is_key_available())
{
m_input_handler.handle_key(m_ui->get_key());
m_input_handler.clear_mode_trash();
}
context().window().forget_timestamp();
}
catch (Kakoune::runtime_error& error)
{
context().print_status({ error.what(), get_face("Error") });
context().hooks().run_hook("RuntimeError", error.what(), context());
}
catch (Kakoune::client_removed&)
{
ClientManager::instance().remove_client(*this);
}
}
else
{
Key key = m_ui->get_key();
if (key == ctrl('c'))
killpg(getpgrp(), SIGINT);
else
m_pending_keys.push_back(key);
} }
context().window().forget_timestamp();
} }
void Client::print_status(DisplayLine status_line) void Client::print_status(DisplayLine status_line)

View File

@ -14,6 +14,9 @@ namespace Kakoune
class UserInterface; class UserInterface;
class Window; class Window;
class String; class String;
struct Key;
enum class EventMode;
class Client : public SafeCountable, public OptionManagerWatcher class Client : public SafeCountable, public OptionManagerWatcher
{ {
@ -28,7 +31,7 @@ public:
Client(Client&&) = delete; Client(Client&&) = delete;
// handle all the keys currently available in the user interface // handle all the keys currently available in the user interface
void handle_available_input(); void handle_available_input(EventMode mode);
void print_status(DisplayLine status_line); void print_status(DisplayLine status_line);
@ -64,6 +67,8 @@ private:
DisplayLine m_status_line; DisplayLine m_status_line;
DisplayLine m_pending_status_line; DisplayLine m_pending_status_line;
DisplayLine m_mode_line; DisplayLine m_mode_line;
std::vector<Key> m_pending_keys;
}; };
} }

View File

@ -50,26 +50,19 @@ Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
return nullptr; return nullptr;
} }
client->ui().set_input_callback([client, this]() { client->ui().set_input_callback([client](EventMode mode) {
try client->handle_available_input(mode);
{
client->handle_available_input();
}
catch (Kakoune::runtime_error& error)
{
client->context().print_status({ error.what(), get_face("Error") });
client->context().hooks().run_hook("RuntimeError", error.what(),
client->context());
}
catch (Kakoune::client_removed&)
{
ClientManager::instance().remove_client(*client);
}
}); });
return client; return client;
} }
void ClientManager::handle_available_inputs() const
{
for (auto& client : m_clients)
client->handle_available_input(EventMode::Normal);
}
void ClientManager::remove_client(Client& client) void ClientManager::remove_client(Client& client)
{ {
for (auto it = m_clients.begin(); it != m_clients.end(); ++it) for (auto it = m_clients.begin(); it != m_clients.end(); ++it)

View File

@ -35,6 +35,7 @@ public:
void redraw_clients() const; void redraw_clients() const;
void clear_mode_trashes() const; void clear_mode_trashes() const;
void handle_available_inputs() const;
Client* get_client_ifp(StringView name); Client* get_client_ifp(StringView name);
Client& get_client(StringView name); Client& get_client(StringView name);

View File

@ -16,8 +16,13 @@ FDWatcher::~FDWatcher()
EventManager::instance().m_fd_watchers.erase(this); EventManager::instance().m_fd_watchers.erase(this);
} }
Timer::Timer(TimePoint date, Callback callback) void FDWatcher::run(EventMode mode)
: m_date{date}, m_callback{std::move(callback)} {
m_callback(*this, mode);
}
Timer::Timer(TimePoint date, Callback callback, EventMode mode)
: m_date{date}, m_callback{std::move(callback)}, m_mode(mode)
{ {
if (EventManager::has_instance()) if (EventManager::has_instance())
EventManager::instance().m_timers.insert(this); EventManager::instance().m_timers.insert(this);
@ -29,10 +34,15 @@ Timer::~Timer()
EventManager::instance().m_timers.erase(this); EventManager::instance().m_timers.erase(this);
} }
void Timer::run() void Timer::run(EventMode mode)
{ {
m_date = TimePoint::max(); if (mode & m_mode)
m_callback(*this); {
m_date = TimePoint::max();
m_callback(*this);
}
else // try again a little later
m_date = Clock::now() + std::chrono::milliseconds{10};
} }
EventManager::EventManager() EventManager::EventManager()
@ -46,7 +56,7 @@ EventManager::~EventManager()
kak_assert(m_timers.empty()); kak_assert(m_timers.empty());
} }
void EventManager::handle_next_events() void EventManager::handle_next_events(EventMode mode)
{ {
std::vector<pollfd> events; std::vector<pollfd> events;
events.reserve(m_fd_watchers.size()); events.reserve(m_fd_watchers.size());
@ -76,7 +86,7 @@ void EventManager::handle_next_events()
auto it = find_if(m_fd_watchers, auto it = find_if(m_fd_watchers,
[fd](FDWatcher* w) { return w->fd() == fd; }); [fd](FDWatcher* w) { return w->fd() == fd; });
if (it != m_fd_watchers.end()) if (it != m_fd_watchers.end())
(*it)->run(); (*it)->run(mode);
} }
} }
@ -84,7 +94,7 @@ void EventManager::handle_next_events()
for (auto& timer : m_timers) for (auto& timer : m_timers)
{ {
if (timer->next_date() <= now) if (timer->next_date() <= now)
timer->run(); timer->run(mode);
} }
} }

View File

@ -2,6 +2,7 @@
#define event_manager_hh_INCLUDED #define event_manager_hh_INCLUDED
#include "utils.hh" #include "utils.hh"
#include "flags.hh"
#include <chrono> #include <chrono>
#include <unordered_set> #include <unordered_set>
@ -9,20 +10,28 @@
namespace Kakoune namespace Kakoune
{ {
enum class EventMode
{
Normal = 1 << 0,
Urgent = 1 << 1
};
template<> struct WithBitOps<EventMode> : std::true_type {};
class FDWatcher class FDWatcher
{ {
public: public:
using Callback = std::function<void (FDWatcher& watcher)>; using Callback = std::function<void (FDWatcher& watcher, EventMode mode)>;
FDWatcher(int fd, Callback callback); FDWatcher(int fd, Callback callback);
~FDWatcher(); ~FDWatcher();
int fd() const { return m_fd; } int fd() const { return m_fd; }
void run() { m_callback(*this); } void run(EventMode mode);
private: private:
FDWatcher(const FDWatcher&) = delete; FDWatcher(const FDWatcher&) = delete;
int m_fd; int m_fd;
Callback m_callback; Callback m_callback;
}; };
using Clock = std::chrono::steady_clock; using Clock = std::chrono::steady_clock;
@ -33,15 +42,17 @@ class Timer
public: public:
using Callback = std::function<void (Timer& timer)>; using Callback = std::function<void (Timer& timer)>;
Timer(TimePoint date, Callback callback); Timer(TimePoint date, Callback callback,
EventMode mode = EventMode::Normal);
~Timer(); ~Timer();
TimePoint next_date() const { return m_date; } TimePoint next_date() const { return m_date; }
void set_next_date(TimePoint date) { m_date = date; } void set_next_date(TimePoint date) { m_date = date; }
void run(); void run(EventMode mode);
private: private:
TimePoint m_date; TimePoint m_date;
EventMode m_mode;
Callback m_callback; Callback m_callback;
}; };
@ -56,7 +67,7 @@ public:
EventManager(); EventManager();
~EventManager(); ~EventManager();
void handle_next_events(); void handle_next_events(EventMode mode);
// force the watchers associated with fd to be executed // force the watchers associated with fd to be executed
// on next handle_next_events call. // on next handle_next_events call.

View File

@ -305,7 +305,7 @@ int run_client(StringView session, StringView init_command)
RemoteClient client{session, make_unique<NCursesUI>(), RemoteClient client{session, make_unique<NCursesUI>(),
get_env_vars(), init_command}; get_env_vars(), init_command};
while (true) while (true)
event_manager.handle_next_events(); event_manager.handle_next_events(EventMode::Normal);
} }
catch (peer_disconnected&) catch (peer_disconnected&)
{ {
@ -407,7 +407,8 @@ int run_server(StringView session, StringView init_command,
while (not terminate and (not client_manager.empty() or daemon)) while (not terminate and (not client_manager.empty() or daemon))
{ {
event_manager.handle_next_events(); event_manager.handle_next_events(EventMode::Normal);
client_manager.handle_available_inputs();
client_manager.clear_mode_trashes(); client_manager.clear_mode_trashes();
buffer_manager.clear_buffer_trash(); buffer_manager.clear_buffer_trash();
client_manager.redraw_clients(); client_manager.redraw_clients();

View File

@ -219,8 +219,10 @@ void on_sigint(int)
} }
NCursesUI::NCursesUI() NCursesUI::NCursesUI()
: m_stdin_watcher{0, [this](FDWatcher&){ if (m_input_callback) : m_stdin_watcher{0, [this](FDWatcher&, EventMode mode) {
m_input_callback(); }} if (m_input_callback)
m_input_callback(mode);
}}
{ {
initscr(); initscr();
raw(); raw();

View File

@ -18,9 +18,6 @@
#include "user_interface.hh" #include "user_interface.hh"
#include "window.hh" #include "window.hh"
#include <signal.h>
#include <unistd.h>
namespace Kakoune namespace Kakoune
{ {
@ -1400,8 +1397,6 @@ KeyMap keymap =
{ Key::PageUp, { "scroll one page up", scroll<Key::PageUp> } }, { Key::PageUp, { "scroll one page up", scroll<Key::PageUp> } },
{ Key::PageDown, { "scroll one page down", scroll<Key::PageDown> } }, { Key::PageDown, { "scroll one page down", scroll<Key::PageDown> } },
{ ctrl('c'), { "interupt", [](Context&, int) { killpg(getpgrp(), SIGINT); } } },
}; };
} }

View File

@ -282,9 +282,9 @@ private:
RemoteUI::RemoteUI(int socket) RemoteUI::RemoteUI(int socket)
: m_socket_watcher(socket, [this](FDWatcher&) { : m_socket_watcher(socket, [this](FDWatcher&, EventMode mode) {
if (m_input_callback) if (m_input_callback)
m_input_callback(); m_input_callback(mode);
}) })
{ {
write_debug("remote client connected: " + write_debug("remote client connected: " +
@ -453,9 +453,9 @@ RemoteClient::RemoteClient(StringView session, std::unique_ptr<UserInterface>&&
msg.write(key); msg.write(key);
} }
m_ui->set_input_callback([this]{ write_next_key(); }); m_ui->set_input_callback([this](EventMode){ write_next_key(); });
m_socket_watcher.reset(new FDWatcher{sock, [this](FDWatcher&){ process_available_messages(); }}); m_socket_watcher.reset(new FDWatcher{sock, [this](FDWatcher&, EventMode){ process_available_messages(); }});
} }
void RemoteClient::process_available_messages() void RemoteClient::process_available_messages()
@ -560,7 +560,10 @@ class Server::Accepter
public: public:
Accepter(int socket) Accepter(int socket)
: m_socket_watcher(socket, : m_socket_watcher(socket,
[this](FDWatcher&) { handle_available_input(); }) [this](FDWatcher&, EventMode mode) {
if (mode == EventMode::Normal)
handle_available_input();
})
{} {}
private: private:
@ -626,7 +629,7 @@ Server::Server(String session_name)
if (listen(listen_sock, 4) == -1) if (listen(listen_sock, 4) == -1)
throw runtime_error("unable to listen on socket "_str + addr.sun_path); throw runtime_error("unable to listen on socket "_str + addr.sun_path);
auto accepter = [this](FDWatcher& watcher) { auto accepter = [this](FDWatcher& watcher, EventMode mode) {
sockaddr_un client_addr; sockaddr_un client_addr;
socklen_t client_addr_len = sizeof(sockaddr_un); socklen_t client_addr_len = sizeof(sockaddr_un);
int sock = accept(watcher.fd(), (sockaddr*) &client_addr, int sock = accept(watcher.fd(), (sockaddr*) &client_addr,

View File

@ -2,6 +2,7 @@
#include "context.hh" #include "context.hh"
#include "debug.hh" #include "debug.hh"
#include "event_manager.hh"
#include "file.hh" #include "file.hh"
#include <cstring> #include <cstring>
@ -59,25 +60,34 @@ String ShellManager::pipe(StringView input,
write(write_pipe[1], input.data(), (int)input.length()); write(write_pipe[1], input.data(), (int)input.length());
close(write_pipe[1]); close(write_pipe[1]);
char buffer[1024]; String error;
while (size_t size = read(read_pipe[0], buffer, 1024))
{ {
if (size == -1) auto pipe_reader = [](String& output, bool& closed) {
break; return [&output, &closed](FDWatcher& watcher, EventMode) {
output += String(buffer, buffer+size); if (closed)
} return;
close(read_pipe[0]); const int fd = watcher.fd();
char buffer[1024];
size_t size = read(fd, buffer, 1024);
if (size <= 0)
{
close(fd);
closed = true;
}
output += String(buffer, buffer+size);
};
};
String errorout; bool stdout_closed = false, stderr_closed = false;
while (size_t size = read(error_pipe[0], buffer, 1024)) FDWatcher stdout_watcher{read_pipe[0], pipe_reader(output, stdout_closed)};
{ FDWatcher stderr_watcher{error_pipe[0], pipe_reader(error, stderr_closed)};
if (size == -1)
break; while (not stdout_closed or not stderr_closed)
errorout += String(buffer, buffer+size); EventManager::instance().handle_next_events(EventMode::Urgent);
} }
close(error_pipe[0]);
if (not errorout.empty()) if (not error.empty())
write_debug("shell stderr: <<<\n" + errorout + ">>>"); write_debug("shell stderr: <<<\n" + error + ">>>");
waitpid(pid, exit_status, 0); waitpid(pid, exit_status, 0);
if (exit_status) if (exit_status)
@ -105,13 +115,7 @@ String ShellManager::pipe(StringView input,
{ {
auto& match = *it; auto& match = *it;
StringView name; StringView name = StringView(match[1].first, match[1].second);
if (match[1].matched)
name = StringView(match[1].first, match[1].second);
else if (match[2].matched)
name = StringView(match[2].first, match[2].second);
else
kak_assert(false);
kak_assert(name.length() > 0); kak_assert(name.length() > 0);
auto local_var = env_vars.find(name); auto local_var = env_vars.find(name);

View File

@ -32,7 +32,9 @@ enum class InfoStyle
MenuDoc MenuDoc
}; };
using InputCallback = std::function<void()>; enum class EventMode;
using InputCallback = std::function<void(EventMode mode)>;
class UserInterface : public SafeCountable class UserInterface : public SafeCountable
{ {