summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxime Coste <frrrwww@gmail.com>2014-11-25 01:00:18 +0000
committerMaxime Coste <frrrwww@gmail.com>2014-11-25 13:52:06 +0000
commit49931fbf056ec1036693c669cec8cde4ea8c95fe (patch)
tree6adb76a625aa106d6d32a3d9796060a9b75312a5
parent0272da65c0abf1f7dd3ab214638b6a4d5d390cc4 (diff)
Separate events between normal and urgent ones
Run urgent ones while executing %sh blocks. Fixes #236
-rw-r--r--src/buffer_utils.cc5
-rw-r--r--src/client.cc44
-rw-r--r--src/client.hh7
-rw-r--r--src/client_manager.cc23
-rw-r--r--src/client_manager.hh1
-rw-r--r--src/event_manager.cc26
-rw-r--r--src/event_manager.hh25
-rw-r--r--src/main.cc5
-rw-r--r--src/ncurses.cc6
-rw-r--r--src/normal.cc5
-rw-r--r--src/remote.cc15
-rw-r--r--src/shell_manager.cc50
-rw-r--r--src/user_interface.hh4
13 files changed, 140 insertions, 76 deletions
diff --git a/src/buffer_utils.cc b/src/buffer_utils.cc
index 12ffae19..33b07b57 100644
--- a/src/buffer_utils.cc
+++ b/src/buffer_utils.cc
@@ -105,7 +105,10 @@ Buffer* create_fifo_buffer(String name, int fd, bool scroll)
ValueId fifo_watcher_id = s_fifo_watcher_id;
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;
// 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
diff --git a/src/client.cc b/src/client.cc
index ea6af7b9..39c3bc34 100644
--- a/src/client.cc
+++ b/src/client.cc
@@ -7,8 +7,12 @@
#include "file.hh"
#include "remote.hh"
#include "client_manager.hh"
+#include "event_manager.hh"
#include "window.hh"
+#include <signal.h>
+#include <unistd.h>
+
namespace Kakoune
{
@@ -34,14 +38,44 @@ Client::~Client()
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)
+ {
+ try
+ {
+ 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
{
- m_input_handler.handle_key(m_ui->get_key());
- m_input_handler.clear_mode_trash();
+ 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)
diff --git a/src/client.hh b/src/client.hh
index ebbc96e8..00ae553a 100644
--- a/src/client.hh
+++ b/src/client.hh
@@ -14,6 +14,9 @@ namespace Kakoune
class UserInterface;
class Window;
class String;
+struct Key;
+
+enum class EventMode;
class Client : public SafeCountable, public OptionManagerWatcher
{
@@ -28,7 +31,7 @@ public:
Client(Client&&) = delete;
// 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);
@@ -64,6 +67,8 @@ private:
DisplayLine m_status_line;
DisplayLine m_pending_status_line;
DisplayLine m_mode_line;
+
+ std::vector<Key> m_pending_keys;
};
}
diff --git a/src/client_manager.cc b/src/client_manager.cc
index 87abcf3e..4f99e844 100644
--- a/src/client_manager.cc
+++ b/src/client_manager.cc
@@ -50,26 +50,19 @@ Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui,
return nullptr;
}
- client->ui().set_input_callback([client, this]() {
- try
- {
- 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);
- }
+ client->ui().set_input_callback([client](EventMode mode) {
+ client->handle_available_input(mode);
});
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)
{
for (auto it = m_clients.begin(); it != m_clients.end(); ++it)
diff --git a/src/client_manager.hh b/src/client_manager.hh
index 2c2e01bc..9a3faf91 100644
--- a/src/client_manager.hh
+++ b/src/client_manager.hh
@@ -35,6 +35,7 @@ public:
void redraw_clients() const;
void clear_mode_trashes() const;
+ void handle_available_inputs() const;
Client* get_client_ifp(StringView name);
Client& get_client(StringView name);
diff --git a/src/event_manager.cc b/src/event_manager.cc
index 48e85f43..673f9403 100644
--- a/src/event_manager.cc
+++ b/src/event_manager.cc
@@ -16,8 +16,13 @@ FDWatcher::~FDWatcher()
EventManager::instance().m_fd_watchers.erase(this);
}
-Timer::Timer(TimePoint date, Callback callback)
- : m_date{date}, m_callback{std::move(callback)}
+void FDWatcher::run(EventMode mode)
+{
+ 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())
EventManager::instance().m_timers.insert(this);
@@ -29,10 +34,15 @@ Timer::~Timer()
EventManager::instance().m_timers.erase(this);
}
-void Timer::run()
+void Timer::run(EventMode mode)
{
- m_date = TimePoint::max();
- m_callback(*this);
+ if (mode & m_mode)
+ {
+ m_date = TimePoint::max();
+ m_callback(*this);
+ }
+ else // try again a little later
+ m_date = Clock::now() + std::chrono::milliseconds{10};
}
EventManager::EventManager()
@@ -46,7 +56,7 @@ EventManager::~EventManager()
kak_assert(m_timers.empty());
}
-void EventManager::handle_next_events()
+void EventManager::handle_next_events(EventMode mode)
{
std::vector<pollfd> events;
events.reserve(m_fd_watchers.size());
@@ -76,7 +86,7 @@ void EventManager::handle_next_events()
auto it = find_if(m_fd_watchers,
[fd](FDWatcher* w) { return w->fd() == fd; });
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)
{
if (timer->next_date() <= now)
- timer->run();
+ timer->run(mode);
}
}
diff --git a/src/event_manager.hh b/src/event_manager.hh
index e14468ba..a88cd620 100644
--- a/src/event_manager.hh
+++ b/src/event_manager.hh
@@ -2,6 +2,7 @@
#define event_manager_hh_INCLUDED
#include "utils.hh"
+#include "flags.hh"
#include <chrono>
#include <unordered_set>
@@ -9,20 +10,28 @@
namespace Kakoune
{
+enum class EventMode
+{
+ Normal = 1 << 0,
+ Urgent = 1 << 1
+};
+
+template<> struct WithBitOps<EventMode> : std::true_type {};
+
class FDWatcher
{
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() const { return m_fd; }
- void run() { m_callback(*this); }
+ void run(EventMode mode);
private:
FDWatcher(const FDWatcher&) = delete;
- int m_fd;
- Callback m_callback;
+ int m_fd;
+ Callback m_callback;
};
using Clock = std::chrono::steady_clock;
@@ -33,15 +42,17 @@ class Timer
public:
using Callback = std::function<void (Timer& timer)>;
- Timer(TimePoint date, Callback callback);
+ Timer(TimePoint date, Callback callback,
+ EventMode mode = EventMode::Normal);
~Timer();
TimePoint next_date() const { return m_date; }
void set_next_date(TimePoint date) { m_date = date; }
- void run();
+ void run(EventMode mode);
private:
TimePoint m_date;
+ EventMode m_mode;
Callback m_callback;
};
@@ -56,7 +67,7 @@ public:
EventManager();
~EventManager();
- void handle_next_events();
+ void handle_next_events(EventMode mode);
// force the watchers associated with fd to be executed
// on next handle_next_events call.
diff --git a/src/main.cc b/src/main.cc
index 5d3ad6dd..4bb0df86 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -305,7 +305,7 @@ int run_client(StringView session, StringView init_command)
RemoteClient client{session, make_unique<NCursesUI>(),
get_env_vars(), init_command};
while (true)
- event_manager.handle_next_events();
+ event_manager.handle_next_events(EventMode::Normal);
}
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))
{
- event_manager.handle_next_events();
+ event_manager.handle_next_events(EventMode::Normal);
+ client_manager.handle_available_inputs();
client_manager.clear_mode_trashes();
buffer_manager.clear_buffer_trash();
client_manager.redraw_clients();
diff --git a/src/ncurses.cc b/src/ncurses.cc
index fe2ba1b1..3f06c557 100644
--- a/src/ncurses.cc
+++ b/src/ncurses.cc
@@ -219,8 +219,10 @@ void on_sigint(int)
}
NCursesUI::NCursesUI()
- : m_stdin_watcher{0, [this](FDWatcher&){ if (m_input_callback)
- m_input_callback(); }}
+ : m_stdin_watcher{0, [this](FDWatcher&, EventMode mode) {
+ if (m_input_callback)
+ m_input_callback(mode);
+ }}
{
initscr();
raw();
diff --git a/src/normal.cc b/src/normal.cc
index 18c2fe8b..3c4528e0 100644
--- a/src/normal.cc
+++ b/src/normal.cc
@@ -18,9 +18,6 @@
#include "user_interface.hh"
#include "window.hh"
-#include <signal.h>
-#include <unistd.h>
-
namespace Kakoune
{
@@ -1400,8 +1397,6 @@ KeyMap keymap =
{ Key::PageUp, { "scroll one page up", scroll<Key::PageUp> } },
{ Key::PageDown, { "scroll one page down", scroll<Key::PageDown> } },
-
- { ctrl('c'), { "interupt", [](Context&, int) { killpg(getpgrp(), SIGINT); } } },
};
}
diff --git a/src/remote.cc b/src/remote.cc
index 9b8bab66..909f2c09 100644
--- a/src/remote.cc
+++ b/src/remote.cc
@@ -282,9 +282,9 @@ private:
RemoteUI::RemoteUI(int socket)
- : m_socket_watcher(socket, [this](FDWatcher&) {
+ : m_socket_watcher(socket, [this](FDWatcher&, EventMode mode) {
if (m_input_callback)
- m_input_callback();
+ m_input_callback(mode);
})
{
write_debug("remote client connected: " +
@@ -453,9 +453,9 @@ RemoteClient::RemoteClient(StringView session, std::unique_ptr<UserInterface>&&
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()
@@ -560,7 +560,10 @@ class Server::Accepter
public:
Accepter(int socket)
: m_socket_watcher(socket,
- [this](FDWatcher&) { handle_available_input(); })
+ [this](FDWatcher&, EventMode mode) {
+ if (mode == EventMode::Normal)
+ handle_available_input();
+ })
{}
private:
@@ -626,7 +629,7 @@ Server::Server(String session_name)
if (listen(listen_sock, 4) == -1)
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;
socklen_t client_addr_len = sizeof(sockaddr_un);
int sock = accept(watcher.fd(), (sockaddr*) &client_addr,
diff --git a/src/shell_manager.cc b/src/shell_manager.cc
index 8ac683ef..ef12f50d 100644
--- a/src/shell_manager.cc
+++ b/src/shell_manager.cc
@@ -2,6 +2,7 @@
#include "context.hh"
#include "debug.hh"
+#include "event_manager.hh"
#include "file.hh"
#include <cstring>
@@ -59,25 +60,34 @@ String ShellManager::pipe(StringView input,
write(write_pipe[1], input.data(), (int)input.length());
close(write_pipe[1]);
- char buffer[1024];
- while (size_t size = read(read_pipe[0], buffer, 1024))
+ String error;
{
- if (size == -1)
- break;
- output += String(buffer, buffer+size);
+ auto pipe_reader = [](String& output, bool& closed) {
+ return [&output, &closed](FDWatcher& watcher, EventMode) {
+ if (closed)
+ return;
+ 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);
+ };
+ };
+
+ bool stdout_closed = false, stderr_closed = false;
+ FDWatcher stdout_watcher{read_pipe[0], pipe_reader(output, stdout_closed)};
+ FDWatcher stderr_watcher{error_pipe[0], pipe_reader(error, stderr_closed)};
+
+ while (not stdout_closed or not stderr_closed)
+ EventManager::instance().handle_next_events(EventMode::Urgent);
}
- close(read_pipe[0]);
- String errorout;
- while (size_t size = read(error_pipe[0], buffer, 1024))
- {
- if (size == -1)
- break;
- errorout += String(buffer, buffer+size);
- }
- close(error_pipe[0]);
- if (not errorout.empty())
- write_debug("shell stderr: <<<\n" + errorout + ">>>");
+ if (not error.empty())
+ write_debug("shell stderr: <<<\n" + error + ">>>");
waitpid(pid, exit_status, 0);
if (exit_status)
@@ -105,13 +115,7 @@ String ShellManager::pipe(StringView input,
{
auto& match = *it;
- StringView name;
- 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);
+ StringView name = StringView(match[1].first, match[1].second);
kak_assert(name.length() > 0);
auto local_var = env_vars.find(name);
diff --git a/src/user_interface.hh b/src/user_interface.hh
index 43d10377..b0884394 100644
--- a/src/user_interface.hh
+++ b/src/user_interface.hh
@@ -32,7 +32,9 @@ enum class InfoStyle
MenuDoc
};
-using InputCallback = std::function<void()>;
+enum class EventMode;
+
+using InputCallback = std::function<void(EventMode mode)>;
class UserInterface : public SafeCountable
{