Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd9583ec34
|
||
|
|
0104f96d51
|
||
|
|
abbfcc352a
|
||
|
|
f8975a60d8
|
||
|
|
33dc9ad507
|
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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");
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user