116 lines
3.3 KiB
C++
116 lines
3.3 KiB
C++
#include "readline.hpp"
|
|
|
|
#define MAXLINE 512
|
|
|
|
|
|
static std::u32string white_(1, ' ');
|
|
static std::u32string newline_(1, '\n');
|
|
|
|
static std::u32string n_backspaces(int n) {
|
|
std::u32string result(3 * n, 0);
|
|
for (int i = 0; i < n; i++) {
|
|
result[i*3 + 0] = '\b';
|
|
result[i*3 + 1] = ' ';
|
|
result[i*3 + 2] = '\b';
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int common_prefix_length(const std::u32string &a, const std::u32string &b) {
|
|
int minlen = std::min(a.size(), b.size());
|
|
for (int i = 0; i < minlen; i++) {
|
|
if (a[i] != b[i]) return i;
|
|
}
|
|
return minlen;
|
|
}
|
|
|
|
void ReadlineDevice::set_print_callback(print_callback cb) {
|
|
print_cb_ = cb;
|
|
}
|
|
|
|
void ReadlineDevice::set_prompt(std::string_view prompt) {
|
|
desired_prompt_ = drvutil::utf8_to_utf32(prompt, nullptr);
|
|
echo_command();
|
|
}
|
|
|
|
void ReadlineDevice::erase_command() {
|
|
int ccsize = current_prompt_.size() + current_command_.size();
|
|
if (ccsize > 0) {
|
|
print_cb_(n_backspaces(ccsize));
|
|
current_prompt_.clear();
|
|
current_command_.clear();
|
|
}
|
|
}
|
|
|
|
void ReadlineDevice::echo_command() {
|
|
// If the prompt has changed, erase everything and start over.
|
|
if (desired_prompt_ != current_prompt_) {
|
|
int ccsize = current_prompt_.size() + current_command_.size();
|
|
print_cb_(n_backspaces(ccsize));
|
|
print_cb_(desired_prompt_);
|
|
current_command_.clear();
|
|
current_prompt_ = desired_prompt_;
|
|
}
|
|
|
|
// Find out how much of the command matches.
|
|
int match = common_prefix_length(current_command_, desired_command_);
|
|
|
|
// Echo backspaces to remove the non-matching part.
|
|
int remove = current_command_.size() - match;
|
|
if (remove > 0) {
|
|
print_cb_(n_backspaces(remove));
|
|
current_command_ = current_command_.substr(0, match);
|
|
}
|
|
|
|
// Echo the new part.
|
|
std::u32string newpart = desired_command_.substr(current_command_.size());
|
|
if (!newpart.empty()) {
|
|
print_cb_(newpart);
|
|
current_command_ = desired_command_;
|
|
}
|
|
}
|
|
|
|
std::string ReadlineDevice::putcode(char32_t c) {
|
|
if ((c == '\n') && (readline_lastc_ == '\r')) {
|
|
// Ignore newline immediately after carriage return.
|
|
// Otherwise, crlf produces two newlines.
|
|
return "";
|
|
} else if ((c == '\r') || (c == '\n')) {
|
|
echo_command();
|
|
print_cb_(white_ + newline_);
|
|
std::u32string result = desired_command_ + newline_;
|
|
desired_command_.clear();
|
|
current_prompt_.clear();
|
|
current_command_.clear();
|
|
echo_command();
|
|
return drvutil::utf32_to_utf8(result);
|
|
} else if ((c == '\b') || (c == 127)) {
|
|
int len = desired_command_.size();
|
|
if (len > 0) {
|
|
desired_command_ = desired_command_.substr(0, len-1);
|
|
}
|
|
echo_command();
|
|
return "";
|
|
} else if ((c >= 32)&&(c <= 0x10FFFF)) {
|
|
int len = desired_command_.size();
|
|
if (len < MAXLINE) {
|
|
desired_command_ = desired_command_ + c;
|
|
}
|
|
echo_command();
|
|
return "";
|
|
}
|
|
readline_lastc_ = c;
|
|
return "";
|
|
}
|
|
|
|
|
|
void ReadlineDevice::printline(std::string_view s) {
|
|
bool missing_newline = ((s.size() == 0) || (s[s.size() - 1] != '\n'));
|
|
std::u32string utf32 = drvutil::utf8_to_utf32(s, nullptr);
|
|
erase_command();
|
|
print_cb_(utf32);
|
|
if (missing_newline) print_cb_(newline_);
|
|
echo_command();
|
|
}
|
|
|