#include #include #include "elna/shell/state.hpp" namespace elna { std::string cursor_to_column(const std::uint16_t position) { std::string code = std::to_string(position); code.insert(0, "\x1b["); code.push_back('G'); return code; } key::key() : store(0x1bu) { } key::key(const char_t c, const std::uint8_t modifiers) : store(c), modifiers(modifiers == 0 ? 0 : modifiers - 1) { } key::key(const char_t c, const modifier modifiers) : store(c), modifiers(static_cast(modifiers)) { } key::key(const special_key control, const std::uint8_t modifiers) : store(control), modifiers(modifiers == 0 ? 0 : modifiers - 1) { } key::key(const special_key control, const modifier modifiers) : store(control), modifiers(static_cast(modifiers)) { } key::char_t key::character() const { return std::get(this->store); } special_key key::control() const { return std::get(this->store); } bool key::modified(const std::uint8_t modifiers) const noexcept { return this->modifiers == modifiers; } bool key::modified(const modifier modifiers) const noexcept { return this->modifiers == static_cast(modifiers); } bool key::operator==(const char_t c) const { return std::holds_alternative(this->store) && std::get(this->store) == c; } bool key::operator==(const special_key control) const { return std::holds_alternative(this->store) && std::get(this->store) == control; } bool key::operator==(const key& that) const { return this->store == that.store; } bool key::operator!=(const char_t c) const { return !(*this == c); } bool key::operator!=(const special_key control) const { return !(*this == control); } bool key::operator!=(const key& that) const { return !(*this == that); } bool key::is_character() const noexcept { return std::holds_alternative(this->store); } bool key::is_control() const noexcept { return std::holds_alternative(this->store); } bool key::empty() const noexcept { return *this == '\x1b'; } bool operator==(const special_key control, const key& that) { return that == control; } bool operator==(const char c, const key& that) { return that == c; } bool operator!=(const special_key control, const key& that) { return that != control; } bool operator!=(const char c, const key& that) { return that != c; } editor_state::editor_state() { this->input.reserve(1024); } void editor_state::load(const std::string& text) { this->input = text; this->position = this->input.size() + 1; } const std::string& editor_state::command_line() const noexcept { return this->input; } std::size_t editor_state::absolute_position() const noexcept { return this->prompt.size() + this->position; } std::size_t editor_state::relative_position() const noexcept { return this->position; } void editor_state::insert(const std::string& text) { this->input.insert(this->position - 1, text); this->position += text.size(); } void editor_state::insert(const key::char_t text) { if (text == 0u) { this->input.insert(this->position - 1, '\0', 1); ++this->position; return; } key::char_t buffer = boost::endian::native_to_big(text); const char *begin = reinterpret_cast(&buffer); const char *end = begin + sizeof(key::char_t); const char *significant = std::find_if(begin, end, [](const char byte) { return byte != 0; }); this->input.insert(this->position - 1, significant, end - significant); this->position += end - significant; } action editor_state::process_keypress(const key& press) { if (press.is_control()) { switch (press.control()) { case special_key::arrow_left: if (this->position > 1) { --this->position; } return action::move; case special_key::arrow_right: if (this->position + 1 < this->input.size() + this->prompt.size()) { ++this->position; } return action::move; case special_key::arrow_up: case special_key::arrow_down: return action::history; case special_key::home_key: this->position = 1; return action::move; case special_key::end_key: this->position = this->input.size() + 1; return action::move; default: return action::ignore; } } else if (press.modified(modifier::ctrl)) { switch (press.character()) { case 'd': return action::finalize; case 't': return action::complete; default: return action::ignore; } } else { switch (press.character()) { case 127: // Backspace. if (this->position <= this->input.size() + 1 && this->position > 1) { --this->position; this->input.erase(this->position - 1, 1); } return action::redraw; case 9: // Tab. return action::complete; case '\r': return action::finalize; default: if (this->position - 1 < input.size()) { insert(press.character()); return action::redraw; } else { insert(press.character()); return action::write; } } } } }