Rename ed to BEd

This commit is contained in:
2026-08-10 18:15:55 +01:00
parent 31a43e17bb
commit e2a79c60d9
9 changed files with 87 additions and 86 deletions
+9 -8
View File
@@ -10,12 +10,6 @@ Vase is a avl piece tree like structure which handles loading, insertion, erasur
<br/> <br/>
done. done.
#### Ed
Ed is an ed (the posix line editor) implementation using `Vase`.
<br/>
Mostly done.
#### `hl` #### `hl`
`hl` is a stateful super fast syntax highlighter. `hl` is a stateful super fast syntax highlighter.
@@ -38,10 +32,10 @@ It should support:
<br/> <br/>
Not done yet. Not done yet.
#### BEd ### BEd
Better ed.<br/> Better ed.<br/>
An ed implementation but with: An ed implementation (mostly posix compliant) but with:
- Lsp support for autocomplete suggetions when in ed text mode. - Lsp support for autocomplete suggetions when in ed text mode.
- Autocomplete for language snippets + other words found near the cursor etc. - Autocomplete for language snippets + other words found near the cursor etc.
- text mode where arrow keys etc. work as expected. - text mode where arrow keys etc. work as expected.
@@ -73,3 +67,10 @@ An ed implementation but with:
- Nerdfonts & themable colorized outputs. - Nerdfonts & themable colorized outputs.
- Better address marking with ranges. - Better address marking with ranges.
- & A lot more cool stuff. - & A lot more cool stuff.
#### Implementation status:
- Internal buffer:
- Super fast and memory efficient buffer implementation done.
- Loading from files/subshell commands done.
- Most of the posix ed commands except regex ones done.
@@ -3,9 +3,9 @@
#include "internal/vase/vase.h" #include "internal/vase/vase.h"
#include "pch.h" #include "pch.h"
namespace crib::commands::ed { namespace crib::commands::bed {
struct ed_error : std::runtime_error { struct bed_error : std::runtime_error {
ed_error(const char *msg) : std::runtime_error(msg) {} bed_error(const char *msg) : std::runtime_error(msg) {}
}; };
struct Command { struct Command {
@@ -58,7 +58,7 @@ struct Command {
std::string argument; std::string argument;
}; };
struct Ed { struct BEd {
crib::internal::vase::Vase vase; crib::internal::vase::Vase vase;
uint64_t line = 0; uint64_t line = 0;
bool modified = false; bool modified = false;
@@ -73,7 +73,7 @@ struct Ed {
std::filesystem::path save_path = ""; std::filesystem::path save_path = "";
std::string last_message = ""; std::string last_message = "";
Ed(std::string file_path, bool suppress_mode, std::string prompt_) BEd(std::string file_path, bool suppress_mode, std::string prompt_)
: vase("/tmp"), suppress_mode(suppress_mode), prompt(prompt_) { : vase("/tmp"), suppress_mode(suppress_mode), prompt(prompt_) {
if (prompt == "") if (prompt == "")
prompt = "*"; prompt = "*";
@@ -96,4 +96,4 @@ struct Ed {
std::string summary(); std::string summary();
void help(); void help();
void run(std::vector<std::string>); void run(std::vector<std::string>);
} // namespace crib::commands::ed } // namespace crib::commands::bed
+5 -5
View File
@@ -1,12 +1,12 @@
#include "cli.h" #include "cli.h"
#include "commands/ed/ed.h" #include "commands/bed/bed.h"
namespace crib::cli { namespace crib::cli {
static const std::unordered_map<std::string, Command> commands = { static const std::unordered_map<std::string, Command> commands = {
{"ed", {"bed",
{crib::commands::ed::run, {crib::commands::bed::run,
crib::commands::ed::summary, crib::commands::bed::summary,
crib::commands::ed::help}}, crib::commands::bed::help}},
}; };
bool use_color() { bool use_color() {
@@ -1,9 +1,9 @@
#include "commands/ed/ed.h" #include "commands/bed/bed.h"
#include "cli.h" #include "cli.h"
namespace crib::commands::ed { namespace crib::commands::bed {
std::string summary() { std::string summary() {
return "A POSIX-compliant line editor."; return "Better Ed, a modern take on a posix compliant line editor.";
} }
void help() {} void help() {}
@@ -26,15 +26,15 @@ void run(std::vector<std::string> args) {
file = args[i]; file = args[i];
} }
} }
Ed ed(file, suppress, prompt); BEd bed(file, suppress, prompt);
std::string command; std::string command;
while (true) { while (true) {
std::string cmd; std::string cmd;
if (ed.prompt_mode) if (bed.prompt_mode)
std::cout << ed.prompt; std::cout << bed.prompt;
bool eof = !std::getline(std::cin, cmd); bool eof = !std::getline(std::cin, cmd);
if (!ed.handle(cmd, eof)) if (!bed.handle(cmd, eof))
return; return;
} }
} }
} // namespace crib::commands::ed } // namespace crib::commands::bed
@@ -1,7 +1,7 @@
#include "commands/ed/ed.h" #include "commands/bed/bed.h"
namespace crib::commands::ed { namespace crib::commands::bed {
bool Ed::handle(std::string cmd, bool eof) { bool BEd::handle(std::string cmd, bool eof) {
using namespace crib::internal::vase; using namespace crib::internal::vase;
try { try {
if (line > vase.lines()) if (line > vase.lines())
@@ -15,7 +15,7 @@ bool Ed::handle(std::string cmd, bool eof) {
case Command::Type::Quit: case Command::Type::Quit:
if (modified && !was_quitting) { if (modified && !was_quitting) {
quitting = true; quitting = true;
throw ed_error("Buffer modified."); throw bed_error("Buffer modified.");
} else { } else {
return false; return false;
} }
@@ -31,15 +31,15 @@ bool Ed::handle(std::string cmd, bool eof) {
line++; line++;
} }
if (line == 0) if (line == 0)
throw ed_error("Line 0 is invalid."); throw bed_error("Line 0 is invalid.");
if (line > vase.lines()) if (line > vase.lines())
throw ed_error("Line position too high."); throw bed_error("Line position too high.");
Iterator it = vase.iterate(line - 1, Direction::Forward); Iterator it = vase.iterate(line - 1, Direction::Forward);
it.next(); it.next();
std::cout << it.line << std::endl; std::cout << it.line << std::endl;
} break; } break;
case Command::Type::Invalid: case Command::Type::Invalid:
throw ed_error("Invalid command."); throw bed_error("Invalid command.");
case Command::Type::HelpToggle: case Command::Type::HelpToggle:
help_mode = !help_mode; help_mode = !help_mode;
if (help_mode && last_message != "") if (help_mode && last_message != "")
@@ -62,9 +62,9 @@ bool Ed::handle(std::string cmd, bool eof) {
resolve_address(command.end, &line_end); resolve_address(command.end, &line_end);
} }
if (line_start == 0) if (line_start == 0)
throw ed_error("Line 0 is invalid."); throw bed_error("Line 0 is invalid.");
if (line_end < line_start) if (line_end < line_start)
throw ed_error("Invalid address range."); throw bed_error("Invalid address range.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward); Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start++ <= line_end) while (it.next() && line_start++ <= line_end)
std::cout << it.line << std::endl; std::cout << it.line << std::endl;
@@ -80,9 +80,9 @@ bool Ed::handle(std::string cmd, bool eof) {
resolve_address(command.end, &line_end); resolve_address(command.end, &line_end);
} }
if (line_start == 0) if (line_start == 0)
throw ed_error("Line 0 is invalid."); throw bed_error("Line 0 is invalid.");
if (line_end < line_start) if (line_end < line_start)
throw ed_error("Invalid address range."); throw bed_error("Invalid address range.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward); Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start++ <= line_end) while (it.next() && line_start++ <= line_end)
std::cout << list_string(it.line) << std::endl; std::cout << list_string(it.line) << std::endl;
@@ -98,9 +98,9 @@ bool Ed::handle(std::string cmd, bool eof) {
resolve_address(command.end, &line_end); resolve_address(command.end, &line_end);
} }
if (line_start == 0) if (line_start == 0)
throw ed_error("Line 0 is invalid."); throw bed_error("Line 0 is invalid.");
if (line_end < line_start) if (line_end < line_start)
throw ed_error("Invalid address range."); throw bed_error("Invalid address range.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward); Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start <= line_end) while (it.next() && line_start <= line_end)
std::cout << line_start++ << "\t" << it.line << std::endl; std::cout << line_start++ << "\t" << it.line << std::endl;
@@ -171,7 +171,7 @@ bool Ed::handle(std::string cmd, bool eof) {
resolve_address(command.end, &line_end); resolve_address(command.end, &line_end);
} }
if (line_end < line_start) if (line_end < line_start)
throw ed_error("Invalid address range."); throw bed_error("Invalid address range.");
std::string text; std::string text;
std::string cline; std::string cline;
uint64_t line_count = 0; uint64_t line_count = 0;
@@ -203,7 +203,7 @@ bool Ed::handle(std::string cmd, bool eof) {
resolve_address(command.end, &line_end); resolve_address(command.end, &line_end);
} }
if (line_end < line_start) if (line_end < line_start)
throw ed_error("Invalid address range."); throw bed_error("Invalid address range.");
vase.snapshot(); vase.snapshot();
vase.erase({{line_start - 1, 0}, {line_end, 0}}); vase.erase({{line_start - 1, 0}, {line_end, 0}});
modified = true; modified = true;
@@ -216,7 +216,7 @@ bool Ed::handle(std::string cmd, bool eof) {
if (path.empty()) { if (path.empty()) {
path = save_path; path = save_path;
if (path.empty()) if (path.empty())
throw ed_error("Need file to write to."); throw bed_error("Need file to write to.");
} }
uint64_t line_start = 1; uint64_t line_start = 1;
uint64_t line_end = vase.lines(); uint64_t line_end = vase.lines();
@@ -229,36 +229,36 @@ bool Ed::handle(std::string cmd, bool eof) {
resolve_address(command.end, &line_end); resolve_address(command.end, &line_end);
} }
if (vase.lines() && line_start == 0) if (vase.lines() && line_start == 0)
throw ed_error("Line 0 is invalid."); throw bed_error("Line 0 is invalid.");
if (line_end < line_start) if (line_end < line_start)
throw ed_error("Invalid address range."); throw bed_error("Invalid address range.");
uint64_t bytes = 0; uint64_t bytes = 0;
if (path[0] == '!') { if (path[0] == '!') {
FILE *pipe = popen(path.c_str() + 1, "w"); FILE *pipe = popen(path.c_str() + 1, "w");
if (!pipe) if (!pipe)
throw ed_error("Error starting command."); throw bed_error("Error starting command.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward); Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start++ <= line_end) { while (it.next() && line_start++ <= line_end) {
it.line += '\n'; it.line += '\n';
bytes += it.line.size(); bytes += it.line.size();
if (fputs(it.line.c_str(), pipe) == EOF) { if (fputs(it.line.c_str(), pipe) == EOF) {
pclose(pipe); pclose(pipe);
throw ed_error("Error writing to command."); throw bed_error("Error writing to command.");
} }
} }
if (pclose(pipe) == -1) if (pclose(pipe) == -1)
throw ed_error("Error closing command."); throw bed_error("Error closing command.");
} else { } else {
std::ofstream file(path, std::ios::out | std::ios::trunc); std::ofstream file(path, std::ios::out | std::ios::trunc);
if (!file) if (!file)
throw ed_error("Error writing to file."); throw bed_error("Error writing to file.");
Iterator it = vase.iterate(line_start - 1, Direction::Forward); Iterator it = vase.iterate(line_start - 1, Direction::Forward);
while (it.next() && line_start++ <= line_end) { while (it.next() && line_start++ <= line_end) {
file << it.line << '\n'; file << it.line << '\n';
bytes += it.line.size() + 1; bytes += it.line.size() + 1;
} }
if (!file) if (!file)
throw ed_error("Error writing to file."); throw bed_error("Error writing to file.");
save_path = path; save_path = path;
modified = false; modified = false;
} }
@@ -275,9 +275,9 @@ bool Ed::handle(std::string cmd, bool eof) {
resolve_address(command.end, &line_end); resolve_address(command.end, &line_end);
} }
if (line_end < line_start) if (line_end < line_start)
throw ed_error("Invalid address range."); throw bed_error("Invalid address range.");
if (line_start == 0) if (line_start == 0)
throw ed_error("Invalid line number given."); throw bed_error("Invalid line number given.");
if (line_end == line_start) if (line_end == line_start)
break; break;
vase.snapshot(); vase.snapshot();
@@ -294,20 +294,20 @@ bool Ed::handle(std::string cmd, bool eof) {
resolve_address(command.start, &mark_line); resolve_address(command.start, &mark_line);
} }
if (mark_line == 0) if (mark_line == 0)
throw ed_error("Invalid line number given."); throw bed_error("Invalid line number given.");
char mark = command.argument[0]; char mark = command.argument[0];
marks[mark - 'a'] = mark_line; marks[mark - 'a'] = mark_line;
} break; } break;
case Command::Type::Edit: { case Command::Type::Edit: {
if (modified && !was_editing) { if (modified && !was_editing) {
editing = true; editing = true;
throw ed_error("Buffer modified."); throw bed_error("Buffer modified.");
} }
std::string path = command.argument; std::string path = command.argument;
if (path.empty()) { if (path.empty()) {
path = save_path; path = save_path;
if (path.empty()) if (path.empty())
throw ed_error("Need file to read from."); throw bed_error("Need file to read from.");
} }
if (path[0] == '!') { if (path[0] == '!') {
path.erase(1); path.erase(1);
@@ -329,7 +329,7 @@ bool Ed::handle(std::string cmd, bool eof) {
if (path.empty()) { if (path.empty()) {
path = save_path; path = save_path;
if (path.empty()) if (path.empty())
throw ed_error("Need file to read from."); throw bed_error("Need file to read from.");
} }
if (path[0] == '!') { if (path[0] == '!') {
path.erase(0, 1); path.erase(0, 1);
@@ -350,7 +350,7 @@ bool Ed::handle(std::string cmd, bool eof) {
case Command::Type::Filename: { case Command::Type::Filename: {
std::string path = command.argument; std::string path = command.argument;
if (path.empty()) if (path.empty())
throw ed_error("No filename given."); throw bed_error("No filename given.");
save_path = path; save_path = path;
std::cout << save_path.string() << std::endl; std::cout << save_path.string() << std::endl;
} break; } break;
@@ -373,7 +373,7 @@ bool Ed::handle(std::string cmd, bool eof) {
break; break;
} }
} }
} catch (ed_error &e) { } catch (bed_error &e) {
last_message = e.what(); last_message = e.what();
std::cout << "?" << std::endl; std::cout << "?" << std::endl;
if (help_mode) if (help_mode)
@@ -381,4 +381,4 @@ bool Ed::handle(std::string cmd, bool eof) {
} }
return true; return true;
} }
} // namespace crib::commands::ed } // namespace crib::commands::bed
@@ -1,7 +1,7 @@
#include "commands/ed/ed.h" #include "commands/bed/bed.h"
namespace crib::commands::ed { namespace crib::commands::bed {
void Ed::append(std::string text, uint64_t line) { void BEd::append(std::string text, uint64_t line) {
using namespace crib::internal::vase; using namespace crib::internal::vase;
Point p = {line, 0}; Point p = {line, 0};
if (!vase.lines()) { if (!vase.lines()) {
@@ -15,15 +15,15 @@ void Ed::append(std::string text, uint64_t line) {
vase.insert(&p, text); vase.insert(&p, text);
} }
void Ed::remove(uint64_t start_line, uint64_t end_line) { void BEd::remove(uint64_t start_line, uint64_t end_line) {
vase.erase({{start_line - 1, 0}, {end_line, 0}}); vase.erase({{start_line - 1, 0}, {end_line, 0}});
} }
void Ed::join(uint64_t start_line, uint64_t end_line) { void BEd::join(uint64_t start_line, uint64_t end_line) {
vase.regex_search_replace("\\n", {{start_line - 1, 0}, {end_line, 0}}, "", ""); vase.regex_search_replace("\\n", {{start_line - 1, 0}, {end_line, 0}}, "", "");
} }
std::string Ed::list_string(std::string_view s) { std::string BEd::list_string(std::string_view s) {
uint32_t width = 80; uint32_t width = 80;
winsize ws{}; winsize ws{};
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0) if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col != 0)
@@ -80,4 +80,4 @@ std::string Ed::list_string(std::string_view s) {
out += '$'; out += '$';
return out; return out;
} }
} // namespace crib::commands::ed } // namespace crib::commands::bed
@@ -1,7 +1,7 @@
#include "commands/ed/ed.h" #include "commands/bed/bed.h"
namespace crib::commands::ed { namespace crib::commands::bed {
void Ed::parse_address(std::string_view cmd, uint64_t &i, Command::Address &addr) { void BEd::parse_address(std::string_view cmd, uint64_t &i, Command::Address &addr) {
auto skip_space = [&] { auto skip_space = [&] {
while (i < cmd.size() && (cmd[i] == ' ' || cmd[i] == '\t')) while (i < cmd.size() && (cmd[i] == ' ' || cmd[i] == '\t'))
++i; ++i;
@@ -26,7 +26,7 @@ void Ed::parse_address(std::string_view cmd, uint64_t &i, Command::Address &addr
if (i < cmd.size() && 'a' <= cmd[i] && cmd[i] <= 'z') if (i < cmd.size() && 'a' <= cmd[i] && cmd[i] <= 'z')
i++; i++;
else else
throw ed_error("Invalid mark."); throw bed_error("Invalid mark.");
addr.mark = cmd[i]; addr.mark = cmd[i];
break; break;
case '/': { case '/': {
@@ -135,7 +135,7 @@ void Ed::parse_address(std::string_view cmd, uint64_t &i, Command::Address &addr
} }
} }
void Ed::resolve_address(Command::Address addr, uint64_t *out_line) { void BEd::resolve_address(Command::Address addr, uint64_t *out_line) {
switch (addr.type) { switch (addr.type) {
case Command::Address::Type::None: case Command::Address::Type::None:
break; break;
@@ -153,7 +153,7 @@ void Ed::resolve_address(Command::Address addr, uint64_t *out_line) {
case Command::Address::Type::Mark: case Command::Address::Type::Mark:
*out_line = marks[addr.mark - 'a']; *out_line = marks[addr.mark - 'a'];
if (!*out_line) if (!*out_line)
throw ed_error("Unset mark used."); throw bed_error("Unset mark used.");
break; break;
case Command::Address::Type::SearchForward: case Command::Address::Type::SearchForward:
if (addr.regex == "") if (addr.regex == "")
@@ -169,10 +169,10 @@ void Ed::resolve_address(Command::Address addr, uint64_t *out_line) {
break; break;
} }
if ((int64_t)*out_line + addr.offset < 0) if ((int64_t)*out_line + addr.offset < 0)
throw ed_error("Line position can't be negative."); throw bed_error("Line position can't be negative.");
else else
*out_line += addr.offset; *out_line += addr.offset;
if (*out_line > vase.lines()) if (*out_line > vase.lines())
throw ed_error("Line position too high."); throw bed_error("Line position too high.");
} }
} // namespace crib::commands::ed } // namespace crib::commands::bed
@@ -1,7 +1,7 @@
#include "commands/ed/ed.h" #include "commands/bed/bed.h"
namespace crib::commands::ed { namespace crib::commands::bed {
Command Ed::parse(std::string cmd, bool eof) { Command BEd::parse(std::string cmd, bool eof) {
Command command = {}; Command command = {};
if (eof) if (eof)
return (command.type = Command::Type::Quit, command); return (command.type = Command::Type::Quit, command);
@@ -107,7 +107,7 @@ Command Ed::parse(std::string cmd, bool eof) {
if (cmd[i] == ' ' || cmd[i] == '\t') if (cmd[i] == ' ' || cmd[i] == '\t')
skip_space(); skip_space();
else else
throw ed_error("Invalid command."); throw bed_error("Invalid command.");
command.argument = cmd.substr(i); command.argument = cmd.substr(i);
return command; return command;
case 'e': case 'e':
@@ -118,7 +118,7 @@ Command Ed::parse(std::string cmd, bool eof) {
if (cmd[i] == ' ' || cmd[i] == '\t') if (cmd[i] == ' ' || cmd[i] == '\t')
skip_space(); skip_space();
else else
throw ed_error("Invalid command."); throw bed_error("Invalid command.");
command.argument = cmd.substr(i); command.argument = cmd.substr(i);
return command; return command;
case 'E': case 'E':
@@ -129,7 +129,7 @@ Command Ed::parse(std::string cmd, bool eof) {
if (cmd[i] == ' ' || cmd[i] == '\t') if (cmd[i] == ' ' || cmd[i] == '\t')
skip_space(); skip_space();
else else
throw ed_error("Invalid command."); throw bed_error("Invalid command.");
command.argument = cmd.substr(i); command.argument = cmd.substr(i);
return command; return command;
case 'f': case 'f':
@@ -140,18 +140,18 @@ Command Ed::parse(std::string cmd, bool eof) {
if (cmd[i] == ' ' || cmd[i] == '\t') if (cmd[i] == ' ' || cmd[i] == '\t')
skip_space(); skip_space();
else else
throw ed_error("Invalid command."); throw bed_error("Invalid command.");
command.argument = cmd.substr(i); command.argument = cmd.substr(i);
return command; return command;
case 'k': case 'k':
command.type = Command::Type::Mark; command.type = Command::Type::Mark;
i++; i++;
if (i >= cmd.size()) if (i >= cmd.size())
throw ed_error("Invalid command."); throw bed_error("Invalid command.");
if ('a' <= cmd[i] && cmd[i] <= 'z') if ('a' <= cmd[i] && cmd[i] <= 'z')
command.argument = cmd[i]; command.argument = cmd[i];
else else
throw ed_error("Invalid mark used."); throw bed_error("Invalid mark used.");
break; break;
case '#': case '#':
command.type = Command::Type::Dump; command.type = Command::Type::Dump;
@@ -163,8 +163,8 @@ Command Ed::parse(std::string cmd, bool eof) {
command.suffix = cmd[i++]; command.suffix = cmd[i++];
skip_space(); skip_space();
if (i != cmd.size()) if (i != cmd.size())
throw ed_error("Invalid command."); throw bed_error("Invalid command.");
} }
return command; return command;
} }
} // namespace crib::commands::ed } // namespace crib::commands::bed