Add support for $kak_command_fifo and $kak_response_fifo
Those fifos are accessible during %sh{...} blocks, the command fifo executes commands written to it once the write end side is closed (multiple open/write/close sequences are supported), the response fifo is a simple helper fifo available to write response back to the shell process An example use of this feature is to request some list options content from without being limited by the environment size: ``` %sh{ echo "echo -to-file $kak_response_fifo -quoting shell -- %opt{some_list}" > $kak_command_fifo eval "set -- $(cat $kak_response_fifo)" } ```
This commit is contained in:
parent
6215bff764
commit
3d7fb8ddbc
|
@ -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
|
Kakoune uses a shell command, such as the `|`, `!` or `$` normal mode commands
|
||||||
(See <<keys#,`:doc keys`>>).
|
(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
|
== File expansions
|
||||||
|
|
||||||
Expansions with the type `file` will expand to the content of the filename
|
Expansions with the type `file` will expand to the content of the filename
|
||||||
|
|
|
@ -46,6 +46,7 @@ public:
|
||||||
|
|
||||||
void run(FdEvents events, EventMode mode);
|
void run(FdEvents events, EventMode mode);
|
||||||
|
|
||||||
|
void reset_fd(int fd) { m_fd = fd; }
|
||||||
void close_fd();
|
void close_fd();
|
||||||
void disable() { m_fd = -1; }
|
void disable() { m_fd = -1; }
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "client.hh"
|
#include "client.hh"
|
||||||
#include "clock.hh"
|
#include "clock.hh"
|
||||||
#include "context.hh"
|
#include "context.hh"
|
||||||
|
#include "command_manager.hh"
|
||||||
#include "display_buffer.hh"
|
#include "display_buffer.hh"
|
||||||
#include "event_manager.hh"
|
#include "event_manager.hh"
|
||||||
#include "face_registry.hh"
|
#include "face_registry.hh"
|
||||||
|
@ -163,18 +164,19 @@ Vector<String> generate_env(StringView cmdline, const Context& context, GetValue
|
||||||
return env;
|
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,
|
return {fd, FdEvents::Read, EventMode::Urgent,
|
||||||
[&contents, &pipe](FDWatcher& watcher, FdEvents, EventMode) {
|
[fd, &contents, on_close](FDWatcher& watcher, FdEvents, EventMode) {
|
||||||
char buffer[1024];
|
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)
|
if (size <= 0)
|
||||||
{
|
{
|
||||||
pipe.close_read_fd();
|
|
||||||
watcher.disable();
|
watcher.disable();
|
||||||
|
on_close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
contents += StringView{buffer, buffer+size};
|
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(
|
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{};
|
auto start_time = profile ? Clock::now() : Clock::time_point{};
|
||||||
|
|
||||||
|
Optional<CommandFifos> command_fifos;
|
||||||
|
|
||||||
auto kak_env = generate_env(cmdline, context, [&](StringView name, Quoting quoting) {
|
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())
|
if (auto it = shell_context.env_vars.find(name); it != shell_context.env_vars.end())
|
||||||
return it->value;
|
return it->value;
|
||||||
return join(get_val(name, context) | transform(quoter(quoting)), ' ', false);
|
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();
|
auto wait_time = Clock::now();
|
||||||
|
|
||||||
String stdout_contents, stderr_contents;
|
String stdout_contents, stderr_contents;
|
||||||
auto stdout_reader = make_pipe_reader(child_stdout, stdout_contents);
|
auto stdout_reader = make_reader(child_stdout.read_fd(), stdout_contents, [&]{ child_stdout.close_read_fd(); });
|
||||||
auto stderr_reader = make_pipe_reader(child_stderr, stderr_contents);
|
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);
|
auto stdin_writer = make_pipe_writer(child_stdin, input);
|
||||||
|
|
||||||
// block SIGCHLD to make sure we wont receive it before
|
// block SIGCHLD to make sure we wont receive it before
|
||||||
|
|
Loading…
Reference in New Issue
Block a user