From 4b6933a7cb5e0ef583071fffc0c97b829e72c684 Mon Sep 17 00:00:00 2001 From: Albert Cervin Date: Wed, 31 Jan 2024 23:24:02 +0100 Subject: Fix syntax predicate creation It is now created when parsing the queries. Also, make completion popup directly. --- src/dged/location.c | 32 +++--- src/dged/minibuffer.c | 20 +--- src/dged/syntax.c | 273 ++++++++++++++++++++++++-------------------------- src/dged/text.c | 8 ++ src/main/cmds.c | 80 +++++++++++---- src/main/completion.c | 50 +++++++-- src/main/completion.h | 7 +- src/main/main.c | 4 +- 8 files changed, 271 insertions(+), 203 deletions(-) diff --git a/src/dged/location.c b/src/dged/location.c index cac0333..0c7e973 100644 --- a/src/dged/location.c +++ b/src/dged/location.c @@ -2,25 +2,19 @@ bool location_is_between(struct location location, struct location start, struct location end) { - if (location.line >= start.line && location.line <= end.line) { - if (location.line == end.line && location.col <= end.col && - location.line == start.line && location.col >= start.col) { - // only one line - return true; - } else if (location.line == start.line && location.line != end.line && - location.col >= start.col) { - // we are on the first line - return true; - } else if (location.line == end.line && location.line != start.line && - location.col <= end.col) { - // we are on the last line - return true; - } else if (location.line != end.line && location.line != start.line) { - // we are on lines in between - return true; - } - } - return false; + return (location.line >= start.line && location.line <= end.line) && + ( + // inbetween + (location.line != end.line && location.line != start.line) || + // first line + (location.line == start.line && location.line != end.line && + location.col >= start.col) || + // last line + (location.line == end.line && location.line != start.line && + location.col <= end.col) || + // only one line + (location.line == end.line && location.col <= end.col && + location.line == start.line && location.col >= start.col)); } int location_compare(struct location l1, struct location l2) { diff --git a/src/dged/minibuffer.c b/src/dged/minibuffer.c index 634a864..bbe80ed 100644 --- a/src/dged/minibuffer.c +++ b/src/dged/minibuffer.c @@ -18,7 +18,6 @@ static struct minibuffer { char prompt[128]; struct command_ctx prompt_command_ctx; bool prompt_active; - bool clear; struct window *prev_window; struct buffer *message_buffer; @@ -65,9 +64,9 @@ int32_t minibuffer_execute() { } } - minibuffer_abort_prompt(); int32_t res = execute_command(c->self, c->commands, c->active_window, c->buffers, argc, (const char **)argv); + minibuffer_abort_prompt(); free(l); @@ -81,10 +80,8 @@ void update(struct buffer *buffer, void *userdata) { struct timespec current; struct minibuffer *mb = (struct minibuffer *)userdata; clock_gettime(CLOCK_MONOTONIC, ¤t); - if ((!mb->prompt_active && current.tv_sec >= mb->expires.tv_sec) || - mb->clear) { - buffer_clear(buffer); - mb->clear = false; + if ((!mb->prompt_active && current.tv_sec >= mb->expires.tv_sec)) { + minibuffer_clear(); } } @@ -96,7 +93,6 @@ void minibuffer_init(struct buffer *buffer, struct buffers *buffers) { g_minibuffer.buffer = buffer; g_minibuffer.expires.tv_sec = 0; g_minibuffer.expires.tv_nsec = 0; - g_minibuffer.clear = false; g_minibuffer.prompt_active = false; buffer_add_update_hook(g_minibuffer.buffer, update, &g_minibuffer); @@ -111,7 +107,6 @@ void echo(uint32_t timeout, const char *fmt, va_list args) { clock_gettime(CLOCK_MONOTONIC, &g_minibuffer.expires); g_minibuffer.expires.tv_sec += timeout; - g_minibuffer.clear = false; static char buff[2048]; size_t nbytes = vsnprintf(buff, 2048, fmt, args); @@ -188,17 +183,12 @@ static void minibuffer_setup(struct command_ctx command_ctx, windows_set_active(minibuffer_window()); } + minibuffer_clear(); if (initial != NULL) { buffer_set_text(g_minibuffer.buffer, (uint8_t *)initial, strlen(initial)); - // there might be an earlier clear request but - // we have sort of taken care of that here - g_minibuffer.clear = false; - // TODO: what to do with these buffer_view_goto_end_of_line(window_buffer_view(minibuffer_window())); - } else { - minibuffer_clear(); } } @@ -259,7 +249,7 @@ bool minibuffer_displaying() { void minibuffer_clear() { g_minibuffer.expires.tv_sec = 0; g_minibuffer.expires.tv_nsec = 0; - g_minibuffer.clear = true; + buffer_clear(g_minibuffer.buffer); } bool minibuffer_focused() { return g_minibuffer.prompt_active; } diff --git a/src/dged/syntax.c b/src/dged/syntax.c index 5f94d60..0935080 100644 --- a/src/dged/syntax.c +++ b/src/dged/syntax.c @@ -15,7 +15,6 @@ #include "buffer.h" #include "display.h" #include "hash.h" -#include "hashmap.h" #include "minibuffer.h" #include "path.h" #include "text.h" @@ -26,13 +25,41 @@ static bool treesitter_path_allocated = false; static const char *parser_filename = "parser"; static const char *highlight_path = "queries/highlights.scm"; -HASHMAP_ENTRY_TYPE(re_cache_entry, regex_t); +// TODO: move to own file +#define s8(s) ((struct s8){s, strlen(s)}) + +struct s8 { + char *s; + uint32_t l; +}; + +static bool s8eq(struct s8 s1, struct s8 s2) { + return s1.l == s2.l && memcmp(s1.s, s2.s, s1.l) == 0; +} + +static char *s8tocstr(struct s8 s) { + char *cstr = (char *)malloc(s.l + 1); + memcpy(cstr, s.s, s.l); + cstr[s.l] = '\0'; + return cstr; +} + +struct predicate { + uint32_t pattern_idx; + + bool (*eval)(struct s8, uint32_t, struct s8[], struct s8, void *); + uint32_t argc; + struct s8 argv[32]; + void *data; + + void (*cleanup)(void *); +}; struct highlight { TSParser *parser; TSTree *tree; TSQuery *query; - HASHMAP(struct re_cache_entry) re_cache; + VEC(struct predicate) predicates; void *dlhandle; }; @@ -43,11 +70,13 @@ static void delete_parser(struct buffer *buffer, void *userdata) { ts_query_delete(highlight->query); } - HASHMAP_FOR_EACH(&highlight->re_cache, struct re_cache_entry * entry) { - regfree(&entry->value); + VEC_FOR_EACH(&highlight->predicates, struct predicate * p) { + if (p->cleanup != NULL) { + p->cleanup(p->data); + } } - HASHMAP_DESTROY(&highlight->re_cache); + VEC_DESTROY(&highlight->predicates); ts_tree_delete(highlight->tree); ts_parser_delete(highlight->parser); @@ -101,6 +130,78 @@ static const char *lang_folder(struct buffer *buffer) { return fld; } +static bool eval_match(struct s8 capname, uint32_t argc, struct s8 argv[], + struct s8 value, void *data) { + regex_t *regex = (regex_t *)data; + if (regex == NULL) { + return false; + } + + char *text = s8tocstr(value); + bool match = regexec(regex, text, 0, NULL, 0) == 0; + + free(text); + return match; +} + +static void cleanup_match(void *data) { + regex_t *regex = (regex_t *)data; + if (regex != NULL) { + regfree(regex); + free(regex); + } +} + +static void create_predicates(struct highlight *h, uint32_t pattern_index) { + uint32_t npreds = 0; + const TSQueryPredicateStep *predicate_steps = + ts_query_predicates_for_pattern(h->query, pattern_index, &npreds); + + struct s8 capname; + struct s8 args[32] = {0}; + uint32_t argc = 0; + for (uint32_t predi = 0; predi < npreds; ++predi) { + const TSQueryPredicateStep *step = &predicate_steps[predi]; + switch (step->type) { + case TSQueryPredicateStepTypeCapture: + capname.s = (char *)ts_query_capture_name_for_id(h->query, step->value_id, + &capname.l); + break; + + case TSQueryPredicateStepTypeString: + args[argc].s = (char *)ts_query_string_value_for_id( + h->query, step->value_id, &args[argc].l); + ++argc; + break; + + case TSQueryPredicateStepTypeDone: + if (s8eq(args[0], s8("match?"))) { + regex_t *re = calloc(1, sizeof(regex_t)); + char *val = s8tocstr(args[1]); + + if (regcomp(re, val, 0) == 0) { + VEC_APPEND(&h->predicates, struct predicate * pred); + pred->pattern_idx = pattern_index; + pred->eval = eval_match; + pred->cleanup = cleanup_match; + pred->argc = 1; + pred->data = re; + + memset(pred->argv, 0, sizeof(struct s8) * 32); + memcpy(pred->argv, args, sizeof(struct s8)); + } else { + free(re); + } + + free(val); + } + + argc = 0; + break; + } + } +} + static TSQuery *setup_queries(const char *lang_root, TSTree *tree) { const char *filename = join_path(lang_root, highlight_path); @@ -150,123 +251,28 @@ static TSQuery *setup_queries(const char *lang_root, TSTree *tree) { return q; } -#define s8(s) ((struct s8){s, strlen(s)}) - -struct s8 { - char *s; - uint32_t l; -}; - -static bool s8eq(struct s8 s1, struct s8 s2) { - return s1.l == s2.l && memcmp(s1.s, s2.s, s1.l) == 0; -} - -char *s8tocstr(struct s8 s) { - char *cstr = (char *)malloc(s.l + 1); - memcpy(cstr, s.s, s.l); - cstr[s.l] = '\0'; - return cstr; -} - -static bool eval_match(struct s8 capname, uint32_t argc, struct s8 argv[], - struct s8 value, void *data) { - regex_t *regex = (regex_t *)data; - if (regex == NULL) { - return false; - } - - char *text = s8tocstr(value); - bool match = regexec(regex, text, 0, NULL, 0) == 0; - - free(text); - return match; -} - -static void cleanup_match(void *data) { - regex_t *regex = (regex_t *)data; - if (regex != NULL) { - regfree(regex); - free(regex); - } -} - -struct predicate { - bool (*fn)(struct s8, uint32_t, struct s8[], struct s8, void *); - void (*cleanup)(void *); - uint32_t argc; - struct s8 argv[32]; - void *data; -}; - -typedef VEC(struct predicate) predicate_vec; - -static regex_t *compile_re_cached(struct highlight *h, struct s8 expr) { - char *val = s8tocstr(expr); - HASHMAP_GET(&h->re_cache, struct re_cache_entry, val, regex_t * re); - if (re == NULL) { - regex_t new_re; - if (regcomp(&new_re, val, 0) == 0) { - HASHMAP_APPEND(&h->re_cache, struct re_cache_entry, val, - struct re_cache_entry * new); - if (new != NULL) { - new->value = new_re; - re = &new->value; +static bool eval_predicates(struct highlight *h, struct text *text, + TSPoint start, TSPoint end, uint32_t pattern_index, + struct s8 cname) { + VEC_FOR_EACH(&h->predicates, struct predicate * p) { + if (p->pattern_idx == pattern_index) { + struct text_chunk txt = + text_get_region(text, start.row, start.column, end.row, end.column); + bool result = + p->eval(cname, p->argc, p->argv, + (struct s8){.s = txt.text, .l = txt.nbytes}, p->data); + + if (txt.allocated) { + free(txt.text); } - } - } - free(val); - return re; -} - -static predicate_vec create_predicates(struct highlight *h, - uint32_t pattern_index) { - predicate_vec predicates; - - uint32_t npreds = 0; - const TSQueryPredicateStep *predicate_steps = - ts_query_predicates_for_pattern(h->query, pattern_index, &npreds); - - VEC_INIT(&predicates, 8); - - bool result = true; - struct s8 capname; - struct s8 args[32] = {0}; - uint32_t argc = 0; - for (uint32_t predi = 0; predi < npreds; ++predi) { - const TSQueryPredicateStep *step = &predicate_steps[predi]; - switch (step->type) { - case TSQueryPredicateStepTypeCapture: - capname.s = (char *)ts_query_capture_name_for_id(h->query, step->value_id, - &capname.l); - break; - - case TSQueryPredicateStepTypeString: - args[argc].s = (char *)ts_query_string_value_for_id( - h->query, step->value_id, &args[argc].l); - ++argc; - break; - - case TSQueryPredicateStepTypeDone: - if (s8eq(args[0], s8("match?"))) { - VEC_APPEND(&predicates, struct predicate * pred); - pred->fn = eval_match; - pred->cleanup = NULL; - pred->argc = 1; - - // cache the regex - pred->data = compile_re_cached(h, args[1]); - - memset(pred->argv, 0, sizeof(struct s8) * 32); - memcpy(pred->argv, args, sizeof(struct s8)); + if (!result) { + return false; } - - argc = 0; - break; } } - return predicates; + return true; } static void update_parser(struct buffer *buffer, void *userdata, @@ -284,7 +290,6 @@ static void update_parser(struct buffer *buffer, void *userdata, } // take results and set text properties - // TODO: can reuse the cursor TSQueryCursor *cursor = ts_query_cursor_new(); uint32_t end_line = origin.line + height >= buffer_num_lines(buffer) ? buffer_num_lines(buffer) - 1 @@ -296,8 +301,6 @@ static void update_parser(struct buffer *buffer, void *userdata, TSQueryMatch match; while (ts_query_cursor_next_match(cursor, &match)) { - predicate_vec predicates = create_predicates(h, match.pattern_index); - for (uint32_t capi = 0; capi < match.capture_count; ++capi) { const TSQueryCapture *cap = &match.captures[capi]; TSPoint start = ts_node_start_point(cap->node); @@ -307,20 +310,8 @@ static void update_parser(struct buffer *buffer, void *userdata, cname.s = (char *)ts_query_capture_name_for_id(h->query, cap->index, &cname.l); - bool predicates_match = true; - VEC_FOR_EACH(&predicates, struct predicate * pred) { - struct text_chunk txt = text_get_region( - buffer->text, start.row, start.column, end.row, end.column); - predicates_match &= - pred->fn(cname, pred->argc, pred->argv, - (struct s8){.s = txt.text, .l = txt.nbytes}, pred->data); - - if (txt.allocated) { - free(txt.text); - } - } - - if (!predicates_match) { + if (!eval_predicates(h, buffer->text, start, end, match.pattern_index, + cname)) { continue; } @@ -388,13 +379,6 @@ static void update_parser(struct buffer *buffer, void *userdata, }, }); } - - VEC_FOR_EACH(&predicates, struct predicate * pred) { - if (pred->cleanup != NULL) { - pred->cleanup(pred->data); - } - } - VEC_DESTROY(&predicates); } ts_query_cursor_delete(cursor); @@ -520,8 +504,13 @@ static void create_parser(struct buffer *buffer, void *userdata) { }; hl->tree = ts_parser_parse(hl->parser, NULL, i); hl->query = setup_queries(lang_root, hl->tree); + + VEC_INIT(&hl->predicates, 8); + uint32_t npatterns = ts_query_pattern_count(hl->query); + for (uint32_t pi = 0; pi < npatterns; ++pi) { + create_predicates(hl, pi); + } hl->dlhandle = h; - HASHMAP_INIT(&hl->re_cache, 64, hash_name); free((void *)lang_root); diff --git a/src/dged/text.c b/src/dged/text.c index bc2b1fc..30036a0 100644 --- a/src/dged/text.c +++ b/src/dged/text.c @@ -184,10 +184,18 @@ void insert_at(struct text *text, uint32_t line, uint32_t col, uint8_t *data, } uint32_t text_line_length(struct text *text, uint32_t lineidx) { + if (lineidx >= text_num_lines(text)) { + return 0; + } + return text->lines[lineidx].nchars; } uint32_t text_line_size(struct text *text, uint32_t lineidx) { + if (lineidx >= text_num_lines(text)) { + return 0; + } + return text->lines[lineidx].nbytes; } diff --git a/src/main/cmds.c b/src/main/cmds.c index 5a1d7b6..838f2ea 100644 --- a/src/main/cmds.c +++ b/src/main/cmds.c @@ -13,6 +13,7 @@ #include "dged/minibuffer.h" #include "dged/path.h" #include "dged/settings.h" +#include "dged/utf8.h" #include "bindings.h" #include "completion.h" @@ -69,7 +70,7 @@ int32_t run_interactive(struct command_ctx ctx, int argc, const char *argv[]) { int32_t do_switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) { disable_completion(minibuffer_buffer()); - const char *bufname = argv[0]; + const char *bufname = NULL; if (argc == 0) { // switch back to prev buffer if (window_has_prev_buffer(ctx.active_window)) { @@ -77,8 +78,9 @@ int32_t do_switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) { } else { return 0; } + } else { + bufname = argv[0]; } - struct buffer *buf = buffers_find(ctx.buffers, bufname); if (buf == NULL) { @@ -96,10 +98,14 @@ static void switch_buffer_comp_inserted() { minibuffer_execute(); } int32_t switch_buffer(struct command_ctx ctx, int argc, const char *argv[]) { if (argc == 0) { + minibuffer_clear(); struct completion_provider providers[] = {buffer_provider()}; enable_completion(minibuffer_buffer(), ((struct completion_trigger){ - .kind = CompletionTrigger_Input, .nchars = 0}), + .kind = CompletionTrigger_Input, + .input = + (struct completion_trigger_input){ + .nchars = 0, .trigger_initially = true}}), providers, 1, switch_buffer_comp_inserted); ctx.self = &do_switch_buffer_command; @@ -132,14 +138,39 @@ int32_t timers(struct command_ctx ctx, int argc, const char *argv[]) { } void buffer_to_list_line(struct buffer *buffer, void *userdata) { - struct buffer_view *listbuf = (struct buffer_view *)userdata; + struct buffer *listbuf = (struct buffer *)userdata; + + const char *path = buffer->filename != NULL ? buffer->filename : ""; char buf[1024]; - size_t written = - snprintf(buf, 1024, "%-16s %s\n", buffer->name, - buffer->filename != NULL ? buffer->filename : ""); + size_t written = snprintf(buf, 1024, "%-24s %s\n", buffer->name, path); if (written > 0) { - buffer_view_add(listbuf, (uint8_t *)buf, written); + struct location begin = buffer_end(listbuf); + buffer_add(listbuf, begin, (uint8_t *)buf, written); + size_t namelen = strlen(buffer->name); + uint32_t nchars = utf8_nchars(buffer->name, namelen); + buffer_add_text_property( + listbuf, begin, + (struct location){.line = begin.line, .col = begin.col + nchars}, + (struct text_property){.type = TextProperty_Colors, + .colors = (struct text_property_colors){ + .set_bg = false, + .set_fg = true, + .fg = Color_Green, + }}); + + size_t pathlen = strlen(path); + uint32_t nchars_path = utf8_nchars((uint8_t *)path, pathlen); + buffer_add_text_property( + listbuf, (struct location){.line = begin.line, .col = begin.col + 24}, + (struct location){.line = begin.line, + .col = begin.col + 24 + nchars_path}, + (struct text_property){.type = TextProperty_Colors, + .colors = (struct text_property_colors){ + .set_bg = false, + .set_fg = true, + .fg = Color_Blue, + }}); } } @@ -169,21 +200,22 @@ int32_t buflist_visit_cmd(struct command_ctx ctx, int argc, int32_t buflist_close_cmd(struct command_ctx ctx, int argc, const char *argv[]) { - window_close(ctx.active_window); + return execute_command(&do_switch_buffer_command, ctx.commands, + ctx.active_window, ctx.buffers, argc, argv); return 0; } -void buflist_refresh(struct buffers *buffers, struct buffer_view *target) { - buffer_set_readonly(target->buffer, false); - buffer_clear(target->buffer); - buffer_view_goto_beginning(target); - buffers_for_each(buffers, buffer_to_list_line, target); - buffer_set_readonly(target->buffer, true); +void buflist_refresh(struct buffer *buffer, void *userdata) { + struct buffers *buffers = (struct buffers *)userdata; + buffer_set_readonly(buffer, false); + buffer_clear(buffer); + buffers_for_each(buffers, buffer_to_list_line, buffer); + buffer_set_readonly(buffer, true); } int32_t buflist_refresh_cmd(struct command_ctx ctx, int argc, const char *argv[]) { - buflist_refresh(ctx.buffers, window_buffer_view(ctx.active_window)); + buflist_refresh(window_buffer(ctx.active_window), ctx.buffers); return 0; } @@ -196,7 +228,8 @@ int32_t buffer_list(struct command_ctx ctx, int argc, const char *argv[]) { struct window *w = ctx.active_window; window_set_buffer(ctx.active_window, b); - buflist_refresh(ctx.buffers, window_buffer_view(w)); + buffer_add_update_hook(b, buflist_refresh, ctx.buffers); + buflist_refresh(b, ctx.buffers); static struct command buflist_visit = { .name = "buflist-visit", @@ -232,6 +265,7 @@ static int32_t open_file(struct buffers *buffers, struct window *active_window, const char *pth) { if (active_window == minibuffer_window()) { + minibuffer_echo_timeout(4, "cannot open files in the minibuffer"); return 1; } @@ -266,10 +300,14 @@ static int32_t open_file(struct buffers *buffers, struct window *active_window, int32_t find_file(struct command_ctx ctx, int argc, const char *argv[]) { const char *pth = NULL; if (argc == 0) { + minibuffer_clear(); struct completion_provider providers[] = {path_provider()}; enable_completion(minibuffer_buffer(), ((struct completion_trigger){ - .kind = CompletionTrigger_Input, .nchars = 0}), + .kind = CompletionTrigger_Input, + .input = + (struct completion_trigger_input){ + .nchars = 0, .trigger_initially = true}}), providers, 1, find_file_comp_inserted); return minibuffer_prompt(ctx, "find file: "); } @@ -293,10 +331,14 @@ int32_t find_file_relative(struct command_ctx ctx, int argc, char *dir = dirname(filename); size_t dirlen = strlen(dir); if (argc == 0) { + minibuffer_clear(); struct completion_provider providers[] = {path_provider()}; enable_completion(minibuffer_buffer(), ((struct completion_trigger){ - .kind = CompletionTrigger_Input, .nchars = 0}), + .kind = CompletionTrigger_Input, + .input = + (struct completion_trigger_input){ + .nchars = 0, .trigger_initially = true}}), providers, 1, find_file_comp_inserted); ctx.self = &find_file_command; diff --git a/src/main/completion.c b/src/main/completion.c index 414df9c..80af01c 100644 --- a/src/main/completion.c +++ b/src/main/completion.c @@ -231,7 +231,7 @@ static void on_buffer_insert(struct buffer *buffer, struct region inserted, ctx->trigger_current_nchars += nchars; - if (ctx->trigger_current_nchars < ctx->trigger.nchars) { + if (ctx->trigger_current_nchars < ctx->trigger.input.nchars) { return; } @@ -277,6 +277,33 @@ void init_completion(struct buffers *buffers) { VEC_INIT(&g_active_completions, 32); } +struct oneshot_completion { + uint32_t hook_id; + struct active_completion_ctx *ctx; +}; + +static void cleanup_oneshot(void *userdata) { free(userdata); } + +static void oneshot_completion_hook(struct buffer *buffer, void *userdata) { + struct oneshot_completion *comp = (struct oneshot_completion *)userdata; + + // activate completion + g_state.active = true; + g_state.ctx = comp->ctx; + + struct window *w = window_find_by_buffer(buffer); + if (w != NULL) { + struct buffer_view *v = window_buffer_view(w); + update_completions(buffer, comp->ctx, v->dot); + } else { + update_completions(buffer, comp->ctx, + (struct location){.line = 0, .col = 0}); + } + + // this is a oneshot after all + buffer_remove_update_hook(buffer, comp->hook_id, cleanup_oneshot); +} + void enable_completion(struct buffer *source, struct completion_trigger trigger, struct completion_provider *providers, uint32_t nproviders, insert_cb on_completion_inserted) { @@ -306,6 +333,16 @@ void enable_completion(struct buffer *source, struct completion_trigger trigger, .insert_hook_id = insert_hook_id, .remove_hook_id = remove_hook_id, })); + + // do we want to trigger initially? + if (ctx->trigger.kind == CompletionTrigger_Input && + ctx->trigger.input.trigger_initially) { + struct oneshot_completion *comp = + calloc(1, sizeof(struct oneshot_completion)); + comp->ctx = ctx; + comp->hook_id = + buffer_add_update_hook(source, oneshot_completion_hook, comp); + } } static void hide_completion() { @@ -410,10 +447,6 @@ static uint32_t complete_path(struct completion_context ctx, void *userdata) { size_t inlen = strlen(path); - if (len == 0) { - goto done; - } - if (ctx.max_ncompletions == 0) { goto done; } @@ -434,6 +467,8 @@ static uint32_t complete_path(struct completion_context ctx, void *userdata) { } errno = 0; + size_t filelen = strlen(file); + bool file_is_curdir = (filelen == 1 && memcmp(file, ".", 1) == 0); while (n < ctx.max_ncompletions) { struct dirent *de = readdir(d); if (de == NULL && errno != 0) { @@ -449,7 +484,10 @@ static uint32_t complete_path(struct completion_context ctx, void *userdata) { case DT_REG: case DT_LNK: if (!is_hidden(de->d_name) && - (strncmp(file, de->d_name, strlen(file)) == 0 || strlen(file) == 0)) { + (filelen == 0 || file_is_curdir || + (filelen <= strlen(de->d_name) && + memcmp(file, de->d_name, filelen) == 0))) { + const char *disp = strdup(de->d_name); ctx.completions[n] = (struct completion){ .display = disp, diff --git a/src/main/completion.h b/src/main/completion.h index 751d152..b53c942 100644 --- a/src/main/completion.h +++ b/src/main/completion.h @@ -33,11 +33,16 @@ enum completion_trigger_kind { CompletionTrigger_Char = 1, }; +struct completion_trigger_input { + uint32_t nchars; + bool trigger_initially; +}; + struct completion_trigger { enum completion_trigger_kind kind; union { uint32_t c; - uint32_t nchars; + struct completion_trigger_input input; }; }; diff --git a/src/main/main.c b/src/main/main.c index 7b9a812..c40f438 100644 --- a/src/main/main.c +++ b/src/main/main.c @@ -229,8 +229,10 @@ int main(int argc, char *argv[]) { struct buffer initial_buffer = buffer_create("welcome"); if (filename != NULL) { buffer_destroy(&initial_buffer); - initial_buffer = buffer_from_file(filename); + const char *absfile = to_abspath(filename); + initial_buffer = buffer_from_file(absfile); free((void *)filename); + free((void *)absfile); } else { const char *welcome_txt = "Welcome to the editor for datagubbar 👴\n"; buffer_set_text(&initial_buffer, (uint8_t *)welcome_txt, -- cgit v1.2.3