Fixes.
This commit is contained in:
@@ -1,10 +0,0 @@
|
||||
#include "bed.h"
|
||||
#include "internal/io/command.h"
|
||||
#include "internal/io/io.h"
|
||||
|
||||
namespace bed::internal::io {
|
||||
std::pair<std::string, bool> IO::get_command(BEd &ctx) {
|
||||
CommandIO cio(ctx, *this);
|
||||
return cio.run();
|
||||
}
|
||||
} // namespace bed::internal::io
|
||||
+5
-151
@@ -1,156 +1,10 @@
|
||||
#include "internal/ui/command.h"
|
||||
#include "bed.h"
|
||||
#include "internal/io/io.h"
|
||||
|
||||
namespace bed::internal::io {
|
||||
/*template <typename F>
|
||||
static void for_each_cluster(std::string_view s, F &&f) {
|
||||
unicode_width_state_t state;
|
||||
unicode_width_init(&state);
|
||||
size_t i = 0;
|
||||
while (i < s.size()) {
|
||||
unsigned char c = static_cast<unsigned char>(s[i]);
|
||||
size_t bytes = 1;
|
||||
int width = 0;
|
||||
if (c < 128) {
|
||||
width = unicode_width_process(&state, c);
|
||||
} else {
|
||||
uint_least32_t cp;
|
||||
size_t decoded = grapheme_decode_utf8(s.data() + i, s.size() - i, &cp);
|
||||
bytes = decoded > 0 ? decoded : 1;
|
||||
width = unicode_width_process(&state, cp);
|
||||
}
|
||||
if (width < 0)
|
||||
width = 0;
|
||||
f(i, bytes, width);
|
||||
i += bytes;
|
||||
}
|
||||
}
|
||||
|
||||
static int display_width(std::string_view s) {
|
||||
int w = 0;
|
||||
for_each_cluster(s, [&](size_t, size_t, int cw) { w += cw; });
|
||||
return w;
|
||||
}
|
||||
|
||||
static uint16_t count_clusters(std::string_view s) {
|
||||
uint16_t n = 0;
|
||||
for_each_cluster(s, [&](size_t, size_t, int) { ++n; });
|
||||
return n;
|
||||
}
|
||||
|
||||
static std::vector<uint16_t> wrap_offsets(std::string_view line, uint16_t avail) {
|
||||
std::vector<uint16_t> offsets{0};
|
||||
int col = 0;
|
||||
for_each_cluster(line, [&](uint16_t i, uint16_t, int w) {
|
||||
if (col + w > avail && col > 0) {
|
||||
offsets.push_back(i);
|
||||
col = 0;
|
||||
}
|
||||
col += w;
|
||||
});
|
||||
return offsets;
|
||||
}
|
||||
|
||||
// The word under/before `byte_pos`, split on plain ASCII spaces. Used to
|
||||
// pick what prefix to hand the suggestion trie.
|
||||
// TODO: change to use libgrapheme word break here.
|
||||
static std::string current_word(const std::string &line, size_t byte_pos) {
|
||||
size_t start = (byte_pos == 0) ? std::string::npos : line.rfind(' ', byte_pos - 1);
|
||||
start = (start == std::string::npos) ? 0 : start + 1;
|
||||
if (byte_pos < start)
|
||||
byte_pos = start;
|
||||
return line.substr(start, byte_pos - start);
|
||||
}*/
|
||||
|
||||
CommandIO::CommandIO(BEd &bed, IO &io) : bed(bed), io(io) {
|
||||
prompt = bed.prompt(bed);
|
||||
cursor = 0;
|
||||
}
|
||||
|
||||
std::pair<std::string, bool> CommandIO::run() {
|
||||
auto [row, col] = io.cursor_position();
|
||||
auto [rows, cols] = io.terminal_size();
|
||||
if (row > rows)
|
||||
throw fatal_error("Invalid cursor position.", 1);
|
||||
start = row;
|
||||
height = rows - row + 1;
|
||||
term_width = cols;
|
||||
term_height = rows;
|
||||
redraw();
|
||||
|
||||
// main loop.
|
||||
KeyEvent res;
|
||||
bool running = true;
|
||||
while (running) {
|
||||
res = io.read_key();
|
||||
switch (res.type) {
|
||||
case KeyEvent::KeyType::EOF_:
|
||||
running = false;
|
||||
break;
|
||||
case KeyEvent::KeyType::MOUSE:
|
||||
case KeyEvent::KeyType::RESIZE:
|
||||
break;
|
||||
case KeyEvent::KeyType::CHAR:
|
||||
switch (res.modifier) {
|
||||
case KeyEvent::Modifier::SHIFT:
|
||||
case KeyEvent::Modifier::ALT:
|
||||
case KeyEvent::Modifier::CTRL_ALT:
|
||||
case KeyEvent::Modifier::CTRL:
|
||||
break;
|
||||
case KeyEvent::Modifier::NONE:
|
||||
if (res.text[0] == '\b' || res.text[0] == 0x7f) {
|
||||
if (cursor > 0)
|
||||
cmd.erase(--cursor, 1);
|
||||
} else if (res.text[0] == '\n') {
|
||||
running = false;
|
||||
} else {
|
||||
cmd.insert(cursor++, res.text);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case KeyEvent::KeyType::PASTE:
|
||||
cmd.insert(cursor, res.text);
|
||||
cursor += res.text.size();
|
||||
break;
|
||||
case KeyEvent::KeyType::SPECIAL:
|
||||
switch (res.special_key) {
|
||||
case KeyEvent::SpecialKey::UNKNOWN:
|
||||
case KeyEvent::SpecialKey::UP:
|
||||
case KeyEvent::SpecialKey::DOWN:
|
||||
break;
|
||||
case KeyEvent::SpecialKey::RIGHT:
|
||||
if (cursor < cmd.size())
|
||||
cursor++;
|
||||
break;
|
||||
case KeyEvent::SpecialKey::LEFT:
|
||||
if (cursor > 0)
|
||||
cursor--;
|
||||
break;
|
||||
case KeyEvent::SpecialKey::DELETE:
|
||||
if (cursor < cmd.size())
|
||||
cmd.erase(cursor, 1);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
|
||||
for (uint16_t i = 1; i < height; ++i) {
|
||||
io.move_cursor(start + i, 1);
|
||||
io.write("\x1b[2K", 4);
|
||||
}
|
||||
io.move_cursor(start, 1);
|
||||
io.write("\n", 1);
|
||||
return {cmd, false};
|
||||
}
|
||||
|
||||
void CommandIO::redraw() {
|
||||
io.move_cursor(start, 1);
|
||||
io.write("\x1b[2K", 4);
|
||||
io.move_cursor(start, 1);
|
||||
io.write(prompt);
|
||||
io.write(cmd);
|
||||
io.move_cursor(start, prompt.size() + cursor + 1);
|
||||
std::pair<std::string, bool> IO::get_command(BEd &ctx) {
|
||||
ui::CommandIO cio(ctx, *this);
|
||||
return cio.run();
|
||||
}
|
||||
} // namespace bed::internal::io
|
||||
|
||||
+19
-11
@@ -2,7 +2,8 @@
|
||||
|
||||
namespace bed::internal::io {
|
||||
termios IO::orig_termios{};
|
||||
bool IO::cleaned = false;
|
||||
termios IO::raw_termios{};
|
||||
bool IO::cleaned = true;
|
||||
volatile std::atomic_bool IO::resized(false);
|
||||
|
||||
IO::IO() {
|
||||
@@ -14,16 +15,13 @@ IO::IO() {
|
||||
sa.sa_flags = 0;
|
||||
if (sigaction(SIGWINCH, &sa, nullptr) == -1)
|
||||
throw fatal_error("Can't install SIGWINCH handler.", 1);
|
||||
struct termios raw = orig_termios;
|
||||
raw.c_iflag &= ~(BRKINT | ISTRIP | IXON);
|
||||
raw.c_cflag |= (CS8);
|
||||
raw.c_lflag &= ~(ECHO | ICANON | ISIG);
|
||||
raw.c_cc[VMIN] = 1;
|
||||
raw.c_cc[VTIME] = 0;
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw) == -1)
|
||||
throw fatal_error("Can't set terminal state.", 1);
|
||||
std::string os = "\x1b[?2004h";
|
||||
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
||||
raw_termios = orig_termios;
|
||||
raw_termios.c_iflag &= ~(BRKINT | ISTRIP | IXON);
|
||||
raw_termios.c_cflag |= (CS8);
|
||||
raw_termios.c_lflag &= ~(ECHO | ICANON | ISIG);
|
||||
raw_termios.c_cc[VMIN] = 1;
|
||||
raw_termios.c_cc[VTIME] = 0;
|
||||
enable_raw();
|
||||
atexit(cleanup);
|
||||
}
|
||||
|
||||
@@ -70,6 +68,16 @@ std::pair<uint16_t, uint16_t> IO::cursor_position() {
|
||||
return {(uint16_t)row, (uint16_t)col};
|
||||
}
|
||||
|
||||
void IO::enable_raw() {
|
||||
if (!cleaned)
|
||||
return;
|
||||
std::string os = "\x1b[?2004h";
|
||||
write_all(STDOUT_FILENO, os.c_str(), os.size());
|
||||
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_termios) == -1)
|
||||
throw fatal_error("Can't set raw terminal state.", 1);
|
||||
cleaned = false;
|
||||
}
|
||||
|
||||
void IO::cleanup() {
|
||||
if (cleaned)
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user