typedef struct {
char **matches; // 所有匹配项
int count; // 匹配项总数
char *base_dir; // 当前基础目录
char *original_prefix; // 原始前缀
char *current_text; // 当前输入文本
int last_state; // 上一次使用的state值
int current_level; // 当前目录层级
char *persistent_path; // 持久化路径(同时作为初始化标志)
int is_directory;
int max_level; // 新增:最大层级限制
} CompletionState;
static CompletionState comp_state = {
.matches = NULL,
.count = 0,
.base_dir = NULL,
.original_prefix = NULL,
.current_text = NULL,
.last_state = -1,
.current_level = 0,
.persistent_path = NULL,
.is_directory = 0,
.max_level = -1
};
// 重置补全状态
void reset_completion_state() {
if (comp_state.matches) {
for (int i = 0; i < comp_state.count; i++) {
XFREE(MTYPE_TMP, comp_state.matches[i]);
}
XFREE(MTYPE_TMP, comp_state.matches);
comp_state.matches = NULL;
}
if (comp_state.original_prefix) {
XFREE(MTYPE_TMP, comp_state.original_prefix);
}
if (comp_state.base_dir) {
XFREE(MTYPE_TMP, comp_state.base_dir);
}
if (comp_state.current_text) {
XFREE(MTYPE_TMP, comp_state.current_text);
}
if (comp_state.persistent_path) {
XFREE(MTYPE_TMP, comp_state.persistent_path);
}
comp_state.count = 0;
comp_state.original_prefix = NULL;
comp_state.base_dir = NULL;
comp_state.current_text = NULL;
comp_state.last_state = -1;
comp_state.current_level = 0;
comp_state.persistent_path = NULL;
comp_state.is_directory = 0;
comp_state.max_level = -1;
}
// 路径比较函数(目录优先)
int compare_paths(const void *a, const void *b) {
const char *sa = *(const char **)a;
const char *sb = *(const char **)b;
int is_dir_a = (sa[strlen(sa)-1] == '/');
int is_dir_b = (sb[strlen(sb)-1] == '/');
if (is_dir_a && !is_dir_b) return -1;
if (!is_dir_a && is_dir_b) return 1;
return strcmp(sa, sb);
}
// 路径解析函数(优化后)
void parse_input_text(const char *text, char **base_dir, char **prefix, int *level) {
*base_dir = NULL;
*prefix = NULL;
const char *slash = strrchr(text, '/');
if (slash) {
size_t base_len = slash - text + 1;
*base_dir = (char *)XCALLOC(MTYPE_TMP, base_len + 1);
strncpy(*base_dir, text, base_len);
(*base_dir)[base_len] = '\0';
*prefix = XSTRDUP(MTYPE_TMP, slash + 1);
*level = comp_state.current_level + 1;
} else {
*base_dir = XSTRDUP(MTYPE_TMP, "");
*prefix = XSTRDUP(MTYPE_TMP, text);
*level = 0;
}
}
// 上下文一致性判断(优化后)
int is_same_completion_context(const char *text) {
if (!comp_state.persistent_path || comp_state.max_level == -1) {
return 0;
}
char *temp_base = NULL;
char *temp_prefix = NULL;
int current_level = 0;
parse_input_text(text, &temp_base, &temp_prefix, ¤t_level);
printf("is_same_completion_context: text = %s, temp_base = %s, current_level = %d\n", text, temp_base, current_level);
int same_path = strncmp(text, comp_state.persistent_path, strlen(comp_state.persistent_path)) == 0;
// 只有在当前路径是持久化路径的子目录或匹配时才继续
int within_level = (current_level <= comp_state.max_level);
XFREE(MTYPE_TMP, temp_base);
XFREE(MTYPE_TMP, temp_prefix);
printf("is_same_completion_context: same_path=%d, within_level=%d\n", same_path, within_level);
return same_path && within_level;
}
// 生成当前目录下的路径匹配项
char **generate_current_dir_paths(const char *base_dir, const char *prefix, int add_empty) {
const char *scan_dir = base_dir;
if (!scan_dir || !*scan_dir) {
scan_dir = ".";
}
printf("[generate] Scanning directory: '%s' with prefix: '%s'\n", scan_dir, prefix ? prefix : "(none)");
DIR *dir = opendir(scan_dir);
if (!dir) {
if (add_empty) {
char **matches = (char **)XCALLOC(MTYPE_TMP, sizeof(char *));
matches[0] = XSTRDUP(MTYPE_TMP, "");
return matches;
}
return NULL;
}
int capacity = 32;
int count = 0;
char **matches = (char **)XCALLOC(MTYPE_TMP, capacity * sizeof(char *));
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
const char *name = entry->d_name;
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) continue;
// 跳过隐藏文件
if (name[0] == '.') continue;
// 跳过软链接(直接判断 d_type)
if (entry->d_type == DT_LNK) {
printf("跳过软链接: %s\n", name);
continue;
}
if (prefix && *prefix && strncmp(name, prefix, strlen(prefix)) != 0) continue;
int is_dir = (entry->d_type == DT_DIR);
if (entry->d_type == DT_UNKNOWN) {
char full_path[PATH_MAX];
snprintf(full_path, sizeof(full_path), "%s/%s", scan_dir, name);
struct stat st;
if (stat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) {
is_dir = 1;
}
}
char *new_name;
if (is_dir) {
new_name = (char *)XCALLOC(MTYPE_TMP, strlen(name) + 2);
snprintf(new_name, strlen(name) + 2, "%s/", name);
} else {
new_name = XSTRDUP(MTYPE_TMP, name);
}
if (count >= capacity) {
capacity *= 2;
matches = (char **)XREALLOC(MTYPE_TMP, matches, capacity * sizeof(char *));
}
matches[count++] = new_name;
}
closedir(dir);
if (count > 0) {
qsort(matches, count, sizeof(char *), compare_paths);
}
matches = (char **)XREALLOC(MTYPE_TMP, matches, (count + 2) * sizeof(char *));
matches[count] = NULL;
if (!add_empty) {
matches[count] = XSTRDUP(MTYPE_TMP, "");
matches[count + 1] = NULL;
}
printf("[generate] Found %d matches.\n", count);
return matches;
}
// 主补全函数(关键优化点)
char *filename_completion_function(const char *text, int state) {
printf("\n[COMP] Entering: text='%s', state=%d, last_state=%d\n",
text, state, comp_state.last_state);
char current_dir[PATH_MAX];
char abs_path[PATH_MAX];
int reset_completion = 0;
// 初始化新补全
if (state == 0) {
printf(" Starting new completion\n");
int same_context = is_same_completion_context(text);
printf(" Same context: %d\n", same_context);
getcwd(current_dir, sizeof(current_dir));
snprintf(abs_path, sizeof(abs_path), "%s/%s", current_dir, text);
printf("\nfilename_completion_function: reset_completion = %d\n", reset_completion);
if (access(abs_path, F_OK) != 0) {
reset_completion = 1;
}
int same_reset = 0;
struct stat st_tmp;
char tmp_path[PATH_MAX];
char *tmp_base = NULL;
char *tmp_prefix = NULL;
int tmp_level = 0;
parse_input_text(text, &tmp_base, &tmp_prefix, &tmp_level);
printf("\nfilename_completion_function: text = %s, tmp_base = %s, tmp_prefix = %s\n",
text, tmp_base, tmp_prefix);
if (comp_state.matches) {
for (int i = 0; comp_state.matches[i]; i++) {
printf("\nfilename_completion_function: comp_state.matches[%d] = %s\n", i, comp_state.matches[i]);
if (strncmp(comp_state.matches[i], tmp_prefix, strlen(tmp_prefix)) == 0) {
int tmp_i = i;
snprintf(tmp_path, sizeof(tmp_path), "%s/%s%s", current_dir, tmp_base, comp_state.matches[i]);
printf("\nfilename_completion_function: tmp_path = %s, current_dir = %s, comp_state.matches[%d] = %s, tmp_i = %d\n",
tmp_path, current_dir, i, comp_state.matches[i], tmp_i);
if (stat(tmp_path, &st_tmp) == 0 && S_ISDIR(st_tmp.st_mode)) {
same_reset = 1;
printf("\nfilename_completion_function: same_reset = %d\n", same_reset);
break;
}
}
}
}
printf("\nfilename_completion_function: current_dir = %s, text = %s, abs_path = %s, reset_completion = %d\n",
current_dir, text, abs_path, reset_completion);
if ((!same_context) || (reset_completion && same_reset)) {
reset_completion_state();
char *current_base = NULL;
char *current_prefix = NULL;
int current_level = 0;
parse_input_text(text, ¤t_base, ¤t_prefix, ¤t_level);
comp_state.base_dir = current_base;
comp_state.current_text = XSTRDUP(MTYPE_TMP, text);
comp_state.original_prefix = XSTRDUP(MTYPE_TMP, current_prefix);
comp_state.current_level = current_level;
comp_state.max_level = current_level + 3; // 可配置最大补全层级
// 检查是否用户已经明确进入了子目录
int ends_with_slash = text[strlen(text) - 1] == '/';
if (ends_with_slash) {
// 用户明确进入子目录,允许下层补全
comp_state.persistent_path = XSTRDUP(MTYPE_TMP, text);
comp_state.max_level = current_level + 3;
} else {
// 用户未进入子目录,只补全当前目录项
comp_state.persistent_path = XSTRDUP(MTYPE_TMP, current_base);
comp_state.max_level = current_level;
}
// 生成当前目录的补全项
comp_state.matches = generate_current_dir_paths(current_base, current_prefix, current_level == 0);
comp_state.count = 0;
if (comp_state.matches) {
while (comp_state.matches[comp_state.count]) {
comp_state.count++;
}
printf(" Generated %d matches\n", comp_state.count);
} else {
comp_state.count = 0;
}
comp_state.last_state = -1; // 重置索引
//XFREE(MTYPE_TMP, current_base);
//XFREE(MTYPE_TMP, current_prefix);
}
}
printf("\nfilename_completion_function: comp_state.base_dir = %s, comp_state.current_text = %s, comp_state.original_prefix = %s, comp_state.persistent_path = %s\n",
comp_state.base_dir, comp_state.current_text, comp_state.original_prefix, comp_state.persistent_path);
// 没有匹配项
if (!comp_state.matches || comp_state.count == 0) {
printf("[COMP] No matches found\n");
return NULL;
}
// 获取下一个匹配项
int next_index = (comp_state.last_state + 1) % comp_state.count;
comp_state.last_state = next_index;
char *match = comp_state.matches[next_index];
// 构建完整路径
char *full_path = NULL;
if (comp_state.base_dir && strlen(comp_state.base_dir) > 0) {
int base_len = strlen(comp_state.base_dir);
int needs_slash = base_len > 0 && comp_state.base_dir[base_len - 1] != '/';
full_path = (char *)XCALLOC(MTYPE_TMP, strlen(match) + base_len + 2);
if (needs_slash) {
snprintf(full_path, strlen(match) + base_len + 2, "%s/%s", comp_state.base_dir, match);
} else {
snprintf(full_path, strlen(match) + base_len + 2, "%s%s", comp_state.base_dir, match);
}
} else {
full_path = XSTRDUP(MTYPE_TMP, match);
}
printf("[COMP] Returning: '%s'\n", full_path);
return full_path;
}
最新发布