Add text mode handling.
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
#include "internal/marks/marks.h"
|
#include "internal/marks/marks.h"
|
||||||
#include "internal/theme/theme.h"
|
#include "internal/theme/theme.h"
|
||||||
#include "internal/ui/command.h"
|
#include "internal/ui/command.h"
|
||||||
|
#include "internal/ui/text_mode.h"
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
namespace bed {
|
namespace bed {
|
||||||
|
|||||||
@@ -71,9 +71,6 @@ struct IO {
|
|||||||
std::pair<uint16_t, uint16_t> cursor_position();
|
std::pair<uint16_t, uint16_t> cursor_position();
|
||||||
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_text(BEd &);
|
|
||||||
|
|
||||||
KeyEvent read_key();
|
KeyEvent read_key();
|
||||||
void write(const char *, uint64_t);
|
void write(const char *, uint64_t);
|
||||||
void write(std::string_view);
|
void write(std::string_view);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "internal/io/io.h"
|
#include "definitions.h"
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
|
|
||||||
namespace bed::internal::ui {
|
namespace bed::internal::ui {
|
||||||
@@ -38,9 +38,8 @@ struct CommandIO {
|
|||||||
uint16_t term_height;
|
uint16_t term_height;
|
||||||
uint16_t term_width;
|
uint16_t term_width;
|
||||||
BEd &bed;
|
BEd &bed;
|
||||||
io::IO &io;
|
|
||||||
|
|
||||||
CommandIO(BEd &, io::IO &);
|
CommandIO(BEd &);
|
||||||
std::pair<std::string, bool> run();
|
std::pair<std::string, bool> run();
|
||||||
void redraw();
|
void redraw();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "definitions.h"
|
||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
namespace bed::internal::ui {
|
||||||
|
struct TextMode {
|
||||||
|
std::string cmd;
|
||||||
|
uint16_t cursor;
|
||||||
|
uint16_t start;
|
||||||
|
uint16_t height;
|
||||||
|
uint16_t term_height;
|
||||||
|
uint16_t term_width;
|
||||||
|
BEd &bed;
|
||||||
|
|
||||||
|
TextMode(BEd &);
|
||||||
|
std::pair<vase::Shard *, bool> run();
|
||||||
|
void grow(size_t required_height);
|
||||||
|
void redraw();
|
||||||
|
};
|
||||||
|
} // namespace bed::internal::ui
|
||||||
+10
-2
@@ -46,7 +46,8 @@ BEd::~BEd() {
|
|||||||
|
|
||||||
void BEd::run() {
|
void BEd::run() {
|
||||||
while (true) {
|
while (true) {
|
||||||
auto [cmd, eof] = io.get_command(*this);
|
internal::ui::CommandIO command(*this);
|
||||||
|
auto [cmd, eof] = command.run();
|
||||||
try {
|
try {
|
||||||
handle(cmd, eof);
|
handle(cmd, eof);
|
||||||
} catch (ed_error &e) {
|
} catch (ed_error &e) {
|
||||||
@@ -124,8 +125,15 @@ void BEd::handle(std::string_view cmd, bool eof) {
|
|||||||
throw ed_error("Line number can't be zero.");
|
throw ed_error("Line number can't be zero.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal::vase::Shard *text = nullptr;
|
||||||
|
if (c.function->input_mode == internal::functions::Function::InputMode::Text) {
|
||||||
|
internal::ui::TextMode tm(*this);
|
||||||
|
auto [a, b] = tm.run();
|
||||||
|
if (!b)
|
||||||
|
text = a;
|
||||||
|
}
|
||||||
if (c.function->handle)
|
if (c.function->handle)
|
||||||
c.function->handle(*this, address, nullptr, c.argument, nullptr);
|
c.function->handle(*this, address, text, c.argument, nullptr);
|
||||||
if (c.suffix)
|
if (c.suffix)
|
||||||
c.suffix->handle(*this);
|
c.suffix->handle(*this);
|
||||||
if (c.temp_address)
|
if (c.temp_address)
|
||||||
|
|||||||
@@ -53,6 +53,24 @@ void Function::register_posix(BEd &ctx) {
|
|||||||
throw fatal_error("Quitting", 0);
|
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(
|
ctx.functions.insert(
|
||||||
"j",
|
"j",
|
||||||
Function{
|
Function{
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -62,15 +62,15 @@ static std::string current_word(const std::string &line, size_t byte_pos) {
|
|||||||
return line.substr(start, byte_pos - start);
|
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)
|
if (bed.prompt_mode)
|
||||||
prompt = bed.prompt(bed);
|
prompt = bed.prompt(bed);
|
||||||
cursor = 0;
|
cursor = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<std::string, bool> CommandIO::run() {
|
std::pair<std::string, bool> CommandIO::run() {
|
||||||
auto [row, col] = io.cursor_position();
|
auto [row, col] = bed.io.cursor_position();
|
||||||
auto [rows, cols] = io.terminal_size();
|
auto [rows, cols] = bed.io.terminal_size();
|
||||||
if (row > rows)
|
if (row > rows)
|
||||||
throw fatal_error("Invalid cursor position.", 1);
|
throw fatal_error("Invalid cursor position.", 1);
|
||||||
start = row;
|
start = row;
|
||||||
@@ -79,11 +79,10 @@ std::pair<std::string, bool> CommandIO::run() {
|
|||||||
term_height = rows;
|
term_height = rows;
|
||||||
redraw();
|
redraw();
|
||||||
|
|
||||||
// main loop.
|
|
||||||
io::KeyEvent res;
|
io::KeyEvent res;
|
||||||
bool running = true;
|
bool running = true;
|
||||||
while (running) {
|
while (running) {
|
||||||
res = io.read_key();
|
res = bed.io.read_key();
|
||||||
switch (res.type) {
|
switch (res.type) {
|
||||||
case io::KeyEvent::KeyType::EOF_:
|
case io::KeyEvent::KeyType::EOF_:
|
||||||
running = false;
|
running = false;
|
||||||
@@ -139,20 +138,20 @@ std::pair<std::string, bool> CommandIO::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint16_t i = 1; i < height; ++i) {
|
for (uint16_t i = 1; i < height; ++i) {
|
||||||
io.move_cursor(start + i, 1);
|
bed.io.move_cursor(start + i, 1);
|
||||||
io.write("\x1b[2K", 4);
|
bed.io.write("\x1b[2K", 4);
|
||||||
}
|
}
|
||||||
io.move_cursor(start, 1);
|
bed.io.move_cursor(start, 1);
|
||||||
io.write("\n", 1);
|
bed.io.write("\n", 1);
|
||||||
return {cmd, false};
|
return {cmd, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandIO::redraw() {
|
void CommandIO::redraw() {
|
||||||
io.move_cursor(start, 1);
|
bed.io.move_cursor(start, 1);
|
||||||
io.write("\x1b[2K", 4);
|
bed.io.write("\x1b[2K", 4);
|
||||||
io.move_cursor(start, 1);
|
bed.io.move_cursor(start, 1);
|
||||||
io.write(prompt);
|
bed.io.write(prompt);
|
||||||
io.write(cmd);
|
bed.io.write(cmd);
|
||||||
io.move_cursor(start, prompt.size() + cursor + 1);
|
bed.io.move_cursor(start, prompt.size() + cursor + 1);
|
||||||
}
|
}
|
||||||
} // namespace bed::internal::ui
|
} // namespace bed::internal::ui
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user