diff --git a/README.md b/README.md index ad5223f..d7451f2 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,6 @@ Vase is a avl piece tree like structure which handles loading, insertion, erasur
done. -#### Ed - -Ed is an ed (the posix line editor) implementation using `Vase`. -
-Mostly done. - #### `hl` `hl` is a stateful super fast syntax highlighter. @@ -38,10 +32,10 @@ It should support:
Not done yet. -#### BEd +### BEd Better ed.
-An ed implementation but with: +An ed implementation (mostly posix compliant) but with: - Lsp support for autocomplete suggetions when in ed text mode. - Autocomplete for language snippets + other words found near the cursor etc. - text mode where arrow keys etc. work as expected. @@ -73,3 +67,10 @@ An ed implementation but with: - Nerdfonts & themable colorized outputs. - Better address marking with ranges. - & 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. diff --git a/include/commands/ed/ed.h b/include/commands/bed/bed.h similarity index 88% rename from include/commands/ed/ed.h rename to include/commands/bed/bed.h index afca18c..edfb1ed 100644 --- a/include/commands/ed/ed.h +++ b/include/commands/bed/bed.h @@ -3,9 +3,9 @@ #include "internal/vase/vase.h" #include "pch.h" -namespace crib::commands::ed { -struct ed_error : std::runtime_error { - ed_error(const char *msg) : std::runtime_error(msg) {} +namespace crib::commands::bed { +struct bed_error : std::runtime_error { + bed_error(const char *msg) : std::runtime_error(msg) {} }; struct Command { @@ -58,7 +58,7 @@ struct Command { std::string argument; }; -struct Ed { +struct BEd { crib::internal::vase::Vase vase; uint64_t line = 0; bool modified = false; @@ -73,7 +73,7 @@ struct Ed { std::filesystem::path save_path = ""; 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_) { if (prompt == "") prompt = "*"; @@ -96,4 +96,4 @@ struct Ed { std::string summary(); void help(); void run(std::vector); -} // namespace crib::commands::ed +} // namespace crib::commands::bed diff --git a/src/cli/cli.cc b/src/cli/cli.cc index 5bcb045..596412b 100644 --- a/src/cli/cli.cc +++ b/src/cli/cli.cc @@ -1,12 +1,12 @@ #include "cli.h" -#include "commands/ed/ed.h" +#include "commands/bed/bed.h" namespace crib::cli { static const std::unordered_map commands = { - {"ed", - {crib::commands::ed::run, - crib::commands::ed::summary, - crib::commands::ed::help}}, + {"bed", + {crib::commands::bed::run, + crib::commands::bed::summary, + crib::commands::bed::help}}, }; bool use_color() { diff --git a/src/commands/ed/ed.cc b/src/commands/bed/bed.cc similarity index 70% rename from src/commands/ed/ed.cc rename to src/commands/bed/bed.cc index 7f05e4b..0ee9d90 100644 --- a/src/commands/ed/ed.cc +++ b/src/commands/bed/bed.cc @@ -1,9 +1,9 @@ -#include "commands/ed/ed.h" +#include "commands/bed/bed.h" #include "cli.h" -namespace crib::commands::ed { +namespace crib::commands::bed { std::string summary() { - return "A POSIX-compliant line editor."; + return "Better Ed, a modern take on a posix compliant line editor."; } void help() {} @@ -26,15 +26,15 @@ void run(std::vector args) { file = args[i]; } } - Ed ed(file, suppress, prompt); + BEd bed(file, suppress, prompt); std::string command; while (true) { std::string cmd; - if (ed.prompt_mode) - std::cout << ed.prompt; + if (bed.prompt_mode) + std::cout << bed.prompt; bool eof = !std::getline(std::cin, cmd); - if (!ed.handle(cmd, eof)) + if (!bed.handle(cmd, eof)) return; } } -} // namespace crib::commands::ed +} // namespace crib::commands::bed diff --git a/src/commands/ed/handle.cc b/src/commands/bed/handle.cc similarity index 87% rename from src/commands/ed/handle.cc rename to src/commands/bed/handle.cc index ff021df..f9c82c7 100644 --- a/src/commands/ed/handle.cc +++ b/src/commands/bed/handle.cc @@ -1,7 +1,7 @@ -#include "commands/ed/ed.h" +#include "commands/bed/bed.h" -namespace crib::commands::ed { -bool Ed::handle(std::string cmd, bool eof) { +namespace crib::commands::bed { +bool BEd::handle(std::string cmd, bool eof) { using namespace crib::internal::vase; try { if (line > vase.lines()) @@ -15,7 +15,7 @@ bool Ed::handle(std::string cmd, bool eof) { case Command::Type::Quit: if (modified && !was_quitting) { quitting = true; - throw ed_error("Buffer modified."); + throw bed_error("Buffer modified."); } else { return false; } @@ -31,15 +31,15 @@ bool Ed::handle(std::string cmd, bool eof) { line++; } if (line == 0) - throw ed_error("Line 0 is invalid."); + throw bed_error("Line 0 is invalid."); 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); it.next(); std::cout << it.line << std::endl; } break; case Command::Type::Invalid: - throw ed_error("Invalid command."); + throw bed_error("Invalid command."); case Command::Type::HelpToggle: help_mode = !help_mode; if (help_mode && last_message != "") @@ -62,9 +62,9 @@ bool Ed::handle(std::string cmd, bool eof) { resolve_address(command.end, &line_end); } if (line_start == 0) - throw ed_error("Line 0 is invalid."); + throw bed_error("Line 0 is invalid."); 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); while (it.next() && line_start++ <= line_end) std::cout << it.line << std::endl; @@ -80,9 +80,9 @@ bool Ed::handle(std::string cmd, bool eof) { resolve_address(command.end, &line_end); } if (line_start == 0) - throw ed_error("Line 0 is invalid."); + throw bed_error("Line 0 is invalid."); 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); while (it.next() && line_start++ <= line_end) 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); } if (line_start == 0) - throw ed_error("Line 0 is invalid."); + throw bed_error("Line 0 is invalid."); 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); while (it.next() && line_start <= line_end) 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); } if (line_end < line_start) - throw ed_error("Invalid address range."); + throw bed_error("Invalid address range."); std::string text; std::string cline; uint64_t line_count = 0; @@ -203,7 +203,7 @@ bool Ed::handle(std::string cmd, bool eof) { resolve_address(command.end, &line_end); } if (line_end < line_start) - throw ed_error("Invalid address range."); + throw bed_error("Invalid address range."); vase.snapshot(); vase.erase({{line_start - 1, 0}, {line_end, 0}}); modified = true; @@ -216,7 +216,7 @@ bool Ed::handle(std::string cmd, bool eof) { if (path.empty()) { path = save_path; 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_end = vase.lines(); @@ -229,36 +229,36 @@ bool Ed::handle(std::string cmd, bool eof) { resolve_address(command.end, &line_end); } 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) - throw ed_error("Invalid address range."); + throw bed_error("Invalid address range."); uint64_t bytes = 0; if (path[0] == '!') { FILE *pipe = popen(path.c_str() + 1, "w"); if (!pipe) - throw ed_error("Error starting command."); + throw bed_error("Error starting command."); Iterator it = vase.iterate(line_start - 1, Direction::Forward); while (it.next() && line_start++ <= line_end) { it.line += '\n'; bytes += it.line.size(); if (fputs(it.line.c_str(), pipe) == EOF) { pclose(pipe); - throw ed_error("Error writing to command."); + throw bed_error("Error writing to command."); } } if (pclose(pipe) == -1) - throw ed_error("Error closing command."); + throw bed_error("Error closing command."); } else { std::ofstream file(path, std::ios::out | std::ios::trunc); 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); while (it.next() && line_start++ <= line_end) { file << it.line << '\n'; bytes += it.line.size() + 1; } if (!file) - throw ed_error("Error writing to file."); + throw bed_error("Error writing to file."); save_path = path; modified = false; } @@ -275,9 +275,9 @@ bool Ed::handle(std::string cmd, bool eof) { resolve_address(command.end, &line_end); } if (line_end < line_start) - throw ed_error("Invalid address range."); + throw bed_error("Invalid address range."); if (line_start == 0) - throw ed_error("Invalid line number given."); + throw bed_error("Invalid line number given."); if (line_end == line_start) break; vase.snapshot(); @@ -294,20 +294,20 @@ bool Ed::handle(std::string cmd, bool eof) { resolve_address(command.start, &mark_line); } if (mark_line == 0) - throw ed_error("Invalid line number given."); + throw bed_error("Invalid line number given."); char mark = command.argument[0]; marks[mark - 'a'] = mark_line; } break; case Command::Type::Edit: { if (modified && !was_editing) { editing = true; - throw ed_error("Buffer modified."); + throw bed_error("Buffer modified."); } std::string path = command.argument; if (path.empty()) { path = save_path; if (path.empty()) - throw ed_error("Need file to read from."); + throw bed_error("Need file to read from."); } if (path[0] == '!') { path.erase(1); @@ -329,7 +329,7 @@ bool Ed::handle(std::string cmd, bool eof) { if (path.empty()) { path = save_path; if (path.empty()) - throw ed_error("Need file to read from."); + throw bed_error("Need file to read from."); } if (path[0] == '!') { path.erase(0, 1); @@ -350,7 +350,7 @@ bool Ed::handle(std::string cmd, bool eof) { case Command::Type::Filename: { std::string path = command.argument; if (path.empty()) - throw ed_error("No filename given."); + throw bed_error("No filename given."); save_path = path; std::cout << save_path.string() << std::endl; } break; @@ -373,7 +373,7 @@ bool Ed::handle(std::string cmd, bool eof) { break; } } - } catch (ed_error &e) { + } catch (bed_error &e) { last_message = e.what(); std::cout << "?" << std::endl; if (help_mode) @@ -381,4 +381,4 @@ bool Ed::handle(std::string cmd, bool eof) { } return true; } -} // namespace crib::commands::ed +} // namespace crib::commands::bed diff --git a/src/commands/ed/helpers.cc b/src/commands/bed/helpers.cc similarity index 83% rename from src/commands/ed/helpers.cc rename to src/commands/bed/helpers.cc index f24b073..c3c4e57 100644 --- a/src/commands/ed/helpers.cc +++ b/src/commands/bed/helpers.cc @@ -1,7 +1,7 @@ -#include "commands/ed/ed.h" +#include "commands/bed/bed.h" -namespace crib::commands::ed { -void Ed::append(std::string text, uint64_t line) { +namespace crib::commands::bed { +void BEd::append(std::string text, uint64_t line) { using namespace crib::internal::vase; Point p = {line, 0}; if (!vase.lines()) { @@ -15,15 +15,15 @@ void Ed::append(std::string text, uint64_t line) { 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}}); } -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}}, "", ""); } -std::string Ed::list_string(std::string_view s) { +std::string BEd::list_string(std::string_view s) { uint32_t width = 80; winsize ws{}; 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 += '$'; return out; } -} // namespace crib::commands::ed +} // namespace crib::commands::bed diff --git a/src/commands/ed/parsers/address.cc b/src/commands/bed/parsers/address.cc similarity index 90% rename from src/commands/ed/parsers/address.cc rename to src/commands/bed/parsers/address.cc index ee68937..105f77d 100644 --- a/src/commands/ed/parsers/address.cc +++ b/src/commands/bed/parsers/address.cc @@ -1,7 +1,7 @@ -#include "commands/ed/ed.h" +#include "commands/bed/bed.h" -namespace crib::commands::ed { -void Ed::parse_address(std::string_view cmd, uint64_t &i, Command::Address &addr) { +namespace crib::commands::bed { +void BEd::parse_address(std::string_view cmd, uint64_t &i, Command::Address &addr) { auto skip_space = [&] { while (i < cmd.size() && (cmd[i] == ' ' || cmd[i] == '\t')) ++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') i++; else - throw ed_error("Invalid mark."); + throw bed_error("Invalid mark."); addr.mark = cmd[i]; break; 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) { case Command::Address::Type::None: break; @@ -153,7 +153,7 @@ void Ed::resolve_address(Command::Address addr, uint64_t *out_line) { case Command::Address::Type::Mark: *out_line = marks[addr.mark - 'a']; if (!*out_line) - throw ed_error("Unset mark used."); + throw bed_error("Unset mark used."); break; case Command::Address::Type::SearchForward: if (addr.regex == "") @@ -169,10 +169,10 @@ void Ed::resolve_address(Command::Address addr, uint64_t *out_line) { break; } 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 *out_line += addr.offset; 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 diff --git a/src/commands/ed/parsers/command.cc b/src/commands/bed/parsers/command.cc similarity index 89% rename from src/commands/ed/parsers/command.cc rename to src/commands/bed/parsers/command.cc index e86cd4d..93d414f 100644 --- a/src/commands/ed/parsers/command.cc +++ b/src/commands/bed/parsers/command.cc @@ -1,7 +1,7 @@ -#include "commands/ed/ed.h" +#include "commands/bed/bed.h" -namespace crib::commands::ed { -Command Ed::parse(std::string cmd, bool eof) { +namespace crib::commands::bed { +Command BEd::parse(std::string cmd, bool eof) { Command command = {}; if (eof) 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') skip_space(); else - throw ed_error("Invalid command."); + throw bed_error("Invalid command."); command.argument = cmd.substr(i); return command; case 'e': @@ -118,7 +118,7 @@ Command Ed::parse(std::string cmd, bool eof) { if (cmd[i] == ' ' || cmd[i] == '\t') skip_space(); else - throw ed_error("Invalid command."); + throw bed_error("Invalid command."); command.argument = cmd.substr(i); return command; case 'E': @@ -129,7 +129,7 @@ Command Ed::parse(std::string cmd, bool eof) { if (cmd[i] == ' ' || cmd[i] == '\t') skip_space(); else - throw ed_error("Invalid command."); + throw bed_error("Invalid command."); command.argument = cmd.substr(i); return command; case 'f': @@ -140,18 +140,18 @@ Command Ed::parse(std::string cmd, bool eof) { if (cmd[i] == ' ' || cmd[i] == '\t') skip_space(); else - throw ed_error("Invalid command."); + throw bed_error("Invalid command."); command.argument = cmd.substr(i); return command; case 'k': command.type = Command::Type::Mark; i++; if (i >= cmd.size()) - throw ed_error("Invalid command."); + throw bed_error("Invalid command."); if ('a' <= cmd[i] && cmd[i] <= 'z') command.argument = cmd[i]; else - throw ed_error("Invalid mark used."); + throw bed_error("Invalid mark used."); break; case '#': command.type = Command::Type::Dump; @@ -163,8 +163,8 @@ Command Ed::parse(std::string cmd, bool eof) { command.suffix = cmd[i++]; skip_space(); if (i != cmd.size()) - throw ed_error("Invalid command."); + throw bed_error("Invalid command."); } return command; } -} // namespace crib::commands::ed +} // namespace crib::commands::bed diff --git a/src/commands/ed/parsers/regex.cc b/src/commands/bed/parsers/regex.cc similarity index 100% rename from src/commands/ed/parsers/regex.cc rename to src/commands/bed/parsers/regex.cc