2024-02-25 15:16:19 +01:00
|
|
|
#include <algorithm>
|
|
|
|
#include <boost/endian.hpp>
|
|
|
|
|
2024-02-22 21:29:25 +01:00
|
|
|
#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()
|
2024-02-25 15:16:19 +01:00
|
|
|
: store(0x1bu)
|
2024-02-22 21:29:25 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-02-25 15:16:19 +01:00
|
|
|
key::key(const char_t c, const std::uint8_t modifiers)
|
2024-02-22 21:29:25 +01:00
|
|
|
: store(c), modifiers(modifiers == 0 ? 0 : modifiers - 1)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-02-25 15:16:19 +01:00
|
|
|
key::key(const char_t c, const modifier modifiers)
|
2024-02-22 21:29:25 +01:00
|
|
|
: store(c), modifiers(static_cast<uint8_t>(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<uint8_t>(modifiers))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-02-25 15:16:19 +01:00
|
|
|
key::char_t key::character() const
|
2024-02-22 21:29:25 +01:00
|
|
|
{
|
2024-02-25 15:16:19 +01:00
|
|
|
return std::get<char_t>(this->store);
|
2024-02-22 21:29:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
special_key key::control() const
|
|
|
|
{
|
|
|
|
return std::get<special_key>(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<std::uint8_t>(modifiers);
|
|
|
|
}
|
|
|
|
|
2024-02-25 15:16:19 +01:00
|
|
|
bool key::operator==(const char_t c) const
|
2024-02-22 21:29:25 +01:00
|
|
|
{
|
2024-02-25 15:16:19 +01:00
|
|
|
return std::holds_alternative<char_t>(this->store)
|
|
|
|
&& std::get<char_t>(this->store) == c;
|
2024-02-22 21:29:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool key::operator==(const special_key control) const
|
|
|
|
{
|
|
|
|
return std::holds_alternative<special_key>(this->store)
|
|
|
|
&& std::get<special_key>(this->store) == control;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool key::operator==(const key& that) const
|
|
|
|
{
|
|
|
|
return this->store == that.store;
|
|
|
|
}
|
|
|
|
|
2024-02-25 15:16:19 +01:00
|
|
|
bool key::operator!=(const char_t c) const
|
2024-02-22 21:29:25 +01:00
|
|
|
{
|
|
|
|
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
|
|
|
|
{
|
2024-02-25 15:16:19 +01:00
|
|
|
return std::holds_alternative<char_t>(this->store);
|
2024-02-22 21:29:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool key::is_control() const noexcept
|
|
|
|
{
|
|
|
|
return std::holds_alternative<special_key>(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;
|
2024-02-25 15:16:19 +01:00
|
|
|
this->position = this->input.size() + 1;
|
2024-02-22 21:29:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-02-25 15:16:19 +01:00
|
|
|
std::size_t editor_state::relative_position() const noexcept
|
|
|
|
{
|
|
|
|
return this->position;
|
|
|
|
}
|
|
|
|
|
2024-02-22 21:29:25 +01:00
|
|
|
void editor_state::insert(const std::string& text)
|
|
|
|
{
|
2024-02-25 15:16:19 +01:00
|
|
|
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<key::char_t>(text);
|
|
|
|
|
|
|
|
const char *begin = reinterpret_cast<char *>(&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;
|
2024-02-22 21:29:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2024-02-25 15:16:19 +01:00
|
|
|
case 9: // Tab.
|
|
|
|
return action::complete;
|
2024-02-22 21:29:25 +01:00
|
|
|
case '\r':
|
|
|
|
return action::finalize;
|
|
|
|
default:
|
2024-02-25 15:16:19 +01:00
|
|
|
if (this->position - 1 < input.size())
|
|
|
|
{
|
|
|
|
insert(press.character());
|
|
|
|
return action::redraw;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
insert(press.character());
|
|
|
|
return action::write;
|
|
|
|
}
|
2024-02-22 21:29:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|