Add help and history prune commands

- And other minor fixes/cleanup.
This commit is contained in:
2026-09-03 11:40:07 +01:00
parent cd9583ec34
commit 662df68ab4
6 changed files with 218 additions and 42 deletions
+4 -2
View File
@@ -178,7 +178,7 @@ bool GenericBuffer::redo(BEd &ctx) {
return true;
}
void GenericBuffer::prune(int keep) {
uint64_t GenericBuffer::prune(int keep) {
size_t drop =
undo_stack.size() > (size_t)keep
? undo_stack.size() - keep
@@ -197,11 +197,13 @@ void GenericBuffer::prune(int keep) {
vase::Shard::release(item.text);
}
redo_stack.clear();
return undo_stack.size();
}
bool GenericBuffer::waste() {
return save_path.empty()
&& root == nullptr;
&& root == nullptr
&& undo_stack.empty();
}
void GenericBuffer::load(BEd &ctx, vase::Shard *text) {
+1 -1
View File
@@ -128,7 +128,7 @@ void ClipBuffer::substitute(
if (old_lines)
ctx.marks.erase(name, line, old_lines);
if (new_lines)
ctx.marks.insert(name, line, new_lines - 1);
ctx.marks.insert(name, line, new_lines);
}
);
clip_write(s);
+173 -11
View File
@@ -3,14 +3,134 @@
#include "internal/functions/suffixes.h"
namespace bed::internal::functions {
namespace ansi {
constexpr auto reset = "\033[0m";
constexpr auto bold = "\033[1m";
constexpr auto cyan = "\033[36m";
constexpr auto yellow = "\033[33m";
constexpr auto magenta = "\033[35m";
constexpr auto green = "\033[32m";
constexpr auto blue = "\033[34m";
} // namespace ansi
static void append_field(std::string &msg, std::string_view label, std::string_view label_color, std::string_view value) {
if (value.empty())
return;
msg += "\n";
msg += ansi::bold;
msg += label_color;
msg += label;
msg += ansi::reset;
msg += ": ";
msg += value;
}
static std::string_view address_kind_str(Function::AddressKind k) {
switch (k) {
case Function::AddressKind::None:
return "none";
case Function::AddressKind::Line:
return "1 line";
case Function::AddressKind::Range:
return "range";
}
return "";
}
static std::string_view input_mode_str(Function::InputMode m) {
switch (m) {
case Function::InputMode::None:
return "";
case Function::InputMode::Text:
return "text";
case Function::InputMode::Interactive:
return "interactive";
case Function::InputMode::CommandList:
return "command list";
}
return "";
}
static std::string_view argument_kind_str(Function::ArgumentKind a) {
switch (a) {
case Function::ArgumentKind::None:
return "";
case Function::ArgumentKind::Regex:
return "regex";
case Function::ArgumentKind::Shell:
return "shell command";
case Function::ArgumentKind::Any:
return "any";
case Function::ArgumentKind::File:
return "file";
case Function::ArgumentKind::Global:
return "ed global-style";
case Function::ArgumentKind::Mark:
return "mark name";
case Function::ArgumentKind::Number:
return "number";
case Function::ArgumentKind::Line:
return "address";
case Function::ArgumentKind::Range:
return "range";
case Function::ArgumentKind::Ruby:
return "ruby code";
}
return "";
}
static std::string describe_function(const Function &func) {
std::string msg = ansi::bold;
msg += func.desc;
msg += ansi::reset;
append_field(msg, "Default address", ansi::cyan, func.default_address);
append_field(msg, "Address", ansi::yellow, address_kind_str(func.address_kind));
if (func.accept_zero)
append_field(msg, "Zero address", ansi::magenta, "allowed");
append_field(msg, "Input", ansi::green, input_mode_str(func.input_mode));
append_field(msg, "Argument", ansi::blue, argument_kind_str(func.argument_kind));
return msg;
}
void Function::register_extented(BEd &ctx) {
ctx.functions.insert(
"*",
Function{
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::Any,
.input_mode = Function::InputMode::None,
.desc = "Explain a command",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](
BEd &ctx,
const buffer::Address &,
vase::Shard *,
const Argument &arg_,
std::vector<buffer::Line> *
) {
auto &str = std::get<std::string>(arg_);
if (str.empty()) {
ctx.io.write_line(describe_function(ctx.no_op));
return;
}
auto len = ctx.functions.longest_match(str);
if (len == str.size()) {
ctx.io.write_line(describe_function(*ctx.functions.get_ptr(str)));
return;
}
throw ed_error("Not a valid function name.");
},
}
);
ctx.functions.insert(
"#",
Function{
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::Any,
.input_mode = Function::InputMode::None,
.desc = "Comment.",
.desc = "Write a comment",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -29,7 +149,7 @@ void Function::register_extented(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::Any,
.input_mode = Function::InputMode::None,
.desc = "Echo given message.",
.desc = "Echo the given message (replacing $1-$4 with address information)",
.default_address = "",
.accept_zero = true,
.pre_text_mode = nullptr,
@@ -57,17 +177,30 @@ void Function::register_extented(BEd &ctx) {
str[i - 1] = '\n';
continue;
}
if (str[i] == '$' && i < str.size() && '1' <= str[i + 1] && str[i + 1] <= '2') {
bool one = str[i + 1] == '1';
if (str[i] == '$' && i < str.size() && '1' <= str[i + 1] && str[i + 1] <= '4') {
char c = str[i + 1];
str.erase(i, 2);
if (one) {
switch (c) {
case '1': {
auto start = std::to_string(addr.start);
str.insert(i, start);
i += start.size();
} else {
} break;
case '2': {
auto end = std::to_string(addr.end);
str.insert(i, end);
i += end.size();
} break;
case '3': {
str.insert(i, addr.buffername);
i += addr.buffername.size();
} break;
case '4': {
auto &buf = ctx.buffer(addr.buffername);
auto filename = buf.filename().string();
str.insert(i, filename);
i += filename.size();
} break;
}
continue;
}
@@ -83,7 +216,7 @@ void Function::register_extented(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::Any,
.input_mode = Function::InputMode::None,
.desc = "Change directory.",
.desc = "Change directory",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -121,7 +254,7 @@ void Function::register_extented(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Print directory.",
.desc = "Print the current working directory",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -145,7 +278,7 @@ void Function::register_extented(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::Range,
.input_mode = Function::InputMode::None,
.desc = "Exchange a range of lines for another.",
.desc = "Exchange a range of lines for another",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -175,7 +308,7 @@ void Function::register_extented(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Redo last undo.",
.desc = "Redo the last undo modification",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -202,7 +335,7 @@ void Function::register_extented(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "List history versions.",
.desc = "List available history versions",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -222,5 +355,34 @@ void Function::register_extented(BEd &ctx) {
}
}
);
ctx.functions.insert(
"hp",
Function{
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::Number,
.input_mode = Function::InputMode::None,
.desc = "Prune old history versions",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg_,
std::vector<buffer::Line> *
) {
auto &addr = std::get<std::string>(addr_);
auto &arg = std::get<int64_t>(arg_);
auto &buf_ = ctx.buffer(addr);
if (buf_.kind != buffer::Buffer::Kind::Generic)
throw ed_error("Can't prune buffer.");
auto &buf = *(buffer::GenericBuffer *)&buf_;
auto versions = buf.prune(arg);
if (!ctx.suppress_mode)
ctx.io.write_line(std::format("{} undo versions left.", versions));
}
}
);
}
} // namespace bed::internal::functions
+29 -26
View File
@@ -34,7 +34,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Line,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::Text,
.desc = "Append text to a line.",
.desc = "Append text to a line",
.default_address = ".",
.accept_zero = true,
.pre_text_mode = nullptr,
@@ -57,7 +57,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::Text,
.desc = "Change set of lines.",
.desc = "Change a range of lines",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -80,7 +80,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Delete set of lines.",
.desc = "Delete a range of lines",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -102,7 +102,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::File,
.input_mode = Function::InputMode::None,
.desc = "Try load a file into the current buffer.",
.desc = "Load a file into the current buffer, warn once if unsaved changes exist",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -152,7 +152,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::File,
.input_mode = Function::InputMode::None,
.desc = "Load a file into the current buffer.",
.desc = "Load a file into the current buffer, discarding unsaved changes",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -198,7 +198,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::File,
.input_mode = Function::InputMode::None,
.desc = "Set a save path.",
.desc = "Set/print a save path",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -248,7 +248,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Toggle help mode.",
.desc = "Toggle help mode",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -271,7 +271,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Line,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::Text,
.desc = "Insert text before a line.",
.desc = "Insert text before a line",
.default_address = ".",
.accept_zero = true,
.pre_text_mode = nullptr,
@@ -296,7 +296,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Join a set of lines.",
.desc = "Join a range of lines",
.default_address = ".,.+1",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -318,7 +318,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Line,
.argument_kind = Function::ArgumentKind::Mark,
.input_mode = Function::InputMode::None,
.desc = "Mark a line.",
.desc = "Mark a line",
.default_address = ".",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -340,7 +340,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "List (print unambiguous) range",
.desc = "Print a range, showing nonprinting characters unambiguously",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -363,7 +363,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::Line,
.input_mode = Function::InputMode::None,
.desc = "Move a range of lines.",
.desc = "Move a range of lines",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -407,7 +407,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Print range with line numbers",
.desc = "Print a range with line numbers",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -430,7 +430,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Print range",
.desc = "Print a range",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -453,7 +453,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::Any,
.input_mode = Function::InputMode::None,
.desc = "Toggle/set prompt.",
.desc = "Toggle/set prompt string",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -482,7 +482,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Try quitting.",
.desc = "Quit, warn once if unsaved changes exist",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -513,7 +513,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Force quit.",
.desc = "Quit without saving, discarding unsaved changes",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -534,7 +534,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Line,
.argument_kind = Function::ArgumentKind::File,
.input_mode = Function::InputMode::None,
.desc = "Read from file into buffer.",
.desc = "Read a file's contents into the buffer after the given address",
.default_address = "$",
.accept_zero = true,
.pre_text_mode = nullptr,
@@ -581,7 +581,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::Regex,
.input_mode = Function::InputMode::None,
.desc = "Substitute regex.",
.desc = "Substitute pattern in range for replacement",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -620,7 +620,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::Line,
.input_mode = Function::InputMode::None,
.desc = "Copy a range of lines.",
.desc = "Copy a range of lines",
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -650,7 +650,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Undo last modification to buffer.",
.desc = "Undo the last modification to the buffer",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -677,7 +677,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::File,
.input_mode = Function::InputMode::None,
.desc = "Write buffer to disk.",
.desc = "Write a range of lines to disk",
.default_address = "0,$",
.accept_zero = true,
.pre_text_mode = nullptr,
@@ -729,7 +729,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Print line numbers",
.desc = "Print the line number of the given address",
.default_address = "$",
.accept_zero = true,
.pre_text_mode = nullptr,
@@ -758,7 +758,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::Shell,
.input_mode = Function::InputMode::None,
.desc = "Run a shell command.",
.desc = "Run a shell command",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
@@ -785,7 +785,7 @@ void Function::register_posix(BEd &ctx) {
.address_kind = Function::AddressKind::Line,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Prints a line and jumps to it.",
.desc = "Print given line and move the cursor to it",
.default_address = ".+1",
.accept_zero = true,
.pre_text_mode = nullptr,
@@ -800,13 +800,16 @@ void Function::register_posix(BEd &ctx) {
if (addr.number != 0)
ctx.buffer(addr.buffername).print(ctx, addr.number, addr.number);
ctx.current() = addr;
ctx.prev().buffername = addr.buffername;
ctx.prev().start = addr.number;
ctx.prev().end = addr.number;
}
};
ctx.eof_op = Function{
.address_kind = Function::AddressKind::None,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::None,
.desc = "Try quitting.",
.desc = "Quit, warn once if unsaved changes exist",
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,