Compare commits
5
Commits
956bdd6039
...
cd9583ec34
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd9583ec34
|
||
|
|
0104f96d51
|
||
|
|
abbfcc352a
|
||
|
|
f8975a60d8
|
||
|
|
33dc9ad507
|
@@ -14,10 +14,8 @@ struct HistoryItem {
|
||||
|
||||
struct GenericBuffer : ShardBuffer {
|
||||
uint64_t base_version{0};
|
||||
std::chrono::system_clock::time_point timestamp{
|
||||
std::chrono::system_clock::now()
|
||||
};
|
||||
std::string action{"created"};
|
||||
std::chrono::system_clock::time_point timestamp;
|
||||
std::string action;
|
||||
std::filesystem::path save_path{};
|
||||
std::vector<HistoryItem> undo_stack;
|
||||
std::vector<HistoryItem> redo_stack;
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@ void GenericBuffer::list_history(BEd &ctx) {
|
||||
std::tm tm = *std::localtime(&time);
|
||||
ctx.io.write_line(
|
||||
std::format(
|
||||
"X {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
||||
"* {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
||||
current,
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_mon + 1,
|
||||
@@ -58,7 +58,7 @@ void GenericBuffer::list_history(BEd &ctx) {
|
||||
std::tm tm = *std::localtime(&time);
|
||||
ctx.io.write_line(
|
||||
std::format(
|
||||
" {} {:04}-{:02}-{:02} {:02}:{:02}:{:02}",
|
||||
" {} {:04}-{:02}-{:02} {:02}:{:02}:{:02} {}",
|
||||
version,
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_mon + 1,
|
||||
@@ -90,15 +90,19 @@ HistoryBuffer *GenericBuffer::get_history(uint64_t version) {
|
||||
}
|
||||
|
||||
void GenericBuffer::snapshot(std::string action_) {
|
||||
vase::Shard::retain(root);
|
||||
undo_stack.push_back(
|
||||
HistoryItem{
|
||||
syntax::retain(parse),
|
||||
root,
|
||||
timestamp,
|
||||
action
|
||||
}
|
||||
);
|
||||
if (base_version == 0) {
|
||||
base_version++;
|
||||
} else {
|
||||
vase::Shard::retain(root);
|
||||
undo_stack.push_back(
|
||||
HistoryItem{
|
||||
syntax::retain(parse),
|
||||
root,
|
||||
timestamp,
|
||||
action
|
||||
}
|
||||
);
|
||||
}
|
||||
for (auto &item : redo_stack) {
|
||||
syntax::release(item.parse_state);
|
||||
vase::Shard::release(item.text);
|
||||
@@ -126,6 +130,8 @@ bool GenericBuffer::undo(BEd &ctx) {
|
||||
root = prev.text;
|
||||
syntax::release(parse);
|
||||
parse = prev.parse_state;
|
||||
timestamp = prev.timestamp;
|
||||
action = prev.summary;
|
||||
state = Modified;
|
||||
if (!root) {
|
||||
ctx.prev().buffername = name;
|
||||
@@ -157,6 +163,8 @@ bool GenericBuffer::redo(BEd &ctx) {
|
||||
root = next.text;
|
||||
syntax::release(parse);
|
||||
parse = next.parse_state;
|
||||
timestamp = next.timestamp;
|
||||
action = next.summary;
|
||||
state = Modified;
|
||||
if (!root) {
|
||||
ctx.prev().buffername = name;
|
||||
@@ -213,6 +221,7 @@ void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
|
||||
ctx.prev().start = 1;
|
||||
ctx.prev().end = text->lines + 1;
|
||||
}
|
||||
ctx.current() = {name, lines()};
|
||||
syntax::release(parse);
|
||||
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().start = line + 1;
|
||||
ctx.prev().end = line + text->lines + 1;
|
||||
ctx.current() = {name, line + text->lines + 1};
|
||||
root = vase::insert(&ctx.append, root, text, line);
|
||||
ctx.marks.insert(name, line, text->lines + 1);
|
||||
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));
|
||||
root = vase::erase(root, start_line, end_line);
|
||||
ctx.prev().buffername = name;
|
||||
ctx.prev().start = std::min(start_line, lines());
|
||||
ctx.prev().end = std::min(start_line, lines());
|
||||
uint64_t l = 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);
|
||||
if (parse.lang)
|
||||
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().start = start_line;
|
||||
ctx.prev().end = start_line + text->lines;
|
||||
ctx.current() = {name, start_line + text->lines};
|
||||
uint64_t new_count = text->lines + 1;
|
||||
uint64_t old_count = end_line - start_line + 1;
|
||||
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().start = start_line;
|
||||
ctx.prev().end = start_line;
|
||||
ctx.current() = {name, start_line};
|
||||
ctx.marks.collapse(name, start_line, end_line - start_line);
|
||||
if (parse.lang)
|
||||
syntax::erase(parse, root, start_line, end_line - start_line);
|
||||
@@ -321,11 +335,11 @@ void GenericBuffer::substitute(
|
||||
if (edit)
|
||||
edit->insert(line, new_lines);
|
||||
}
|
||||
ctx.current() = {name, line + new_lines};
|
||||
}
|
||||
);
|
||||
if (edit)
|
||||
edit->commit(root);
|
||||
ctx.prev().buffername = name;
|
||||
state = Modified;
|
||||
}
|
||||
} // namespace bed::internal::buffer
|
||||
|
||||
@@ -4,6 +4,79 @@
|
||||
|
||||
namespace bed::internal::functions {
|
||||
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(
|
||||
"cd",
|
||||
Function{
|
||||
@@ -93,7 +166,6 @@ void Function::register_extented(BEd &ctx) {
|
||||
vase::Shard::release(text);
|
||||
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(
|
||||
"history-list",
|
||||
"hl",
|
||||
Function{
|
||||
.address_kind = Function::AddressKind::None,
|
||||
.argument_kind = Function::ArgumentKind::None,
|
||||
|
||||
@@ -47,7 +47,6 @@ void Function::register_posix(BEd &ctx) {
|
||||
) {
|
||||
auto addr = std::get<buffer::Line>(addr_);
|
||||
ctx.buffer(addr.buffername).append(ctx, text, addr.number);
|
||||
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
|
||||
vase::Shard::release(text);
|
||||
}
|
||||
}
|
||||
@@ -71,7 +70,6 @@ void Function::register_posix(BEd &ctx) {
|
||||
) {
|
||||
auto addr = std::get<buffer::Range>(addr_);
|
||||
ctx.buffer(addr.buffername).replace(ctx, text, addr.start, addr.end);
|
||||
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
|
||||
vase::Shard::release(text);
|
||||
}
|
||||
}
|
||||
@@ -95,7 +93,6 @@ void Function::register_posix(BEd &ctx) {
|
||||
) {
|
||||
auto addr = std::get<buffer::Range>(addr_);
|
||||
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);
|
||||
throw;
|
||||
}
|
||||
ctx.io.write_line(std::format("{}", buf.bytes()));
|
||||
ctx.current() = {addr, buf.lines()};
|
||||
if (!ctx.suppress_mode)
|
||||
ctx.io.write_line(std::format("{}", buf.bytes()));
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -190,8 +187,8 @@ void Function::register_posix(BEd &ctx) {
|
||||
vase::Shard::release(s);
|
||||
throw;
|
||||
}
|
||||
ctx.io.write_line(std::format("{}", buf.bytes()));
|
||||
ctx.current() = {addr, buf.lines()};
|
||||
if (!ctx.suppress_mode)
|
||||
ctx.io.write_line(std::format("{}", buf.bytes()));
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -289,7 +286,6 @@ void Function::register_posix(BEd &ctx) {
|
||||
if (addr.number)
|
||||
addr.number--;
|
||||
ctx.buffer(addr.buffername).append(ctx, text, addr.number);
|
||||
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
|
||||
vase::Shard::release(text);
|
||||
}
|
||||
}
|
||||
@@ -313,7 +309,6 @@ void Function::register_posix(BEd &ctx) {
|
||||
) {
|
||||
auto addr = std::get<buffer::Range>(addr_);
|
||||
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);
|
||||
throw;
|
||||
}
|
||||
ctx.current() = {arg.buffername, arg.number + addr.end - addr.start + 1};
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -457,9 +451,9 @@ void Function::register_posix(BEd &ctx) {
|
||||
"P",
|
||||
Function{
|
||||
.address_kind = Function::AddressKind::None,
|
||||
.argument_kind = Function::ArgumentKind::None,
|
||||
.argument_kind = Function::ArgumentKind::Any,
|
||||
.input_mode = Function::InputMode::None,
|
||||
.desc = "Toggle prompt.",
|
||||
.desc = "Toggle/set prompt.",
|
||||
.default_address = "",
|
||||
.accept_zero = false,
|
||||
.pre_text_mode = nullptr,
|
||||
@@ -467,13 +461,18 @@ void Function::register_posix(BEd &ctx) {
|
||||
BEd &ctx,
|
||||
const buffer::Address &,
|
||||
vase::Shard *,
|
||||
const Argument &,
|
||||
const Argument &arg_,
|
||||
std::vector<buffer::Line> *
|
||||
) {
|
||||
ctx.prompt_mode = !ctx.prompt_mode;
|
||||
if (ctx.prompt_mode && !ctx.prompt) {
|
||||
ctx.prompt = [](BEd &) { return "*"; };
|
||||
auto &arg = std::get<std::string>(arg_);
|
||||
if (arg.size()) {
|
||||
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 {
|
||||
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);
|
||||
} catch (...) {
|
||||
vase::Shard::release(s);
|
||||
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);
|
||||
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.");
|
||||
vase::write_file(path, text);
|
||||
};
|
||||
ctx.io.write(std::format("{}\n", text ? text->length + 1 : 0));
|
||||
vase::Shard::release(text);
|
||||
} catch (...) {
|
||||
vase::Shard::release(text);
|
||||
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));
|
||||
else
|
||||
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};
|
||||
},
|
||||
}
|
||||
@@ -774,7 +776,8 @@ void Function::register_posix(BEd &ctx) {
|
||||
if (ctx.escape_command(cmd, filename.string()))
|
||||
ctx.io.write(cmd + "\n");
|
||||
ctx.io.run_pty(cmd);
|
||||
ctx.io.write("!\n");
|
||||
if (!ctx.suppress_mode)
|
||||
ctx.io.write("!\n");
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -165,12 +165,13 @@ void Parser::operation() {
|
||||
j++;
|
||||
}
|
||||
std::string replacement(peek_str(j));
|
||||
std::string options;
|
||||
if (peek(j) != '\0') {
|
||||
advance(j + 1);
|
||||
} else {
|
||||
advance(j);
|
||||
options = "p";
|
||||
}
|
||||
std::string options;
|
||||
if (peek() != '\0') {
|
||||
options = std::string(peek_str());
|
||||
advance(peek_str().size());
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -90,12 +90,7 @@ Shard *substitute(
|
||||
if (matches.empty())
|
||||
return root;
|
||||
std::vector<ReplacePart> replace_parts = parse_replace(ap, replace);
|
||||
struct Edit {
|
||||
uint64_t line;
|
||||
uint64_t old_lines;
|
||||
uint64_t new_lines;
|
||||
};
|
||||
uint64_t orig_line = start;
|
||||
uint64_t current_line = 1;
|
||||
int64_t line_delta = 0;
|
||||
std::vector<Shard *> pieces;
|
||||
pieces.reserve(matches.size() * 2 + 1);
|
||||
@@ -109,7 +104,7 @@ Shard *substitute(
|
||||
Shard::release(remaining);
|
||||
pieces.push_back(keep);
|
||||
remaining = rest;
|
||||
orig_line += keep ? keep->lines : 0;
|
||||
current_line += keep ? keep->lines : 0;
|
||||
}
|
||||
auto [dropped, rest2] = Shard::split(remaining, match.end - match.start);
|
||||
Shard::release(remaining);
|
||||
@@ -152,13 +147,11 @@ Shard *substitute(
|
||||
}
|
||||
}
|
||||
Shard::release(dropped);
|
||||
if (old_lines || new_lines) {
|
||||
uint64_t report_line = (uint64_t)((int64_t)orig_line + line_delta);
|
||||
if (on_edit)
|
||||
on_edit(report_line, old_lines, new_lines);
|
||||
line_delta += (int64_t)new_lines - (int64_t)old_lines;
|
||||
}
|
||||
orig_line += old_lines;
|
||||
uint64_t report_line = (uint64_t)((int64_t)current_line + line_delta);
|
||||
if (on_edit)
|
||||
on_edit(report_line, old_lines, new_lines);
|
||||
line_delta += (int64_t)new_lines - (int64_t)old_lines;
|
||||
current_line += old_lines;
|
||||
cursor = match.end;
|
||||
}
|
||||
pieces.push_back(remaining);
|
||||
|
||||
Reference in New Issue
Block a user