Reqrite project as BEd specific.
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
#include "internal/address/address.h"
|
||||
|
||||
namespace bed::internal::address {
|
||||
Address::Address(std::string &cmd, uint64_t &i) {
|
||||
base = None{};
|
||||
auto skip_space = [&] {
|
||||
while (i < cmd.size() && (cmd[i] == ' ' || cmd[i] == '\t'))
|
||||
++i;
|
||||
};
|
||||
skip_space();
|
||||
if (i >= cmd.size())
|
||||
return;
|
||||
switch (cmd[i]) {
|
||||
case '.':
|
||||
base = Current();
|
||||
i++;
|
||||
break;
|
||||
case '$':
|
||||
base = Last();
|
||||
i++;
|
||||
break;
|
||||
case '\'': {
|
||||
i++;
|
||||
if (i < cmd.size() && 'a' <= cmd[i] && cmd[i] <= 'z')
|
||||
i++;
|
||||
else
|
||||
throw address_error("Invalid mark.");
|
||||
base = Mark(cmd[i]);
|
||||
} break;
|
||||
case '/': {
|
||||
i++;
|
||||
uint64_t start = i;
|
||||
while (true) {
|
||||
if (i >= cmd.size())
|
||||
break;
|
||||
if (cmd[i] == '/')
|
||||
break;
|
||||
else if (cmd[i] == '\\')
|
||||
i += 2;
|
||||
else if (cmd[i] == '[' && i + 1 < cmd.size() && cmd[i + 1] == '[')
|
||||
while (i < cmd.size() && !(cmd[i - 1] == ']' && cmd[i] == ']'))
|
||||
i++;
|
||||
else if (cmd[i] == '[')
|
||||
while (i < cmd.size() && cmd[i] != ']')
|
||||
i++;
|
||||
else
|
||||
i++;
|
||||
}
|
||||
base = RegexLine(Direction::Forward, cmd.substr(start, i - start));
|
||||
} break;
|
||||
case '?': {
|
||||
i++;
|
||||
uint64_t start = i;
|
||||
while (true) {
|
||||
if (i >= cmd.size())
|
||||
break;
|
||||
if (cmd[i] == '?')
|
||||
break;
|
||||
else if (cmd[i] == '\\')
|
||||
i += 2;
|
||||
else if (cmd[i] == '[' && i + 1 < cmd.size() && cmd[i + 1] == '[')
|
||||
while (i < cmd.size() && !(cmd[i - 1] == ']' && cmd[i] == ']'))
|
||||
i++;
|
||||
else if (cmd[i] == '[')
|
||||
while (i < cmd.size() && cmd[i] != ']')
|
||||
i++;
|
||||
else
|
||||
i++;
|
||||
}
|
||||
base = RegexLine(Direction::Backward, cmd.substr(start, i - start));
|
||||
} break;
|
||||
case '+': {
|
||||
base = Current();
|
||||
i++;
|
||||
skip_space();
|
||||
uint64_t start = i;
|
||||
int64_t num = 0;
|
||||
while (i < cmd.size() && '0' <= cmd[i] && cmd[i] <= '9') {
|
||||
num = num * 10 + (cmd[i] - '0');
|
||||
i++;
|
||||
}
|
||||
if (start == i)
|
||||
num = 1;
|
||||
offset += num;
|
||||
} break;
|
||||
case '-': {
|
||||
base = Current();
|
||||
i++;
|
||||
skip_space();
|
||||
uint64_t start = i;
|
||||
int64_t num = 0;
|
||||
while (i < cmd.size() && '0' <= cmd[i] && cmd[i] <= '9') {
|
||||
num = num * 10 + (cmd[i] - '0');
|
||||
i++;
|
||||
}
|
||||
if (start == i)
|
||||
num = 1;
|
||||
offset -= num;
|
||||
} break;
|
||||
default: {
|
||||
if ('0' <= cmd[i] && cmd[i] <= '9') {
|
||||
uint64_t num = 0;
|
||||
while (i < cmd.size() && '0' <= cmd[i] && cmd[i] <= '9') {
|
||||
num = num * 10 + (cmd[i] - '0');
|
||||
i++;
|
||||
}
|
||||
base = Number(num);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
skip_space();
|
||||
while (i < cmd.size() && (cmd[i] == '+' || cmd[i] == '-' || ('0' <= cmd[i] && cmd[i] <= '9'))) {
|
||||
bool positive = cmd[i] != '-';
|
||||
if (cmd[i] == '+' || cmd[i] == '-') {
|
||||
i++;
|
||||
skip_space();
|
||||
}
|
||||
uint64_t start = i;
|
||||
int64_t num = 0;
|
||||
while (i < cmd.size() && '0' <= cmd[i] && cmd[i] <= '9') {
|
||||
num = num * 10 + (cmd[i] - '0');
|
||||
i++;
|
||||
}
|
||||
if (start == i)
|
||||
num = 1;
|
||||
offset += positive ? num : -num;
|
||||
skip_space();
|
||||
}
|
||||
}
|
||||
} // namespace bed::internal::address
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "bed.h"
|
||||
#include "internal/address/address.h"
|
||||
|
||||
namespace bed::internal::address {
|
||||
Address::Result Address::handle(BEd &ctx, std::string &cmd, uint64_t &i) {
|
||||
bool prev_given = false;
|
||||
Address prev;
|
||||
Address curr;
|
||||
while (i < cmd.size()) {
|
||||
if (cmd[i] == '%') {
|
||||
prev_given = true;
|
||||
prev.base = Number(ctx.active->prev_range.start);
|
||||
prev.offset = 0;
|
||||
curr.base = Number(ctx.active->prev_range.end);
|
||||
curr.offset = 0;
|
||||
} else {
|
||||
curr = Address(cmd, i);
|
||||
}
|
||||
if (i < cmd.size() && (cmd[i] == ',' || cmd[i] == ';')) {
|
||||
if (std::holds_alternative<None>(curr.base)) {
|
||||
if (cmd[i] == ',')
|
||||
curr.base = Number(1);
|
||||
else
|
||||
curr.base = Current();
|
||||
curr.offset = 0;
|
||||
prev_given = false;
|
||||
} else {
|
||||
if (cmd[i] == ';')
|
||||
ctx.active->jump(curr.resolve(ctx));
|
||||
prev_given = true;
|
||||
}
|
||||
prev = std::move(curr);
|
||||
} else {
|
||||
if (std::holds_alternative<None>(curr.base)) {
|
||||
if (std::holds_alternative<std::monostate>(prev.base))
|
||||
return {};
|
||||
if (prev_given) {
|
||||
curr = prev;
|
||||
} else {
|
||||
curr.base = Last();
|
||||
curr.offset = 0;
|
||||
}
|
||||
return {{prev.resolve(ctx), curr.resolve(ctx)}, 2};
|
||||
}
|
||||
return {{curr.resolve(ctx)}, 1};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}; // namespace bed::internal::address
|
||||
@@ -0,0 +1,40 @@
|
||||
#include "bed.h"
|
||||
#include "internal/address/address.h"
|
||||
|
||||
namespace bed::internal::address {
|
||||
uint64_t Address::resolve(BEd &ctx) {
|
||||
uint64_t result = std::visit(
|
||||
[&](auto const &addr) -> uint64_t {
|
||||
uint64_t line = 0;
|
||||
|
||||
using T = std::decay_t<decltype(addr)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, std::monostate>) {
|
||||
throw address_error("empty address");
|
||||
} else if constexpr (std::is_same_v<T, None>) {
|
||||
throw address_error("no address");
|
||||
} else if constexpr (std::is_same_v<T, Current>) {
|
||||
line = ctx.active->line;
|
||||
} else if constexpr (std::is_same_v<T, Last>) {
|
||||
line = ctx.active->vase.lines();
|
||||
} else if constexpr (std::is_same_v<T, Number>) {
|
||||
line = addr.i;
|
||||
} else {
|
||||
throw ed_error("Unhandled address given.");
|
||||
}
|
||||
|
||||
if (offset < 0 && line < (uint64_t)-offset)
|
||||
throw address_error("Can't have negative addresses");
|
||||
line += offset;
|
||||
|
||||
if (line > ctx.active->vase.lines())
|
||||
throw address_error("Line number too high.");
|
||||
|
||||
return line;
|
||||
},
|
||||
base
|
||||
);
|
||||
|
||||
return result + offset;
|
||||
}
|
||||
}; // namespace bed::internal::address
|
||||
@@ -0,0 +1,161 @@
|
||||
#include "internal/buffer/buffer.h"
|
||||
|
||||
namespace bed::internal::buffer {
|
||||
Buffer::Buffer() : vase("/tmp") {
|
||||
line = vase.lines();
|
||||
modified = false;
|
||||
}
|
||||
|
||||
Buffer::Buffer(std::string command) : vase(command, "/tmp") {
|
||||
line = vase.lines();
|
||||
if (!line)
|
||||
return;
|
||||
prev_range.start = 1;
|
||||
prev_range.end = line;
|
||||
modified = false;
|
||||
}
|
||||
|
||||
Buffer::Buffer(std::filesystem::path path) : vase(path, "/tmp") {
|
||||
line = vase.lines();
|
||||
if (!line)
|
||||
return;
|
||||
prev_range.start = 1;
|
||||
prev_range.end = line;
|
||||
save_path = path;
|
||||
modified = false;
|
||||
}
|
||||
|
||||
void Buffer::load(std::string command) {
|
||||
vase::Vase new_vase = vase::Vase(command, "/tmp");
|
||||
vase = std::move(new_vase);
|
||||
line = vase.lines();
|
||||
if (!line) {
|
||||
prev_range.start = 0;
|
||||
prev_range.end = 0;
|
||||
} else {
|
||||
prev_range.start = 1;
|
||||
prev_range.end = line;
|
||||
}
|
||||
modified = false;
|
||||
}
|
||||
|
||||
void Buffer::load(std::filesystem::path path) {
|
||||
vase::Vase new_vase = vase::Vase(path, "/tmp");
|
||||
vase = std::move(new_vase);
|
||||
line = vase.lines();
|
||||
if (!line) {
|
||||
prev_range.start = 0;
|
||||
prev_range.end = 0;
|
||||
} else {
|
||||
prev_range.start = 1;
|
||||
prev_range.end = line;
|
||||
}
|
||||
save_path = path;
|
||||
modified = false;
|
||||
}
|
||||
|
||||
void Buffer::jump(uint64_t n_line) {
|
||||
if (n_line > vase.lines())
|
||||
throw ed_error("Line number too high.");
|
||||
line = n_line;
|
||||
prev_range.start = line;
|
||||
prev_range.end = line;
|
||||
}
|
||||
|
||||
void Buffer::append(std::string text, uint64_t line) {
|
||||
using namespace bed::internal::vase;
|
||||
Point p = {line, 0};
|
||||
if (!vase.lines()) {
|
||||
text.pop_back();
|
||||
} else if (line == vase.lines()) {
|
||||
p.row--;
|
||||
p.col = UINT64_MAX;
|
||||
text = "\n" + text;
|
||||
text.pop_back();
|
||||
}
|
||||
prev_range.start = p.row + 1;
|
||||
vase.insert(&p, text);
|
||||
prev_range.end = p.row + 1;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
void Buffer::remove(uint64_t start_line, uint64_t end_line) {
|
||||
vase.erase({{start_line - 1, 0}, {end_line, 0}});
|
||||
prev_range.start = start_line;
|
||||
prev_range.end = start_line;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
void Buffer::join(uint64_t start_line, uint64_t end_line) {
|
||||
vase.regex_search_replace(R"(\n)", {{start_line - 1, 0}, {end_line, 0}}, "", "g");
|
||||
prev_range.start = start_line;
|
||||
prev_range.end = start_line;
|
||||
modified = true;
|
||||
}
|
||||
|
||||
void Buffer::print(uint64_t start_line, uint64_t end_line) {
|
||||
vase::Iterator it = vase.iterate(start_line - 1, Direction::Forward);
|
||||
while (it.next() && start_line++ <= end_line)
|
||||
std::cout << it.line << std::endl;
|
||||
prev_range.start = start_line;
|
||||
prev_range.end = end_line;
|
||||
}
|
||||
|
||||
std::string Buffer::list_string(std::string_view s) {
|
||||
uint32_t width = 80;
|
||||
winsize ws{};
|
||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0)
|
||||
width = ws.ws_col;
|
||||
std::string out;
|
||||
out.reserve(s.size());
|
||||
const uint32_t max_width = width > 1 ? width - 1 : 1;
|
||||
uint32_t column = 0;
|
||||
auto append = [&](std::string_view text) {
|
||||
if (column + text.size() > max_width) {
|
||||
out += "\\\n";
|
||||
column = 0;
|
||||
}
|
||||
out += text;
|
||||
column += text.size();
|
||||
};
|
||||
for (unsigned char c : s) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
append("\\\\");
|
||||
break;
|
||||
case '$':
|
||||
append("\\$");
|
||||
break;
|
||||
case '\a':
|
||||
append("\\a");
|
||||
break;
|
||||
case '\b':
|
||||
append("\\b");
|
||||
break;
|
||||
case '\f':
|
||||
append("\\f");
|
||||
break;
|
||||
case '\r':
|
||||
append("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
append("\\t");
|
||||
break;
|
||||
case '\v':
|
||||
append("\\v");
|
||||
break;
|
||||
default:
|
||||
if (!std::isprint(c)) {
|
||||
char buf[5];
|
||||
std::snprintf(buf, sizeof(buf), "\\%03o", c);
|
||||
append(buf);
|
||||
} else {
|
||||
append(std::string_view((const char *)&c, 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
out += '$';
|
||||
return out;
|
||||
}
|
||||
} // namespace bed::internal::buffer
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "internal/commands/commands.h"
|
||||
#include "bed.h"
|
||||
#include "internal/commands/suffixes.h"
|
||||
|
||||
namespace bed::internal::commands {
|
||||
void Suffix::register_suffixes(BEd &ctx) {
|
||||
ctx.suffixes['p' - 'a'] = Suffix{
|
||||
.desc = "Prints current line.",
|
||||
.handle = [](BEd &ctx) {
|
||||
auto line = ctx.active->line;
|
||||
ctx.active->print(line, line);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void Command::register_posix(BEd &ctx) {
|
||||
ctx.no_op = Command{
|
||||
.address_mode = Command::AddressMode::Single,
|
||||
.suffix = Command::SuffixKind::None,
|
||||
.desc = "Prints a line and jumps to it (default: .+1)",
|
||||
.accept_zero = false,
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t> addresses, std::string_view) {
|
||||
uint64_t line;
|
||||
if (addresses.size())
|
||||
line = addresses[0];
|
||||
else
|
||||
line = ctx.active->line + 1;
|
||||
if (line == 0)
|
||||
throw ed_error("Line 0 is invalid.");
|
||||
ctx.active->jump(line);
|
||||
ctx.active->print(line, line);
|
||||
}
|
||||
};
|
||||
ctx.eof_op = Command{
|
||||
.address_mode = Command::AddressMode::None,
|
||||
.suffix = Command::SuffixKind::None,
|
||||
.desc = "Try quitting.",
|
||||
.accept_zero = false,
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t>, std::string_view) {
|
||||
for (auto &[name, buffer] : ctx.buffers)
|
||||
if (buffer->modified)
|
||||
throw ed_error("Buffer " + name + " modified.");
|
||||
throw fatal_error("", 0);
|
||||
}
|
||||
};
|
||||
ctx.commands.insert(
|
||||
"a",
|
||||
Command{
|
||||
.address_mode = Command::AddressMode::Single,
|
||||
.suffix = Command::SuffixKind::Suffix,
|
||||
.desc = "Append lines at address (default: .)",
|
||||
.accept_zero = true,
|
||||
.handle = [](BEd &, std::span<const uint64_t>, std::string_view) {
|
||||
// TODO: start a text editing session, then append its stuff.
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.commands.insert(
|
||||
"q",
|
||||
Command{
|
||||
.address_mode = Command::AddressMode::None,
|
||||
.suffix = Command::SuffixKind::None,
|
||||
.desc = "Try quitting.",
|
||||
.accept_zero = false,
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t>, std::string_view) {
|
||||
for (auto &[name, buffer] : ctx.buffers)
|
||||
if (buffer->modified)
|
||||
throw ed_error("Buffer " + name + " modified.");
|
||||
throw fatal_error("", 0);
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.commands.insert(
|
||||
"E",
|
||||
Command{
|
||||
.address_mode = Command::AddressMode::None,
|
||||
.suffix = Command::SuffixKind::Argument,
|
||||
.desc = "Load a file into the current buffer.",
|
||||
.accept_zero = true,
|
||||
.handle = [](BEd &ctx, std::span<const uint64_t>, std::string_view file) {
|
||||
bool empty = false;
|
||||
if (file.empty())
|
||||
empty = true;
|
||||
uint64_t i = 0;
|
||||
while (i < file.length() && (file[i] == ' ' || file[i] == '\t'))
|
||||
i++;
|
||||
if (i >= file.size())
|
||||
empty = true;
|
||||
if (empty) {
|
||||
if (ctx.active->save_path == "")
|
||||
throw ed_error("Need filename!");
|
||||
ctx.active->load(ctx.active->save_path);
|
||||
} else if (file[i] == '!') {
|
||||
file = file.substr(1);
|
||||
ctx.active->load(file);
|
||||
} else {
|
||||
ctx.active->load(std::filesystem::path(file));
|
||||
}
|
||||
std::cout << ctx.active->vase.length() << std::endl;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
} // namespace bed::internal::commands
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "internal/vase/vase.h"
|
||||
|
||||
namespace crib::internal::vase {
|
||||
namespace bed::internal::vase {
|
||||
AppendBuffer::AppendBuffer(std::filesystem::path base_dir) {
|
||||
base_dir /= "tapp.XXXXXX";
|
||||
char *s = strdup(base_dir.c_str());
|
||||
@@ -69,4 +69,4 @@ const char *AppendBuffer::read(uint64_t pos) {
|
||||
uint64_t AppendBuffer::length() {
|
||||
return current_size;
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "internal/vase/vase.h"
|
||||
|
||||
namespace crib::internal::vase {
|
||||
namespace bed::internal::vase {
|
||||
OriginalBuffer::OriginalBuffer(std::filesystem::path base_dir) {
|
||||
if (!std::filesystem::exists(base_dir) || !std::filesystem::is_directory(base_dir))
|
||||
throw std::runtime_error("Swap directory does not exist or is not a directory.");
|
||||
@@ -44,4 +44,4 @@ const char *OriginalBuffer::read(uint64_t pos) {
|
||||
uint64_t OriginalBuffer::length() {
|
||||
return len;
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "internal/vase/vase.h"
|
||||
|
||||
namespace crib::internal::vase {
|
||||
namespace bed::internal::vase {
|
||||
LineIterator::LineIterator(Shard *root, uint64_t line_num, Direction dir)
|
||||
: it(root, dir), dir(dir) {
|
||||
it.seek_line(line_num);
|
||||
@@ -72,4 +72,4 @@ bool LineIterator::next(std::string *line) {
|
||||
uint64_t LineIterator::byte_offset() {
|
||||
return last_line_offset;
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "internal/vase/vase.h"
|
||||
|
||||
namespace crib::internal::vase {
|
||||
namespace bed::internal::vase {
|
||||
PetalIterator::PetalIterator(Shard *r, Direction dir)
|
||||
: dir(dir), root(r) {
|
||||
if (!root)
|
||||
@@ -168,4 +168,4 @@ bool PetalIterator::next(const char **data, uint64_t *out_len) {
|
||||
uint64_t PetalIterator::byte_offset() {
|
||||
return last_offset;
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "internal/vase/vase.h"
|
||||
|
||||
namespace crib::internal::vase {
|
||||
namespace bed::internal::vase {
|
||||
std::vector<Vase::ReplacePart> Vase::parse_replace(std::string_view s) {
|
||||
std::vector<ReplacePart> parts;
|
||||
std::string constant;
|
||||
@@ -156,4 +156,4 @@ std::vector<Range> Vase::regex_search(
|
||||
result.push_back({point_of(match.start), point_of(match.end)});
|
||||
return result;
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "internal/vase/vase.h"
|
||||
|
||||
namespace crib::internal::vase {
|
||||
namespace bed::internal::vase {
|
||||
std::vector<Vase::RegexMatch> Vase::_regex_search(
|
||||
std::string_view pattern, Range range, std::string_view options
|
||||
) {
|
||||
@@ -244,4 +244,4 @@ std::vector<Vase::RegexMatch> Vase::_regex_search(
|
||||
|
||||
return results;
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "internal/vase/vase.h"
|
||||
|
||||
namespace crib::internal::vase {
|
||||
namespace bed::internal::vase {
|
||||
void Shard::retain(Shard *n) {
|
||||
if (n)
|
||||
n->refs++;
|
||||
@@ -424,4 +424,4 @@ void Shard::dump(Shard *node, int depth) {
|
||||
<< "\"\n";
|
||||
}
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "internal/vase/vase.h"
|
||||
|
||||
namespace crib::internal::vase {
|
||||
namespace bed::internal::vase {
|
||||
Vase::Vase(std::filesystem::path path, std::filesystem::path swapdir)
|
||||
: path(path), swapdir(swapdir) {
|
||||
if (!std::filesystem::exists(swapdir) || !std::filesystem::is_directory(swapdir))
|
||||
@@ -507,4 +507,4 @@ void Vase::move_lines(Point *point, uint64_t amount, Direction dir) {
|
||||
}
|
||||
clamp(point);
|
||||
}
|
||||
} // namespace crib::internal::vase
|
||||
} // namespace bed::internal::vase
|
||||
|
||||
Reference in New Issue
Block a user