diff --git a/src/bed/bed.cc b/src/bed/bed.cc new file mode 100644 index 0000000..b247d01 --- /dev/null +++ b/src/bed/bed.cc @@ -0,0 +1,73 @@ +#include "bed.h" +#include "internal/parser/parser.h" + +namespace bed { +BEd::BEd(std::vector args) + : theme(internal::theme::Theme::default_theme()), io(*this) { + std::string prompt_ = ""; + std::string file = ""; + bool suppress = false; + bool color = true; + 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 (args[i] == "--no-color") { + color = false; + } else if (args[i] == "-v" || args[i] == "--verbose") { + help_mode = true; + } else if (args[i] == "-h" || args[i] == "--help") { + print_help(); + throw fatal_error("", 0); + } 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; + const char *no_color = getenv("NO_COLOR"); + if (no_color && *no_color != '\0') + color = false; + const char *colorterm = getenv("COLORTERM"); + if (colorterm + && strcmp(colorterm, "truecolor") != 0 + && strcmp(colorterm, "24bit") != 0) + color = false; + color_mode = color; + internal::functions::Function::register_posix(*this); + internal::functions::Function::register_extented(*this); + internal::functions::Suffix::register_suffixes(*this); + languages["ruby"] = new internal::syntax::Language(internal::syntax::ruby::lang_ruby()); + buffers["clip"] = new internal::buffer::ClipBuffer("clip"); + current() = {"default", 0}; + try { + if (file != "") + handle(":default:E " + file, false); + } catch (ed_error &e) { + io.write_line("?"); + if (help_mode) + io.write_line(e.what()); + last_help = e.what(); + } +} + +BEd::~BEd() { + for (auto &[_, buffer] : buffers) + delete buffer; + for (auto &[_, lang] : languages) + delete lang; +} + +void BEd::print_help() { + io.write("BEd - A line editor.\n"); +} +} // namespace bed diff --git a/src/bed/handle.cc b/src/bed/handle.cc new file mode 100644 index 0000000..d4652c3 --- /dev/null +++ b/src/bed/handle.cc @@ -0,0 +1,132 @@ +#include "bed.h" +#include "internal/parser/parser.h" + +namespace bed { +void BEd::run() { + while (true) { + internal::ui::CommandIO command(*this); + auto [cmd, eof] = command.run(); + try { + handle(cmd, eof); + } catch (ed_error &e) { + io.apply(internal::io::Token::Warning); + io.write_line("?"); + if (help_mode) + io.write_line(e.what()); + io.reset(); + last_help = e.what(); + } + } +} + +void BEd::handle(std::string_view cmd, bool eof) { + if (eof && cmd.empty()) { + eof_op.handle(*this, "", nullptr, std::monostate(), nullptr); + return; + } + internal::parser::Command c = internal::parser::Parser::get_command(cmd, *this); + if (c.temp_address) { + marks.get(251) = marks.get(250); + prev_2 = prev_1; + temporary_current = true; + } + internal::buffer::Address address; + switch (c.function->address_kind) { + case internal::functions::Function::AddressKind::None: { + auto a = internal::parser::AddressPromise::get_line(*this, c.addresses); + if (!a.has_value()) { + auto vec = internal::parser::Parser::get_addresses(c.function->default_address, *this); + a = internal::parser::AddressPromise::get_line(*this, vec); + if (!a.has_value()) + a = current(); + } + address = a->buffername; + } break; + case internal::functions::Function::AddressKind::Line: { + auto a = internal::parser::AddressPromise::get_line(*this, c.addresses); + if (!a.has_value()) { + auto vec = internal::parser::Parser::get_addresses(c.function->default_address, *this); + a = internal::parser::AddressPromise::get_line(*this, vec); + if (!a.has_value()) + a = current(); + } + address = *a; + } break; + case internal::functions::Function::AddressKind::Range: { + auto a = internal::parser::AddressPromise::get_range(*this, c.addresses); + if (!a.has_value()) { + auto vec = internal::parser::Parser::get_addresses(c.function->default_address, *this); + a = internal::parser::AddressPromise::get_range(*this, vec); + if (!a.has_value()) + a = internal::buffer::Range(current(), current()); + } + address = *a; + } break; + } + if (std::holds_alternative(c.argument)) { + if (c.argument_addresses.empty()) + throw ed_error("Function needs address argument."); + auto a = internal::parser::AddressPromise::get_line(*this, c.argument_addresses); + if (a.has_value()) + c.argument = *a; + else + c.argument = current(); + } else if (std::holds_alternative(c.argument)) { + if (c.argument_addresses.empty()) + throw ed_error("Function needs address argument."); + auto a = internal::parser::AddressPromise::get_range(*this, c.argument_addresses); + if (a.has_value()) + c.argument = *a; + else + c.argument = internal::buffer::Range(current(), current()); + } + if (!c.function->accept_zero) { + if (std::holds_alternative(address)) { + if (std::get(address).number == 0) + throw ed_error("Line number can't be zero."); + } else if (std::holds_alternative(address)) { + auto r = std::get(address); + if (r.start == 0 || r.end == 0) + throw ed_error("Line number can't be zero."); + } + } + internal::vase::Shard *text = nullptr; + if (c.function->input_mode == internal::functions::Function::InputMode::Text) { + internal::vase::Shard *vase = nullptr; + internal::syntax::Language *lang = nullptr; + void *state = nullptr; + if (c.function->pre_text_mode) + std::tie(vase, lang, state) = c.function->pre_text_mode(*this, address, c.argument); + internal::ui::text_mode::TextMode tm(*this, vase, lang, state); + auto [a, b] = tm.run(); + if (!b) { + text = a; + } else { + if (a) { + auto p = internal::syntax::make_parser(a, a->lines + 1, lang); + auto cancel_buf = new internal::buffer::ReadonlyBuffer("cancel", a, p); + internal::syntax::release(p); + buffers["cancel"] = cancel_buf; + cancel_buf->useless = false; + internal::vase::Shard::release(a); + } + throw ed_error("Operation cancelled."); + } + } + if (c.function->handle) + c.function->handle(*this, address, text, c.argument, nullptr); + if (c.suffix) + c.suffix->handle(*this); + if (c.temp_address) + temporary_current = false; + for (auto it = buffers.begin(); it != buffers.end();) { + internal::buffer::Buffer *buf = it->second; + if (buf->waste()) { + delete buf; + it = buffers.erase(it); + } else { + ++it; + } + } +} +} // namespace bed diff --git a/src/bed/run.cc b/src/bed/run.cc index b5caf0a..b423962 100644 --- a/src/bed/run.cc +++ b/src/bed/run.cc @@ -2,75 +2,6 @@ #include "internal/parser/parser.h" namespace bed { -BEd::BEd(std::vector args) - : theme(internal::theme::Theme::default_theme()), io(*this) { - std::string prompt_ = ""; - std::string file = ""; - bool suppress = false; - bool color = true; - 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 (args[i] == "--no-color") { - color = false; - } else if (args[i] == "-v" || args[i] == "--verbose") { - help_mode = true; - } else if (args[i] == "-h" || args[i] == "--help") { - print_help(); - throw fatal_error("", 0); - } 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; - const char *no_color = getenv("NO_COLOR"); - if (no_color && *no_color != '\0') - color = false; - const char *colorterm = getenv("COLORTERM"); - if (colorterm - && strcmp(colorterm, "truecolor") != 0 - && strcmp(colorterm, "24bit") != 0) - color = false; - color_mode = color; - internal::functions::Function::register_posix(*this); - internal::functions::Function::register_extented(*this); - internal::functions::Suffix::register_suffixes(*this); - languages["ruby"] = new internal::syntax::Language(internal::syntax::ruby::lang_ruby()); - buffers["clip"] = new internal::buffer::ClipBuffer("clip"); - current() = {"default", 0}; - try { - if (file != "") - handle(":default:E " + file, false); - } catch (ed_error &e) { - io.write_line("?"); - if (help_mode) - io.write_line(e.what()); - last_help = e.what(); - } -} - -BEd::~BEd() { - for (auto &[_, buffer] : buffers) - delete buffer; - for (auto &[_, lang] : languages) - delete lang; -} - -void BEd::print_help() { - io.write("BEd - A line editor.\n"); -} - void BEd::run() { while (true) { internal::ui::CommandIO command(*this); @@ -88,117 +19,6 @@ void BEd::run() { } } -void BEd::handle(std::string_view cmd, bool eof) { - if (eof && cmd.empty()) { - eof_op.handle(*this, "", nullptr, std::monostate(), nullptr); - return; - } - internal::parser::Command c = internal::parser::Parser::get_command(cmd, *this); - if (c.temp_address) { - marks.get(251) = marks.get(250); - prev_2 = prev_1; - temporary_current = true; - } - internal::buffer::Address address; - switch (c.function->address_kind) { - case internal::functions::Function::AddressKind::None: { - auto a = internal::parser::AddressPromise::get_line(*this, c.addresses); - if (!a.has_value()) { - auto vec = internal::parser::Parser::get_addresses(c.function->default_address, *this); - a = internal::parser::AddressPromise::get_line(*this, vec); - if (!a.has_value()) - a = current(); - } - address = a->buffername; - } break; - case internal::functions::Function::AddressKind::Line: { - auto a = internal::parser::AddressPromise::get_line(*this, c.addresses); - if (!a.has_value()) { - auto vec = internal::parser::Parser::get_addresses(c.function->default_address, *this); - a = internal::parser::AddressPromise::get_line(*this, vec); - if (!a.has_value()) - a = current(); - } - address = *a; - } break; - case internal::functions::Function::AddressKind::Range: { - auto a = internal::parser::AddressPromise::get_range(*this, c.addresses); - if (!a.has_value()) { - auto vec = internal::parser::Parser::get_addresses(c.function->default_address, *this); - a = internal::parser::AddressPromise::get_range(*this, vec); - if (!a.has_value()) - a = internal::buffer::Range(current(), current()); - } - address = *a; - } break; - } - if (std::holds_alternative(c.argument)) { - if (c.argument_addresses.empty()) - throw ed_error("Function needs address argument."); - auto a = internal::parser::AddressPromise::get_line(*this, c.argument_addresses); - if (a.has_value()) - c.argument = *a; - else - c.argument = current(); - } else if (std::holds_alternative(c.argument)) { - if (c.argument_addresses.empty()) - throw ed_error("Function needs address argument."); - auto a = internal::parser::AddressPromise::get_range(*this, c.argument_addresses); - if (a.has_value()) - c.argument = *a; - else - c.argument = internal::buffer::Range(current(), current()); - } - if (!c.function->accept_zero) { - if (std::holds_alternative(address)) { - if (std::get(address).number == 0) - throw ed_error("Line number can't be zero."); - } else if (std::holds_alternative(address)) { - auto r = std::get(address); - if (r.start == 0 || r.end == 0) - throw ed_error("Line number can't be zero."); - } - } - internal::vase::Shard *text = nullptr; - if (c.function->input_mode == internal::functions::Function::InputMode::Text) { - internal::vase::Shard *vase = nullptr; - internal::syntax::Language *lang = nullptr; - void *state = nullptr; - if (c.function->pre_text_mode) - std::tie(vase, lang, state) = c.function->pre_text_mode(*this, address, c.argument); - internal::ui::text_mode::TextMode tm(*this, vase, lang, state); - auto [a, b] = tm.run(); - if (!b) { - text = a; - } else { - if (a) { - auto p = internal::syntax::make_parser(a, a->lines + 1, lang); - auto cancel_buf = new internal::buffer::ReadonlyBuffer("cancel", a, p); - internal::syntax::release(p); - buffers["cancel"] = cancel_buf; - cancel_buf->useless = false; - internal::vase::Shard::release(a); - } - throw ed_error("Operation cancelled."); - } - } - if (c.function->handle) - c.function->handle(*this, address, text, c.argument, nullptr); - if (c.suffix) - c.suffix->handle(*this); - if (c.temp_address) - temporary_current = false; - for (auto it = buffers.begin(); it != buffers.end();) { - internal::buffer::Buffer *buf = it->second; - if (buf->waste()) { - delete buf; - it = buffers.erase(it); - } else { - ++it; - } - } -} - internal::buffer::Buffer &BEd::buffer(const std::string &name) { if (name.empty()) throw ed_error("Can't have empty buffer name"); diff --git a/src/internal/ui/text_mode/text_mode.cc b/src/internal/ui/text_mode/text_mode.cc index 84c4b98..f93be6a 100644 --- a/src/internal/ui/text_mode/text_mode.cc +++ b/src/internal/ui/text_mode/text_mode.cc @@ -2,6 +2,8 @@ #include "bed.h" namespace bed::internal::ui::text_mode { +// TODO: make this into a proper layout engine, in order to avoid so many expensive calculations repeatedly. + template static void for_each_cluster(std::string_view s, F &&f) { size_t cluster_start = 0;