diff --git a/LICENSE.md b/LICENSE.md
index 3ffbdc7..6141996 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -627,7 +627,7 @@ attach them to the start of each source file to most effectively state
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
- Crib - Text editing tools.
+ BEd - Better Ed.
Copyright (C) 2026 Syed (me@syedm.dev)
This program is free software: you can redistribute it and/or modify
diff --git a/Makefile b/Makefile
index 9474637..9f765f3 100644
--- a/Makefile
+++ b/Makefile
@@ -6,8 +6,8 @@ INCLUDE_DIR := include
CXX ?= g++
CC ?= gcc
-TARGET_DEBUG := $(BIN_DIR)/crib-dbg
-TARGET_RELEASE := $(BIN_DIR)/crib
+TARGET_DEBUG := $(BIN_DIR)/bed-dbg
+TARGET_RELEASE := $(BIN_DIR)/bed
PCH_DIR_DEBUG := $(OBJ_DIR)/debug/pch
PCH_DIR_RELEASE := $(OBJ_DIR)/release/pch
diff --git a/README.md b/README.md
index 861cb44..b607986 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,4 @@
-# Crib
-
-A set of text editing tools (still in early development).
-
-### Modules
-
-#### `Vase`
-
-Vase is a avl piece tree like structure which handles loading, insertion, erasure, regex searching, regex replacing etc.
-
-done.
-
-#### `hl`
-
-`hl` is a stateful super fast syntax highlighter.
-
-Not done yet.
-
-#### `lsp`
-
-`lsp` is a generic plug for lsp's.
-
-
-It should support:
-- Lsp over ssh or stdin/out or unix sockets etc.
-- Handling multiple lsps at once (including over a single file.)
-- Converting offsets encoding between utf16 - utf8 according to lsp standards.
-- Loading lsp features etc. automatically.
-- Error handling.
-- And more.
-
-Not done yet.
-
-### BEd
+## BEd
Better ed.
An ed implementation (mostly posix compliant) but with:
@@ -65,11 +32,42 @@ An ed implementation (mostly posix compliant) but with:
- File operations, directory & files listing.
- Nerdfonts & themable colorized outputs.
- Better address marking with ranges.
+- text copy/paste & registers.
- & A lot more cool stuff.
-#### Implementation status:
+### 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.
+
+### Modules
+
+#### `Vase`
+
+Vase is a avl piece tree like structure which handles loading, insertion, erasure, regex searching, regex replacing etc.
+
+done.
+
+#### `hl`
+
+`hl` is a stateful super fast syntax highlighter.
+
+mostly done.
+
+#### `lsp`
+
+`lsp` is a generic plug for lsp's.
+
+
+It should support:
+- Lsp over ssh or stdin/out or unix sockets etc.
+- Handling multiple lsps at once (including over a single file.)
+- Converting offsets encoding between utf16 - utf8 according to lsp standards.
+- Loading lsp features etc. automatically.
+- Error handling.
+- And more.
+
+Not done yet.
+
diff --git a/flake.nix b/flake.nix
index 502e229..d7744cb 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,5 +1,5 @@
{
- description = "Crib - An IDE.";
+ description = "BEd - Better Ed.";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
@@ -47,17 +47,16 @@
'';
};
- crib = pkgs.stdenv.mkDerivation {
- pname = "crib";
+ bed = pkgs.stdenv.mkDerivation {
+ pname = "bed";
version = "0.1.0";
src = ./.;
nativeBuildInputs = with hostPkgs; [
+ bash
pkg-config
gnumake
- xxd
- bash
findutils
binutils
];
@@ -78,19 +77,19 @@
installPhase = ''
mkdir -p $out/bin
- cp bin/crib $out/bin/crib
+ cp bin/bed $out/bin/bed
'';
};
in
{
packages.${system} = {
- default = crib;
- crib = crib;
+ default = bed;
+ bed = bed;
mruby = mruby;
};
devShells.${system}.default = hostPkgs.mkShell {
- inputsFrom = [ crib ];
+ inputsFrom = [ bed ];
packages = [
hostPkgs.clang-tools
@@ -99,7 +98,6 @@
hostPkgs.gnumake
hostPkgs.bear
hostPkgs.ruby
- hostPkgs.xxd
hostPkgs.gdb
hostPkgs.binutils
hostPkgs.findutils
diff --git a/include/bed.h b/include/bed.h
new file mode 100644
index 0000000..dfd545a
--- /dev/null
+++ b/include/bed.h
@@ -0,0 +1,32 @@
+#pragma once
+
+#include "definitions.h"
+#include "internal/buffer/buffer.h"
+#include "internal/commands/commands.h"
+#include "internal/commands/suffixes.h"
+#include "internal/marks/marks.h"
+#include "pch.h"
+
+namespace bed {
+struct BEd {
+ internal::trie::Trie commands;
+ internal::commands::Command no_op;
+ internal::commands::Command eof_op;
+ std::array, 26> suffixes;
+
+ bool help_mode = false;
+ std::string last_help = "";
+ bool prompt_mode = true;
+ std::function prompt = nullptr;
+ bool suppress_mode = false;
+ std::string last_regex = "";
+
+ std::unordered_map buffers;
+ internal::buffer::Buffer *active;
+
+ BEd(std::vector args);
+ ~BEd();
+ void handle(std::string_view cmd, bool eof);
+ void run();
+};
+} // namespace bed
diff --git a/include/cli.h b/include/cli.h
deleted file mode 100644
index e54ae0b..0000000
--- a/include/cli.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#pragma once
-
-#include "pch.h"
-
-namespace crib::cli {
-struct Command {
- void (*run)(std::vector);
- std::string (*summary)();
- void (*help)();
-};
-
-struct cli_error : std::runtime_error {
- int exit_code;
- cli_error(std::string msg, int code)
- : std::runtime_error(std::move(msg)), exit_code(code) {}
-};
-
-extern bool use_colors;
-void help();
-int execute(const std::string &name, const Command &cmd, int argc, char *argv[]);
-int run(int argc, char *argv[]);
-} // namespace crib::cli
diff --git a/include/commands/bed/bed.h b/include/commands/bed/bed.h
deleted file mode 100644
index edfb1ed..0000000
--- a/include/commands/bed/bed.h
+++ /dev/null
@@ -1,99 +0,0 @@
-#pragma once
-
-#include "internal/vase/vase.h"
-#include "pch.h"
-
-namespace crib::commands::bed {
-struct bed_error : std::runtime_error {
- bed_error(const char *msg) : std::runtime_error(msg) {}
-};
-
-struct Command {
- enum struct Type : uint8_t {
- Invalid,
- Quit,
- ForceQuit,
- PromptToggle,
- HelpToggle,
- Help,
- Print,
- List,
- Number,
- Append,
- Insert,
- Change,
- Delete,
- Write,
- Join,
- Mark,
- Edit,
- ForceEdit,
- Filename,
- Dump,
- None
- } type;
-
- struct Address {
- enum struct Type : uint8_t {
- None,
- Current,
- Last,
- Number,
- Mark,
- SearchForward,
- SearchBackward
- } type;
- int64_t number = 0;
- std::string regex = "";
- int64_t offset = 0;
- char mark = '\0';
- };
-
- Address start{};
- Address end{};
- static constexpr uint8_t SEMICOLON = 0b01;
- static constexpr uint8_t RANGE = 0b10;
- uint8_t address_flags = 0;
- char suffix = '\0';
- std::string argument;
-};
-
-struct BEd {
- crib::internal::vase::Vase vase;
- uint64_t line = 0;
- bool modified = false;
- bool quitting = false;
- bool editing = false;
- uint64_t marks[26]{0};
- std::string last_regex = "";
- bool help_mode = false;
- bool suppress_mode = false;
- bool prompt_mode = false;
- std::string prompt = "*";
- std::filesystem::path save_path = "";
- std::string last_message = "";
-
- BEd(std::string file_path, bool suppress_mode, std::string prompt_)
- : vase("/tmp"), suppress_mode(suppress_mode), prompt(prompt_) {
- if (prompt == "")
- prompt = "*";
- else
- prompt_mode = true;
- if (file_path != "")
- handle("E " + file_path, false);
- }
-
- void parse_address(std::string_view cmd, uint64_t &i, Command::Address &addr);
- void resolve_address(Command::Address addr, uint64_t *out_line);
- Command parse(std::string cmd, bool eof);
- void append(std::string text, uint64_t line);
- void remove(uint64_t start_line, uint64_t end_line);
- std::string list_string(std::string_view s);
- void join(uint64_t start_line, uint64_t end_line);
- bool handle(std::string cmd, bool eof);
-};
-
-std::string summary();
-void help();
-void run(std::vector);
-} // namespace crib::commands::bed
diff --git a/include/definitions.h b/include/definitions.h
new file mode 100644
index 0000000..a633e7f
--- /dev/null
+++ b/include/definitions.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#include "internal/vase/vase.h"
+#include "pch.h"
+
+namespace bed {
+struct fatal_error : std::runtime_error {
+ uint8_t code;
+ fatal_error(std::string msg, uint8_t code)
+ : std::runtime_error(msg), code(code) {}
+};
+
+struct ed_error : std::runtime_error {
+ ed_error(std::string msg) : std::runtime_error(msg) {}
+};
+
+struct BEd;
+} // namespace bed
diff --git a/include/internal/address/address.h b/include/internal/address/address.h
new file mode 100644
index 0000000..50a98c7
--- /dev/null
+++ b/include/internal/address/address.h
@@ -0,0 +1,115 @@
+#pragma once
+
+#include "definitions.h"
+#include "internal/generic.h"
+#include "pch.h"
+
+namespace bed::internal::address {
+struct address_error : ed_error {
+ address_error(const char *msg) : ed_error(msg) {}
+};
+
+struct Address {
+ // % refers to range of whatever was resulted from the previous modification.
+ // it expands in theory to a Num,Num and so can be followed by chaining more adresses the ed way.
+ // this is handled and resolved by the handle function,
+ // the constuctor and resolve etc. are also only called in the handle function,
+ // i.e. handle is the only public facing API from this namespace & class for now.
+ //
+ // in case of any issue an instance of address_error(const char *) is thrown.
+
+ struct Result {
+ std::array data{};
+ uint8_t size{0};
+ std::span span(uint8_t max = UINT8_MAX) const {
+ return {data.data(), std::min(size, max)};
+ }
+ };
+
+ struct None {};
+
+ // Posix ed types of addressing.
+ struct Current {}; // .
+
+ struct Last {}; // $
+
+ struct Number { // n
+ uint64_t i;
+ };
+
+ struct Mark { // 'm
+ char m;
+ };
+
+ struct RegexLine { // /re/ or ?re?
+ internal::Direction dir;
+ std::string re;
+ };
+
+ // BEd Extended types of addressing.
+ struct Regex { // &/re/ or &?re?
+ internal::Direction dir; // returns next/previous line containing the regex
+ std::string re; // (not only if it matches full line)
+ };
+
+ struct Diagnostic { // #n# for diagnostic number n. (From lsp.)
+ uint16_t n;
+ };
+
+ struct DiagnosticNext { // ^ or % for previous/next diagnostic
+ internal::Direction dir;
+ };
+
+ struct SymbolDefinition { // goto symbol definition. (From lsp or using internal language parsers)
+ std::string sym;
+ };
+
+ struct SymbolReference { // goto nth symbol reference
+ uint16_t n;
+ std::string sym;
+ };
+
+ struct SymbolReferenceNext { // >sym> or
+ base{};
+
+ // they can then be followed by any number of +n or -n etc accumulating in.
+ int64_t offset = 0;
+
+ Address(std::string &cmd, uint64_t &i);
+ Address() = default;
+
+ uint64_t resolve(BEd &ctx);
+
+ static Result handle(BEd &ctx, std::string &cmd, uint64_t &i);
+};
+} // namespace bed::internal::address
diff --git a/include/internal/buffer/buffer.h b/include/internal/buffer/buffer.h
new file mode 100644
index 0000000..5ad24c3
--- /dev/null
+++ b/include/internal/buffer/buffer.h
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "definitions.h"
+#include "internal/marks/marks.h"
+#include "pch.h"
+
+namespace bed::internal::buffer {
+struct Buffer {
+ internal::vase::Vase vase;
+ internal::marks::MarksEngine marks;
+ uint64_t line = 0;
+ bool modified;
+ std::filesystem::path save_path = "";
+ struct {
+ uint64_t start{0};
+ uint64_t end{0};
+ } prev_range;
+
+ Buffer();
+ Buffer(std::string command);
+ Buffer(std::filesystem::path path);
+
+ ~Buffer() = default;
+
+ void load(std::string command);
+ void load(std::filesystem::path path);
+ void jump(uint64_t n_line);
+ void join(uint64_t start_line, uint64_t end_line);
+ void remove(uint64_t start_line, uint64_t end_line);
+ void append(std::string text, uint64_t line);
+ void print(uint64_t start_line, uint64_t end_line);
+ std::string list_string(std::string_view s);
+};
+} // namespace bed::internal::buffer
diff --git a/include/internal/commands/commands.h b/include/internal/commands/commands.h
new file mode 100644
index 0000000..e6aec59
--- /dev/null
+++ b/include/internal/commands/commands.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "definitions.h"
+#include "internal/trie/trie.h"
+#include "pch.h"
+
+namespace bed::internal::commands {
+struct Command {
+ enum struct AddressMode : uint8_t {
+ None,
+ Single,
+ Range
+ } address_mode;
+
+ enum struct SuffixKind : uint8_t {
+ None,
+ Suffix,
+ Argument,
+ Continuation
+ } suffix;
+
+ std::string desc;
+
+ bool accept_zero;
+
+ void (*handle)(BEd &, std::span, std::string_view);
+
+ static void register_posix(BEd &ctx);
+};
+} // namespace bed::internal::commands
diff --git a/include/internal/commands/suffixes.h b/include/internal/commands/suffixes.h
new file mode 100644
index 0000000..454ad7f
--- /dev/null
+++ b/include/internal/commands/suffixes.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "definitions.h"
+#include "pch.h"
+
+namespace bed::internal::commands {
+struct Suffix {
+ std::string desc;
+ void (*handle)(BEd &);
+
+ static void register_suffixes(BEd &ctx);
+};
+} // namespace bed::internal::commands
diff --git a/include/internal/vase/iterators/iter.h b/include/internal/generic.h
similarity index 62%
rename from include/internal/vase/iterators/iter.h
rename to include/internal/generic.h
index cf873ec..0d61e08 100644
--- a/include/internal/vase/iterators/iter.h
+++ b/include/internal/generic.h
@@ -1,9 +1,8 @@
#pragma once
-#include "../shard.h"
#include "pch.h"
-namespace crib::internal::vase {
+namespace bed::internal {
enum struct Direction : uint8_t {
Forward,
Backward
diff --git a/include/internal/marks/marks.h b/include/internal/marks/marks.h
new file mode 100644
index 0000000..3487c99
--- /dev/null
+++ b/include/internal/marks/marks.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "pch.h"
+
+namespace bed::internal::marks {
+struct MarksEngine {};
+} // namespace bed::internal::marks
diff --git a/include/internal/trie/trie.h b/include/internal/trie/trie.h
new file mode 100644
index 0000000..fde446a
--- /dev/null
+++ b/include/internal/trie/trie.h
@@ -0,0 +1,246 @@
+#pragma once
+
+#include "pch.h"
+
+namespace bed::internal::trie {
+template
+struct Trie {
+ using V = std::conditional_t, std::monostate, T>;
+
+ struct Node {
+ std::string edge;
+ std::optional value{};
+ std::vector children;
+ Node(std::string e = {}) : edge(std::move(e)) {}
+ ~Node() {
+ for (auto *c : children)
+ delete c;
+ };
+ } root;
+
+ bool case_sensitive;
+
+ Trie(bool cs = true) : case_sensitive(cs) {}
+
+ void insert(std::string_view key)
+ requires std::is_void_v
+ {
+ _insert(key, std::monostate{});
+ }
+
+ void insert(std::string_view key, V el)
+ requires(!std::is_void_v)
+ {
+ _insert(key, std::move(el));
+ }
+
+ void _insert(std::string_view key, V &&el) {
+ Node *current = &root;
+ uint64_t pos = 0;
+ while (pos < key.size()) {
+ Node *child = find_child(*current, key[pos]);
+ if (!child) {
+ auto *node = new Node(std::string(key.substr(pos)));
+ node->value = std::move(el);
+ current->children.push_back(node);
+ return;
+ }
+ const auto common = common_prefix(child->edge, key.substr(pos));
+ if (common == child->edge.size()) {
+ pos += common;
+ current = child;
+ continue;
+ }
+ auto *split = new Node(child->edge.substr(0, common));
+ child->edge.erase(0, common);
+ split->children.push_back(child);
+ current->children.erase(
+ std::find(current->children.begin(), current->children.end(), child)
+ );
+ current->children.push_back(split);
+ pos += common;
+ if (pos == key.size()) {
+ split->value = std::move(el);
+ return;
+ }
+ auto *node = new Node(std::string(key.substr(pos)));
+ node->value = std::move(el);
+ split->children.push_back(node);
+ return;
+ }
+ current->value = std::move(el);
+ }
+
+ void remove(std::string_view key) {
+ _remove(root, key, 0);
+ }
+
+ bool _remove(Node &node, std::string_view key, uint64_t pos) {
+ if (pos == key.size()) {
+ if (!node.value)
+ return false;
+ node.value.reset();
+ return true;
+ }
+ Node *child = find_child(node, key[pos]);
+ if (!child)
+ return false;
+ const auto remaining = key.substr(pos);
+ const auto common = common_prefix(child->edge, remaining);
+ if (common != child->edge.size())
+ return false;
+ const auto child_pos = pos + common;
+ if (!_remove(*child, key, child_pos))
+ return false;
+ if (!child->value && child->children.empty()) {
+ auto it = std::find(
+ node.children.begin(),
+ node.children.end(),
+ child
+ );
+ node.children.erase(it);
+ delete child;
+ return true;
+ }
+ if (!child->value && child->children.size() == 1) {
+ Node *grandchild = child->children.front();
+ child->edge += grandchild->edge;
+ child->value = std::move(grandchild->value);
+ child->children = std::move(grandchild->children);
+ grandchild->children.clear();
+ delete grandchild;
+ }
+ return true;
+ }
+
+ std::vector search(std::string_view prefix) {
+ std::vector result;
+ Node *current = &root;
+ std::string key;
+ uint64_t pos = 0;
+ while (pos < prefix.size()) {
+ Node *child = find_child(*current, prefix[pos]);
+ if (!child)
+ return result;
+ const auto remaining = prefix.substr(pos);
+ const auto common = common_prefix(child->edge, remaining);
+ if (common == 0)
+ return result;
+ if (common < child->edge.size()) {
+ if (common == remaining.size()) {
+ key += child->edge;
+ collect(*child, key, result);
+ return result;
+ }
+ return result;
+ }
+ key += child->edge;
+ pos += common;
+ current = child;
+ }
+ collect(*current, key, result);
+ return result;
+ }
+
+ static void collect(
+ const Node &node,
+ std::string &key,
+ std::vector &result
+ ) {
+ if (node.value)
+ result.push_back(key);
+ for (auto *child : node.children) {
+ const auto old_size = key.size();
+ key += child->edge;
+ collect(*child, key, result);
+ key.resize(old_size);
+ }
+ }
+
+ uint64_t longest_match(std::string_view input) {
+ Node *current = &root;
+ uint64_t pos = 0;
+ uint64_t longest = 0;
+ if (current->value)
+ longest = 0;
+ while (pos < input.size()) {
+ Node *child = find_child(*current, input[pos]);
+ if (!child)
+ break;
+ const auto remaining = input.substr(pos);
+ const auto common = common_prefix(child->edge, remaining);
+ if (common != child->edge.size())
+ break;
+ pos += common;
+ current = child;
+ if (current->value)
+ longest = pos;
+ }
+ return longest;
+ }
+
+ bool matches(std::string_view key)
+ requires(std::is_void_v)
+ {
+ Node *current = &root;
+ uint64_t pos = 0;
+ while (pos < key.size()) {
+ Node *child = find_child(*current, key[pos]);
+ if (!child)
+ return false;
+ const auto remaining = key.substr(pos);
+ const auto common = common_prefix(child->edge, remaining);
+ if (common != child->edge.size())
+ return false;
+ pos += common;
+ current = child;
+ }
+ return current->value.has_value();
+ }
+
+ std::optional get(std::string_view key)
+ requires(!std::is_void_v)
+ {
+ Node *current = &root;
+ uint64_t pos = 0;
+ while (pos < key.size()) {
+ Node *child = find_child(*current, key[pos]);
+ if (!child)
+ return std::nullopt;
+ const auto remaining = key.substr(pos);
+ const auto common = common_prefix(child->edge, remaining);
+ if (common != child->edge.size())
+ return std::nullopt;
+ pos += common;
+ current = child;
+ }
+ if (!current->value)
+ return std::nullopt;
+ return *current->value;
+ }
+
+ bool equal_char(char a, char b) const {
+ if (case_sensitive)
+ return a == b;
+ return std::tolower((unsigned char)a) == std::tolower((unsigned char)b);
+ }
+
+ uint64_t common_prefix(
+ std::string_view a,
+ std::string_view b
+ ) {
+ const auto n = std::min(a.size(), b.size());
+ uint64_t i = 0;
+ while (i < n && equal_char(a[i], b[i]))
+ ++i;
+ return i;
+ }
+
+ Node *find_child(Node &node, char first) {
+ for (auto *child : node.children)
+ if (equal_char(child->edge.front(), first))
+ return child;
+ return nullptr;
+ }
+};
+} // namespace bed::internal::trie
diff --git a/include/internal/vase/buffer/append.h b/include/internal/vase/buffer/append.h
index eac53ad..e258e6b 100644
--- a/include/internal/vase/buffer/append.h
+++ b/include/internal/vase/buffer/append.h
@@ -4,7 +4,7 @@
#include "buffer.h"
#include "pch.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
struct AppendBuffer : Buffer {
char *buf = nullptr;
uint64_t allocated_capacity = 0;
@@ -22,4 +22,4 @@ struct AppendBuffer : Buffer {
private:
void grow(uint64_t len);
};
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/include/internal/vase/buffer/buffer.h b/include/internal/vase/buffer/buffer.h
index fe54580..2f1812a 100644
--- a/include/internal/vase/buffer/buffer.h
+++ b/include/internal/vase/buffer/buffer.h
@@ -2,10 +2,10 @@
#include "pch.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
struct Buffer {
virtual const char *read(uint64_t pos) = 0;
virtual uint64_t length() = 0;
virtual ~Buffer() = default;
};
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/include/internal/vase/buffer/original.h b/include/internal/vase/buffer/original.h
index 689f36f..f2e9d41 100644
--- a/include/internal/vase/buffer/original.h
+++ b/include/internal/vase/buffer/original.h
@@ -3,7 +3,7 @@
#include "buffer.h"
#include "pch.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
struct OriginalBuffer : Buffer {
const char *buf;
uint64_t len;
@@ -16,4 +16,4 @@ struct OriginalBuffer : Buffer {
const char *read(uint64_t pos) override;
uint64_t length() override;
};
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/include/internal/vase/constants.h b/include/internal/vase/constants.h
index 648e5ba..a6b4579 100644
--- a/include/internal/vase/constants.h
+++ b/include/internal/vase/constants.h
@@ -2,6 +2,6 @@
#include "pch.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
constexpr uint64_t PETAL_SIZE_MAX = 32 * 1024;
}
diff --git a/include/internal/vase/iterators/line.h b/include/internal/vase/iterators/line.h
index 72514ee..9c12a04 100644
--- a/include/internal/vase/iterators/line.h
+++ b/include/internal/vase/iterators/line.h
@@ -4,7 +4,7 @@
#include "pch.h"
#include "petal.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
struct LineIterator {
PetalIterator it;
const char *chunk;
@@ -70,4 +70,4 @@ struct Iterator {
private:
LineIterator it;
};
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/include/internal/vase/iterators/petal.h b/include/internal/vase/iterators/petal.h
index 059d102..129f537 100644
--- a/include/internal/vase/iterators/petal.h
+++ b/include/internal/vase/iterators/petal.h
@@ -1,10 +1,10 @@
#pragma once
#include "../shard.h"
-#include "iter.h"
+#include "internal/generic.h"
#include "pch.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
struct PetalIterator {
Direction dir;
Shard *root = nullptr;
@@ -26,4 +26,4 @@ struct PetalIterator {
private:
Petal *_next(uint64_t *offset);
};
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/include/internal/vase/shard.h b/include/internal/vase/shard.h
index 1787f82..508317a 100644
--- a/include/internal/vase/shard.h
+++ b/include/internal/vase/shard.h
@@ -5,7 +5,7 @@
#include "constants.h"
#include "pch.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
struct Shard {
enum struct Kind : uint8_t {
Branch,
@@ -63,4 +63,4 @@ struct Petal : Shard {
source(source), pos(pos) {};
};
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/include/internal/vase/vase.h b/include/internal/vase/vase.h
index 0d673f8..1d01d38 100644
--- a/include/internal/vase/vase.h
+++ b/include/internal/vase/vase.h
@@ -7,7 +7,7 @@
#include "pch.h"
#include "shard.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
struct Point {
uint64_t row;
uint64_t col;
@@ -114,4 +114,4 @@ private:
std::string_view pattern, Range range, std::string_view options
);
};
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/src/bed.cc b/src/bed.cc
new file mode 100644
index 0000000..786bcf8
--- /dev/null
+++ b/src/bed.cc
@@ -0,0 +1,5 @@
+#include "bed.h"
+
+namespace bed {
+void run(std::vector) {}
+} // namespace bed
diff --git a/src/bed/handle.cc b/src/bed/handle.cc
new file mode 100644
index 0000000..3764b3d
--- /dev/null
+++ b/src/bed/handle.cc
@@ -0,0 +1,95 @@
+#include "bed.h"
+#include "internal/address/address.h"
+#include "internal/commands/commands.h"
+
+namespace bed {
+void BEd::handle(std::string_view cmd_, bool eof) {
+ using namespace internal::commands;
+ std::string cmd(cmd_);
+ if (eof) {
+ eof_op.handle(*this, {}, "");
+ return;
+ }
+ uint64_t i = 0;
+ auto skip_space = [&] {
+ while (i < cmd.size() && (cmd[i] == ' ' || cmd[i] == '\t'))
+ ++i;
+ };
+ auto addresses = internal::address::Address::handle(*this, cmd, i);
+ if (i >= cmd.size()) {
+ if (!no_op.accept_zero)
+ for (auto l : addresses.span())
+ if (l == 0)
+ throw ed_error("Invalid address given.");
+ switch (no_op.address_mode) {
+ case Command::AddressMode::None:
+ no_op.handle(*this, addresses.span(0), "");
+ break;
+ case Command::AddressMode::Single:
+ no_op.handle(*this, addresses.span(1), "");
+ break;
+ case Command::AddressMode::Range:
+ no_op.handle(*this, addresses.span(2), "");
+ break;
+ }
+ return;
+ }
+ uint64_t len = commands.longest_match(cmd.substr(i));
+ if (len == 0)
+ throw ed_error("Command not found.");
+ std::optional command_opt = commands.get(cmd.substr(i, len));
+ i += len;
+ if (!command_opt)
+ throw ed_error("Command error.");
+ Command command = command_opt.value();
+ std::string argument;
+ Suffix *suffix = nullptr;
+ switch (command.suffix) {
+ case Command::SuffixKind::None:
+ skip_space();
+ if (i < cmd.size())
+ throw ed_error("Command error.");
+ break;
+ case Command::SuffixKind::Suffix:
+ if (i < cmd.size()) {
+ if (suffixes[cmd[i] - 'a'].has_value())
+ suffix = &(suffixes[cmd[i++] - 'a'].value());
+ skip_space();
+ std::cout << i << " " << cmd << std::endl;
+ if (i < cmd.size())
+ throw ed_error("Command error.");
+ }
+ break;
+ case Command::SuffixKind::Argument:
+ if (i < cmd.size()) {
+ if (cmd[i] == ' ' || cmd[i] == '\t')
+ skip_space();
+ else
+ throw ed_error("Command Error.");
+ argument = cmd.substr(i);
+ }
+ break;
+ case Command::SuffixKind::Continuation:
+ argument = cmd.substr(i);
+ break;
+ }
+ if (!command.accept_zero)
+ for (auto l : addresses.span())
+ if (l == 0)
+ throw ed_error("Invalid address given.");
+ switch (command.address_mode) {
+ case Command::AddressMode::None:
+ command.handle(*this, addresses.span(0), argument);
+ break;
+ case Command::AddressMode::Single:
+ command.handle(*this, addresses.span(1), argument);
+ break;
+ case Command::AddressMode::Range:
+ command.handle(*this, addresses.span(2), argument);
+ break;
+ }
+ if (suffix)
+ suffix->handle(*this);
+ return;
+}
+} // namespace bed
diff --git a/src/bed/run.cc b/src/bed/run.cc
new file mode 100644
index 0000000..8e311c6
--- /dev/null
+++ b/src/bed/run.cc
@@ -0,0 +1,53 @@
+#include "bed.h"
+
+namespace bed {
+BEd::BEd(std::vector args) {
+ internal::commands::Command::register_posix(*this);
+ internal::commands::Suffix::register_suffixes(*this);
+ std::string prompt_ = "";
+ std::string file = "";
+ bool suppress = false;
+ for (size_t i = 1; i < args.size(); i++) {
+ if (args[i] == "-p") {
+ i++;
+ if (i >= args.size())
+ throw fatal_error("Prompt not specified!", 1);
+ prompt_ = args[i];
+ } else if (args[i] == "-s") {
+ suppress = true;
+ } else {
+ if (file.size())
+ throw fatal_error("Invalid arguments given.", 1);
+ file = args[i];
+ }
+ }
+ if (prompt_ != "")
+ prompt = [p = std::move(prompt_)](BEd &) { return p; };
+ else
+ prompt_mode = false;
+ suppress_mode = suppress;
+ active = new internal::buffer::Buffer();
+ buffers.emplace("0", active);
+ if (file != "")
+ handle("E " + file, false);
+}
+
+BEd::~BEd() {
+ for (auto &[_, buffer] : buffers)
+ delete buffer;
+}
+
+void BEd::run() {
+ while (true) {
+ std::string cmd;
+ if (prompt_mode)
+ std::cout << prompt(*this);
+ bool eof = !std::getline(std::cin, cmd);
+ try {
+ handle(cmd, eof);
+ } catch (ed_error &e) {
+ std::cout << e.what() << std::endl;
+ }
+ }
+}
+} // namespace bed
diff --git a/src/cli/cli.cc b/src/cli/cli.cc
deleted file mode 100644
index 596412b..0000000
--- a/src/cli/cli.cc
+++ /dev/null
@@ -1,72 +0,0 @@
-#include "cli.h"
-#include "commands/bed/bed.h"
-
-namespace crib::cli {
-static const std::unordered_map commands = {
- {"bed",
- {crib::commands::bed::run,
- crib::commands::bed::summary,
- crib::commands::bed::help}},
-};
-
-bool use_color() {
- if (std::getenv("NO_COLOR"))
- return false;
- if (std::getenv("CLICOLOR_FORCE"))
- return true;
- if (!isatty(STDERR_FILENO))
- return false;
- const char *term = std::getenv("TERM");
- if (!term || std::string_view(term) == "dumb" || std::string_view(term) == "xterm")
- return false;
- if (const char *clicolor = std::getenv("CLICOLOR"))
- return std::string_view(clicolor) != "0";
- return true;
-}
-
-bool use_colors = use_color();
-
-void help() {
- std::cout << "Usage: crib [options]\n\n"
- << "Where command is one of\n";
- for (auto &[name, cmd] : commands)
- std::cout << " " << name << " - " << cmd.summary() << "\n";
-}
-
-int execute(const std::string &name, const Command &cmd, int argc, char *argv[]) {
- try {
- std::vector args(argv, argv + argc);
- cmd.run(args);
- return 0;
- } catch (const cli_error &e) {
- std::cerr << "Error running " << name << ": " << e.what() << '\n';
- return e.exit_code;
- } catch (const std::exception &e) {
- std::cerr << "Error running " << name << ": " << e.what() << '\n';
- return 1;
- } catch (...) {
- std::cerr << "Unknown error running " << name << '\n';
- return 1;
- }
-}
-
-int run(int argc, char *argv[]) {
- std::string invoked_as = std::filesystem::path(argv[0]).filename();
-
- if (auto it = commands.find(invoked_as); it != commands.end())
- return execute(it->first, it->second, argc, argv);
-
- if (argc > 1)
- if (auto it = commands.find(argv[1]); it != commands.end())
- return execute(it->first, it->second, argc - 1, argv + 1);
-
- if (argc > 1 && std::string(argv[1]) == "--help") {
- help();
- return 0;
- }
-
- std::cerr << "Invalid command given.\n\n";
- help();
- return 2;
-}
-} // namespace crib::cli
diff --git a/src/commands/bed/bed.cc b/src/commands/bed/bed.cc
deleted file mode 100644
index 0ee9d90..0000000
--- a/src/commands/bed/bed.cc
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "commands/bed/bed.h"
-#include "cli.h"
-
-namespace crib::commands::bed {
-std::string summary() {
- return "Better Ed, a modern take on a posix compliant line editor.";
-}
-
-void help() {}
-
-void run(std::vector args) {
- std::string prompt = "";
- std::string file;
- bool suppress = false;
- for (size_t i = 1; i < args.size(); i++) {
- if (args[i] == "-p") {
- i++;
- if (i >= args.size())
- throw crib::cli::cli_error("Prompt not specified!", 1);
- prompt = args[i];
- } else if (args[i] == "-s") {
- suppress = true;
- } else {
- if (file.size())
- throw crib::cli::cli_error("Invalid arguments given.", 1);
- file = args[i];
- }
- }
- BEd bed(file, suppress, prompt);
- std::string command;
- while (true) {
- std::string cmd;
- if (bed.prompt_mode)
- std::cout << bed.prompt;
- bool eof = !std::getline(std::cin, cmd);
- if (!bed.handle(cmd, eof))
- return;
- }
-}
-} // namespace crib::commands::bed
diff --git a/src/commands/bed/handle.cc b/src/commands/bed/handle.cc
deleted file mode 100644
index f9c82c7..0000000
--- a/src/commands/bed/handle.cc
+++ /dev/null
@@ -1,384 +0,0 @@
-#include "commands/bed/bed.h"
-
-namespace crib::commands::bed {
-bool BEd::handle(std::string cmd, bool eof) {
- using namespace crib::internal::vase;
- try {
- if (line > vase.lines())
- line = vase.lines();
- Command command = parse(cmd, eof);
- bool was_quitting = quitting;
- quitting = false;
- bool was_editing = editing;
- editing = false;
- switch (command.type) {
- case Command::Type::Quit:
- if (modified && !was_quitting) {
- quitting = true;
- throw bed_error("Buffer modified.");
- } else {
- return false;
- }
- case Command::Type::ForceQuit:
- return false;
- case Command::Type::None: {
- if (command.start.type != Command::Address::Type::None) {
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line);
- else
- resolve_address(command.start, &line);
- } else {
- line++;
- }
- if (line == 0)
- throw bed_error("Line 0 is invalid.");
- if (line > vase.lines())
- 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 bed_error("Invalid command.");
- case Command::Type::HelpToggle:
- help_mode = !help_mode;
- if (help_mode && last_message != "")
- std::cout << last_message << std::endl;
- break;
- case Command::Type::Help:
- if (last_message != "")
- std::cout << last_message << std::endl;
- break;
- case Command::Type::PromptToggle:
- prompt_mode = !prompt_mode;
- break;
- case Command::Type::Print: {
- uint64_t line_start = line;
- uint64_t line_end = line;
- if (command.start.type != Command::Address::Type::None) {
- resolve_address(command.start, &line_start);
- line_end = line_start;
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line_end);
- }
- if (line_start == 0)
- throw bed_error("Line 0 is invalid.");
- if (line_end < line_start)
- 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;
- line = line_end;
- } break;
- case Command::Type::List: {
- uint64_t line_start = line;
- uint64_t line_end = line;
- if (command.start.type != Command::Address::Type::None) {
- resolve_address(command.start, &line_start);
- line_end = line_start;
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line_end);
- }
- if (line_start == 0)
- throw bed_error("Line 0 is invalid.");
- if (line_end < line_start)
- 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;
- line = line_end;
- } break;
- case Command::Type::Number: {
- uint64_t line_start = line;
- uint64_t line_end = line;
- if (command.start.type != Command::Address::Type::None) {
- resolve_address(command.start, &line_start);
- line_end = line_start;
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line_end);
- }
- if (line_start == 0)
- throw bed_error("Line 0 is invalid.");
- if (line_end < line_start)
- 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;
- line = line_end;
- } break;
- case Command::Type::Append: {
- if (command.start.type != Command::Address::Type::None) {
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line);
- else
- resolve_address(command.start, &line);
- }
- std::string text;
- std::string cline;
- uint64_t line_count = 0;
- while (std::getline(std::cin, cline)) {
- if (cline == ".")
- break;
- line_count++;
- text.append(cline);
- text.push_back('\n');
- }
- if (text.empty()) {
- if (line == 0)
- line = 1;
- break;
- }
- vase.snapshot();
- append(text, line);
- modified = true;
- line += line_count;
- } break;
- case Command::Type::Insert: {
- if (command.start.type != Command::Address::Type::None) {
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line);
- else
- resolve_address(command.start, &line);
- }
- if (line == 0)
- line = 1;
- std::string text;
- std::string cline;
- uint64_t line_count = 0;
- while (std::getline(std::cin, cline)) {
- if (cline == ".")
- break;
- line_count++;
- text.append(cline);
- text.push_back('\n');
- }
- if (text.empty())
- break;
- vase.snapshot();
- append(text, line - 1);
- modified = true;
- line += line_count - 1;
- } break;
- case Command::Type::Change: {
- uint64_t line_start = line;
- uint64_t line_end = line;
- if (command.start.type != Command::Address::Type::None) {
- resolve_address(command.start, &line_start);
- if (line_start == 0)
- line_start = 1;
- line_end = line_start;
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line_end);
- }
- if (line_end < line_start)
- throw bed_error("Invalid address range.");
- std::string text;
- std::string cline;
- uint64_t line_count = 0;
- while (std::getline(std::cin, cline)) {
- if (cline == ".")
- break;
- line_count++;
- text.append(cline);
- text.push_back('\n');
- }
- vase.snapshot();
- remove(line_start, line_end);
- line = line_start;
- if (text.empty())
- break;
- append(text, line);
- modified = true;
- line += line_count;
- } break;
- case Command::Type::Delete: {
- uint64_t line_start = line;
- uint64_t line_end = line;
- if (command.start.type != Command::Address::Type::None) {
- resolve_address(command.start, &line_start);
- if (line_start == 0)
- line_start = 1;
- line_end = line_start;
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line_end);
- }
- if (line_end < line_start)
- throw bed_error("Invalid address range.");
- vase.snapshot();
- vase.erase({{line_start - 1, 0}, {line_end, 0}});
- modified = true;
- line = line_start;
- if (line > vase.lines())
- line = vase.lines();
- } break;
- case Command::Type::Write: {
- std::string path = command.argument;
- if (path.empty()) {
- path = save_path;
- if (path.empty())
- throw bed_error("Need file to write to.");
- }
- uint64_t line_start = 1;
- uint64_t line_end = vase.lines();
- if (vase.lines() == 0)
- line_start = line_end = 0;
- if (command.start.type != Command::Address::Type::None) {
- resolve_address(command.start, &line_start);
- line_end = line_start;
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line_end);
- }
- if (vase.lines() && line_start == 0)
- throw bed_error("Line 0 is invalid.");
- if (line_end < line_start)
- throw bed_error("Invalid address range.");
- uint64_t bytes = 0;
- if (path[0] == '!') {
- FILE *pipe = popen(path.c_str() + 1, "w");
- if (!pipe)
- 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 bed_error("Error writing to command.");
- }
- }
- if (pclose(pipe) == -1)
- throw bed_error("Error closing command.");
- } else {
- std::ofstream file(path, std::ios::out | std::ios::trunc);
- if (!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 bed_error("Error writing to file.");
- save_path = path;
- modified = false;
- }
- if (!suppress_mode)
- std::cout << bytes << std::endl;
- } break;
- case Command::Type::Join: {
- uint64_t line_start = line;
- uint64_t line_end = line + 1;
- if (command.start.type != Command::Address::Type::None) {
- resolve_address(command.start, &line_start);
- line_end = line_start;
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &line_end);
- }
- if (line_end < line_start)
- throw bed_error("Invalid address range.");
- if (line_start == 0)
- throw bed_error("Invalid line number given.");
- if (line_end == line_start)
- break;
- vase.snapshot();
- join(line_start, line_end);
- line = line_start;
- modified = true;
- } break;
- case Command::Type::Mark: {
- uint64_t mark_line = line;
- if (command.start.type != Command::Address::Type::None) {
- if (command.address_flags & Command::RANGE)
- resolve_address(command.end, &mark_line);
- else
- resolve_address(command.start, &mark_line);
- }
- if (mark_line == 0)
- 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 bed_error("Buffer modified.");
- }
- std::string path = command.argument;
- if (path.empty()) {
- path = save_path;
- if (path.empty())
- throw bed_error("Need file to read from.");
- }
- if (path[0] == '!') {
- path.erase(1);
- Vase new_vase(std::string(path), "/tmp");
- vase = std::move(new_vase);
- modified = true;
- } else {
- Vase new_vase(std::filesystem::path(path), "/tmp");
- vase = std::move(new_vase);
- save_path = path;
- modified = false;
- }
- line = vase.lines();
- if (!suppress_mode)
- std::cout << vase.length() << std::endl;
- } break;
- case Command::Type::ForceEdit: {
- std::string path = command.argument;
- if (path.empty()) {
- path = save_path;
- if (path.empty())
- throw bed_error("Need file to read from.");
- }
- if (path[0] == '!') {
- path.erase(0, 1);
- Vase new_vase(std::string(path), "/tmp");
- vase = std::move(new_vase);
- modified = true;
- } else {
- Vase new_vase(std::filesystem::path(path), "/tmp");
- vase = std::move(new_vase);
- save_path = path;
- modified = false;
- }
- line = vase.lines();
- std::fill(std::begin(marks), std::end(marks), '\0');
- if (!suppress_mode)
- std::cout << vase.length() << std::endl;
- } break;
- case Command::Type::Filename: {
- std::string path = command.argument;
- if (path.empty())
- throw bed_error("No filename given.");
- save_path = path;
- std::cout << save_path.string() << std::endl;
- } break;
- case Command::Type::Dump:
- Shard::dump(vase.root);
- break;
- }
- if (command.suffix) {
- Iterator it = vase.iterate(line - 1, Direction::Forward);
- it.next();
- switch (command.suffix) {
- case 'p':
- std::cout << it.line << std::endl;
- break;
- case 'n':
- std::cout << line << "\t" << it.line << std::endl;
- break;
- case 'l':
- std::cout << list_string(it.line) << std::endl;
- break;
- }
- }
- } catch (bed_error &e) {
- last_message = e.what();
- std::cout << "?" << std::endl;
- if (help_mode)
- std::cout << e.what() << std::endl;
- }
- return true;
-}
-} // namespace crib::commands::bed
diff --git a/src/commands/bed/helpers.cc b/src/commands/bed/helpers.cc
deleted file mode 100644
index c3c4e57..0000000
--- a/src/commands/bed/helpers.cc
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "commands/bed/bed.h"
-
-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()) {
- text.pop_back();
- } else if (line == vase.lines()) {
- p.row--;
- p.col = UINT64_MAX;
- text = "\n" + text;
- text.pop_back();
- }
- vase.insert(&p, text);
-}
-
-void BEd::remove(uint64_t start_line, uint64_t end_line) {
- vase.erase({{start_line - 1, 0}, {end_line, 0}});
-}
-
-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 BEd::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 crib::commands::bed
diff --git a/src/commands/bed/parsers/command.cc b/src/commands/bed/parsers/command.cc
deleted file mode 100644
index 035ad85..0000000
--- a/src/commands/bed/parsers/command.cc
+++ /dev/null
@@ -1,176 +0,0 @@
-#include "commands/bed/bed.h"
-
-namespace crib::commands::bed {
-Command BEd::parse(std::string cmd, bool eof) {
- Command command = {};
- if (eof)
- return (command.type = Command::Type::Quit, command);
- uint64_t i = 0;
- auto skip_space = [&] {
- while (i < cmd.size() && (cmd[i] == ' ' || cmd[i] == '\t'))
- ++i;
- };
- Command::Address first;
- Command::Address second;
- parse_address(cmd, i, first);
- bool have_range = false;
- while (i < cmd.size() && (cmd[i] == ',' || cmd[i] == ';')) {
- char sep = cmd[i++];
- if (sep == ';')
- resolve_address(first, &line);
- command.address_flags = sep == ';' ? Command::SEMICOLON : 0;
- if (have_range)
- first = second;
- parse_address(cmd, i, second);
- have_range = true;
- }
- if (have_range) {
- if (first.type != Command::Address::Type::None
- && second.type == Command::Address::Type::None) {
- second = first;
- } else if (first.type == Command::Address::Type::None) {
- if (command.address_flags & Command::SEMICOLON) {
- first.type = Command::Address::Type::Current;
- } else {
- first.type = Command::Address::Type::Number;
- first.number = 1;
- }
- if (second.type == Command::Address::Type::None)
- second.type = Command::Address::Type::Last;
- }
- command.address_flags |= Command::RANGE;
- command.start = first;
- command.end = second;
- } else {
- command.start = first;
- }
- if (i >= cmd.size()) {
- command.type = Command::Type::None;
- return command;
- }
- switch (cmd[i]) {
- case 'q':
- command.type = Command::Type::Quit;
- skip_space();
- if (i >= cmd.size())
- return command;
- throw bed_error("Invalid command.");
- case 'Q':
- command.type = Command::Type::ForceQuit;
- skip_space();
- if (i >= cmd.size())
- return command;
- throw bed_error("Invalid command.");
- case 'p':
- command.type = Command::Type::Print;
- i++;
- break;
- case 'l':
- command.type = Command::Type::List;
- i++;
- break;
- case 'n':
- command.type = Command::Type::Number;
- i++;
- break;
- case 'P':
- command.type = Command::Type::PromptToggle;
- i++;
- break;
- case 'H':
- command.type = Command::Type::HelpToggle;
- i++;
- break;
- case 'h':
- command.type = Command::Type::Help;
- i++;
- break;
- case 'a':
- command.type = Command::Type::Append;
- i++;
- break;
- case 'i':
- command.type = Command::Type::Insert;
- i++;
- break;
- case 'c':
- command.type = Command::Type::Change;
- i++;
- break;
- case 'd':
- command.type = Command::Type::Delete;
- i++;
- break;
- case 'j':
- command.type = Command::Type::Join;
- i++;
- break;
- case 'w':
- command.type = Command::Type::Write;
- i++;
- if (i >= cmd.size())
- return command;
- if (cmd[i] == ' ' || cmd[i] == '\t')
- skip_space();
- else
- throw bed_error("Invalid command.");
- command.argument = cmd.substr(i);
- return command;
- case 'e':
- command.type = Command::Type::Edit;
- i++;
- if (i >= cmd.size())
- return command;
- if (cmd[i] == ' ' || cmd[i] == '\t')
- skip_space();
- else
- throw bed_error("Invalid command.");
- command.argument = cmd.substr(i);
- return command;
- case 'E':
- command.type = Command::Type::ForceEdit;
- i++;
- if (i >= cmd.size())
- return command;
- if (cmd[i] == ' ' || cmd[i] == '\t')
- skip_space();
- else
- throw bed_error("Invalid command.");
- command.argument = cmd.substr(i);
- return command;
- case 'f':
- command.type = Command::Type::Filename;
- i++;
- if (i >= cmd.size())
- return command;
- if (cmd[i] == ' ' || cmd[i] == '\t')
- skip_space();
- else
- 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 bed_error("Invalid command.");
- if ('a' <= cmd[i] && cmd[i] <= 'z')
- command.argument = cmd[i];
- else
- throw bed_error("Invalid mark used.");
- break;
- case '#':
- command.type = Command::Type::Dump;
- i++;
- break;
- }
- if (i < cmd.size()) {
- if (cmd[i] == 'l' || cmd[i] == 'n' || cmd[i] == 'p')
- command.suffix = cmd[i++];
- skip_space();
- if (i != cmd.size())
- throw bed_error("Invalid command.");
- }
- return command;
-}
-} // namespace crib::commands::bed
diff --git a/src/commands/bed/parsers/regex.cc b/src/commands/bed/parsers/regex.cc
deleted file mode 100644
index e69de29..0000000
diff --git a/src/commands/bed/parsers/address.cc b/src/internal/address/address.cc
similarity index 54%
rename from src/commands/bed/parsers/address.cc
rename to src/internal/address/address.cc
index 105f77d..9813551 100644
--- a/src/commands/bed/parsers/address.cc
+++ b/src/internal/address/address.cc
@@ -1,36 +1,33 @@
-#include "commands/bed/bed.h"
+#include "internal/address/address.h"
-namespace crib::commands::bed {
-void BEd::parse_address(std::string_view cmd, uint64_t &i, Command::Address &addr) {
+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()) {
- addr.type = Command::Address::Type::None;
+ if (i >= cmd.size())
return;
- }
switch (cmd[i]) {
case '.':
- addr.type = Command::Address::Type::Current;
+ base = Current();
i++;
break;
case '$':
- addr.type = Command::Address::Type::Last;
+ base = Last();
i++;
break;
- case '\'':
- addr.type = Command::Address::Type::Mark;
+ case '\'': {
i++;
if (i < cmd.size() && 'a' <= cmd[i] && cmd[i] <= 'z')
i++;
else
- throw bed_error("Invalid mark.");
- addr.mark = cmd[i];
- break;
+ throw address_error("Invalid mark.");
+ base = Mark(cmd[i]);
+ } break;
case '/': {
- addr.type = Command::Address::Type::SearchForward;
i++;
uint64_t start = i;
while (true) {
@@ -49,10 +46,9 @@ void BEd::parse_address(std::string_view cmd, uint64_t &i, Command::Address &add
else
i++;
}
- addr.regex = cmd.substr(start, i - start);
+ base = RegexLine(Direction::Forward, cmd.substr(start, i - start));
} break;
case '?': {
- addr.type = Command::Address::Type::SearchBackward;
i++;
uint64_t start = i;
while (true) {
@@ -71,9 +67,10 @@ void BEd::parse_address(std::string_view cmd, uint64_t &i, Command::Address &add
else
i++;
}
- addr.regex = cmd.substr(start, i - start);
+ base = RegexLine(Direction::Backward, cmd.substr(start, i - start));
} break;
case '+': {
+ base = Current();
i++;
skip_space();
uint64_t start = i;
@@ -84,9 +81,10 @@ void BEd::parse_address(std::string_view cmd, uint64_t &i, Command::Address &add
}
if (start == i)
num = 1;
- addr.offset += num;
+ offset += num;
} break;
case '-': {
+ base = Current();
i++;
skip_space();
uint64_t start = i;
@@ -97,19 +95,17 @@ void BEd::parse_address(std::string_view cmd, uint64_t &i, Command::Address &add
}
if (start == i)
num = 1;
- addr.offset -= num;
+ offset -= num;
} break;
default: {
if ('0' <= cmd[i] && cmd[i] <= '9') {
- int64_t num = 0;
- addr.type = Command::Address::Type::Number;
+ uint64_t num = 0;
while (i < cmd.size() && '0' <= cmd[i] && cmd[i] <= '9') {
num = num * 10 + (cmd[i] - '0');
i++;
}
- addr.number = num;
+ base = Number(num);
} else {
- addr.type = Command::Address::Type::None;
return;
}
break;
@@ -130,49 +126,8 @@ void BEd::parse_address(std::string_view cmd, uint64_t &i, Command::Address &add
}
if (start == i)
num = 1;
- addr.offset += positive ? num : -num;
+ offset += positive ? num : -num;
skip_space();
}
}
-
-void BEd::resolve_address(Command::Address addr, uint64_t *out_line) {
- switch (addr.type) {
- case Command::Address::Type::None:
- break;
- case Command::Address::Type::Current:
- *out_line = line;
- if (*out_line > vase.lines())
- *out_line = vase.lines();
- break;
- case Command::Address::Type::Number:
- *out_line = addr.number;
- break;
- case Command::Address::Type::Last:
- *out_line = vase.lines();
- break;
- case Command::Address::Type::Mark:
- *out_line = marks[addr.mark - 'a'];
- if (!*out_line)
- throw bed_error("Unset mark used.");
- break;
- case Command::Address::Type::SearchForward:
- if (addr.regex == "")
- addr.regex = last_regex;
- last_regex = addr.regex;
- // TODO: use vase.regex_search(regex, range, options);
- break;
- case Command::Address::Type::SearchBackward:
- if (addr.regex == "")
- addr.regex = last_regex;
- last_regex = addr.regex;
- // TODO
- break;
- }
- if ((int64_t)*out_line + addr.offset < 0)
- throw bed_error("Line position can't be negative.");
- else
- *out_line += addr.offset;
- if (*out_line > vase.lines())
- throw bed_error("Line position too high.");
-}
-} // namespace crib::commands::bed
+} // namespace bed::internal::address
diff --git a/src/internal/address/handle.cc b/src/internal/address/handle.cc
new file mode 100644
index 0000000..8492e0d
--- /dev/null
+++ b/src/internal/address/handle.cc
@@ -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(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(curr.base)) {
+ if (std::holds_alternative(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
diff --git a/src/internal/address/resolve.cc b/src/internal/address/resolve.cc
new file mode 100644
index 0000000..28e14d7
--- /dev/null
+++ b/src/internal/address/resolve.cc
@@ -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;
+
+ if constexpr (std::is_same_v) {
+ throw address_error("empty address");
+ } else if constexpr (std::is_same_v) {
+ throw address_error("no address");
+ } else if constexpr (std::is_same_v) {
+ line = ctx.active->line;
+ } else if constexpr (std::is_same_v) {
+ line = ctx.active->vase.lines();
+ } else if constexpr (std::is_same_v) {
+ 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
diff --git a/src/internal/buffer/buffer.cc b/src/internal/buffer/buffer.cc
new file mode 100644
index 0000000..30e2ed7
--- /dev/null
+++ b/src/internal/buffer/buffer.cc
@@ -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
diff --git a/src/internal/commands/commands.cc b/src/internal/commands/commands.cc
new file mode 100644
index 0000000..b929e85
--- /dev/null
+++ b/src/internal/commands/commands.cc
@@ -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 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, 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, 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, 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, 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
diff --git a/src/internal/vase/buffer/append.cc b/src/internal/vase/buffer/append.cc
index 6691aac..f0000cd 100644
--- a/src/internal/vase/buffer/append.cc
+++ b/src/internal/vase/buffer/append.cc
@@ -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
diff --git a/src/internal/vase/buffer/original.cc b/src/internal/vase/buffer/original.cc
index 6ab5df0..9935b08 100644
--- a/src/internal/vase/buffer/original.cc
+++ b/src/internal/vase/buffer/original.cc
@@ -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
diff --git a/src/internal/vase/iterators/line.cc b/src/internal/vase/iterators/line.cc
index 81186b5..e878fad 100644
--- a/src/internal/vase/iterators/line.cc
+++ b/src/internal/vase/iterators/line.cc
@@ -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
diff --git a/src/internal/vase/iterators/petal.cc b/src/internal/vase/iterators/petal.cc
index 174166a..8d8756e 100644
--- a/src/internal/vase/iterators/petal.cc
+++ b/src/internal/vase/iterators/petal.cc
@@ -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
diff --git a/src/internal/vase/regex/api.cc b/src/internal/vase/regex/api.cc
index d564601..2cdbcf2 100644
--- a/src/internal/vase/regex/api.cc
+++ b/src/internal/vase/regex/api.cc
@@ -1,6 +1,6 @@
#include "internal/vase/vase.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
std::vector Vase::parse_replace(std::string_view s) {
std::vector parts;
std::string constant;
@@ -156,4 +156,4 @@ std::vector Vase::regex_search(
result.push_back({point_of(match.start), point_of(match.end)});
return result;
}
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/src/internal/vase/regex/core.cc b/src/internal/vase/regex/core.cc
index f4cdde8..d0a2fd8 100644
--- a/src/internal/vase/regex/core.cc
+++ b/src/internal/vase/regex/core.cc
@@ -1,6 +1,6 @@
#include "internal/vase/vase.h"
-namespace crib::internal::vase {
+namespace bed::internal::vase {
std::vector Vase::_regex_search(
std::string_view pattern, Range range, std::string_view options
) {
@@ -244,4 +244,4 @@ std::vector Vase::_regex_search(
return results;
}
-} // namespace crib::internal::vase
+} // namespace bed::internal::vase
diff --git a/src/internal/vase/shard.cc b/src/internal/vase/shard.cc
index ed87fb2..66045b2 100644
--- a/src/internal/vase/shard.cc
+++ b/src/internal/vase/shard.cc
@@ -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
diff --git a/src/internal/vase/vase.cc b/src/internal/vase/vase.cc
index c41985e..be4ca58 100644
--- a/src/internal/vase/vase.cc
+++ b/src/internal/vase/vase.cc
@@ -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
diff --git a/src/main.cc b/src/main.cc
index 734069c..0bd5f68 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -1,6 +1,18 @@
-#include "cli.h"
+#include "bed.h"
#include "pch.h"
int main(int argc, char *argv[]) {
- return crib::cli::run(argc, argv);
+ std::vector args(argv, argv + argc);
+ try {
+ bed::BEd ed(args);
+ ed.run();
+ } catch (bed::fatal_error &e) {
+ if (std::string_view(e.what()) != "")
+ std::cout << "Fatal error: " << e.what() << std::endl;
+ return e.code;
+ } catch (...) {
+ std::cout << "Unexpected error." << std::endl;
+ return 1;
+ }
+ return 0;
}