Add x function and other bug fixes.

This commit is contained in:
2026-08-31 12:56:14 +01:00
parent c0f9c6a234
commit 569b69263a
11 changed files with 328 additions and 43 deletions
+33 -4
View File
@@ -51,13 +51,15 @@ std::filesystem::path GenericBuffer::filename() {
};
void GenericBuffer::append(BEd &ctx, vase::Shard *text, uint64_t line) {
if (!text)
return;
ctx.prev().buffername = name;
ctx.prev().start = line + 1;
ctx.prev().end = line + text->lines + 1;
root = vase::insert(&ctx.append, root, text, line);
ctx.marks.insert(name, ctx.prev().start, ctx.prev().end);
ctx.marks.insert(name, line, text->lines + 1);
if (parser)
parser->insert(root, ctx.prev().start, ctx.prev().end);
parser->insert(root, line, text->lines + 1);
state = Modified;
}
@@ -72,6 +74,33 @@ void GenericBuffer::remove(BEd &ctx, uint64_t start_line, uint64_t end_line) {
state = Modified;
}
void GenericBuffer::replace(BEd &ctx, vase::Shard *text, uint64_t start_line, uint64_t end_line) {
if (!text) {
remove(ctx, start_line, end_line);
return;
}
ctx.prev().buffername = name;
ctx.prev().start = start_line;
ctx.prev().end = start_line + text->lines;
uint64_t new_count = text->lines + 1;
uint64_t old_count = end_line - start_line + 1;
root = vase::replace(root, text, start_line, end_line);
if (parser) {
parser->begin_edit();
parser->erase(start_line, old_count);
parser->insert(start_line, new_count);
parser->end_edit(root);
}
if (new_count > old_count) {
uint64_t diff = new_count - old_count;
ctx.marks.insert(name, end_line, diff);
} else if (new_count < old_count) {
uint64_t diff = old_count - new_count;
ctx.marks.collapse(name, start_line + new_count - 1, diff);
}
state = Modified;
}
void GenericBuffer::join(BEd &ctx, uint64_t start_line, uint64_t end_line) {
root = vase::join(root, start_line, end_line);
ctx.prev().buffername = name;
@@ -107,9 +136,9 @@ void GenericBuffer::substitute(
parser->erase(line, old_lines);
}
if (new_lines) {
ctx.marks.insert(name, line, line + new_lines - 1);
ctx.marks.insert(name, line, line + new_lines);
if (parser)
parser->insert(line, line + new_lines - 1);
parser->insert(line, new_lines);
}
}
);
+26 -2
View File
@@ -54,12 +54,12 @@ std::filesystem::path ClipBuffer::filename() {
void ClipBuffer::append(BEd &ctx, vase::Shard *text, uint64_t line) {
ctx.prev().buffername = name;
ctx.prev().start = line + 1;
ctx.prev().end = line + text->lines + 1;
ctx.prev().end = line + (text ? text->lines + 1 : 0);
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
s = vase::insert(&ctx.append, s, text, line);
clip_write(s);
vase::Shard::release(s);
ctx.marks.insert(name, ctx.prev().start, ctx.prev().end);
ctx.marks.insert(name, line, text->lines + 1);
}
void ClipBuffer::remove(BEd &ctx, uint64_t start_line, uint64_t end_line) {
@@ -73,6 +73,30 @@ void ClipBuffer::remove(BEd &ctx, uint64_t start_line, uint64_t end_line) {
ctx.marks.erase(name, start_line, end_line - start_line + 1);
}
void ClipBuffer::replace(BEd &ctx, vase::Shard *text, uint64_t start_line, uint64_t end_line) {
if (!text) {
remove(ctx, start_line, end_line);
return;
}
ctx.prev().buffername = name;
ctx.prev().start = start_line;
ctx.prev().end = start_line + text->lines;
uint64_t new_count = text->lines + 1;
uint64_t old_count = end_line - start_line + 1;
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
s = vase::replace(s, text, start_line, end_line);
clip_write(s);
vase::Shard::release(s);
if (new_count > old_count) {
uint64_t diff = new_count - old_count;
ctx.marks.insert(name, end_line, diff);
} else if (new_count < old_count) {
uint64_t diff = old_count - new_count;
ctx.marks.collapse(name, start_line + new_count - 1, diff);
}
state = Modified;
}
void ClipBuffer::join(BEd &ctx, uint64_t start_line, uint64_t end_line) {
auto s = vase::Shard::from_command("xclip -selection clipboard -o", true);
s = vase::join(s, start_line, end_line);
+45 -2
View File
@@ -14,7 +14,13 @@ void Function::register_extented(BEd &ctx) {
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &, const buffer::Address &, vase::Shard *, const Argument &arg_, std::vector<buffer::Line> *) {
.handle = [](
BEd &,
const buffer::Address &,
vase::Shard *,
const Argument &arg_,
std::vector<buffer::Line> *
) {
auto path = std::get<std::string>(arg_);
const auto first = path.find_first_not_of(" \t");
const auto last = path.find_last_not_of(" \t");
@@ -46,7 +52,13 @@ void Function::register_extented(BEd &ctx) {
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
char cwd[PATH_MAX];
if (!getcwd(cwd, sizeof(cwd)))
throw ed_error("Can't determine current directory.");
@@ -54,5 +66,36 @@ void Function::register_extented(BEd &ctx) {
},
}
);
ctx.functions.insert(
"x",
Function{
.address_kind = Function::AddressKind::Range,
.argument_kind = Function::ArgumentKind::Range,
.input_mode = Function::InputMode::None,
.desc = "Exchange a range of lines for another.",
.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<buffer::Range>(addr_);
auto arg = std::get<buffer::Range>(arg_);
auto text = ctx.buffer(addr.buffername).copy(addr.start, addr.end);
try {
ctx.buffer(arg.buffername).replace(ctx, text, arg.start, arg.end);
vase::Shard::release(text);
} catch (...) {
vase::Shard::release(text);
throw;
}
ctx.current() = {arg.buffername, arg.start + addr.end - addr.start + 1};
},
}
);
}
} // namespace bed::internal::functions
+183 -33
View File
@@ -38,7 +38,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".",
.accept_zero = true,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *text, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *text,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Line>(addr_);
ctx.buffer(addr.buffername).append(ctx, text, addr.number);
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
@@ -56,10 +62,15 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *text, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *text,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).remove(ctx, addr.start, addr.end);
ctx.buffer(addr.buffername).append(ctx, text, addr.start - 1);
ctx.buffer(addr.buffername).replace(ctx, text, addr.start, addr.end);
ctx.current() = {ctx.prev().buffername, ctx.prev().end};
vase::Shard::release(text);
}
@@ -75,7 +86,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).remove(ctx, addr.start, addr.end);
ctx.current() = {addr.buffername, addr.start};
@@ -92,7 +109,13 @@ void Function::register_posix(BEd &ctx) {
.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> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg,
std::vector<buffer::Line> *
) {
auto &addr = std::get<std::string>(addr_);
auto &buf = ctx.buffer(addr);
if (buf.state == buffer::Buffer::Modified) {
@@ -136,7 +159,13 @@ void Function::register_posix(BEd &ctx) {
.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> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg,
std::vector<buffer::Line> *
) {
auto &addr = std::get<std::string>(addr_);
auto &buf = ctx.buffer(addr);
vase::Shard *s = nullptr;
@@ -176,7 +205,13 @@ void Function::register_posix(BEd &ctx) {
.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> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg,
std::vector<buffer::Line> *
) {
auto &addr = std::get<std::string>(addr_);
auto &buf_ = ctx.buffer(addr);
if (buf_.kind != buffer::Buffer::Kind::Generic)
@@ -202,7 +237,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
ctx.io.write_line(ctx.last_help);
}
}
@@ -217,7 +258,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
ctx.help_mode = !ctx.help_mode;
if (ctx.help_mode)
ctx.io.write_line(ctx.last_help);
@@ -234,7 +281,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".",
.accept_zero = true,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *text, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *text,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Line>(addr_);
if (addr.number)
addr.number--;
@@ -254,7 +307,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".,.+1",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).join(ctx, addr.start, addr.end);
ctx.current() = {addr.buffername, addr.start};
@@ -271,7 +330,13 @@ void Function::register_posix(BEd &ctx) {
.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> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg,
std::vector<buffer::Line> *
) {
auto &addr = std::get<buffer::Line>(addr_);
ctx.mark(std::get<char>(arg), addr);
}
@@ -287,7 +352,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).list_print(ctx, addr.start, addr.end);
ctx.current() = {addr.buffername, addr.end};
@@ -304,7 +375,13 @@ void Function::register_posix(BEd &ctx) {
.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> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg_,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
auto arg = std::get<buffer::Line>(arg_);
if (arg.buffername == addr.buffername
@@ -343,7 +420,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).number_print(ctx, addr.start, addr.end);
ctx.current() = {addr.buffername, addr.end};
@@ -360,7 +443,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".,.",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
ctx.buffer(addr.buffername).print(ctx, addr.start, addr.end);
ctx.current() = {addr.buffername, addr.end};
@@ -377,7 +466,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
ctx.prompt_mode = !ctx.prompt_mode;
if (ctx.prompt_mode && !ctx.prompt) {
ctx.prompt = [](BEd &) { return "*"; };
@@ -395,7 +490,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
std::string modified_buffers;
for (auto &[name, buffer] : ctx.buffers) {
if (buffer->state == buffer::Buffer::Modified) {
@@ -420,7 +521,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &, const buffer::Address &, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &,
const buffer::Address &,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
throw fatal_error("Force Quitting", 0);
}
}
@@ -435,7 +542,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "$",
.accept_zero = true,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &arg, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg,
std::vector<buffer::Line> *
) {
auto &addr = std::get<buffer::Line>(addr_);
auto &buf = ctx.buffer(addr.buffername);
vase::Shard *s = nullptr;
@@ -476,7 +589,13 @@ void Function::register_posix(BEd &ctx) {
.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> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg_,
std::vector<buffer::Line> *
) {
auto &addr = std::get<buffer::Range>(addr_);
auto arg = std::get<RegexArg>(arg_);
if (arg.expression == "")
@@ -509,7 +628,13 @@ void Function::register_posix(BEd &ctx) {
.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> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg_,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
auto arg = std::get<buffer::Line>(arg_);
auto text = ctx.buffer(addr.buffername).copy(addr.start, addr.end);
@@ -517,11 +642,6 @@ void Function::register_posix(BEd &ctx) {
ctx.buffer(arg.buffername).append(ctx, text, arg.number);
vase::Shard::release(text);
} catch (...) {
if (!addr.start) {
vase::Shard::release(text);
throw;
}
ctx.buffer(addr.buffername).append(ctx, text, --addr.start);
vase::Shard::release(text);
throw;
}
@@ -539,7 +659,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "0,$",
.accept_zero = true,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &arg, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Range>(addr_);
auto &buf = ctx.buffer(addr.buffername);
vase::Shard *text;
@@ -584,7 +710,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "$",
.accept_zero = true,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
auto &addr = std::get<buffer::Range>(addr_);
if (addr.start == addr.end)
ctx.io.write_line(std::format(":{}:{}", addr.buffername, addr.start));
@@ -604,7 +736,13 @@ void Function::register_posix(BEd &ctx) {
.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> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &arg_,
std::vector<buffer::Line> *
) {
auto &addr = std::get<std::string>(addr_);
auto filename = ctx.buffer(addr).filename();
auto &arg = std::get<ShellArg>(arg_);
@@ -624,7 +762,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = ".+1",
.accept_zero = true,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &addr_,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
auto addr = std::get<buffer::Line>(addr_);
if (addr.number != 0)
ctx.buffer(addr.buffername).print(ctx, addr.number, addr.number);
@@ -639,7 +783,13 @@ void Function::register_posix(BEd &ctx) {
.default_address = "",
.accept_zero = false,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &, vase::Shard *, const Argument &, std::vector<buffer::Line> *) {
.handle = [](
BEd &ctx,
const buffer::Address &,
vase::Shard *,
const Argument &,
std::vector<buffer::Line> *
) {
std::string modified_buffers;
for (auto &[name, buffer] : ctx.buffers) {
if (buffer->state == buffer::Buffer::Modified) {
+6 -1
View File
@@ -123,9 +123,14 @@ std::pair<vase::Shard *, bool> TextMode::run() {
cursor = cmd.size();
running = false;
}
if (cmd.size() == 2 && cmd.compare(0, 2, ".\n") == 0) {
cmd.clear();
cursor = 0;
running = false;
}
}
size_t total_lines = 1 + std::count(cmd.begin(), cmd.end(), '\n');
size_t total_lines = cmd.size() ? 1 + std::count(cmd.begin(), cmd.end(), '\n') : 0;
uint16_t last_row = start + total_lines;
bed.io.move_cursor(last_row, 1);
bed.io.write("\n", 1);
+30
View File
@@ -173,6 +173,8 @@ Shard *insert(AppendStorage *ap, Shard *root, Shard *text, uint64_t line) {
Shard::retain(text);
return text;
}
if (!text)
return root;
if (line > root->lines + 1)
throw ed_error("line out of range");
Shard::retain(text);
@@ -201,6 +203,34 @@ Shard *insert(AppendStorage *ap, Shard *root, Shard *text, uint64_t line) {
return result;
}
Shard *replace(Shard *root, Shard *text, uint64_t start, uint64_t end) {
if (!start || !end)
throw ed_error("Invalid range.");
start--;
end--;
if (!root)
throw ed_error("line range out of bounds");
uint64_t line_count = root->lines + 1;
if (start > end || end >= line_count)
throw ed_error("line range out of bounds");
uint64_t start_offset = offset_of(root, start);
uint64_t end_offset =
(end + 1 == line_count)
? root->length
: offset_of(root, end + 1) - 1;
auto [left, rest] = Shard::split(root, start_offset);
auto [middle, right] = Shard::split(rest, end_offset - start_offset);
Shard *a = Shard::concat(left, text);
Shard *new_root = Shard::concat(a, right);
Shard::release(left);
Shard::release(rest);
Shard::release(middle);
Shard::release(right);
Shard::release(a);
Shard::release(root);
return new_root;
}
Shard *erase(Shard *root, uint64_t start, uint64_t end) {
if (!start || !end)
throw ed_error("Invalid range.");