summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/pages/expansions.asciidoc18
-rw-r--r--src/event_manager.hh1
-rw-r--r--src/shell_manager.cc63
3 files changed, 74 insertions, 8 deletions
diff --git a/doc/pages/expansions.asciidoc b/doc/pages/expansions.asciidoc
index f1b4c802..93aab184 100644
--- a/doc/pages/expansions.asciidoc
+++ b/doc/pages/expansions.asciidoc
@@ -166,6 +166,24 @@ TIP: These environment variables are also available in other contexts where
Kakoune uses a shell command, such as the `|`, `!` or `$` normal mode commands
(See <<keys#,`:doc keys`>>).
+=== Command and Response fifo
+
+Inside shell expansions, `$kak_command_fifo` refers to a named pipe that
+accepts Kakoune commands to be executed as soon as the fifo is closed. This
+named pipe can be opened and closed multiple times which makes it possible
+to interleave shell and Kakoune commands. `$kak_response_fifo` refers to
+a named pipe that can be used to return data from Kakoune.
+
+---
+%sh{
+ echo "write $kak_response_fifo" > $kak_command_fifo
+ content="$(cat $kak_response_fifo)"
+}
+---
+
+This also makes it possible to pass data bigger than the system environment
+size limit.
+
== File expansions
Expansions with the type `file` will expand to the content of the filename
diff --git a/src/event_manager.hh b/src/event_manager.hh
index b7a9720f..edf52e3e 100644
--- a/src/event_manager.hh
+++ b/src/event_manager.hh
@@ -46,6 +46,7 @@ public:
void run(FdEvents events, EventMode mode);
+ void reset_fd(int fd) { m_fd = fd; }
void close_fd();
void disable() { m_fd = -1; }
diff --git a/src/shell_manager.cc b/src/shell_manager.cc
index 8e1cefc5..298412b7 100644
--- a/src/shell_manager.cc
+++ b/src/shell_manager.cc
@@ -4,6 +4,7 @@
#include "client.hh"
#include "clock.hh"
#include "context.hh"
+#include "command_manager.hh"
#include "display_buffer.hh"
#include "event_manager.hh"
#include "face_registry.hh"
@@ -163,18 +164,19 @@ Vector<String> generate_env(StringView cmdline, const Context& context, GetValue
return env;
}
-FDWatcher make_pipe_reader(Pipe& pipe, String& contents)
+template<typename OnClose>
+FDWatcher make_reader(int fd, String& contents, OnClose&& on_close)
{
- return {pipe.read_fd(), FdEvents::Read, EventMode::Urgent,
- [&contents, &pipe](FDWatcher& watcher, FdEvents, EventMode) {
+ return {fd, FdEvents::Read, EventMode::Urgent,
+ [fd, &contents, on_close](FDWatcher& watcher, FdEvents, EventMode) {
char buffer[1024];
- while (fd_readable(pipe.read_fd()))
+ while (fd_readable(fd))
{
- size_t size = ::read(pipe.read_fd(), buffer, sizeof(buffer));
+ size_t size = ::read(fd, buffer, sizeof(buffer));
if (size <= 0)
{
- pipe.close_read_fd();
watcher.disable();
+ on_close();
return;
}
contents += StringView{buffer, buffer+size};
@@ -206,6 +208,41 @@ FDWatcher make_pipe_writer(Pipe& pipe, StringView contents)
}};
}
+struct CommandFifos
+{
+ String base_dir;
+ String command;
+ FDWatcher command_watcher;
+
+ CommandFifos(Context& context, const ShellContext& shell_context)
+ : base_dir(format("{}/kak-fifo.XXXXXX", tmpdir())),
+ command_watcher([&] {
+ mkdtemp(base_dir.data()),
+ mkfifo(command_fifo_path().c_str(), 0600);
+ mkfifo(response_fifo_path().c_str(), 0600);
+ int fd = open(command_fifo_path().c_str(), O_RDONLY | O_NONBLOCK);
+ return make_reader(fd, command, [&, fd] {
+ close(fd);
+ CommandManager::instance().execute(command, context, shell_context);
+ command.clear();
+ command_watcher.reset_fd(open(command_fifo_path().c_str(), O_RDONLY | O_NONBLOCK));
+ });
+ }())
+ {
+ }
+
+ ~CommandFifos()
+ {
+ command_watcher.close_fd();
+ unlink(command_fifo_path().c_str());
+ unlink(response_fifo_path().c_str());
+ rmdir(base_dir.c_str());
+ }
+
+ String command_fifo_path() const { return format("{}/command-fifo", base_dir); }
+ String response_fifo_path() const { return format("{}/response-fifo", base_dir); }
+};
+
}
std::pair<String, int> ShellManager::eval(
@@ -219,7 +256,17 @@ std::pair<String, int> ShellManager::eval(
auto start_time = profile ? Clock::now() : Clock::time_point{};
+ Optional<CommandFifos> command_fifos;
+
auto kak_env = generate_env(cmdline, context, [&](StringView name, Quoting quoting) {
+ if (name == "command_fifo" or name == "response_fifo")
+ {
+ if (not command_fifos)
+ command_fifos.emplace(const_cast<Context&>(context), shell_context);
+ return name == "command_fifo" ?
+ command_fifos->command_fifo_path() : command_fifos->response_fifo_path();
+ }
+
if (auto it = shell_context.env_vars.find(name); it != shell_context.env_vars.end())
return it->value;
return join(get_val(name, context) | transform(quoter(quoting)), ' ', false);
@@ -259,8 +306,8 @@ std::pair<String, int> ShellManager::eval(
auto wait_time = Clock::now();
String stdout_contents, stderr_contents;
- auto stdout_reader = make_pipe_reader(child_stdout, stdout_contents);
- auto stderr_reader = make_pipe_reader(child_stderr, stderr_contents);
+ auto stdout_reader = make_reader(child_stdout.read_fd(), stdout_contents, [&]{ child_stdout.close_read_fd(); });
+ auto stderr_reader = make_reader(child_stderr.read_fd(), stderr_contents, [&]{ child_stderr.close_read_fd(); });
auto stdin_writer = make_pipe_writer(child_stdin, input);
// block SIGCHLD to make sure we wont receive it before