Add text mode handling.

This commit is contained in:
2026-08-30 14:33:39 +01:00
parent 87d9148b82
commit 7a18aa0db2
10 changed files with 254 additions and 61 deletions
+18
View File
@@ -53,6 +53,24 @@ void Function::register_posix(BEd &ctx) {
throw fatal_error("Quitting", 0);
}
};
ctx.functions.insert(
"a",
Function{
.address_kind = Function::AddressKind::Line,
.argument_kind = Function::ArgumentKind::None,
.input_mode = Function::InputMode::Text,
.desc = "Append text to a line.",
.default_address = ".",
.accept_zero = true,
.pre_text_mode = nullptr,
.handle = [](BEd &ctx, const buffer::Address &addr_, vase::Shard *text, const Argument &, std::vector<buffer::Line> *) {
auto addr = std::get<buffer::Line>(addr_);
ctx.buffer(addr.buffername).append(ctx, text, addr.number);
ctx.current() = {ctx.prev.buffername, ctx.prev.end};
vase::Shard::release(text);
}
}
);
ctx.functions.insert(
"j",
Function{
-10
View File
@@ -1,10 +0,0 @@
#include "internal/ui/command.h"
#include "bed.h"
#include "internal/io/io.h"
namespace bed::internal::io {
std::pair<std::string, bool> IO::get_command(BEd &ctx) {
ui::CommandIO cio(ctx, *this);
return cio.run();
}
} // namespace bed::internal::io
-28
View File
@@ -1,28 +0,0 @@
#include "bed.h"
#include "internal/io/io.h"
namespace bed::internal::io {
std::pair<std::string, bool> IO::get_text(BEd &) {
uint16_t start;
uint16_t height;
{
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;
}
enable_mouse();
// uint16_t width = cols;
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
+14 -15
View File
@@ -62,15 +62,15 @@ static std::string current_word(const std::string &line, size_t byte_pos) {
return line.substr(start, byte_pos - start);
}*/
CommandIO::CommandIO(BEd &bed, io::IO &io) : bed(bed), io(io) {
CommandIO::CommandIO(BEd &bed) : bed(bed) {
if (bed.prompt_mode)
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();
auto [row, col] = bed.io.cursor_position();
auto [rows, cols] = bed.io.terminal_size();
if (row > rows)
throw fatal_error("Invalid cursor position.", 1);
start = row;
@@ -79,11 +79,10 @@ std::pair<std::string, bool> CommandIO::run() {
term_height = rows;
redraw();
// main loop.
io::KeyEvent res;
bool running = true;
while (running) {
res = io.read_key();
res = bed.io.read_key();
switch (res.type) {
case io::KeyEvent::KeyType::EOF_:
running = false;
@@ -139,20 +138,20 @@ std::pair<std::string, bool> CommandIO::run() {
}
for (uint16_t i = 1; i < height; ++i) {
io.move_cursor(start + i, 1);
io.write("\x1b[2K", 4);
bed.io.move_cursor(start + i, 1);
bed.io.write("\x1b[2K", 4);
}
io.move_cursor(start, 1);
io.write("\n", 1);
bed.io.move_cursor(start, 1);
bed.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);
bed.io.move_cursor(start, 1);
bed.io.write("\x1b[2K", 4);
bed.io.move_cursor(start, 1);
bed.io.write(prompt);
bed.io.write(cmd);
bed.io.move_cursor(start, prompt.size() + cursor + 1);
}
} // namespace bed::internal::ui
+188
View File
@@ -0,0 +1,188 @@
#include "internal/ui/text_mode.h"
#include "bed.h"
namespace bed::internal::ui {
TextMode::TextMode(BEd &bed) : bed(bed) {
cursor = 0;
}
std::pair<vase::Shard *, bool> TextMode::run() {
auto [row, col] = bed.io.cursor_position();
auto [rows, cols] = bed.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;
cmd.clear();
cursor = 0;
redraw();
bool running = true;
while (running) {
io::KeyEvent res = bed.io.read_key();
switch (res.type) {
case io::KeyEvent::KeyType::EOF_:
running = false;
break;
case io::KeyEvent::KeyType::MOUSE:
case io::KeyEvent::KeyType::RESIZE:
break;
case io::KeyEvent::KeyType::CHAR:
switch (res.modifier) {
case io::KeyEvent::Modifier::SHIFT:
case io::KeyEvent::Modifier::ALT:
case io::KeyEvent::Modifier::CTRL_ALT:
case io::KeyEvent::Modifier::CTRL:
break;
case io::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') {
size_t lines = 1 + std::count(cmd.begin(), cmd.end(), '\n');
grow(lines + 1);
cmd.insert(cursor++, 1, '\n');
} else {
cmd.insert(cursor, res.text);
cursor += res.text.size();
}
break;
}
break;
case io::KeyEvent::KeyType::PASTE:
cmd.insert(cursor, res.text);
cursor += res.text.size();
break;
case io::KeyEvent::KeyType::SPECIAL:
switch (res.special_key) {
case io::KeyEvent::SpecialKey::UNKNOWN:
break;
case io::KeyEvent::SpecialKey::RIGHT:
if (cursor < cmd.size())
++cursor;
break;
case io::KeyEvent::SpecialKey::LEFT:
if (cursor > 0)
--cursor;
break;
case io::KeyEvent::SpecialKey::UP: {
size_t line_start =
cmd.rfind('\n', cursor == 0 ? 0 : cursor - 1);
if (line_start == std::string::npos)
line_start = 0;
else
++line_start;
size_t col = cursor - line_start;
if (line_start == 0)
break;
size_t prev_end = line_start - 1;
size_t prev_start =
cmd.rfind('\n', prev_end == 0 ? 0 : prev_end - 1);
if (prev_start == std::string::npos)
prev_start = 0;
else
++prev_start;
size_t prev_len = prev_end - prev_start;
cursor = prev_start + std::min(col, prev_len);
break;
}
case io::KeyEvent::SpecialKey::DOWN: {
size_t line_start =
cmd.rfind('\n', cursor == 0 ? 0 : cursor - 1);
if (line_start == std::string::npos)
line_start = 0;
else
++line_start;
size_t col = cursor - line_start;
size_t line_end = cmd.find('\n', cursor);
if (line_end == std::string::npos)
line_end = cmd.size();
if (line_end == cmd.size())
break;
size_t next_start = line_end + 1;
size_t next_end = cmd.find('\n', next_start);
if (next_end == std::string::npos)
next_end = cmd.size();
size_t next_len = next_end - next_start;
cursor = next_start + std::min(col, next_len);
break;
}
case io::KeyEvent::SpecialKey::DELETE:
if (cursor < cmd.size())
cmd.erase(cursor, 1);
break;
}
break;
}
redraw();
if (cmd.size() >= 3 && cmd.compare(cmd.size() - 3, 3, "\n.\n") == 0) {
cmd.erase(cmd.size() - 3);
cursor = cmd.size();
running = false;
}
}
size_t total_lines = 1 + std::count(cmd.begin(), cmd.end(), '\n');
uint16_t last_row = start + total_lines;
bed.io.move_cursor(last_row, 1);
bed.io.write("\n", 1);
return {vase::Shard::from_string(cmd.data(), cmd.length(), true), false};
}
void TextMode::grow(size_t required_height) {
auto [rows, cols] = bed.io.terminal_size();
term_height = rows;
term_width = cols;
if (required_height > height)
height = required_height;
long overflow = long(start) + long(height) - 1 - long(rows);
if (overflow <= 0)
return;
bed.io.move_cursor(rows, 1);
for (long i = 0; i < overflow; ++i)
bed.io.write("\n", 1);
start -= overflow;
if (start < 1)
start = 1;
}
void TextMode::redraw() {
auto [rows, cols] = bed.io.terminal_size();
term_height = rows;
term_width = cols;
for (uint16_t i = 0; i < height; ++i) {
bed.io.move_cursor(start + i, 1);
bed.io.write("\x1b[2K", 4);
}
size_t line = 0;
size_t line_start = 0;
for (size_t i = 0; i < cursor; ++i) {
if (cmd[i] == '\n') {
++line;
line_start = i + 1;
}
}
size_t col = cursor - line_start;
size_t pos = 0;
uint16_t screen_line = start;
while (pos <= cmd.size()) {
size_t end = cmd.find('\n', pos);
if (end == std::string::npos)
end = cmd.size();
if (screen_line < start + height) {
size_t len = std::min(end - pos, size_t(term_width));
bed.io.move_cursor(screen_line, 1);
bed.io.write(cmd.substr(pos, len));
}
if (end == cmd.size())
break;
pos = end + 1;
++screen_line;
}
size_t vis_col = std::min(col, size_t(term_width - 1));
bed.io.move_cursor(start + line, vis_col + 1);
}
} // namespace bed::internal::ui