From 1558a1b36b15eb9bde28e05beea43b619e6257c6 Mon Sep 17 00:00:00 2001 From: Albert Cervin Date: Mon, 18 Mar 2024 22:07:36 +0100 Subject: More work on languages/syntax Implement another predicate and add javascript. --- src/dged/lang.c | 2 + src/dged/s8.c | 18 +++++++++ src/dged/s8.h | 2 + src/dged/syntax.c | 119 +++++++++++++++++++++++++++++++++++++++--------------- 4 files changed, 109 insertions(+), 32 deletions(-) (limited to 'src/dged') diff --git a/src/dged/lang.c b/src/dged/lang.c index d2d7b34..070b96e 100644 --- a/src/dged/lang.c +++ b/src/dged/lang.c @@ -46,6 +46,8 @@ void languages_init(bool register_default) { define_lang("Nix", "nix", "^.*\\.nix$", 2, false); define_lang("Make", "make", "^.*(Makefile|\\.mk)$", 4, true); define_lang("Python", "python", "^.*\\.py$", 4, false); + define_lang("Markdown", "markdown", "^.*\\.md$", 4, false); + define_lang("Javascript", "javascript", "^.*\\.js$", 4, false); define_lang("Git Commit Message", "gitcommit", "^.*COMMIT_EDITMSG$", 4, false); } diff --git a/src/dged/s8.c b/src/dged/s8.c index 0566fde..c4544b9 100644 --- a/src/dged/s8.c +++ b/src/dged/s8.c @@ -23,3 +23,21 @@ char *s8tocstr(struct s8 s) { cstr[s.l] = '\0'; return cstr; } + +bool s8startswith(struct s8 s, struct s8 prefix) { + if (prefix.l > s.l) { + return false; + } + + return memcmp(s.s, prefix.s, prefix.l) == 0; +} + +struct s8 s8dup(struct s8 s) { + struct s8 new = {0}; + new.l = s.l; + + new.s = (char *)malloc(s.l); + memcpy(new.s, s.s, s.l); + + return new; +} diff --git a/src/dged/s8.h b/src/dged/s8.h index 955a642..76bcc34 100644 --- a/src/dged/s8.h +++ b/src/dged/s8.h @@ -14,5 +14,7 @@ struct s8 { bool s8eq(struct s8 s1, struct s8 s2); int s8cmp(struct s8 s1, struct s8 s2); char *s8tocstr(struct s8 s); +bool s8startswith(struct s8 s, struct s8 prefix); +struct s8 s8dup(struct s8 s); #endif diff --git a/src/dged/syntax.c b/src/dged/syntax.c index 1544d33..3ee750c 100644 --- a/src/dged/syntax.c +++ b/src/dged/syntax.c @@ -121,6 +121,25 @@ static const char *lang_folder(struct buffer *buffer, const char *path) { return fld; } +static bool eval_eq(struct s8 capname, uint32_t argc, struct s8 argv[], + struct s8 value, void *data) { + const struct s8 *cmp_to = (const struct s8 *)data; + if (data == NULL) { + return false; + } + + return s8eq(value, *cmp_to); +} + +static void cleanup_eq(void *data) { + struct s8 *s = (struct s8 *)data; + if (s != NULL) { + free(s->s); + s->l = 0; + free(s); + } +} + static bool eval_match(struct s8 capname, uint32_t argc, struct s8 argv[], struct s8 value, void *data) { regex_t *regex = (regex_t *)data; @@ -185,6 +204,15 @@ static void create_predicates(struct highlight *h, uint32_t pattern_index) { } free(val); + } else if (s8eq(args[0], s8("eq?"))) { + struct s8 *val = calloc(1, sizeof(struct s8)); + *val = s8dup(args[1]); + VEC_APPEND(&h->predicates, struct predicate * pred); + pred->pattern_idx = pattern_index; + pred->eval = eval_eq; + pred->cleanup = cleanup_eq; + pred->argc = 1; + pred->data = val; } argc = 0; @@ -217,8 +245,6 @@ static TSQuery *setup_queries(const char *lang_root, TSTree *tree) { TSQuery *q = ts_query_new(ts_tree_language(tree), (char *)data, len, &error_offset, &error); - munmap(data, len); - if (error != TSQueryErrorNone) { const char *msg = "unknown error"; switch (error) { @@ -235,10 +261,27 @@ static TSQuery *setup_queries(const char *lang_root, TSTree *tree) { msg = "capture"; break; } - message("ts query error at byte offset %d: %s", error_offset, msg); + + // calculate line + const char *chars = (const char *)data; + uint64_t byteoff = 0; + uint32_t lineno = 1; + uint32_t colno = 0; + while (byteoff < error_offset) { + if (chars[byteoff] == '\n') { + ++lineno; + colno = 0; + } + ++colno; + ++byteoff; + } + message("ts query error at (%d, %d): %s, %.*s", lineno, colno, msg); + + munmap(data, len); return NULL; } + munmap(data, len); return q; } @@ -266,6 +309,9 @@ static bool eval_predicates(struct highlight *h, struct text *text, return true; } +#define match_cname(cname, capture) \ + (s8eq(cname, s8(capture)) || s8startswith(cname, s8(capture "."))) + static void update_parser(struct buffer *buffer, void *userdata, struct location origin, uint32_t width, uint32_t height) { @@ -308,50 +354,41 @@ static void update_parser(struct buffer *buffer, void *userdata, bool highlight = false; uint32_t color = 0; - if (s8eq(cname, s8("keyword"))) { + if (match_cname(cname, "keyword")) { highlight = true; color = Color_Blue; - } else if (s8eq(cname, s8("operator"))) { + } else if (match_cname(cname, "operator")) { highlight = true; color = Color_Magenta; - } else if (s8eq(cname, s8("delimiter"))) { + } else if (match_cname(cname, "delimiter")) { + highlight = false; + } else if (s8eq(cname, s8("text"))) { highlight = false; - } else if (s8eq(cname, s8("string")) || - s8eq(cname, s8("string.special")) || - s8eq(cname, s8("string.special.path")) || - s8eq(cname, s8("text.title")) || s8eq(cname, s8("text.uri")) || - s8eq(cname, s8("string.special.uri"))) { + } else if (match_cname(cname, "string") || match_cname(cname, "text")) { highlight = true; color = Color_Green; - } else if (s8eq(cname, s8("constant"))) { + } else if (match_cname(cname, "constant")) { highlight = true; color = Color_Yellow; - } else if (s8eq(cname, s8("attribute"))) { + } else if (match_cname(cname, "attribute")) { highlight = true; color = Color_Yellow; - } else if (s8eq(cname, s8("number"))) { + } else if (match_cname(cname, "number")) { highlight = true; color = Color_Yellow; - } else if (s8eq(cname, s8("function")) || - s8eq(cname, s8("function.macro")) || - s8eq(cname, s8("function.method")) || - s8eq(cname, s8("function.builtin")) || - s8eq(cname, s8("function.signal")) || - s8eq(cname, s8("function.special"))) { + } else if (match_cname(cname, "function")) { highlight = true; color = Color_Yellow; - } else if (s8eq(cname, s8("property"))) { + } else if (match_cname(cname, "property")) { highlight = false; - } else if (s8eq(cname, s8("label"))) { + } else if (match_cname(cname, "label")) { highlight = false; - } else if (s8eq(cname, s8("type")) || s8eq(cname, s8("type.builtin"))) { + } else if (match_cname(cname, "type")) { highlight = true; color = Color_Cyan; - } else if (s8eq(cname, s8("variable")) || - s8eq(cname, s8("variable.builtin")) || - s8eq(cname, s8("variable.parameter"))) { + } else if (match_cname(cname, "variable")) { highlight = false; - } else if (s8eq(cname, s8("comment"))) { + } else if (match_cname(cname, "comment")) { highlight = true; color = Color_BrightBlack; } @@ -524,6 +561,12 @@ 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); + if (hl->query == NULL) { + ts_parser_delete(hl->parser); + free((void *)lang_root); + return; + } + VEC_INIT(&hl->predicates, 8); uint32_t npatterns = ts_query_pattern_count(hl->query); for (uint32_t pi = 0; pi < npatterns; ++pi) { @@ -549,14 +592,26 @@ void syntax_init(uint32_t grammar_path_len, const char *grammar_path[]) { treesitter_path[i] = strdup(grammar_path[i]); } - // TODO: check that it exists + // special-case some of the built-in languages + // that have grammar names different from the default struct language l = lang_from_id("gitcommit"); - lang_setting_set_default(&l, "grammar", - (struct setting_value){.type = Setting_String, - .string_value = "gitcommit"}); + if (!lang_is_fundamental(&l)) { + lang_setting_set_default( + &l, "grammar", + (struct setting_value){.type = Setting_String, + .string_value = "gitcommit"}); + lang_destroy(&l); + } + + l = lang_from_id("cxx"); + if (!lang_is_fundamental(&l)) { + lang_setting_set_default( + &l, "grammar", + (struct setting_value){.type = Setting_String, .string_value = "cpp"}); + lang_destroy(&l); + } buffer_add_create_hook(create_parser, NULL); - lang_destroy(&l); } void syntax_teardown() { -- cgit v1.2.3