diff --git a/include/internal/buffer/clip.h b/include/internal/buffer/clip.h index 3f4a1a6..88ed46e 100644 --- a/include/internal/buffer/clip.h +++ b/include/internal/buffer/clip.h @@ -24,6 +24,7 @@ struct ClipBuffer : Buffer { void join(BEd &ctx, uint64_t start_line, uint64_t end_line) override; void remove(BEd &ctx, uint64_t start_line, uint64_t end_line) override; void append(BEd &ctx, vase::Shard *text, uint64_t line) override; + void replace(BEd &ctx, vase::Shard *text, uint64_t start_line, uint64_t end_line) override; void print(BEd &ctx, uint64_t start_line, uint64_t end_line) override; void number_print(BEd &ctx, uint64_t start_line, uint64_t end_line) override; void list_print(BEd &ctx, uint64_t start_line, uint64_t end_line) override; diff --git a/include/internal/buffer/decl.h b/include/internal/buffer/decl.h index f49572f..8ee6f39 100644 --- a/include/internal/buffer/decl.h +++ b/include/internal/buffer/decl.h @@ -43,6 +43,7 @@ struct Buffer { virtual void join(BEd &ctx, uint64_t start_line, uint64_t end_line) = 0; virtual void remove(BEd &ctx, uint64_t start_line, uint64_t end_line) = 0; virtual void append(BEd &ctx, vase::Shard *text, uint64_t line) = 0; + virtual void replace(BEd &ctx, vase::Shard *text, uint64_t start_line, uint64_t end_line) = 0; virtual void print(BEd &ctx, uint64_t start_line, uint64_t end_line) = 0; virtual void number_print(BEd &ctx, uint64_t start_line, uint64_t end_line) = 0; virtual void list_print(BEd &ctx, uint64_t start_line, uint64_t end_line) = 0; diff --git a/include/internal/buffer/generic.h b/include/internal/buffer/generic.h index a4a3758..78e0c75 100644 --- a/include/internal/buffer/generic.h +++ b/include/internal/buffer/generic.h @@ -28,6 +28,7 @@ struct GenericBuffer : Buffer { void join(BEd &ctx, uint64_t start_line, uint64_t end_line) override; void remove(BEd &ctx, uint64_t start_line, uint64_t end_line) override; void append(BEd &ctx, vase::Shard *text, uint64_t line) override; + void replace(BEd &ctx, vase::Shard *text, uint64_t start_line, uint64_t end_line) override; void print(BEd &ctx, uint64_t start_line, uint64_t end_line) override; void number_print(BEd &ctx, uint64_t start_line, uint64_t end_line) override; void list_print(BEd &ctx, uint64_t start_line, uint64_t end_line) override; diff --git a/include/internal/marks/marks.h b/include/internal/marks/marks.h index ddc5bd3..4c7b880 100644 --- a/include/internal/marks/marks.h +++ b/include/internal/marks/marks.h @@ -22,7 +22,7 @@ struct MarksEngine { continue; if (marks[i].number == UINT64_MAX) continue; - if (marks[i].number >= start) + if (marks[i].number > start) marks[i].number += count; } } diff --git a/include/internal/vase/vase.h b/include/internal/vase/vase.h index a8d190d..d9d6d87 100644 --- a/include/internal/vase/vase.h +++ b/include/internal/vase/vase.h @@ -48,6 +48,7 @@ std::string to_string(Shard *root, Range range); Shard *insert(AppendStorage *ap, Shard *root, Shard *text, uint64_t line); Shard *erase(Shard *root, uint64_t start, uint64_t end); +Shard *replace(Shard *root, Shard *text, uint64_t start, uint64_t end); Shard *join(Shard *root, uint64_t start, uint64_t end); Shard *copy(Shard *root, uint64_t start, uint64_t end); void write_file(std::filesystem::path path, Shard *text); diff --git a/src/internal/buffer/generic.cc b/src/internal/buffer/generic.cc index 941137e..cc4390f 100644 --- a/src/internal/buffer/generic.cc +++ b/src/internal/buffer/generic.cc @@ -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); } } ); diff --git a/src/internal/buffer/special/clip.cc b/src/internal/buffer/special/clip.cc index 7ae2c09..e3189a3 100644 --- a/src/internal/buffer/special/clip.cc +++ b/src/internal/buffer/special/clip.cc @@ -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); diff --git a/src/internal/functions/extended.cc b/src/internal/functions/extended.cc index 4ccdc65..ec91b4a 100644 --- a/src/internal/functions/extended.cc +++ b/src/internal/functions/extended.cc @@ -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 *) { + .handle = []( + BEd &, + const buffer::Address &, + vase::Shard *, + const Argument &arg_, + std::vector * + ) { auto path = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &, + vase::Shard *, + const Argument &, + std::vector * + ) { 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 * + ) { + auto addr = std::get(addr_); + auto arg = std::get(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 diff --git a/src/internal/functions/posix.cc b/src/internal/functions/posix.cc index ac535b1..5411dc0 100644 --- a/src/internal/functions/posix.cc +++ b/src/internal/functions/posix.cc @@ -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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *text, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *text, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg, + std::vector * + ) { auto &addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg, + std::vector * + ) { auto &addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg, + std::vector * + ) { auto &addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &, + vase::Shard *, + const Argument &, + std::vector * + ) { 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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &, + vase::Shard *, + const Argument &, + std::vector * + ) { 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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *text, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg, + std::vector * + ) { auto &addr = std::get(addr_); ctx.mark(std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg_, + std::vector * + ) { auto addr = std::get(addr_); auto arg = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &, + vase::Shard *, + const Argument &, + std::vector * + ) { 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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &, + vase::Shard *, + const Argument &, + std::vector * + ) { 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 *) { + .handle = []( + BEd &, + const buffer::Address &, + vase::Shard *, + const Argument &, + std::vector * + ) { 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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg, + std::vector * + ) { auto &addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg_, + std::vector * + ) { auto &addr = std::get(addr_); auto arg = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg_, + std::vector * + ) { auto addr = std::get(addr_); auto arg = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &, + std::vector * + ) { auto &addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &arg_, + std::vector * + ) { auto &addr = std::get(addr_); auto filename = ctx.buffer(addr).filename(); auto &arg = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &addr_, + vase::Shard *, + const Argument &, + std::vector * + ) { auto addr = std::get(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 *) { + .handle = []( + BEd &ctx, + const buffer::Address &, + vase::Shard *, + const Argument &, + std::vector * + ) { std::string modified_buffers; for (auto &[name, buffer] : ctx.buffers) { if (buffer->state == buffer::Buffer::Modified) { diff --git a/src/internal/ui/text_mode/text_mode.cc b/src/internal/ui/text_mode/text_mode.cc index e615e32..69c27f2 100644 --- a/src/internal/ui/text_mode/text_mode.cc +++ b/src/internal/ui/text_mode/text_mode.cc @@ -123,9 +123,14 @@ std::pair 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); diff --git a/src/internal/vase/vase.cc b/src/internal/vase/vase.cc index 41c5458..9765718 100644 --- a/src/internal/vase/vase.cc +++ b/src/internal/vase/vase.cc @@ -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.");