| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- module;
- #include <cstdio>
- module Core.ReadLine;
- import Core.Array;
- import Core.Clock;
- import Core.Logger;
- import Core.Queue;
- import Core.Thread;
- import Core.Terminal;
- import Core.TerminalConstants;
- import Core.Types;
- import Core.Unicode;
- import Core.Std;
- static constexpr size_t HISTORY_LENGTH = 10;
- static constexpr size_t CONSOLE_BUFFER_SIZE = 256;
- struct ConsoleLine {
- Core::Array<char, 256> data{};
- size_t length = 0;
- };
- static std::atomic_bool running = true;
- static Core::Thread readThread;
- static Core::Queue<ConsoleLine, 10> buffer;
- static ConsoleLine currentBuffer;
- static size_t move = 0;
- static size_t cursorMove = 0;
- static Core::Mutex bufferMutex;
- static ConsoleLine history[HISTORY_LENGTH];
- static size_t historyOffset = 0;
- static size_t historyIndex = 0;
- static size_t historyLength = 0;
- static void addChar(u64 c) {
- Core::UTF8 u = Core::convertUnicodeToUTF8(static_cast<u32>(c));
- for(u32 k = 0; k < u.length; k++) {
- if(currentBuffer.length >= CONSOLE_BUFFER_SIZE - 1) {
- return;
- }
- for(size_t i = 0; i < move; i++) {
- currentBuffer.data[currentBuffer.length - i] =
- currentBuffer.data[currentBuffer.length - i - 1];
- }
- currentBuffer.length++;
- currentBuffer.data[currentBuffer.length - move - 1] =
- static_cast<char>(u.data[k]);
- currentBuffer.data[currentBuffer.length] = '\0';
- }
- }
- static void print(const char* s) {
- fputs(s, stdout);
- }
- static void refreshLine(const char* prefix) {
- print(prefix);
- print(currentBuffer.data.begin());
- if(cursorMove > 0) {
- Core::moveCursorLeft(static_cast<int>(cursorMove));
- }
- fflush(stdout);
- Core::clearTerminalLine();
- }
- static bool clear() {
- move = 0;
- cursorMove = 0;
- currentBuffer.length = 0;
- currentBuffer.data[0] = '\0';
- historyOffset = 0;
- return false;
- }
- static void addToHistory() {
- if(historyLength < HISTORY_LENGTH) {
- historyLength++;
- }
- history[historyIndex] = currentBuffer;
- historyIndex = (historyIndex + 1) % HISTORY_LENGTH;
- }
- static void addLine() {
- addToHistory();
- Core::MutexGuard mg(bufferMutex);
- buffer.add(currentBuffer);
- clear();
- }
- static bool removeChar() {
- size_t pos = currentBuffer.length - move;
- if(pos > 0) {
- size_t l = 1;
- while(pos >= l) {
- if(!Core::isUTF8Remainder(
- static_cast<u8>(currentBuffer.data[pos - l]))) {
- break;
- }
- l++;
- }
- currentBuffer.length -= l;
- for(size_t i = pos - l; i <= currentBuffer.length; i++) {
- currentBuffer.data[i] = currentBuffer.data[i + l];
- }
- }
- return false;
- }
- static bool handleControlKey(u64 c) {
- switch(c) {
- case 3: return clear();
- case 10:
- case 13: return true;
- case 127: return removeChar();
- }
- return false;
- }
- static void copyHistory() {
- currentBuffer = history
- [(historyIndex - historyOffset + HISTORY_LENGTH) % HISTORY_LENGTH];
- move = 0;
- cursorMove = 0;
- }
- static void handleUpArrow() {
- if(historyOffset >= historyLength) {
- return;
- }
- historyOffset++;
- copyHistory();
- }
- static void handleDownArrow() {
- if(historyOffset <= 1) {
- return;
- }
- historyOffset--;
- copyHistory();
- }
- static char getMoved() {
- return currentBuffer.data[currentBuffer.length - move];
- }
- static void handleLeftArrow() {
- if(move < currentBuffer.length) {
- move++;
- while(move < currentBuffer.length &&
- Core::isUTF8Remainder(static_cast<u8>(getMoved()))) {
- move++;
- }
- cursorMove++;
- }
- }
- static void handleRightArrow() {
- if(move > 0) {
- move--;
- while(move > 0 && Core::isUTF8Remainder(static_cast<u8>(getMoved()))) {
- move--;
- }
- cursorMove--;
- }
- }
- static void handleChars() {
- while(true) {
- u64 c = Core::getRawChar();
- if(c == 0) {
- break;
- } else if(c == Core::Terminal::KEY_ARROW_UP) {
- handleUpArrow();
- } else if(c == Core::Terminal::KEY_ARROW_DOWN) {
- handleDownArrow();
- } else if(c == Core::Terminal::KEY_ARROW_RIGHT) {
- handleRightArrow();
- } else if(c == Core::Terminal::KEY_ARROW_LEFT) {
- handleLeftArrow();
- } else if(c == Core::Terminal::KEY_DELETE) {
- if(move > 0) {
- handleRightArrow();
- removeChar();
- }
- } else if(iscntrl(c & 0x7FFF'FFFF)) {
- if(handleControlKey(c)) {
- addLine();
- return;
- }
- } else if(!Core::isSpecialChar(c)) {
- addChar(c);
- }
- }
- }
- static void loop(void*) {
- while(running) {
- handleChars();
- refreshLine("> ");
- Core::Clock::sleepMillis(1);
- }
- }
- bool Core::startReadLine(void) {
- if(enterRawTerminal()) {
- report(LogLevel::WARNING, "cannot set terminal attributes");
- }
- running = true;
- if(readThread.start(loop, nullptr)) {
- report(LogLevel::ERROR, "cannot start read thread");
- stopReadLine();
- return true;
- }
- return false;
- }
- bool Core::readLine(char* buffer_, size_t n) {
- if(buffer.getLength() == 0) {
- return false;
- }
- Core::MutexGuard mg(bufferMutex);
- snprintf(buffer_, n, "%s", buffer[0].data.begin());
- buffer.remove();
- return true;
- }
- void Core::stopReadLine() {
- running = false;
- readThread.join();
- if(Core::leaveRawTerminal()) {
- report(LogLevel::WARNING, "cannot restore terminal attributes");
- }
- buffer.clear();
- }
|