#include "filecomplete.h" #include #include #include #include #ifdef USE_BOOST_FILESYSTEM #include namespace fs = boost::filesystem; #else #include namespace fs = std::filesystem; #endif #ifdef _MSC_VER #ifndef _CRT_SECURE_NO_WARNINGS #define _CRT_SECURE_NO_WARNINGS #endif #endif #include #include #include #define MAX_OF(x, y) (((x) > (y)) ? (x) : (y)) #if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN64) #ifndef OS_WINDOWS #define OS_WINDOWS #include #include #include #endif #elif defined(__APPLE__) || defined(__unix__) || defined(__unix) || defined(unix) || defined(__linux__) #ifndef OS_UNIX #define OS_UNIX #include #include #include #endif #else #error Unknown environment! #endif #if defined(OS_WINDOWS) #define ENTER 13 #define BACKSPACE 8 #define LEFT 75 #define RIGHT 77 #define UP 72 #define DOWN 80 #define DEL 83 #define CTRL_C 3 #define SPECIAL_SEQ_1 0 #define SPECIAL_SEQ_2 224 #define COLOR_TYPE uint16_t #define DEFAULT_TITLE_COLOR 160 #define DEFAULT_PREDICT_COLOR 8 #define DEFAULT_MAIN_COLOR 7 #elif defined(OS_UNIX) #define ENTER 10 #define BACKSPACE 127 #define LEFT 68 #define RIGHT 67 #define UP 65 #define DOWN 66 #define DEL 51 #define DEL_AFTER 126 #define SPECIAL_SEQ_1 27 #define SPECIAL_SEQ_2 91 #define COLOR_TYPE char* #define DEFAULT_TITLE_COLOR "0;30;102" #define DEFAULT_PREDICT_COLOR "90" #define DEFAULT_MAIN_COLOR "0" #endif #define SPACE 32 #define TAB 9 constexpr int buf_size = 1024 * 100; static std::vector> buf{}; static std::vector word{}; static size_t wo{}; static size_t len{}; static short cy{}; static short cx{}; static char* main_buf{}; void trans2buf(char* buffer); void clear_line(); void set_cursor_x(short x); short get_cursor_y(); char* fc_readline(); void color_print(const char* text, const COLOR_TYPE color); static std::vector cur_work_content; static std::vector deadline_vch; static std::string str_predict; static std::map> path_cache; void flush_content(const std::string& search_dir, std::vector& out) { out.clear(); fs::path work_path(search_dir); try { if (!fs::exists(work_path)) { return; } } catch (const std::exception& e) { return; } if (path_cache.count(search_dir)) { out = path_cache[search_dir]; } else { for (const auto& entry : fs::directory_iterator(work_path)) { out.push_back(entry.path().lexically_normal().string()); } path_cache[search_dir] = out; } } std::string file_predict(const char* data) { std::string result; std::string cur(data); std::string not_key(" "); std::string search_key; for (const auto& item : deadline_vch) { not_key.push_back(item); } auto fk = cur.find_last_of(not_key); if (fk != std::string::npos) { search_key = cur.substr(fk + 1, cur.size() - fk); } else { search_key = cur; } if (search_key.find("/") == std::string::npos && search_key.find("\\") == std::string::npos) { for (const auto& item : cur_work_content) { if (item != search_key && item.find(search_key) == 0) { return item.substr(search_key.size(), item.size() - search_key.size()); } } } std::string bk_search_path = search_key.substr(0, search_key.find_last_of("\\/") + 1); std::vector sr; flush_content(bk_search_path, sr); for (const auto& item : sr) { if (item != search_key && item.find(search_key) == 0) { return item.substr(search_key.size(), item.size() - search_key.size()); } } return result; } #if defined(OS_UNIX) int _getch() { int character; struct termios old_attr, new_attr; // Backup terminal attributes if (tcgetattr(STDIN_FILENO, &old_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't get terminal attributes\n"); exit(1); } // Disable echo new_attr = old_attr; new_attr.c_lflag &= ~(ICANON | ECHO); if (tcsetattr(STDIN_FILENO, TCSANOW, &new_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't set terminal attributes\n"); exit(1); } // Get input character character = getchar(); // Restore terminal attributes if (tcsetattr(STDIN_FILENO, TCSANOW, &old_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't reset terminal attributes\n"); exit(1); } return character; } #endif std::pair get_wh() { #if defined(OS_WINDOWS) HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE); if (h_console == NULL) { fprintf(stderr, "[ERROR] Couldn't handle terminal\n"); exit(1); } CONSOLE_SCREEN_BUFFER_INFO console_info; if (GetConsoleScreenBufferInfo(h_console, &console_info) == 0) { fprintf(stderr, "[ERROR] Couldn't get terminal info\n"); exit(1); } int cw = console_info.srWindow.Right - console_info.srWindow.Left + 1; int ch = console_info.srWindow.Bottom - console_info.srWindow.Top + 1; auto w = static_cast(cw); auto h = static_cast(ch); return std::make_pair(w, h); #elif defined(OS_UNIX) return std::make_pair(12, 12); #endif } int get_u8_len(unsigned char ch) { if (ch >= 0x00 && ch <= 0x7F) { return 1; } else if ((ch & 0xE0) == 0xC0) { return 2; } else if ((ch & 0xF0) == 0xE0) { return 3; } else if ((ch & 0xF8) == 0xF0) { return 4; } else if ((ch & 0xFC) == 0xF8) { return 5; } else if ((ch & 0xFE) == 0xFC) { return 6; } else { return 0; } } void supply(std::vector& wch, char ch) { #if defined(STRCODE_GBK) wch.push_back(ch); if (ch >= 0 && ch < 128) { return; } auto tch = _getch(); wch.push_back(tch); #else wch.push_back(ch); int length = get_u8_len(static_cast(ch)); if (length == 0) { printf("Invalid Charactor!\n"); exit(1); } for (int i = 1; i < length; ++i) { auto tch = _getch(); wch.push_back(tch); } #endif } void clear_line() { #if defined(OS_WINDOWS) // Get current terminal width short width = get_wh().first; if (width < 1) { fprintf(stderr, "[ERROR] Size of terminal is too small\n"); exit(1); } // Create long empty string char* empty = (char*)malloc(sizeof(char) * width); if (empty) { memset(empty, ' ', width); empty[width - 1] = '\0'; } HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hConsole, &cursorInfo); cursorInfo.bVisible = FALSE; SetConsoleCursorInfo(hConsole, &cursorInfo); // Clear line printf("\r%s\r", empty); // Free line free(empty); #elif defined(OS_UNIX) printf("\033[2K\r"); #endif } void set_cursor_x(short x) { #if defined(OS_WINDOWS) HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE); if (h_console == NULL) { fprintf(stderr, "[ERROR] Couldn't handle terminal\n"); exit(1); } auto wh = get_wh(); // Create position COORD xy; xy.Y = get_cursor_y(); short px = x % wh.first - 1; xy.X = px < 0 ? 0 : px; // Set cursor position if (SetConsoleCursorPosition(h_console, xy) == 0) { auto code = GetLastError(); fprintf(stderr, "[ERROR] Couldn't set terminal cursor position, err=%lu\n", code); exit(1); } #elif defined(OS_UNIX) printf("\033[%d;%dH", get_cursor_y(), x); #endif } short get_cursor_y() { #if defined(OS_WINDOWS) HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE); if (h_console == NULL) { fprintf(stderr, "[ERROR] Couldn't handle terminal\n"); exit(1); } // Get terminal info CONSOLE_SCREEN_BUFFER_INFO console_info; if (GetConsoleScreenBufferInfo(h_console, &console_info) == 0) { fprintf(stderr, "[ERROR] Couldn't get terminal Y position\n"); exit(1); } // Return Y position return console_info.dwCursorPosition.Y; #elif defined(OS_UNIX) struct termios old_attr, new_attr; char ch, buf[30] = {0}; int i = 0, pow = 1, y = 0; // Backup terminal attributes if (tcgetattr(STDIN_FILENO, &new_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't get terminal attributes\n"); exit(1); } // Disable echo old_attr = new_attr; old_attr.c_lflag &= ~(ICANON | ECHO); if (tcsetattr(STDIN_FILENO, TCSANOW, &old_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't set terminal attributes\n"); exit(1); } // Get info about cursor if (write(STDOUT_FILENO, "\033[6n", 4) != 4) { fprintf(stderr, "[ERROR] Couldn't get cursor information\n"); exit(1); } // Get ^[[{this};1R value for (ch = 0; ch != 'R'; i++) { if (read(STDIN_FILENO, &ch, 1) != 1) { fprintf(stderr, "[ERROR] Couldn't read cursor information"); exit(1); } buf[i] = ch; } i -= 2; while (buf[i] != ';') { i -= 1; } i -= 1; while (buf[i] != '[') { y = y + (buf[i] - '0') * pow; pow *= 10; i -= 1; } // Reset attributes if (tcsetattr(0, TCSANOW, &new_attr) == -1) { fprintf(stderr, "[ERROR] Couldn't reset terminal attributes\n"); exit(1); } return (short)y; #endif } void fc_append(char deadline_ch) { deadline_vch.push_back(deadline_ch); } char* fc_readline() { main_buf = (char*)malloc(sizeof(char) * buf_size); std::memset(main_buf, 0x0, buf_size); if (main_buf == NULL) { fprintf(stderr, "[ERROR] Couldn't allocate memory for buffer\n"); exit(1); } char* tmp_buf = (char*)malloc(sizeof(char) * buf_size); std::memset(tmp_buf, 0x0, buf_size); std::shared_ptr deleter(new int(), [tmp_buf](int* p) { delete p; free(tmp_buf); }); flush_content(".", cur_work_content); auto add_newer = [&](const std::vector& wch) { if (wo < buf.size()) { if (len >= buf.size()) { buf.resize(buf.size() * 2); } if (len > 0) { for (size_t i = len - 1; i >= wo; --i) { buf[i + 1] = buf[i]; } } buf[wo] = wch; } else { buf.push_back(wch); } ++wo; ++len; }; while (1) { word.clear(); clear_line(); // Print current buffer color_print(nullptr, DEFAULT_MAIN_COLOR); if (!str_predict.empty()) { color_print(str_predict.data(), DEFAULT_PREDICT_COLOR); } // Move cursor int cur_pos{}; #if defined(STRCODE_GBK) for (int i = 0; i < buf.size() && i < wo; ++i) { cur_pos += static_cast(buf[i].size()); } #else for (int i = 0; i < buf.size() && i < wo; ++i) { if (buf[i].size() > 1) { cur_pos += 2; } else { cur_pos += 1; } } #endif set_cursor_x(cur_pos + 1); // Read character from console int ch = _getch(); switch (ch) { case ENTER: return main_buf; #if defined(OS_WINDWS) case CTRL_C: { exit(0); } #endif case BACKSPACE: { if (wo > 0) { for (size_t i = wo - 1; i < buf.size() - 1; ++i) { buf[i] = buf[i + 1]; } --wo; --len; } break; } case TAB: { // 在这里补全 if (!str_predict.empty()) { std::vector> temp; for (int i = 0; i < str_predict.size();) { std::vector b; char curch = str_predict[i]; b.push_back(curch); if (curch >= 0 && curch < 128) { temp.push_back(b); ++i; } else { #if defined(STRCODE_GBK) int length = 2; #else int length = get_u8_len(curch); #endif for (int z = 1; z < length; ++z) { if ((i + z) < str_predict.size()) { b.push_back(str_predict[i + z]); } } temp.push_back(b); i += length; } } str_predict.clear(); for (const auto& item : temp) { add_newer(item); } continue; } break; } #if defined(OS_WINDOWS) case SPECIAL_SEQ_1: case SPECIAL_SEQ_2: #elif defined(OS_UNIX) case SPECIAL_SEQ_1: #endif { #if defined(OS_UNIX) if (_getch() != SPECIAL_SEQ_2) { continue; } #endif switch (_getch()) { case LEFT: wo = wo < 1 ? 0 : (--wo); break; case RIGHT: wo = wo >= len ? len : (++wo); break; case UP: break; case DOWN: break; case DEL: // Edit buffer like DELETE key #if defined(OS_UNIX) if (_getch() == DEL_AFTER) #endif { } break; default: break; } break; } default: { supply(word, ch); add_newer(word); break; } } // 补正 trans2buf(tmp_buf); str_predict = file_predict(tmp_buf); } return main_buf; } void fc_free(char* str) { buf.clear(); wo = 0; len = 0; free(str); } void trans2buf(char* buffer) { int j = 0; for (size_t i = 0; i < buf.size() && i < len; ++i) { for (const auto& c : buf[i]) { buffer[j++] = c; } } buffer[j++] = '\0'; } void color_print(const char* text, const COLOR_TYPE color) { #if defined(OS_WINDOWS) HANDLE h_console = GetStdHandle(STD_OUTPUT_HANDLE); if (h_console == NULL) { fprintf(stderr, "[ERROR] Couldn't handle terminal\n"); exit(1); } CONSOLE_SCREEN_BUFFER_INFO console_info; COLOR_TYPE backup; // Save current attributes if (GetConsoleScreenBufferInfo(h_console, &console_info) == 0) { fprintf(stderr, "[ERROR] Couldn't get terminal info\n"); exit(1); } auto wh = get_wh(); backup = console_info.wAttributes; // Print colored text if (SetConsoleTextAttribute(h_console, color) == 0) { fprintf(stderr, "[ERROR] Couldn't set terminal color\n"); exit(1); } if (!text) { trans2buf(main_buf); printf("%s", main_buf); } else { printf("%s", text); } HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hConsole, &cursorInfo); cursorInfo.bVisible = TRUE; SetConsoleCursorInfo(hConsole, &cursorInfo); // Restore original color if (SetConsoleTextAttribute(h_console, backup) == 0) { fprintf(stderr, "[ERROR] Couldn't reset terminal color\n"); exit(1); } #elif defined(OS_UNIX) // Set new terminal color printf("\033["); printf("%s", color); printf("m"); if (!text) { trans2buf(main_buf); printf("%s", main_buf); } else { printf("%s", text); } // Resets the text to default color printf("\033[0m"); #endif }