filecomplete/filecomplete.cpp

536 lines
13 KiB
C++
Raw Normal View History

#include "filecomplete.h"
#include <string>
#include <vector>
#ifdef USE_BOOST_FILESYSTEM
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#else
#include <filesystem>
namespace fs = std::filesystem;
#endif
#ifdef _MSC_VER
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_OF(x, y) (((x) > (y)) ? (x) : (y))
#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN64)
#ifndef OS_WINDOWS
#define OS_WINDOWS
#include <conio.h>
#include <stdint.h>
#include <windows.h>
#endif
#elif defined(__APPLE__) || defined(__unix__) || defined(__unix) || \
defined(unix) || defined(__linux__)
#ifndef OS_UNIX
#define OS_UNIX
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#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<std::vector<char>> buf{};
static std::vector<char> word{};
static size_t wo{};
static size_t len{};
static short cy{};
static short cx{};
void clear_line();
void set_cursor_x(short x);
short get_cursor_y();
char* fc_readline();
void color_print(const char* text, COLOR_TYPE color);
static std::vector<std::string> cur_work_content;
static std::vector<std::string> dynamic_content;
static std::vector<char> deadline_vch;
static std::string str_predict;
void flush_content(const std::string& search_dir, std::vector<std::string>& out,
bool reletive)
{
out.clear();
fs::path work_path(search_dir);
if (!fs::exists(work_path)) {
return;
}
for (const auto& entry : fs::directory_iterator(work_path)) {
if (reletive) {
out.push_back(entry.path().filename().string());
} else {
out.push_back(entry.path().string());
}
}
}
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 (cur.find("/") == std::string::npos &&
cur.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());
}
}
}
for (const auto& item : dynamic_content) {
}
return result;
}
std::pair<short, short> get_wh()
{
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<short>(cw);
auto h = static_cast<short>(ch);
return std::make_pair(w, h);
}
void supply(std::vector<char>& wch, char ch)
{
wch.push_back(ch);
if (ch >= 0 && ch < 128) {
return;
}
auto tch = _getch();
wch.push_back(tch);
}
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()
{
char* buffer = (char*)calloc((size_t)buf_size, sizeof(char));
if (buffer == NULL) {
fprintf(stderr, "[ERROR] Couldn't allocate memory for buffer\n");
exit(1);
}
flush_content(fs::current_path().string(), cur_work_content, true);
auto add_newer = [&](const std::vector<char>& 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;
};
// Current hint number
int hint_num = 0;
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);
}
2025-01-09 09:31:05 +08:00
// Move cursor
int cur_pos{};
for (int i = 0; i < buf.size() && i < wo; ++i) {
cur_pos += static_cast<int>(buf[i].size());
}
set_cursor_x(cur_pos + 1);
// Read character from console
int ch = _getch();
switch (ch) {
case ENTER:
return nullptr;
#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<std::vector<char>> temp;
for (int i = 0; i < str_predict.size(); ++i) {
std::vector<char> blk;
char curch = str_predict[i];
blk.push_back(curch);
if (curch >= 0 && curch < 128) {
temp.push_back(blk);
} else {
if ((i + 1) < str_predict.size()) {
blk.push_back(str_predict[i + 1]);
temp.push_back(blk);
++i;
}
}
}
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;
}
}
// 预测
std::string tbuf;
for (size_t i = 0; i < buf.size() && i < len; ++i) {
for (const auto& c : buf[i]) {
tbuf.push_back(c);
}
}
tbuf.push_back('\0');
str_predict = file_predict(tbuf.data());
}
return buffer;
}
void fc_free(char* str)
{
free(str);
}
void color_print(const char* text, 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) {
std::string tbuf;
for (size_t i = 0; i < buf.size() && i < len; ++i) {
for (const auto& c : buf[i]) {
tbuf.push_back(c);
}
}
tbuf.push_back('\0');
printf("%s", tbuf.c_str());
} 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");
// Print colored text
printf("%s", text);
// Resets the text to default color
printf("\033[0m");
#endif
}