Support piped input.
This commit is contained in:
@@ -58,12 +58,23 @@ struct IO {
|
||||
static volatile std::atomic_bool resized;
|
||||
static void handle_sigwinch(int);
|
||||
|
||||
static enum struct Mode {
|
||||
PIPE,
|
||||
TERMINAL
|
||||
} mode;
|
||||
|
||||
std::string pipe_input;
|
||||
std::deque<char> input_queue;
|
||||
|
||||
IO();
|
||||
~IO();
|
||||
|
||||
IO(const IO &) = delete;
|
||||
IO &operator=(const IO &) = delete;
|
||||
|
||||
bool interactive() const {
|
||||
return mode == Mode::TERMINAL;
|
||||
}
|
||||
void enable_mouse();
|
||||
void disable_mouse();
|
||||
|
||||
@@ -71,14 +82,14 @@ struct IO {
|
||||
std::pair<uint16_t, uint16_t> cursor_position();
|
||||
void move_cursor(uint16_t row, uint16_t col);
|
||||
|
||||
std::pair<std::string, bool> read_pipe();
|
||||
|
||||
KeyEvent read_key();
|
||||
void write(const char *, uint64_t);
|
||||
void write(std::string_view);
|
||||
void write_line(std::string_view);
|
||||
void run_pty(const std::string &);
|
||||
|
||||
std::deque<char> input_queue;
|
||||
|
||||
KeyEvent::ReadResult get_next_byte(char &out);
|
||||
void enqueue_bytes(const std::string &bytes);
|
||||
static int utf8_seq_len(uint8_t byte);
|
||||
|
||||
@@ -41,6 +41,8 @@ struct CommandIO {
|
||||
|
||||
CommandIO(BEd &);
|
||||
std::pair<std::string, bool> run();
|
||||
std::pair<std::string, bool> run_pipe();
|
||||
std::pair<std::string, bool> run_terminal();
|
||||
void redraw();
|
||||
};
|
||||
} // namespace bed::internal::ui
|
||||
|
||||
@@ -15,6 +15,8 @@ struct TextMode {
|
||||
|
||||
TextMode(BEd &);
|
||||
std::pair<vase::Shard *, bool> run();
|
||||
std::pair<vase::Shard *, bool> run_pipe();
|
||||
std::pair<vase::Shard *, bool> run_terminal();
|
||||
void grow();
|
||||
void redraw();
|
||||
};
|
||||
|
||||
@@ -1,6 +1,31 @@
|
||||
#include "internal/io/io.h"
|
||||
|
||||
namespace bed::internal::io {
|
||||
std::pair<std::string, bool> IO::read_pipe() {
|
||||
char buf[4096];
|
||||
while (true) {
|
||||
auto pos = pipe_input.find('\n');
|
||||
if (pos != std::string::npos) {
|
||||
std::string line = pipe_input.substr(0, pos);
|
||||
pipe_input.erase(0, pos + 1);
|
||||
return {line, false};
|
||||
}
|
||||
ssize_t n = read(STDIN_FILENO, buf, sizeof(buf));
|
||||
if (n > 0) {
|
||||
pipe_input.append(buf, n);
|
||||
continue;
|
||||
}
|
||||
if (n == 0) {
|
||||
std::string line = std::move(pipe_input);
|
||||
pipe_input.clear();
|
||||
return {line, true};
|
||||
}
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
throw fatal_error("Can't read stdin.", 1);
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent::ReadResult IO::get_next_byte(char &out) {
|
||||
if (!input_queue.empty()) {
|
||||
out = input_queue.front();
|
||||
|
||||
+18
-2
@@ -5,8 +5,12 @@ termios IO::orig_termios{};
|
||||
termios IO::raw_termios{};
|
||||
bool IO::cleaned = true;
|
||||
volatile std::atomic_bool IO::resized(false);
|
||||
IO::Mode IO::mode = IO::Mode::PIPE;
|
||||
|
||||
IO::IO() {
|
||||
if (!isatty(STDIN_FILENO))
|
||||
return;
|
||||
mode = Mode::TERMINAL;
|
||||
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
|
||||
throw fatal_error("Can't get terminal state.", 1);
|
||||
struct sigaction sa{};
|
||||
@@ -30,16 +34,22 @@ IO::~IO() {
|
||||
}
|
||||
|
||||
void IO::enable_mouse() {
|
||||
if (mode == Mode::PIPE)
|
||||
throw fatal_error("no mouse in pipe mode.", 1);
|
||||
const char *seq = "\x1b[?1000h";
|
||||
write_all(STDOUT_FILENO, seq, 8);
|
||||
}
|
||||
|
||||
void IO::disable_mouse() {
|
||||
if (mode == Mode::PIPE)
|
||||
throw fatal_error("no mouse in pipe mode.", 1);
|
||||
const char *seq = "\x1b[?1000l";
|
||||
write_all(STDOUT_FILENO, seq, 8);
|
||||
}
|
||||
|
||||
std::pair<uint16_t, uint16_t> IO::terminal_size() {
|
||||
if (mode == Mode::PIPE)
|
||||
throw fatal_error("no terminal size in pipe mode.", 1);
|
||||
struct winsize ws{};
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
|
||||
throw fatal_error("Can't get terminal size.", 1);
|
||||
@@ -47,6 +57,8 @@ std::pair<uint16_t, uint16_t> IO::terminal_size() {
|
||||
}
|
||||
|
||||
std::pair<uint16_t, uint16_t> IO::cursor_position() {
|
||||
if (mode == Mode::PIPE)
|
||||
throw fatal_error("no cursor in pipe mode.", 1);
|
||||
write_all(STDOUT_FILENO, "\x1b[6n", 4);
|
||||
std::string response;
|
||||
char c;
|
||||
@@ -69,7 +81,7 @@ std::pair<uint16_t, uint16_t> IO::cursor_position() {
|
||||
}
|
||||
|
||||
void IO::enable_raw() {
|
||||
if (!cleaned)
|
||||
if (!cleaned || mode == Mode::PIPE)
|
||||
return;
|
||||
std::string os = "\x1b[?2004h";
|
||||
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
||||
@@ -79,7 +91,7 @@ void IO::enable_raw() {
|
||||
}
|
||||
|
||||
void IO::cleanup() {
|
||||
if (cleaned)
|
||||
if (cleaned || mode == Mode::PIPE)
|
||||
return;
|
||||
std::string os = "\x1b[?1000l\x1b[?2004l";
|
||||
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
||||
@@ -93,6 +105,8 @@ void IO::handle_sigwinch(int) {
|
||||
}
|
||||
|
||||
void IO::move_cursor(uint16_t row, uint16_t col) {
|
||||
if (mode == Mode::PIPE)
|
||||
return;
|
||||
char buf[32];
|
||||
int n = snprintf(buf, sizeof(buf), "\x1b[%u;%uH", row, col);
|
||||
write_all(STDOUT_FILENO, buf, n);
|
||||
@@ -112,6 +126,8 @@ void IO::write_line(std::string_view s) {
|
||||
}
|
||||
|
||||
void IO::run_pty(const std::string &cmd) {
|
||||
if (mode == Mode::PIPE)
|
||||
throw ed_error("Shell running not allowed in pipe mode.");
|
||||
int master_fd = -1;
|
||||
struct winsize ws{};
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
|
||||
|
||||
@@ -69,6 +69,17 @@ CommandIO::CommandIO(BEd &bed) : bed(bed) {
|
||||
}
|
||||
|
||||
std::pair<std::string, bool> CommandIO::run() {
|
||||
if (!bed.io.interactive())
|
||||
return run_pipe();
|
||||
return run_terminal();
|
||||
}
|
||||
|
||||
std::pair<std::string, bool> CommandIO::run_pipe() {
|
||||
bed.io.write(prompt);
|
||||
return bed.io.read_pipe();
|
||||
}
|
||||
|
||||
std::pair<std::string, bool> CommandIO::run_terminal() {
|
||||
auto [row, col] = bed.io.cursor_position();
|
||||
auto [rows, cols] = bed.io.terminal_size();
|
||||
if (row > rows)
|
||||
|
||||
@@ -7,6 +7,23 @@ TextMode::TextMode(BEd &bed) : bed(bed) {
|
||||
}
|
||||
|
||||
std::pair<vase::Shard *, bool> TextMode::run() {
|
||||
if (!bed.io.interactive())
|
||||
return run_pipe();
|
||||
return run_terminal();
|
||||
}
|
||||
|
||||
std::pair<vase::Shard *, bool> TextMode::run_pipe() {
|
||||
cmd.clear();
|
||||
while (true) {
|
||||
auto [str, eof] = bed.io.read_pipe();
|
||||
if (str == "." || eof)
|
||||
break;
|
||||
cmd += str + "\n";
|
||||
}
|
||||
return {vase::Shard::from_string(cmd.data(), cmd.length(), true), false};
|
||||
}
|
||||
|
||||
std::pair<vase::Shard *, bool> TextMode::run_terminal() {
|
||||
auto [row, col] = bed.io.cursor_position();
|
||||
auto [rows, cols] = bed.io.terminal_size();
|
||||
if (row > rows)
|
||||
|
||||
Reference in New Issue
Block a user