summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dged/location.c32
-rw-r--r--src/dged/minibuffer.c20
-rw-r--r--src/dged/syntax.c273
-rw-r--r--src/dged/text.c8
-rw-r--r--src/main/cmds.c80
-rw-r--r--src/main/completion.c50
-rw-r--r--src/main/completion.h7
-rw-r--r--src/main/main.c4
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, &current);
- 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 : "<no-file>";
char buf[1024];
- size_t written =
- snprintf(buf, 1024, "%-16s %s\n", buffer->name,
- buffer->filename != NULL ? buffer->filename : "<no-file>");
+ 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,