From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on inbox.vuxu.org X-Spam-Level: X-Spam-Status: No, score=-3.5 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,DKIM_VALID_EF,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED, T_PDS_PRO_TLD,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 Received: from zero.zsh.org (zero.zsh.org [IPv6:2a02:898:31:0:48:4558:7a:7368]) by inbox.vuxu.org (Postfix) with ESMTP id 8F06E22076 for ; Mon, 12 Feb 2024 04:26:54 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=zsh.org; s=rsa-20210803; h=List-Archive:List-Owner:List-Post:List-Unsubscribe: List-Subscribe:List-Help:List-Id:Sender:Message-ID:Date:Content-ID: Content-Type:MIME-Version:Subject:To:From:Reply-To:Cc: Content-Transfer-Encoding:Content-Description:Resent-Date:Resent-From: Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References; bh=BFTokMdDZSctVmW3T8bHzoX3EULc/GmO/TXn94kC88E=; b=VBq7vqij+eeHgKgcJyNgCyUke0 4PVkFQMotjsp8FZA8/Pjxrctwj019o5hK+rTLPx1L7isYG8OHTja7CDO/jrl/tSmlq8Li2LnIV84g sg05MldMjPABfTTmEdbvhCWBpOb8vRM5itAJus3FfhDNXtatK50m7VXnQxRQ7EyqZ/lykF4/VTA2r DbrMq5GqhM4yZK6z6K+xJy7g1sNM843wtlKMErqk8sCC2CInDJm/3KfzctOg16mgaf+5qkziSC8lN Z2myVVmX7zJ4bqkShqwjNzoGTTQLernrrnqw6wYBPTOqTWbyGuV1lJF0akMDW14ezXZBZZWCViWE0 I2/iRppw==; Received: by zero.zsh.org with local id 1rZMy4-000Lyc-BG; Mon, 12 Feb 2024 03:26:52 +0000 Received: by zero.zsh.org with esmtpsa (TLS1.3:TLS_AES_256_GCM_SHA384:256) id 1rZMxQ-000LeG-4W; Mon, 12 Feb 2024 03:26:13 +0000 Received: from [192.168.178.21] (helo=hydra) by mail.kiddle.eu with esmtp(Exim 4.97.1) (envelope-from ) id 1rZMxO-00000000M4n-2AfA for zsh-workers@zsh.org; Mon, 12 Feb 2024 04:26:10 +0100 From: Oliver Kiddle To: Zsh workers Subject: PATCH: alternate views on .zle.hlgroups MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-ID: <84863.1707708370.1@hydra> Date: Mon, 12 Feb 2024 04:26:10 +0100 Message-ID: <84864-1707708370.319024@8kOf.oEra.sYsr> X-Seq: 52533 Archived-At: X-Loop: zsh-workers@zsh.org Errors-To: zsh-workers-owner@zsh.org Precedence: list Precedence: bulk Sender: zsh-workers-request@zsh.org X-no-archive: yes List-Id: List-Help: , List-Subscribe: , List-Unsubscribe: , List-Post: List-Owner: List-Archive: This patch adds a hlgroup module which defines two extra special variables - .zle.esc and .zle.sgr. These are readonly associative arrays. The purpose of these is to make is easier to apply the attributes defined in .zle.hlgroups to other commands. The same effect can already be achieved using ${(%):-%H\{...\}} but this seemed like a friendlier approach. .zle.esc contains literal escape sequences .zle.sgr is stripped down to just the "Select Graphic Rendition" number sequence. This is useful, with, e.g. GREP_COLORS, LSCOLORS, the list-colors style and some other tools like jq and ag. This implementation uses dupstring() to return memory from the parameter string get function. I don't see existing cases to confirm that this is safe but it appears to work fine in testing. Would be good to have confirmation from someone who has a clearer understanding of what the lifetime of the memory pools is. Naming (hlgroup module in singular, .zle.hlgroups in plural) is chosen for consistency with the existing parameter module. I might otherwise have opted for singular in both cases. But I'm open to other suggestions or completely different ideas on naming. I had two further ideas for things that might be included in the module. I briefly considered what it would involve to support a hook to allow dynamic updates to variables like GREP_COLORS in response to changes to .zle.hlgroups. This didn't seem trivial, especially given that it is currently useful to define it as a nameref (to a variable that works in older zsh). My second thought was support for parsing terminal responses to the escape sequence for getting the background colour. This is just about doable in shell code but not trivial and error-prone. But it can be very helpful to avoid unreadable colour combinations. What form should this best take? A builtin? Having a key bound to \e]11;rgb: may be the best way to avoid interference with a type-ahead buffer but that inconveniently move the setup code to a zle widget. ungetc() in C may work better than my current print -z solution, especially where some of the typeahead text is not even intended for zsh. Oliver diff --git a/Src/Modules/hlgroup.c b/Src/Modules/hlgroup.c new file mode 100644 index 000000000..fcf8cfc81 --- /dev/null +++ b/Src/Modules/hlgroup.c @@ -0,0 +1,182 @@ +#include "hlgroup.mdh" +#include "hlgroup.pro" + +#define GROUPVAR ".zle.hlgroups" + +static const struct gsu_scalar pmesc_gsu = +{ strgetfn, nullstrsetfn, nullunsetfn }; + +/**/ +static char * +convertattr(char *attrstr, int sgr) +{ + zattr atr; + char *r, *s; + int len; + + match_highlight(attrstr, &atr, NULL); + s = zattrescape(atr, sgr ? NULL : &len); + + if (sgr) { + char *c = s, *t = s - 1; + + while (c[0] == '\033' && c[1] == '[') { + c += 2; + while (isdigit(*c) || *c == ';') + *++t = *c++; + t++; + if (*c != 'm') + break; + *t = ';'; + c++; + } + *t = '\0'; + len = t - s; + } + + r = dupstring_wlen(s, len); + free(s); + return r; +} + +/**/ +static HashNode +getgroup(const char *name, int sgr) +{ + Param pm = NULL; + HashNode hn; + HashTable hlg; + Value v; + struct value vbuf; + char *var = GROUPVAR; + + pm = (Param) hcalloc(sizeof(struct param)); + pm->gsu.s = &pmesc_gsu; + pm->node.nam = dupstring(name); + pm->node.flags = PM_SCALAR|PM_SPECIAL; + + if (!(v = getvalue(&vbuf, &var, 0)) || + PM_TYPE(v->pm->node.flags) != PM_HASHED || + !(hlg = v->pm->gsu.h->getfn(v->pm)) || + !(hn = gethashnode2(hlg, name))) + { + pm->u.str = dupstring(""); + pm->node.flags |= PM_UNSET; + } else { + pm->u.str = convertattr(((Param) hn)->u.str, sgr); + } + + return &pm->node; +} + +/**/ +static void +scangroup(ScanFunc func, int flags, int sgr) +{ + struct param pm; + int i; + HashNode hn; + HashTable hlg; + Value v; + struct value vbuf; + char *var = GROUPVAR; + + if (!(v = getvalue(&vbuf, &var, 0)) || + PM_TYPE(v->pm->node.flags) != PM_HASHED) + return; + hlg = v->pm->gsu.h->getfn(v->pm); + + memset((void *)&pm, 0, sizeof(struct param)); + pm.node.flags = PM_SCALAR; + pm.gsu.s = &pmesc_gsu; + + for (i = 0; i < hlg->hsize; i++) + for (hn = hlg->nodes[i]; hn; hn = hn->next) { + pm.u.str = convertattr(((Param) hn)->u.str, sgr); + pm.node.nam = hn->nam; + func(&pm.node, flags); + } +} +/**/ +static HashNode +getpmesc(UNUSED(HashTable ht), const char *name) +{ + return getgroup(name, 0); +} + +/**/ +static void +scanpmesc(UNUSED(HashTable ht), ScanFunc func, int flags) +{ + return scangroup(func, flags, 0); +} + +/**/ +static HashNode +getpmsgr(UNUSED(HashTable ht), const char *name) +{ + return getgroup(name, 1); +} + +/**/ +static void +scanpmsgr(UNUSED(HashTable ht), ScanFunc func, int flags) +{ + return scangroup(func, flags, 1); +} + +static struct paramdef partab[] = { + SPECIALPMDEF(".zle.esc", PM_READONLY_SPECIAL, 0, getpmesc, scanpmesc), + SPECIALPMDEF(".zle.sgr", PM_READONLY_SPECIAL, 0, getpmsgr, scanpmsgr) +}; + +static struct features module_features = { + NULL, 0, + NULL, 0, + NULL, 0, + partab, sizeof(partab)/sizeof(*partab), + 0 +}; + +/**/ +int +setup_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +features_(Module m, char ***features) +{ + *features = featuresarray(m, &module_features); + return 0; +} + +/**/ +int +enables_(Module m, int **enables) +{ + return handlefeatures(m, &module_features, enables); +} + +/**/ +int +boot_(UNUSED(Module m)) +{ + return 0; +} + +/**/ +int +cleanup_(Module m) +{ + return setfeatureenables(m, &module_features, NULL); +} + +/**/ +int +finish_(UNUSED(Module m)) +{ + return 0; +} diff --git a/Src/Modules/hlgroup.mdd b/Src/Modules/hlgroup.mdd new file mode 100644 index 000000000..ee3ba7260 --- /dev/null +++ b/Src/Modules/hlgroup.mdd @@ -0,0 +1,7 @@ +name=zsh/hlgroup +link=either +load=yes + +autofeatures="p:.zle.esc p:.zle.sgr" + +objects="hlgroup.o" diff --git a/Src/prompt.c b/Src/prompt.c index 0d674ceab..7acbe0e47 100644 --- a/Src/prompt.c +++ b/Src/prompt.c @@ -241,6 +241,34 @@ promptexpand(char *s, int ns, char *rs, char *Rs) return new_vars.buf; } +/* Get the escape sequence for a given attribute. */ +/**/ +mod_export char * +zattrescape(zattr atr, int *len) +{ + struct buf_vars new_vars; + zattr savecurrent = txtcurrentattrs; + zattr saveunknown = txtunknownattrs; + + memset(&new_vars, 0, sizeof(new_vars)); + new_vars.last = bv; + bv = &new_vars; + new_vars.bufspc = 256; + new_vars.bp = new_vars.bufline = new_vars.buf = zshcalloc(new_vars.bufspc); + new_vars.dontcount = 1; + + txtunknownattrs = 0; + treplaceattrs(atr); + applytextattributes(TSC_PROMPT); + + bv = new_vars.last; + + txtpendingattrs = txtcurrentattrs = savecurrent; + txtunknownattrs = saveunknown; + + return unmetafy(new_vars.buf, len); +} + /* Parse the argument for %H */ static char * parsehighlight(char *arg, char endchar, zattr *atr)