Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8a270378a
|
||
|
|
6f1c87400a
|
@@ -16,9 +16,13 @@ struct Buffer {
|
|||||||
uint64_t line = 0;
|
uint64_t line = 0;
|
||||||
bool modified;
|
bool modified;
|
||||||
std::filesystem::path save_path = "";
|
std::filesystem::path save_path = "";
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
uint64_t start{0};
|
uint64_t start{0};
|
||||||
uint64_t end{0};
|
uint64_t end{0};
|
||||||
|
std::span<const uint64_t> values() const {
|
||||||
|
return {&start, 2};
|
||||||
|
}
|
||||||
} prev_range;
|
} prev_range;
|
||||||
|
|
||||||
Buffer();
|
Buffer();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
namespace bed::internal::commands {
|
namespace bed::internal::commands {
|
||||||
struct Suffix {
|
struct Suffix {
|
||||||
std::string desc;
|
std::string desc;
|
||||||
void (*handle)(BEd &);
|
void (*handle)(BEd &, std::span<const uint64_t>);
|
||||||
|
|
||||||
static void register_suffixes(BEd &ctx);
|
static void register_suffixes(BEd &ctx);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,12 +5,18 @@
|
|||||||
|
|
||||||
namespace bed::internal::io {
|
namespace bed::internal::io {
|
||||||
struct KeyEvent {
|
struct KeyEvent {
|
||||||
|
enum struct ReadResult {
|
||||||
|
SUCCESS,
|
||||||
|
EOF_,
|
||||||
|
RESIZE
|
||||||
|
};
|
||||||
enum struct KeyType {
|
enum struct KeyType {
|
||||||
NONE,
|
NONE,
|
||||||
CHAR,
|
CHAR,
|
||||||
SPECIAL,
|
SPECIAL,
|
||||||
MOUSE,
|
MOUSE,
|
||||||
PASTE
|
PASTE,
|
||||||
|
RESIZE
|
||||||
};
|
};
|
||||||
enum struct SpecialKey {
|
enum struct SpecialKey {
|
||||||
UP,
|
UP,
|
||||||
@@ -58,6 +64,7 @@ struct IO {
|
|||||||
void move_cursor(uint16_t row, uint16_t col);
|
void move_cursor(uint16_t row, uint16_t col);
|
||||||
|
|
||||||
std::pair<std::string, bool> get_command(BEd &);
|
std::pair<std::string, bool> get_command(BEd &);
|
||||||
|
std::pair<std::string, bool> get_text(BEd &);
|
||||||
|
|
||||||
KeyEvent read_key();
|
KeyEvent read_key();
|
||||||
|
|
||||||
@@ -65,15 +72,17 @@ private:
|
|||||||
static termios orig_termios;
|
static termios orig_termios;
|
||||||
static bool cleaned;
|
static bool cleaned;
|
||||||
static void cleanup();
|
static void cleanup();
|
||||||
|
static volatile std::atomic_bool resized;
|
||||||
|
static void handle_sigwinch(int);
|
||||||
|
|
||||||
std::deque<char> input_queue;
|
std::deque<char> input_queue;
|
||||||
|
|
||||||
bool get_next_byte(char &out);
|
KeyEvent::ReadResult get_next_byte(char &out);
|
||||||
void enqueue_bytes(const std::string &bytes);
|
void enqueue_bytes(const std::string &bytes);
|
||||||
static int utf8_seq_len(uint8_t byte);
|
static int utf8_seq_len(uint8_t byte);
|
||||||
|
|
||||||
std::string read_next_unit();
|
KeyEvent::ReadResult read_next_unit(std::string &out);
|
||||||
bool read_bracketed_paste(std::string &out);
|
KeyEvent::ReadResult read_bracketed_paste(std::string &out);
|
||||||
|
|
||||||
KeyEvent parse_mouse(const std::string &buf);
|
KeyEvent parse_mouse(const std::string &buf);
|
||||||
KeyEvent parse_escape(const std::string &buf);
|
KeyEvent parse_escape(const std::string &buf);
|
||||||
|
|||||||
+2
-2
@@ -90,12 +90,12 @@ void BEd::handle(std::string_view cmd_, bool eof) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (suffix)
|
if (suffix)
|
||||||
suffix->handle(*this);
|
suffix->handle(*this, active->prev_range.values());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BEd::suffix_handle(char s) {
|
void BEd::suffix_handle(char s) {
|
||||||
if (suffixes[s - 'a'].has_value())
|
if (suffixes[s - 'a'].has_value())
|
||||||
suffixes[s - 'a'].value().handle(*this);
|
suffixes[s - 'a'].value().handle(*this, active->prev_range.values());
|
||||||
}
|
}
|
||||||
} // namespace bed
|
} // namespace bed
|
||||||
|
|||||||
@@ -6,18 +6,14 @@ namespace bed::internal::commands {
|
|||||||
void Suffix::register_suffixes(BEd &ctx) {
|
void Suffix::register_suffixes(BEd &ctx) {
|
||||||
ctx.suffixes['p' - 'a'] = Suffix{
|
ctx.suffixes['p' - 'a'] = Suffix{
|
||||||
.desc = "Prints current line.",
|
.desc = "Prints current line.",
|
||||||
.handle = [](BEd &ctx) {
|
.handle = [](BEd &ctx, std::span<const uint64_t> addresses) {
|
||||||
auto line = ctx.active->line;
|
ctx.active->print(ctx, addresses[0], addresses[1]);
|
||||||
ctx.active->print(ctx, line, line);
|
|
||||||
ctx.active->jump(line);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ctx.suffixes['n' - 'a'] = Suffix{
|
ctx.suffixes['n' - 'a'] = Suffix{
|
||||||
.desc = "Prints current line with line number.",
|
.desc = "Prints current line with line number.",
|
||||||
.handle = [](BEd &ctx) {
|
.handle = [](BEd &ctx, std::span<const uint64_t> addresses) {
|
||||||
auto line = ctx.active->line;
|
ctx.active->number_print(ctx, addresses[0], addresses[1]);
|
||||||
ctx.active->number_print(ctx, line, line);
|
|
||||||
ctx.active->jump(line);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,12 +3,16 @@
|
|||||||
|
|
||||||
namespace bed::internal::io {
|
namespace bed::internal::io {
|
||||||
std::pair<std::string, bool> IO::get_command(BEd &ctx) {
|
std::pair<std::string, bool> IO::get_command(BEd &ctx) {
|
||||||
auto [row, col] = cursor_position();
|
uint16_t start;
|
||||||
auto [rows, cols] = terminal_size();
|
uint16_t height;
|
||||||
if (row > rows)
|
{
|
||||||
throw fatal_error("Invalid cursor position.", 1);
|
auto [row, col] = cursor_position();
|
||||||
uint16_t start = row;
|
auto [rows, cols] = terminal_size();
|
||||||
uint16_t height = rows - row + 1;
|
if (row > rows)
|
||||||
|
throw fatal_error("Invalid cursor position.", 1);
|
||||||
|
start = row;
|
||||||
|
height = rows - row + 1;
|
||||||
|
}
|
||||||
// uint16_t width = cols;
|
// uint16_t width = cols;
|
||||||
// TODO: support multiline wrapped commands.
|
// TODO: support multiline wrapped commands.
|
||||||
// also handle ctrl+l to try and clear screen.
|
// also handle ctrl+l to try and clear screen.
|
||||||
@@ -33,6 +37,18 @@ std::pair<std::string, bool> IO::get_command(BEd &ctx) {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
KeyEvent ev = read_key();
|
KeyEvent ev = read_key();
|
||||||
|
if (ev.type == KeyEvent::KeyType::RESIZE) {
|
||||||
|
{
|
||||||
|
auto [row, col] = cursor_position();
|
||||||
|
auto [rows, cols] = terminal_size();
|
||||||
|
if (row > rows)
|
||||||
|
throw fatal_error("Invalid cursor position.", 1);
|
||||||
|
start = row;
|
||||||
|
height = rows - row + 1;
|
||||||
|
}
|
||||||
|
redraw();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
(ev.type == KeyEvent::KeyType::CHAR
|
(ev.type == KeyEvent::KeyType::CHAR
|
||||||
&& ev.modifier == KeyEvent::Modifier::CTRL
|
&& ev.modifier == KeyEvent::Modifier::CTRL
|
||||||
@@ -124,6 +140,21 @@ std::pair<std::string, bool> IO::get_command(BEd &ctx) {
|
|||||||
go_ahead = false;
|
go_ahead = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (ev.type == KeyEvent::KeyType::RESIZE) {
|
||||||
|
{
|
||||||
|
auto [row, col] = cursor_position();
|
||||||
|
auto [rows, cols] = terminal_size();
|
||||||
|
if (row - 1 > rows)
|
||||||
|
throw fatal_error("Invalid cursor position.", 1);
|
||||||
|
start = row - 1;
|
||||||
|
height = rows - row;
|
||||||
|
}
|
||||||
|
redraw();
|
||||||
|
move_cursor(start + 1, 1);
|
||||||
|
write_all(STDOUT_FILENO, "\x1b[2K", 4);
|
||||||
|
write_all(STDOUT_FILENO, msg.c_str(), msg.size());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
move_cursor(start + 1, 1);
|
move_cursor(start + 1, 1);
|
||||||
write_all(STDOUT_FILENO, "\x1b[2K", 4);
|
write_all(STDOUT_FILENO, "\x1b[2K", 4);
|
||||||
|
|||||||
+74
-49
@@ -1,13 +1,22 @@
|
|||||||
#include "internal/io/io.h"
|
#include "internal/io/io.h"
|
||||||
|
|
||||||
namespace bed::internal::io {
|
namespace bed::internal::io {
|
||||||
bool IO::get_next_byte(char &out) {
|
KeyEvent::ReadResult IO::get_next_byte(char &out) {
|
||||||
if (!input_queue.empty()) {
|
if (!input_queue.empty()) {
|
||||||
out = input_queue.front();
|
out = input_queue.front();
|
||||||
input_queue.pop_front();
|
input_queue.pop_front();
|
||||||
return true;
|
return KeyEvent::ReadResult::SUCCESS;
|
||||||
}
|
}
|
||||||
return read(STDIN_FILENO, &out, 1) == 1;
|
if (resized.load())
|
||||||
|
return KeyEvent::ReadResult::RESIZE;
|
||||||
|
ssize_t n = read(STDIN_FILENO, &out, 1);
|
||||||
|
if (n == 1)
|
||||||
|
return KeyEvent::ReadResult::SUCCESS;
|
||||||
|
if (n == -1 && errno == EINTR && resized.load())
|
||||||
|
return KeyEvent::ReadResult::RESIZE;
|
||||||
|
if (n == -1 && errno == EINTR)
|
||||||
|
return get_next_byte(out);
|
||||||
|
return KeyEvent::ReadResult::EOF_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IO::enqueue_bytes(const std::string &bytes) {
|
void IO::enqueue_bytes(const std::string &bytes) {
|
||||||
@@ -26,64 +35,66 @@ int IO::utf8_seq_len(uint8_t byte) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string IO::read_next_unit() {
|
KeyEvent::ReadResult IO::read_next_unit(std::string &out) {
|
||||||
std::string buf;
|
out.clear();
|
||||||
char header;
|
char header;
|
||||||
if (!get_next_byte(header))
|
KeyEvent::ReadResult res = get_next_byte(header);
|
||||||
return buf;
|
if (res != KeyEvent::ReadResult::SUCCESS)
|
||||||
|
return res;
|
||||||
if (header == '\x1b') {
|
if (header == '\x1b') {
|
||||||
buf.push_back(header);
|
out.push_back(header);
|
||||||
char c;
|
char c;
|
||||||
if (!get_next_byte(c))
|
if ((res = get_next_byte(c)) != KeyEvent::ReadResult::SUCCESS)
|
||||||
return buf;
|
return res;
|
||||||
buf.push_back(c);
|
out.push_back(c);
|
||||||
if (c != '[')
|
if (c != '[')
|
||||||
return buf;
|
return KeyEvent::ReadResult::SUCCESS;
|
||||||
if (!get_next_byte(c))
|
if ((res = get_next_byte(c)) != KeyEvent::ReadResult::SUCCESS)
|
||||||
return buf;
|
return res;
|
||||||
buf.push_back(c);
|
out.push_back(c);
|
||||||
if (c == 'M') {
|
if (c == 'M') {
|
||||||
for (int i = 0; i < 3; ++i) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
if (!get_next_byte(c))
|
if ((res = get_next_byte(c)) != KeyEvent::ReadResult::SUCCESS)
|
||||||
break;
|
return res;
|
||||||
buf.push_back(c);
|
out.push_back(c);
|
||||||
}
|
}
|
||||||
return buf;
|
return KeyEvent::ReadResult::SUCCESS;
|
||||||
}
|
}
|
||||||
while ((uint8_t)c < 0x40 || (uint8_t)c > 0x7E) {
|
while ((uint8_t)c < 0x40 || (uint8_t)c > 0x7E) {
|
||||||
if (buf.size() >= 32)
|
if (out.size() >= 32)
|
||||||
break;
|
break;
|
||||||
if (!get_next_byte(c))
|
if ((res = get_next_byte(c)) != KeyEvent::ReadResult::SUCCESS)
|
||||||
break;
|
return res;
|
||||||
buf.push_back(c);
|
out.push_back(c);
|
||||||
}
|
}
|
||||||
return buf;
|
return KeyEvent::ReadResult::SUCCESS;
|
||||||
}
|
}
|
||||||
int seq_len = utf8_seq_len((uint8_t)header);
|
int seq_len = utf8_seq_len((uint8_t)header);
|
||||||
buf.push_back(header);
|
out.push_back(header);
|
||||||
if (seq_len == 1)
|
if (seq_len == 1)
|
||||||
return buf;
|
return KeyEvent::ReadResult::SUCCESS;
|
||||||
for (int i = 1; i < seq_len; i++) {
|
for (int i = 1; i < seq_len; i++) {
|
||||||
char c;
|
char c;
|
||||||
if (!get_next_byte(c)) {
|
if ((res = get_next_byte(c)) != KeyEvent::ReadResult::SUCCESS) {
|
||||||
enqueue_bytes(buf);
|
enqueue_bytes(out);
|
||||||
return {};
|
out.clear();
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
buf.push_back(c);
|
out.push_back(c);
|
||||||
}
|
}
|
||||||
uint32_t prev_cp, cur_cp;
|
uint32_t prev_cp, cur_cp;
|
||||||
grapheme_decode_utf8(buf.data(), buf.size(), &prev_cp);
|
grapheme_decode_utf8(out.data(), out.size(), &prev_cp);
|
||||||
uint16_t state = 0;
|
uint16_t state = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
char next_header;
|
char next_header;
|
||||||
if (!get_next_byte(next_header))
|
if ((res = get_next_byte(next_header)) != KeyEvent::ReadResult::SUCCESS)
|
||||||
break;
|
break;
|
||||||
int next_len = utf8_seq_len((uint8_t)next_header);
|
int next_len = utf8_seq_len((uint8_t)next_header);
|
||||||
std::string next_seq(1, next_header);
|
std::string next_seq(1, next_header);
|
||||||
bool complete = true;
|
bool complete = true;
|
||||||
for (int i = 1; i < next_len; i++) {
|
for (int i = 1; i < next_len; i++) {
|
||||||
char c;
|
char c;
|
||||||
if (!get_next_byte(c)) {
|
if ((res = get_next_byte(c)) != KeyEvent::ReadResult::SUCCESS) {
|
||||||
complete = false;
|
complete = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -98,26 +109,27 @@ std::string IO::read_next_unit() {
|
|||||||
enqueue_bytes(next_seq);
|
enqueue_bytes(next_seq);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
buf += next_seq;
|
out += next_seq;
|
||||||
prev_cp = cur_cp;
|
prev_cp = cur_cp;
|
||||||
}
|
}
|
||||||
return buf;
|
return KeyEvent::ReadResult::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IO::read_bracketed_paste(std::string &out) {
|
KeyEvent::ReadResult IO::read_bracketed_paste(std::string &out) {
|
||||||
|
KeyEvent::ReadResult res = KeyEvent::ReadResult::SUCCESS;
|
||||||
out.clear();
|
out.clear();
|
||||||
std::string window;
|
std::string window;
|
||||||
while (true) {
|
while (true) {
|
||||||
char c;
|
char c;
|
||||||
if (!get_next_byte(c))
|
if ((res = get_next_byte(c)) != KeyEvent::ReadResult::SUCCESS)
|
||||||
return false;
|
return res;
|
||||||
window.push_back(c);
|
window.push_back(c);
|
||||||
if (window.size() == 5 && window == "\x1b[201") {
|
if (window.size() == 5 && window == "\x1b[201") {
|
||||||
char tilde;
|
char tilde;
|
||||||
if (!get_next_byte(tilde))
|
if ((res = get_next_byte(tilde)) != KeyEvent::ReadResult::SUCCESS)
|
||||||
return false;
|
return res;
|
||||||
if (tilde == '~')
|
if (tilde == '~')
|
||||||
return true;
|
return res;
|
||||||
out += window;
|
out += window;
|
||||||
out.push_back(tilde);
|
out.push_back(tilde);
|
||||||
window.clear();
|
window.clear();
|
||||||
@@ -199,32 +211,45 @@ KeyEvent IO::parse_escape(const std::string &buf) {
|
|||||||
|
|
||||||
KeyEvent IO::read_key() {
|
KeyEvent IO::read_key() {
|
||||||
while (true) {
|
while (true) {
|
||||||
std::string buf = read_next_unit();
|
std::string buf;
|
||||||
if (buf.empty()) {
|
auto res = read_next_unit(buf);
|
||||||
KeyEvent ev;
|
KeyEvent ev;
|
||||||
|
switch (res) {
|
||||||
|
case KeyEvent::ReadResult::EOF_:
|
||||||
ev.type = KeyEvent::KeyType::NONE;
|
ev.type = KeyEvent::KeyType::NONE;
|
||||||
return ev;
|
return ev;
|
||||||
|
case KeyEvent::ReadResult::RESIZE:
|
||||||
|
resized.store(false);
|
||||||
|
ev.type = KeyEvent::KeyType::RESIZE;
|
||||||
|
return ev;
|
||||||
|
case KeyEvent::ReadResult::SUCCESS:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (buf.size() >= 6 && buf[0] == '\x1b' && buf[1] == '[' && buf.compare(2, 4, "200~") == 0) {
|
if (buf.size() >= 6 && buf[0] == '\x1b' && buf[1] == '[' && buf.compare(2, 4, "200~") == 0) {
|
||||||
KeyEvent ev;
|
|
||||||
std::string pasted;
|
std::string pasted;
|
||||||
if (read_bracketed_paste(pasted)) {
|
switch (read_bracketed_paste(pasted)) {
|
||||||
|
case KeyEvent::ReadResult::SUCCESS:
|
||||||
ev.type = KeyEvent::KeyType::PASTE;
|
ev.type = KeyEvent::KeyType::PASTE;
|
||||||
ev.text = std::move(pasted);
|
ev.text = std::move(pasted);
|
||||||
} else {
|
break;
|
||||||
|
case KeyEvent::ReadResult::EOF_:
|
||||||
ev.type = KeyEvent::KeyType::NONE;
|
ev.type = KeyEvent::KeyType::NONE;
|
||||||
|
break;
|
||||||
|
case KeyEvent::ReadResult::RESIZE:
|
||||||
|
resized.store(false);
|
||||||
|
ev.type = KeyEvent::KeyType::RESIZE;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return ev;
|
return ev;
|
||||||
}
|
}
|
||||||
if (buf.size() >= 3 && buf[0] == '\x1b' && buf[1] == '[' && buf[2] == 'M') {
|
if (buf.size() >= 3 && buf[0] == '\x1b' && buf[1] == '[' && buf[2] == 'M') {
|
||||||
KeyEvent ev = parse_mouse(buf);
|
ev = parse_mouse(buf);
|
||||||
if (ev.type == KeyEvent::KeyType::NONE)
|
if (ev.type == KeyEvent::KeyType::NONE)
|
||||||
continue;
|
continue;
|
||||||
return ev;
|
return ev;
|
||||||
}
|
}
|
||||||
if (buf.size() >= 2 && buf[0] == '\x1b' && buf[1] == '[')
|
if (buf.size() >= 2 && buf[0] == '\x1b' && buf[1] == '[')
|
||||||
return parse_escape(buf);
|
return parse_escape(buf);
|
||||||
KeyEvent ev;
|
|
||||||
ev.type = KeyEvent::KeyType::CHAR;
|
ev.type = KeyEvent::KeyType::CHAR;
|
||||||
ev.modifier = KeyEvent::Modifier::NONE;
|
ev.modifier = KeyEvent::Modifier::NONE;
|
||||||
if (buf.size() == 1) {
|
if (buf.size() == 1) {
|
||||||
|
|||||||
+12
-1
@@ -3,11 +3,17 @@
|
|||||||
namespace bed::internal::io {
|
namespace bed::internal::io {
|
||||||
termios IO::orig_termios{};
|
termios IO::orig_termios{};
|
||||||
bool IO::cleaned = false;
|
bool IO::cleaned = false;
|
||||||
|
volatile std::atomic_bool IO::resized(false);
|
||||||
|
|
||||||
IO::IO() {
|
IO::IO() {
|
||||||
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
|
if (tcgetattr(STDIN_FILENO, &orig_termios) == -1)
|
||||||
throw fatal_error("Can't get terminal state.", 1);
|
throw fatal_error("Can't get terminal state.", 1);
|
||||||
atexit(cleanup);
|
struct sigaction sa{};
|
||||||
|
sa.sa_handler = handle_sigwinch;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
if (sigaction(SIGWINCH, &sa, nullptr) == -1)
|
||||||
|
throw fatal_error("Can't install SIGWINCH handler.", 1);
|
||||||
struct termios raw = orig_termios;
|
struct termios raw = orig_termios;
|
||||||
raw.c_iflag &= ~(BRKINT | ISTRIP | IXON);
|
raw.c_iflag &= ~(BRKINT | ISTRIP | IXON);
|
||||||
raw.c_cflag |= (CS8);
|
raw.c_cflag |= (CS8);
|
||||||
@@ -18,6 +24,7 @@ IO::IO() {
|
|||||||
throw fatal_error("Can't set terminal state.", 1);
|
throw fatal_error("Can't set terminal state.", 1);
|
||||||
std::string os = "\x1b[?2004h";
|
std::string os = "\x1b[?2004h";
|
||||||
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
||||||
|
atexit(cleanup);
|
||||||
}
|
}
|
||||||
|
|
||||||
IO::~IO() {
|
IO::~IO() {
|
||||||
@@ -73,6 +80,10 @@ void IO::cleanup() {
|
|||||||
cleaned = true;
|
cleaned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IO::handle_sigwinch(int) {
|
||||||
|
resized.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
void IO::move_cursor(uint16_t row, uint16_t col) {
|
void IO::move_cursor(uint16_t row, uint16_t col) {
|
||||||
char buf[32];
|
char buf[32];
|
||||||
int n = snprintf(buf, sizeof(buf), "\x1b[%u;%uH", row, col);
|
int n = snprintf(buf, sizeof(buf), "\x1b[%u;%uH", row, col);
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
#include "bed.h"
|
||||||
|
#include "internal/io/io.h"
|
||||||
|
|
||||||
|
namespace bed::internal::io {
|
||||||
|
std::pair<std::string, bool> IO::get_text(BEd &) {
|
||||||
|
auto [row, col] = cursor_position();
|
||||||
|
auto [rows, cols] = terminal_size();
|
||||||
|
if (row > rows)
|
||||||
|
throw fatal_error("Invalid cursor position.", 1);
|
||||||
|
uint16_t start = row;
|
||||||
|
uint16_t height = rows - row + 1;
|
||||||
|
enable_mouse();
|
||||||
|
// uint16_t width = cols;
|
||||||
|
// TODO: support multiline wrapped commands.
|
||||||
|
// also handle ctrl+l to try and clear screen.
|
||||||
|
|
||||||
|
disable_mouse();
|
||||||
|
for (uint16_t i = 1; i < height; ++i) {
|
||||||
|
move_cursor(start + i, 1);
|
||||||
|
write_all(STDOUT_FILENO, "\x1b[2K", 4);
|
||||||
|
}
|
||||||
|
move_cursor(start, 1);
|
||||||
|
write_all(STDOUT_FILENO, "\n", 1);
|
||||||
|
return {"", true};
|
||||||
|
}
|
||||||
|
} // namespace bed::internal::io
|
||||||
Reference in New Issue
Block a user