#include "elna/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(static_cast(0x1b)) { } key::key(const unsigned char c, const std::uint8_t modifiers) : store(c), modifiers(modifiers == 0 ? 0 : modifiers - 1) { } key::key(const unsigned char 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)) { } unsigned char 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 unsigned char 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 unsigned char 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; } 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; } void editor_state::insert(const std::string& text) { this->input.insert(std::end(this->input), std::cbegin(text), std::cend(text)); } 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 '\r': return action::finalize; default: ++this->position; this->input.push_back(press.character()); return action::write; } } } }