From e40f0a2b6655b9d48c3bf52c67eda8ebab3538ae Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sun, 18 Jun 2023 11:45:04 +1000 Subject: [PATCH] config: support [file:glob] config setting. Make a list of attrs to be added to documents based on file name, and register a doc:appeared- handler to apply them. This makes the config.py script irrelevant. Signed-off-by: NeilBrown --- DOC/TODO.md | 8 ++- edlib.ini | 22 ++++++++- lib-config.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++- python/config.py | 40 --------------- 4 files changed, 149 insertions(+), 45 deletions(-) delete mode 100644 python/config.py diff --git a/DOC/TODO.md b/DOC/TODO.md index a3698224..e682acd4 100644 --- a/DOC/TODO.md +++ b/DOC/TODO.md @@ -35,7 +35,7 @@ the file. - [ ] lib-diff slowness with large diff - [ ] linecount :when used in 'view' mode, stack the counting pane with all the others so it can easily catch view-changed. -- [ ] C config module that reads an ini-style file to set attributes +- [X] C config module that reads an ini-style file to set attributes based on path - [ ] review all doc:char implementations for simplification. - [ ] make it easy for a make-search command to search backwards @@ -71,6 +71,10 @@ Requirements for a v1.0 release Core features ------------- +- [ ] Do I really need global-multicall- or can I just use + notifications. + It would mean more modules would need a private pane, but might + I want that to support module unload anyway? - [ ] if a view creation fails - like render_master_view_attach hitting a python error - there will be no active view so the screen will be meaningless. I need to properly abort and @@ -1082,7 +1086,7 @@ Interaction with gdb would be nice too - things like ### config -- [ ] C config module that reads an ini-style file to set attributes +- [X] C config module that reads an ini-style file to set attributes based on path - [ ] configure "initial_panes" - [ ] discard old auto-load?? diff --git a/edlib.ini b/edlib.ini index 508e6c4c..d3917e7c 100644 --- a/edlib.ini +++ b/edlib.ini @@ -120,4 +120,24 @@ lib-utf8 = attach-utf8 lib-charset = ALWAYS -config = ALWAYS +[file:COMMIT_EDITMSG*] +APPEND view-default = ,textfill,whitespace,autospell +fill-width = 72 + +[file:.stgit] +APPEND view-default = ,textfill,whitespace,autospell +fill-width = 72 + +[file:*.md] + +TESTING APPEND view-default = ,textfill,whitespace +NOTESTING APPEND view-default = ,textfill,whitespace,autospell + +fill-width = 72 +word-wrap = 1 +fill:start-re = "^(" + "[^a-zA-Z0-9\\n]*$|" # empty/puctuation line + " *-|" # list item + " *- *\\[[ X]]|" # todo list item + " *#+|" # section head + " *[0-9]*\\.)" # Numbered list diff --git a/lib-config.c b/lib-config.c index 9fac7b50..192c2785 100644 --- a/lib-config.c +++ b/lib-config.c @@ -125,11 +125,119 @@ static void parse_ini(const char *path safe, ini_handle handle, void *data) fclose(f); } +static bool __glob_match(const char *patn safe, const char *path safe) +{ + while(1) { + switch (*patn) { + case '\0': + return *path == '\0'; + case '?': + if (!*path || *path == '/') + return False; + patn += 1; + path += 1; + break; + case '*': + if (patn[1] == '*') { + if (__glob_match(patn+2, path)) + return True; + } else { + if (__glob_match(patn+1, path)) + return True; + if (*path == '/') + return False; + } + if (!*path) + return False; + path += 1; + break; + default: + if (*patn != *path) + return False; + patn += 1; + path += 1; + break; + } + } +} + +static bool glob_match(const char *patn safe, const char *path safe) +{ + int ret; + if (patn[0] != '/' && !strstarts(patn, "**")) { + /* must match basename */ + const char *sl = strrchr(path, '/'); + if (sl) + path = sl + 1; + } + ret = __glob_match(patn, path); + return ret; +} + struct config_data { struct command c; + struct command appeared; struct pane *root safe; + struct trigger { + char *path safe; + struct attrset *attrs; + struct trigger *next; + } *triggers; }; +static void add_trigger(struct config_data *cd safe, char *path safe, + char *name safe, char *val safe, int append) +{ + struct trigger *t = cd->triggers; + + if (strstarts(name, "TESTING ")) { + if (getenv("EDLIB_TESTING") == NULL) + return; + name += 8; + } + if (strstarts(name, "NOTESTING ")) { + if (getenv("EDLIB_TESTING") != NULL) + return; + name += 10; + } + if (!t || strcmp(t->path, path) != 0) { + alloc(t, pane); + t->next = cd->triggers; + cd->triggers = t; + t->path = strdup(path); + } + if (append) { + const char *old = attr_find(t->attrs, name); + if (old) { + val = strconcat(NULL, old, val); + attr_set_str(&t->attrs, name, val); + free(val); + } else + attr_set_str(&t->attrs, name, val); + } else + attr_set_str(&t->attrs, name, val); +} + +static void config_file(char *path safe, struct pane *doc safe, + struct config_data *cd safe) +{ + struct trigger *t; + + for (t = cd->triggers; t; t = t->next) + if (glob_match(t->path, path)) { + const char *val; + const char *k = ""; + while ((k = attr_get_next_key(t->attrs, k, -1, &val)) != NULL) { + if (strstarts(k, "APPEND ")) + call("doc:append:", doc, 0, NULL, val, + 0, NULL, k + 7); + else + call("doc:set:", doc, 0, NULL, val, + 0, NULL, k); + } + } +} + struct mod_cmd { char *module; int tried; @@ -200,8 +308,7 @@ static void handle(void *data, char *section safe, char *name safe, char *value } if (strstarts(section, "file:")) { - char *k = strconcat(NULL, "global-file-attr:", section+5); - call(k, cd->root, append, NULL, name, 0, NULL, value); + add_trigger(cd, section+5, name, value, append); return; } } @@ -257,6 +364,16 @@ static void config_free(struct command *c safe) free(cd); } +DEF_CMD(config_appeared) +{ + struct config_data *cd = container_of(ci->comm, struct config_data, appeared); + char *path = pane_attr_get(ci->focus, "filename"); + if (!path) + return Efallthrough; + config_file(path, ci->focus, cd); + return Efallthrough; +} + DEF_CMD(config_load) { struct config_data *cd; @@ -267,8 +384,11 @@ DEF_CMD(config_load) alloc(cd, pane); cd->c = config_load; cd->c.free = config_free; + cd->appeared = config_appeared; cd->root = ci->home; call_comm("global-set-command", ci->home, &cd->c, 0, NULL, "config-load"); + call_comm("global-set-command", ci->home, &cd->appeared, + 0, NULL, "doc:appeared-config"); } else { cd = container_of(ci->comm, struct config_data, c); } diff --git a/python/config.py b/python/config.py deleted file mode 100644 index b7c42f4a..00000000 --- a/python/config.py +++ /dev/null @@ -1,40 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright Neil Brown ©2020-2023 -# May be distributed under terms of GPLv2 - see file:COPYING - -# This is a "config" script. It just for experimenting -# with simple configuration. I plan to write a proper -# config module with a more abstract language one day. -# But I want some simple configuration NOW - -from edlib import editor -import os - -def config_appeared(key, focus, **a): - - p = focus['filename'] - - if p and ("COMMIT_EDITMSG" in p or "/.stgit" in p): - focus.call("doc:append:view-default", ",textfill,whitespace,autospell") - focus.call("doc:set:fill-width", "72") - if "/git/lustre-release/" in p: - # looks like a lustre commit, need to limit line width - focus.call("doc:set:fill-width", "70") - focus.call("doc:set:whitespace-width", "60") - - if p and p[-3:] == ".md": - # Until I have a real markdown module, I need this at least. - if os.getenv("EDLIB_TESTING"): - focus.call("doc:append:view-default", ",textfill,whitespace") - else: - focus.call("doc:append:view-default", ",textfill,whitespace,autospell") - focus["fill-width"] = "72" - focus["word-wrap"] = "1" - focus["fill:start-re"] = ("^(" - "[^a-zA-Z0-9\\n]*$|" # empty/puctuation line - " *-|" # list item - " *- *\\[[ X]]|" # todo list item - " *#+|" # section head - " *[0-9]*\\.)") # Numbered list - -editor.call("global-set-command", "doc:appeared-config", config_appeared) -- 2.39.5