#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_prompt(std::string_view prompt) { std::scoped_lock lock(mutex_); 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) { drvutil::console_write(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(); drvutil::console_write(n_backspaces(ccsize)); drvutil::console_write(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) { drvutil::console_write(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()) { drvutil::console_write(newpart); current_command_ = desired_command_; } } std::string ReadlineDevice::putcode(char32_t c) { std::scoped_lock lock(mutex_); 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(); drvutil::console_write(white_ + newline_); std::u32string result = desired_command_ + newline_; desired_command_.clear(); current_prompt_.clear(); current_command_.clear(); 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) { std::scoped_lock lock(mutex_); bool missing_newline = ((s.size() == 0) || (s[s.size() - 1] != '\n')); std::u32string utf32 = drvutil::utf8_to_utf32(s, nullptr); erase_command(); drvutil::console_write(utf32); if (missing_newline) drvutil::console_write(newline_); echo_command(); }