ReadLine.cpp 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. module;
  2. #include <cstdio>
  3. module Core.ReadLine;
  4. import Core.Array;
  5. import Core.Clock;
  6. import Core.Logger;
  7. import Core.Queue;
  8. import Core.Thread;
  9. import Core.Terminal;
  10. import Core.TerminalConstants;
  11. import Core.Types;
  12. import Core.Unicode;
  13. import Core.Std;
  14. static constexpr size_t HISTORY_LENGTH = 10;
  15. static constexpr size_t CONSOLE_BUFFER_SIZE = 256;
  16. struct ConsoleLine {
  17. Core::Array<char, 256> data{};
  18. size_t length = 0;
  19. };
  20. static std::atomic_bool running = true;
  21. static Core::Thread readThread;
  22. static Core::Queue<ConsoleLine, 10> buffer;
  23. static ConsoleLine currentBuffer;
  24. static size_t move = 0;
  25. static size_t cursorMove = 0;
  26. static Core::Mutex bufferMutex;
  27. static ConsoleLine history[HISTORY_LENGTH];
  28. static size_t historyOffset = 0;
  29. static size_t historyIndex = 0;
  30. static size_t historyLength = 0;
  31. static void addChar(u64 c) {
  32. Core::UTF8 u = Core::convertUnicodeToUTF8(static_cast<u32>(c));
  33. for(u32 k = 0; k < u.length; k++) {
  34. if(currentBuffer.length >= CONSOLE_BUFFER_SIZE - 1) {
  35. return;
  36. }
  37. for(size_t i = 0; i < move; i++) {
  38. currentBuffer.data[currentBuffer.length - i] =
  39. currentBuffer.data[currentBuffer.length - i - 1];
  40. }
  41. currentBuffer.length++;
  42. currentBuffer.data[currentBuffer.length - move - 1] =
  43. static_cast<char>(u.data[k]);
  44. currentBuffer.data[currentBuffer.length] = '\0';
  45. }
  46. }
  47. static void print(const char* s) {
  48. fputs(s, stdout);
  49. }
  50. static void refreshLine(const char* prefix) {
  51. print(prefix);
  52. print(currentBuffer.data.begin());
  53. if(cursorMove > 0) {
  54. Core::moveCursorLeft(static_cast<int>(cursorMove));
  55. }
  56. fflush(stdout);
  57. Core::clearTerminalLine();
  58. }
  59. static bool clear() {
  60. move = 0;
  61. cursorMove = 0;
  62. currentBuffer.length = 0;
  63. currentBuffer.data[0] = '\0';
  64. historyOffset = 0;
  65. return false;
  66. }
  67. static void addToHistory() {
  68. if(historyLength < HISTORY_LENGTH) {
  69. historyLength++;
  70. }
  71. history[historyIndex] = currentBuffer;
  72. historyIndex = (historyIndex + 1) % HISTORY_LENGTH;
  73. }
  74. static void addLine() {
  75. addToHistory();
  76. Core::MutexGuard mg(bufferMutex);
  77. buffer.add(currentBuffer);
  78. clear();
  79. }
  80. static bool removeChar() {
  81. size_t pos = currentBuffer.length - move;
  82. if(pos > 0) {
  83. size_t l = 1;
  84. while(pos >= l) {
  85. if(!Core::isUTF8Remainder(
  86. static_cast<u8>(currentBuffer.data[pos - l]))) {
  87. break;
  88. }
  89. l++;
  90. }
  91. currentBuffer.length -= l;
  92. for(size_t i = pos - l; i <= currentBuffer.length; i++) {
  93. currentBuffer.data[i] = currentBuffer.data[i + l];
  94. }
  95. }
  96. return false;
  97. }
  98. static bool handleControlKey(u64 c) {
  99. switch(c) {
  100. case 3: return clear();
  101. case 10:
  102. case 13: return true;
  103. case 127: return removeChar();
  104. }
  105. return false;
  106. }
  107. static void copyHistory() {
  108. currentBuffer = history
  109. [(historyIndex - historyOffset + HISTORY_LENGTH) % HISTORY_LENGTH];
  110. move = 0;
  111. cursorMove = 0;
  112. }
  113. static void handleUpArrow() {
  114. if(historyOffset >= historyLength) {
  115. return;
  116. }
  117. historyOffset++;
  118. copyHistory();
  119. }
  120. static void handleDownArrow() {
  121. if(historyOffset <= 1) {
  122. return;
  123. }
  124. historyOffset--;
  125. copyHistory();
  126. }
  127. static char getMoved() {
  128. return currentBuffer.data[currentBuffer.length - move];
  129. }
  130. static void handleLeftArrow() {
  131. if(move < currentBuffer.length) {
  132. move++;
  133. while(move < currentBuffer.length &&
  134. Core::isUTF8Remainder(static_cast<u8>(getMoved()))) {
  135. move++;
  136. }
  137. cursorMove++;
  138. }
  139. }
  140. static void handleRightArrow() {
  141. if(move > 0) {
  142. move--;
  143. while(move > 0 && Core::isUTF8Remainder(static_cast<u8>(getMoved()))) {
  144. move--;
  145. }
  146. cursorMove--;
  147. }
  148. }
  149. static void handleChars() {
  150. while(true) {
  151. u64 c = Core::getRawChar();
  152. if(c == 0) {
  153. break;
  154. } else if(c == Core::Terminal::KEY_ARROW_UP) {
  155. handleUpArrow();
  156. } else if(c == Core::Terminal::KEY_ARROW_DOWN) {
  157. handleDownArrow();
  158. } else if(c == Core::Terminal::KEY_ARROW_RIGHT) {
  159. handleRightArrow();
  160. } else if(c == Core::Terminal::KEY_ARROW_LEFT) {
  161. handleLeftArrow();
  162. } else if(c == Core::Terminal::KEY_DELETE) {
  163. if(move > 0) {
  164. handleRightArrow();
  165. removeChar();
  166. }
  167. } else if(iscntrl(c & 0x7FFF'FFFF)) {
  168. if(handleControlKey(c)) {
  169. addLine();
  170. return;
  171. }
  172. } else if(!Core::isSpecialChar(c)) {
  173. addChar(c);
  174. }
  175. }
  176. }
  177. static void loop(void*) {
  178. while(running) {
  179. handleChars();
  180. refreshLine("> ");
  181. Core::Clock::sleepMillis(1);
  182. }
  183. }
  184. bool Core::startReadLine(void) {
  185. if(enterRawTerminal()) {
  186. report(LogLevel::WARNING, "cannot set terminal attributes");
  187. }
  188. running = true;
  189. if(readThread.start(loop, nullptr)) {
  190. report(LogLevel::ERROR, "cannot start read thread");
  191. stopReadLine();
  192. return true;
  193. }
  194. return false;
  195. }
  196. bool Core::readLine(char* buffer_, size_t n) {
  197. if(buffer.getLength() == 0) {
  198. return false;
  199. }
  200. Core::MutexGuard mg(bufferMutex);
  201. snprintf(buffer_, n, "%s", buffer[0].data.begin());
  202. buffer.remove();
  203. return true;
  204. }
  205. void Core::stopReadLine() {
  206. running = false;
  207. readThread.join();
  208. if(Core::leaveRawTerminal()) {
  209. report(LogLevel::WARNING, "cannot restore terminal attributes");
  210. }
  211. buffer.clear();
  212. }