Compare commits

...
5 Commits
Author SHA1 Message Date
syedm cd9583ec34 Honor suppress mode.
- Allow P command to create a new prompt string.
2026-09-03 09:53:14 +01:00
syedm 0104f96d51 Add comments and echo commands. 2026-09-03 09:52:33 +01:00
syedm abbfcc352a Support piped input. 2026-09-03 09:52:13 +01:00
syedm f8975a60d8 Fix regex and current() system. 2026-09-03 07:42:50 +01:00
syedm 33dc9ad507 Fix bugs with history setup. 2026-09-03 06:59:55 +01:00
13 changed files with 225 additions and 60 deletions
+2 -4
View File
@@ -14,10 +14,8 @@ struct HistoryItem {
struct GenericBuffer : ShardBuffer { struct GenericBuffer : ShardBuffer {
uint64_t base_version{0}; uint64_t base_version{0};
std::chrono::system_clock::time_point timestamp{ std::chrono::system_clock::time_point timestamp;
std::chrono::system_clock::now() std::string action;
};
std::string action{"created"};
std::filesystem::path save_path{}; std::filesystem::path save_path{};
std::vector<HistoryItem> undo_stack; std::vector<HistoryItem> undo_stack;
std::vector<HistoryItem> redo_stack; std::vector<HistoryItem> redo_stack;
+13 -2
View File
@@ -58,12 +58,23 @@ struct IO {
static volatile std::atomic_bool resized; static volatile std::atomic_bool resized;
static void handle_sigwinch(int); static void handle_sigwinch(int);
static enum struct Mode {
PIPE,
TERMINAL
} mode;
std::string pipe_input;
std::deque<char> input_queue;
IO(); IO();
~IO(); ~IO();
IO(const IO &) = delete; IO(const IO &) = delete;
IO &operator=(const IO &) = delete; IO &operator=(const IO &) = delete;
bool interactive() const {
return mode == Mode::TERMINAL;
}
void enable_mouse(); void enable_mouse();
void disable_mouse(); void disable_mouse();
@@ -71,14 +82,14 @@ struct IO {
std::pair<uint16_t, uint16_t> cursor_position(); std::pair<uint16_t, uint16_t> cursor_position();
void move_cursor(uint16_t row, uint16_t col); void move_cursor(uint16_t row, uint16_t col);
std::pair<std::string, bool> read_pipe();
KeyEvent read_key(); KeyEvent read_key();
void write(const char *, uint64_t); void write(const char *, uint64_t);
void write(std::string_view); void write(std::string_view);
void write_line(std::string_view); void write_line(std::string_view);
void run_pty(const std::string &); void run_pty(const std::string &);
std::deque<char> input_queue;
KeyEvent::ReadResult get_next_byte(char &out); KeyEvent::ReadResult get_next_byte(char &out);
void enqueue_bytes(const std::string &bytes); void enqueue_bytes(const std::string &bytes);
static int utf8_seq_len(uint8_t byte); static int utf8_seq_len(uint8_t byte);
+2
View File
@@ -41,6 +41,8 @@ struct CommandIO {
CommandIO(BEd &); CommandIO(BEd &);
std::pair<std::string, bool> run(); std::pair<std::string, bool> run();
std::pair<std::string, bool> run_pipe();
std::pair<std::string, bool> run_terminal();
void redraw(); void redraw();
}; };
} // namespace bed::internal::ui } // namespace bed::internal::ui
+2
View File
@@ -15,6 +15,8 @@ struct TextMode {
TextMode(BEd &); TextMode(BEd &);
std::pair<vase::Shard *, bool> run(); std::pair<vase::Shard *, bool> run();
std::pair<vase::Shard *, bool> run_pipe();
std::pair<vase::Shard *, bool> run_terminal();
void grow(); void grow();
void redraw(); void redraw();
}; };
+28 -14
View File
@@ -39,7 +39,7 @@ void GenericBuffer::list_history(BEd &ctx) {
std::tm tm = *std::localtime(&time); std::tm tm = *std::localtime(&time);
ctx.io.write_line( ctx.io.write_line(
std::format( std::format(
"X {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}", "* {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
current, current,
tm.tm_year + 1900, tm.tm_year + 1900,
tm.tm_mon + 1, tm.tm_mon + 1,
@@ -58,7 +58,7 @@ void GenericBuffer::list_history(BEd &ctx) {
std::tm tm = *std::localtime(&time); std::tm tm = *std::localtime(&time);
ctx.io.write_line( ctx.io.write_line(
std::format( std::format(
" {} {:04}-{:02}-{:02} {:02}:{:02}:{:02}", " {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
version, version,
tm.tm_year + 1900, tm.tm_year + 1900,
tm.tm_mon + 1, tm.tm_mon + 1,
@@ -90,15 +90,19 @@ HistoryBuffer *GenericBuffer::get_history(uint64_t version) {
} }
void GenericBuffer::snapshot(std::string action_) { void GenericBuffer::snapshot(std::string action_) {
vase::Shard::retain(root); if (base_version == 0) {
undo_stack.push_back( base_version++;
HistoryItem{ } else {
syntax::retain(parse), vase::Shard::retain(root);
root, undo_stack.push_back(
timestamp, HistoryItem{
action syntax::retain(parse),
} root,
); timestamp,
action
}
);
}
for (auto &item : redo_stack) { for (auto &item : redo_stack) {
syntax::release(item.parse_state); syntax::release(item.parse_state);
vase::Shard::release(item.text); vase::Shard::release(item.text);
@@ -126,6 +130,8 @@ bool GenericBuffer::undo(BEd &ctx) {
root = prev.text; root = prev.text;
syntax::release(parse); syntax::release(parse);
parse = prev.parse_state; parse = prev.parse_state;
timestamp = prev.timestamp;
action = prev.summary;
state = Modified; state = Modified;
if (!root) { if (!root) {
ctx.prev().buffername = name; ctx.prev().buffername = name;
@@ -157,6 +163,8 @@ bool GenericBuffer::redo(BEd &ctx) {
root = next.text; root = next.text;
syntax::release(parse); syntax::release(parse);
parse = next.parse_state; parse = next.parse_state;
timestamp = next.timestamp;
action = next.summary;
state = Modified; state = Modified;
if (!root) { if (!root) {
ctx.prev().buffername = name; ctx.prev().buffername = name;
@@ -213,6 +221,7 @@ void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
ctx.prev().start = 1; ctx.prev().start = 1;
ctx.prev().end = text->lines + 1; ctx.prev().end = text->lines + 1;
} }
ctx.current() = {name, lines()};
syntax::release(parse); syntax::release(parse);
parse = syntax::make_parser(root, lines(), ctx.languages["ruby"]); parse = syntax::make_parser(root, lines(), ctx.languages["ruby"]);
} }
@@ -232,6 +241,7 @@ void GenericBuffer::append(BEd &ctx, vase::Shard *text, uint64_t line) {
ctx.prev().buffername = name; ctx.prev().buffername = name;
ctx.prev().start = line + 1; ctx.prev().start = line + 1;
ctx.prev().end = line + text->lines + 1; ctx.prev().end = line + text->lines + 1;
ctx.current() = {name, line + text->lines + 1};
root = vase::insert(&ctx.append, root, text, line); root = vase::insert(&ctx.append, root, text, line);
ctx.marks.insert(name, line, text->lines + 1); ctx.marks.insert(name, line, text->lines + 1);
if (parse.lang) if (parse.lang)
@@ -243,8 +253,10 @@ void GenericBuffer::remove(BEd &ctx, uint64_t start_line, uint64_t end_line) {
snapshot(std::format("Remove lines {} to {}", start_line, end_line)); snapshot(std::format("Remove lines {} to {}", start_line, end_line));
root = vase::erase(root, start_line, end_line); root = vase::erase(root, start_line, end_line);
ctx.prev().buffername = name; ctx.prev().buffername = name;
ctx.prev().start = std::min(start_line, lines()); uint64_t l = std::min(start_line, lines());
ctx.prev().end = std::min(start_line, lines()); ctx.prev().start = l;
ctx.prev().end = l;
ctx.current() = {name, l};
ctx.marks.erase(name, start_line, end_line - start_line + 1); ctx.marks.erase(name, start_line, end_line - start_line + 1);
if (parse.lang) if (parse.lang)
syntax::erase(parse, root, start_line, end_line - start_line + 1); syntax::erase(parse, root, start_line, end_line - start_line + 1);
@@ -260,6 +272,7 @@ void GenericBuffer::replace(BEd &ctx, vase::Shard *text, uint64_t start_line, ui
ctx.prev().buffername = name; ctx.prev().buffername = name;
ctx.prev().start = start_line; ctx.prev().start = start_line;
ctx.prev().end = start_line + text->lines; ctx.prev().end = start_line + text->lines;
ctx.current() = {name, start_line + text->lines};
uint64_t new_count = text->lines + 1; uint64_t new_count = text->lines + 1;
uint64_t old_count = end_line - start_line + 1; uint64_t old_count = end_line - start_line + 1;
root = vase::replace(root, text, start_line, end_line); root = vase::replace(root, text, start_line, end_line);
@@ -285,6 +298,7 @@ void GenericBuffer::join(BEd &ctx, uint64_t start_line, uint64_t end_line) {
ctx.prev().buffername = name; ctx.prev().buffername = name;
ctx.prev().start = start_line; ctx.prev().start = start_line;
ctx.prev().end = start_line; ctx.prev().end = start_line;
ctx.current() = {name, start_line};
ctx.marks.collapse(name, start_line, end_line - start_line); ctx.marks.collapse(name, start_line, end_line - start_line);
if (parse.lang) if (parse.lang)
syntax::erase(parse, root, start_line, end_line - start_line); syntax::erase(parse, root, start_line, end_line - start_line);
@@ -321,11 +335,11 @@ void GenericBuffer::substitute(
if (edit) if (edit)
edit->insert(line, new_lines); edit->insert(line, new_lines);
} }
ctx.current() = {name, line + new_lines};
} }
); );
if (edit) if (edit)
edit->commit(root); edit->commit(root);
ctx.prev().buffername = name;
state = Modified; state = Modified;
} }
} // namespace bed::internal::buffer } // namespace bed::internal::buffer
+74 -2
View File
@@ -4,6 +4,79 @@
namespace bed::internal::functions { namespace bed::internal::functions {
void Function::register_extented(BEd &ctx) { void Function::register_extented(BEd &ctx) {
ctx.functions.insert(
"#",
Function{
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::Any,
.input_mode = Function::InputMode::None,
.desc = "Comment.",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](
BEd &,
const buffer::Address &,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {},
}
);
ctx.functions.insert(
"echo",
Function{
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::Any,
.input_mode = Function::InputMode::None,
.desc = "Echo given message.",
.default_address = "",
.accept_zero = true,
.pre_text_mode = nullptr,
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg_,
std::vector<buffer::Line> *
) {
auto &addr = std::get<buffer::Range>(addr_);
auto str = std::get<std::string>(arg_);
const auto first = str.find_first_not_of(" \t");
const auto last = str.find_last_not_of(" \t");
if (first == std::string::npos)
str.clear();
else
str = str.substr(first, last - first + 1);
for (size_t i = 0; i < str.size();) {
if (str[i] == '\\') {
if (i + 1 >= str.size())
break;
str.erase(i++, 1);
if (str[i - 1] == 'n')
str[i - 1] = '\n';
continue;
}
if (str[i] == '$' && i < str.size() && '1' <= str[i + 1] && str[i + 1] <= '2') {
bool one = str[i + 1] == '1';
str.erase(i, 2);
if (one) {
auto start = std::to_string(addr.start);
str.insert(i, start);
i += start.size();
} else {
auto end = std::to_string(addr.end);
str.insert(i, end);
i += end.size();
}
continue;
}
i++;
}
ctx.io.write_line(str);
},
}
);
ctx.functions.insert( ctx.functions.insert(
"cd", "cd",
Function{ Function{
@@ -93,7 +166,6 @@ void Function::register_extented(BEd &ctx) {
vase::Shard::release(text); vase::Shard::release(text);
throw; throw;
} }
ctx.current() = {arg.buffername, arg.start + addr.end - addr.start + 1};
}, },
} }
); );
@@ -125,7 +197,7 @@ void Function::register_extented(BEd &ctx) {
} }
); );
ctx.functions.insert( ctx.functions.insert(
"history-list", "hl",
Function{ Function{
.address_kind = Function::AddressKind::None, .address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None, .argument_kind = Function::ArgumentKind::None,
+24 -21
View File
@@ -47,7 +47,6 @@ void Function::register_posix(BEd &ctx) {
) { ) {
auto addr = std::get<buffer::Line>(addr_); auto addr = std::get<buffer::Line>(addr_);
ctx.buffer(addr.buffername).append(ctx, text, addr.number); ctx.buffer(addr.buffername).append(ctx, text, addr.number);
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
vase::Shard::release(text); vase::Shard::release(text);
} }
} }
@@ -71,7 +70,6 @@ void Function::register_posix(BEd &ctx) {
) { ) {
auto addr = std::get<buffer::Range>(addr_); auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).replace(ctx, text, addr.start, addr.end); ctx.buffer(addr.buffername).replace(ctx, text, addr.start, addr.end);
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
vase::Shard::release(text); vase::Shard::release(text);
} }
} }
@@ -95,7 +93,6 @@ void Function::register_posix(BEd &ctx) {
) { ) {
auto addr = std::get<buffer::Range>(addr_); auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).remove(ctx, addr.start, addr.end); ctx.buffer(addr.buffername).remove(ctx, addr.start, addr.end);
ctx.current() = {addr.buffername, addr.start};
} }
} }
); );
@@ -144,8 +141,8 @@ void Function::register_posix(BEd &ctx) {
vase::Shard::release(s); vase::Shard::release(s);
throw; throw;
} }
ctx.io.write_line(std::format("{}", buf.bytes())); if (!ctx.suppress_mode)
ctx.current() = {addr, buf.lines()}; ctx.io.write_line(std::format("{}", buf.bytes()));
} }
} }
); );
@@ -190,8 +187,8 @@ void Function::register_posix(BEd &ctx) {
vase::Shard::release(s); vase::Shard::release(s);
throw; throw;
} }
ctx.io.write_line(std::format("{}", buf.bytes())); if (!ctx.suppress_mode)
ctx.current() = {addr, buf.lines()}; ctx.io.write_line(std::format("{}", buf.bytes()));
} }
} }
); );
@@ -289,7 +286,6 @@ void Function::register_posix(BEd &ctx) {
if (addr.number) if (addr.number)
addr.number--; addr.number--;
ctx.buffer(addr.buffername).append(ctx, text, addr.number); ctx.buffer(addr.buffername).append(ctx, text, addr.number);
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
vase::Shard::release(text); vase::Shard::release(text);
} }
} }
@@ -313,7 +309,6 @@ void Function::register_posix(BEd &ctx) {
) { ) {
auto addr = std::get<buffer::Range>(addr_); auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).join(ctx, addr.start, addr.end); ctx.buffer(addr.buffername).join(ctx, addr.start, addr.end);
ctx.current() = {addr.buffername, addr.start};
} }
} }
); );
@@ -403,7 +398,6 @@ void Function::register_posix(BEd &ctx) {
vase::Shard::release(text); vase::Shard::release(text);
throw; throw;
} }
ctx.current() = {arg.buffername, arg.number + addr.end - addr.start + 1};
}, },
} }
); );
@@ -457,9 +451,9 @@ void Function::register_posix(BEd &ctx) {
"P", "P",
Function{ Function{
.address_kind = Function::AddressKind::None, .address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None, .argument_kind = Function::ArgumentKind::Any,
.input_mode = Function::InputMode::None, .input_mode = Function::InputMode::None,
.desc = "Toggle prompt.", .desc = "Toggle/set prompt.",
.default_address = "", .default_address = "",
.accept_zero = false, .accept_zero = false,
.pre_text_mode = nullptr, .pre_text_mode = nullptr,
@@ -467,13 +461,18 @@ void Function::register_posix(BEd &ctx) {
BEd &ctx, BEd &ctx,
const buffer::Address &, const buffer::Address &,
vase::Shard *, vase::Shard *,
const Argument &, const Argument &arg_,
std::vector<buffer::Line> * std::vector<buffer::Line> *
) { ) {
ctx.prompt_mode = !ctx.prompt_mode; auto &arg = std::get<std::string>(arg_);
if (ctx.prompt_mode && !ctx.prompt) { if (arg.size()) {
ctx.prompt = [](BEd &) { return "*"; }; ctx.prompt_mode = true;
ctx.prompt = [arg](BEd &) { return arg; };
return;
} }
ctx.prompt_mode = !ctx.prompt_mode;
if (ctx.prompt_mode && !ctx.prompt)
ctx.prompt = [](BEd &) { return "*"; };
} }
} }
); );
@@ -566,13 +565,13 @@ void Function::register_posix(BEd &ctx) {
}; };
try { try {
buf.append(ctx, s, addr.number); buf.append(ctx, s, addr.number);
ctx.io.write_line(std::format("{}", s ? s->length + 1 : 0));
ctx.current() = {addr.buffername, addr.number + (s ? s->lines + 1 : 0)};
vase::Shard::release(s); vase::Shard::release(s);
} catch (...) { } catch (...) {
vase::Shard::release(s); vase::Shard::release(s);
throw; throw;
} }
if (!ctx.suppress_mode)
ctx.io.write_line(std::format("{}", s ? s->length + 1 : 0));
} }
} }
); );
@@ -642,7 +641,6 @@ void Function::register_posix(BEd &ctx) {
vase::Shard::release(text); vase::Shard::release(text);
throw; throw;
} }
ctx.current() = {arg.buffername, arg.number + addr.end - addr.start + 1};
}, },
} }
); );
@@ -715,12 +713,13 @@ void Function::register_posix(BEd &ctx) {
throw ed_error("Need filename."); throw ed_error("Need filename.");
vase::write_file(path, text); vase::write_file(path, text);
}; };
ctx.io.write(std::format("{}\n", text ? text->length + 1 : 0));
vase::Shard::release(text); vase::Shard::release(text);
} catch (...) { } catch (...) {
vase::Shard::release(text); vase::Shard::release(text);
throw; throw;
} }
if (!ctx.suppress_mode)
ctx.io.write(std::format("{}\n", text ? text->length + 1 : 0));
} }
} }
); );
@@ -746,6 +745,9 @@ void Function::register_posix(BEd &ctx) {
ctx.io.write_line(std::format(":{}:{}", addr.buffername, addr.start)); ctx.io.write_line(std::format(":{}:{}", addr.buffername, addr.start));
else else
ctx.io.write_line(std::format(":{}:{},{}", addr.buffername, addr.start, addr.end)); ctx.io.write_line(std::format(":{}:{},{}", addr.buffername, addr.start, addr.end));
ctx.prev().buffername = addr.buffername;
ctx.prev().start = addr.start;
ctx.prev().end = addr.end;
ctx.current() = {addr.buffername, addr.end}; ctx.current() = {addr.buffername, addr.end};
}, },
} }
@@ -774,7 +776,8 @@ void Function::register_posix(BEd &ctx) {
if (ctx.escape_command(cmd, filename.string())) if (ctx.escape_command(cmd, filename.string()))
ctx.io.write(cmd + "\n"); ctx.io.write(cmd + "\n");
ctx.io.run_pty(cmd); ctx.io.run_pty(cmd);
ctx.io.write("!\n"); if (!ctx.suppress_mode)
ctx.io.write("!\n");
}, },
} }
); );
+25
View File
@@ -1,6 +1,31 @@
#include "internal/io/io.h" #include "internal/io/io.h"
namespace bed::internal::io { 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) { KeyEvent::ReadResult IO::get_next_byte(char &out) {
if (!input_queue.empty()) { if (!input_queue.empty()) {
out = input_queue.front(); out = input_queue.front();
+18 -2
View File
@@ -5,8 +5,12 @@ termios IO::orig_termios{};
termios IO::raw_termios{}; termios IO::raw_termios{};
bool IO::cleaned = true; bool IO::cleaned = true;
volatile std::atomic_bool IO::resized(false); volatile std::atomic_bool IO::resized(false);
IO::Mode IO::mode = IO::Mode::PIPE;
IO::IO() { IO::IO() {
if (!isatty(STDIN_FILENO))
return;
mode = Mode::TERMINAL;
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1) if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
throw fatal_error("Can't get terminal state.", 1); throw fatal_error("Can't get terminal state.", 1);
struct sigaction sa{}; struct sigaction sa{};
@@ -30,16 +34,22 @@ IO::~IO() {
} }
void IO::enable_mouse() { void IO::enable_mouse() {
if (mode == Mode::PIPE)
throw fatal_error("no mouse in pipe mode.", 1);
const char *seq = "\x1b[?1000h"; const char *seq = "\x1b[?1000h";
write_all(STDOUT_FILENO, seq, 8); write_all(STDOUT_FILENO, seq, 8);
} }
void IO::disable_mouse() { void IO::disable_mouse() {
if (mode == Mode::PIPE)
throw fatal_error("no mouse in pipe mode.", 1);
const char *seq = "\x1b[?1000l"; const char *seq = "\x1b[?1000l";
write_all(STDOUT_FILENO, seq, 8); write_all(STDOUT_FILENO, seq, 8);
} }
std::pair<uint16_t, uint16_t> IO::terminal_size() { 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{}; struct winsize ws{};
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
throw fatal_error("Can't get terminal size.", 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() { 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); write_all(STDOUT_FILENO, "\x1b[6n", 4);
std::string response; std::string response;
char c; char c;
@@ -69,7 +81,7 @@ std::pair<uint16_t, uint16_t> IO::cursor_position() {
} }
void IO::enable_raw() { void IO::enable_raw() {
if (!cleaned) if (!cleaned || mode == Mode::PIPE)
return; return;
std::string os = "\x1b[?2004h"; std::string os = "\x1b[?2004h";
write_all(STDOUT_FILENO, os.c_str(), os.size()); write_all(STDOUT_FILENO, os.c_str(), os.size());
@@ -79,7 +91,7 @@ void IO::enable_raw() {
} }
void IO::cleanup() { void IO::cleanup() {
if (cleaned) if (cleaned || mode == Mode::PIPE)
return; return;
std::string os = "\x1b[?1000l\x1b[?2004l"; std::string os = "\x1b[?1000l\x1b[?2004l";
write_all(STDOUT_FILENO, os.c_str(), os.size()); 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) { void IO::move_cursor(uint16_t row, uint16_t col) {
if (mode == Mode::PIPE)
return;
char buf[32]; char buf[32];
int n = snprintf(buf, sizeof(buf), "\x1b[%u;%uH", row, col); int n = snprintf(buf, sizeof(buf), "\x1b[%u;%uH", row, col);
write_all(STDOUT_FILENO, buf, n); 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) { 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; int master_fd = -1;
struct winsize ws{}; struct winsize ws{};
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
+2 -1
View File
@@ -165,12 +165,13 @@ void Parser::operation() {
j++; j++;
} }
std::string replacement(peek_str(j)); std::string replacement(peek_str(j));
std::string options;
if (peek(j) != '\0') { if (peek(j) != '\0') {
advance(j + 1); advance(j + 1);
} else { } else {
advance(j); advance(j);
options = "p";
} }
std::string options;
if (peek() != '\0') { if (peek() != '\0') {
options = std::string(peek_str()); options = std::string(peek_str());
advance(peek_str().size()); advance(peek_str().size());
+11
View File
@@ -69,6 +69,17 @@ CommandIO::CommandIO(BEd &bed) : bed(bed) {
} }
std::pair<std::string, bool> CommandIO::run() { 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 [row, col] = bed.io.cursor_position();
auto [rows, cols] = bed.io.terminal_size(); auto [rows, cols] = bed.io.terminal_size();
if (row > rows) if (row > rows)
+17
View File
@@ -7,6 +7,23 @@ TextMode::TextMode(BEd &bed) : bed(bed) {
} }
std::pair<vase::Shard *, bool> TextMode::run() { 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 [row, col] = bed.io.cursor_position();
auto [rows, cols] = bed.io.terminal_size(); auto [rows, cols] = bed.io.terminal_size();
if (row > rows) if (row > rows)
+7 -14
View File
@@ -90,12 +90,7 @@ Shard *substitute(
if (matches.empty()) if (matches.empty())
return root; return root;
std::vector<ReplacePart> replace_parts = parse_replace(ap, replace); std::vector<ReplacePart> replace_parts = parse_replace(ap, replace);
struct Edit { uint64_t current_line = 1;
uint64_t line;
uint64_t old_lines;
uint64_t new_lines;
};
uint64_t orig_line = start;
int64_t line_delta = 0; int64_t line_delta = 0;
std::vector<Shard *> pieces; std::vector<Shard *> pieces;
pieces.reserve(matches.size() * 2 + 1); pieces.reserve(matches.size() * 2 + 1);
@@ -109,7 +104,7 @@ Shard *substitute(
Shard::release(remaining); Shard::release(remaining);
pieces.push_back(keep); pieces.push_back(keep);
remaining = rest; remaining = rest;
orig_line += keep ? keep->lines : 0; current_line += keep ? keep->lines : 0;
} }
auto [dropped, rest2] = Shard::split(remaining, match.end - match.start); auto [dropped, rest2] = Shard::split(remaining, match.end - match.start);
Shard::release(remaining); Shard::release(remaining);
@@ -152,13 +147,11 @@ Shard *substitute(
} }
} }
Shard::release(dropped); Shard::release(dropped);
if (old_lines || new_lines) { uint64_t report_line = (uint64_t)((int64_t)current_line + line_delta);
uint64_t report_line = (uint64_t)((int64_t)orig_line + line_delta); if (on_edit)
if (on_edit) on_edit(report_line, old_lines, new_lines);
on_edit(report_line, old_lines, new_lines); line_delta += (int64_t)new_lines - (int64_t)old_lines;
line_delta += (int64_t)new_lines - (int64_t)old_lines; current_line += old_lines;
}
orig_line += old_lines;
cursor = match.end; cursor = match.end;
} }
pieces.push_back(remaining); pieces.push_back(remaining);