From e74a6594680b1c29e8e05b4732a6281a892d58d9 Mon Sep 17 00:00:00 2001 From: bakkeby Date: Tue, 16 Jul 2024 21:45:01 +0200 Subject: [PATCH] Refactoring highlight and fuzzyhighlight patches Follow-up on pull request #16 this change refactors and combines the highlight and fuzzy highlight patches into one highlight function. Overall it does not make any sense using: - fuzzy highlighting when exact matching is used or - exact highlighting when fuzzy matching is used As such it makes sense to combine the two such that: - exact highlighting is used when exact matching is used and - fuzzy highlighting is used when fuzzy matching is used The FUZZYHIGHLIGHT_PATCH toggle has been removed in favour of HIGHLIGHT_PATCH. The FUZZYMATCH_PATCH toggle controls whether fuzzy matching is enabled. Enable both FUZZYMATCH_PATCH and HIGHLIGHT_PATCH to enable fuzzy highlighting. Additionally the fuzzy highlight patch only supported single-byte characters and would break when encountering multi-byte UTF-8 characters. This was reported ref. #24. This refactoring includes a change to work out the UTF-8 character length for a given character rather than assuming that every character uses one byte. --- config.def.h | 8 ++--- dmenu.c | 26 ++++++++--------- drw.c | 11 ++++++- drw.h | 6 +++- patch/fuzzyhighlight.c | 66 ------------------------------------------ patch/highlight.c | 51 +++++++++++++++++++++++++++++++- patch/include.c | 6 ++-- patch/xresources.c | 6 ++-- patches.def.h | 18 +++++++----- 9 files changed, 98 insertions(+), 100 deletions(-) delete mode 100644 patch/fuzzyhighlight.c diff --git a/config.def.h b/config.def.h index 3adfe93..a30957a 100644 --- a/config.def.h +++ b/config.def.h @@ -67,10 +67,10 @@ static const unsigned int alphas[][3] = { #if MORECOLOR_PATCH [SchemeMid] = { OPAQUE, baralpha, borderalpha }, #endif // MORECOLOR_PATCH - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH [SchemeSelHighlight] = { OPAQUE, baralpha, borderalpha }, [SchemeNormHighlight] = { OPAQUE, baralpha, borderalpha }, - #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH #if HIGHPRIORITY_PATCH [SchemeHp] = { OPAQUE, baralpha, borderalpha }, #endif // HIGHPRIORITY_PATCH @@ -100,10 +100,10 @@ char *colors[][2] = { #if MORECOLOR_PATCH [SchemeMid] = { "#eeeeee", "#770000" }, #endif // MORECOLOR_PATCH - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH [SchemeSelHighlight] = { "#ffc978", "#005577" }, [SchemeNormHighlight] = { "#ffc978", "#222222" }, - #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH #if HIGHPRIORITY_PATCH [SchemeHp] = { "#bbbbbb", "#333333" }, #endif // HIGHPRIORITY_PATCH diff --git a/dmenu.c b/dmenu.c index c35a16b..b1325b7 100644 --- a/dmenu.c +++ b/dmenu.c @@ -55,10 +55,10 @@ enum { #if MORECOLOR_PATCH SchemeMid, #endif // MORECOLOR_PATCH - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH SchemeNormHighlight, SchemeSelHighlight, - #endif // HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH #if HIGHPRIORITY_PATCH SchemeHp, #endif // HIGHPRIORITY_PATCH @@ -330,11 +330,11 @@ drawitem(struct item *item, int x, int y, int w) case 'p': drw_setscheme(drw, scheme[SchemePurple]); break; - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH case 'h': drw_setscheme(drw, scheme[SchemeNormHighlight]); break; - #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH case 's': drw_setscheme(drw, scheme[SchemeSel]); break; @@ -366,11 +366,11 @@ drawitem(struct item *item, int x, int y, int w) case 'p': drw_setscheme(drw, scheme[SchemePurple]); break; - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH case 'h': drw_setscheme(drw, scheme[SchemeNormHighlight]); break; - #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH case 's': drw_setscheme(drw, scheme[SchemeSel]); break; @@ -471,13 +471,13 @@ drawitem(struct item *item, int x, int y, int w) , True #endif // PANGO_PATCH ); - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH #if EMOJI_HIGHLIGHT_PATCH drawhighlights(item, output + iscomment, x + ((iscomment == 6) ? temppadding : 0), y, w); #else drawhighlights(item, x, y, w); #endif // EMOJI_HIGHLIGHT_PATCH - #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH return r; } @@ -1383,7 +1383,7 @@ paste(void) #if ALPHA_PATCH static void -xinitvisual() +xinitvisual(void) { XVisualInfo *infos; XRenderPictFormat *fmt; @@ -1912,9 +1912,9 @@ usage(void) #if XYW_PATCH " [-X xoffset] [-Y yoffset] [-W width]" // (arguments made upper case due to conflicts) #endif // XYW_PATCH - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH "\n [-nhb color] [-nhf color] [-shb color] [-shf color]" // highlight colors - #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH #if SEPARATOR_PATCH "\n [-d separator] [-D separator]" #endif // SEPARATOR_PATCH @@ -2096,7 +2096,7 @@ main(int argc, char *argv[]) else if (!strcmp(argv[i], "-hp")) hpitems = tokenize(argv[++i], ",", &hplength); #endif // HIGHPRIORITY_PATCH - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH else if (!strcmp(argv[i], "-nhb")) /* normal hi background color */ colors[SchemeNormHighlight][ColBg] = argv[++i]; else if (!strcmp(argv[i], "-nhf")) /* normal hi foreground color */ @@ -2105,7 +2105,7 @@ main(int argc, char *argv[]) colors[SchemeSelHighlight][ColBg] = argv[++i]; else if (!strcmp(argv[i], "-shf")) /* selected hi foreground color */ colors[SchemeSelHighlight][ColFg] = argv[++i]; - #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH #if CARET_WIDTH_PATCH else if (!strcmp(argv[i], "-cw")) /* sets caret witdth */ caret_width = atoi(argv[++i]); diff --git a/drw.c b/drw.c index b8eeb96..b2d9f41 100644 --- a/drw.c +++ b/drw.c @@ -9,7 +9,7 @@ #include "drw.h" #include "util.h" -#if !PANGO_PATCH +#if !PANGO_PATCH || HIGHLIGHT_PATCH #define UTF_INVALID 0xFFFD #define UTF_SIZ 4 @@ -61,6 +61,15 @@ utf8decode(const char *c, long *u, size_t clen) return len; } + +#if HIGHLIGHT_PATCH +size_t +utf8len(const char *c) +{ + long utf8codepoint = 0; + return utf8decode(c, &utf8codepoint, UTF_SIZ); +} +#endif // HIGHLIGHT_PATCH #endif // PANGO_PATCH Drw * diff --git a/drw.h b/drw.h index be4f3aa..7936e8e 100644 --- a/drw.h +++ b/drw.h @@ -68,6 +68,10 @@ unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h); #endif // PANGO_PATCH +#if HIGHLIGHT_PATCH +size_t utf8len(const char *c); +#endif // HIGHLIGHT_PATCH + /* Colorscheme abstraction */ #if ALPHA_PATCH void drw_clr_create(Drw *drw, Clr *dest, const char *clrname, unsigned int alpha); @@ -100,4 +104,4 @@ void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h) #if SCROLL_PATCH #include "patch/scroll.h" -#endif \ No newline at end of file +#endif diff --git a/patch/fuzzyhighlight.c b/patch/fuzzyhighlight.c deleted file mode 100644 index d31a150..0000000 --- a/patch/fuzzyhighlight.c +++ /dev/null @@ -1,66 +0,0 @@ -static void -#if EMOJI_HIGHLIGHT_PATCH -drawhighlights(struct item *item, char *output, int x, int y, int maxw) -#else -drawhighlights(struct item *item, int x, int y, int maxw) -#endif // EMOJI_HIGHLIGHT_PATCH -{ - int i, indent; - char *highlight; - char c; - - #if EMOJI_HIGHLIGHT_PATCH - char *itemtext = output; - #elif TSV_PATCH && !SEPARATOR_PATCH - char *itemtext = item->stext; - #else - char *itemtext = item->text; - #endif // TSV_PATCH - - if (!(strlen(itemtext) && strlen(text))) - return; - - /* Do not highlight items scheduled for output */ - #if MULTI_SELECTION_PATCH - if (issel(item->id)) - return; - #else - if (item->out) - return; - #endif // MULTI_SELECTION_PATCH - - drw_setscheme(drw, scheme[item == sel - ? SchemeSelHighlight - : SchemeNormHighlight]); - for (i = 0, highlight = itemtext; *highlight && text[i];) { - #if FUZZYMATCH_PATCH - if (!fstrncmp(&(*highlight), &text[i], 1)) - #else - if (*highlight == text[i]) - #endif // FUZZYMATCH_PATCH - { - /* get indentation */ - c = *highlight; - *highlight = '\0'; - indent = TEXTW(itemtext) - lrpad; - *highlight = c; - - /* highlight character */ - c = highlight[1]; - highlight[1] = '\0'; - drw_text( - drw, - x + indent + (lrpad / 2), - y, - MIN(maxw - indent - lrpad, TEXTW(highlight) - lrpad), - bh, 0, highlight, 0 - #if PANGO_PATCH - , True - #endif // PANGO_PATCH - ); - highlight[1] = c; - i++; - } - highlight++; - } -} diff --git a/patch/highlight.c b/patch/highlight.c index d2d55d0..b637309 100644 --- a/patch/highlight.c +++ b/patch/highlight.c @@ -7,6 +7,13 @@ drawhighlights(struct item *item, int x, int y, int maxw) { char restorechar, tokens[sizeof text], *highlight, *token; int indentx, highlightlen; + + #if FUZZYMATCH_PATCH + char c; + int i, indent; + int utf8charlen; + #endif // FUZZYMATCH_PATCH + #if EMOJI_HIGHLIGHT_PATCH char *itemtext = output; #elif TSV_PATCH && !SEPARATOR_PATCH @@ -15,6 +22,9 @@ drawhighlights(struct item *item, int x, int y, int maxw) char *itemtext = item->text; #endif // EMOJI_HIGHLIGHT_PATCH | TSV_PATCH + if (!(strlen(itemtext) && strlen(text))) + return; + /* Do not highlight items scheduled for output */ #if MULTI_SELECTION_PATCH if (issel(item->id)) @@ -25,6 +35,45 @@ drawhighlights(struct item *item, int x, int y, int maxw) #endif // MULTI_SELECTION_PATCH drw_setscheme(drw, scheme[item == sel ? SchemeSelHighlight : SchemeNormHighlight]); + + #if FUZZYMATCH_PATCH + if (fuzzy) { + for (i = 0, highlight = itemtext; *highlight && text[i];) { + utf8charlen = utf8len(highlight); + #if FUZZYMATCH_PATCH + if (!fstrncmp(&(*highlight), &text[i], utf8charlen)) + #else + if (*highlight == text[i]) + #endif // FUZZYMATCH_PATCH + { + /* get indentation */ + c = *highlight; + *highlight = '\0'; + indent = TEXTW(itemtext) - lrpad; + *highlight = c; + + /* highlight character */ + c = highlight[utf8charlen]; + highlight[utf8charlen] = '\0'; + drw_text( + drw, + x + indent + (lrpad / 2), + y, + MIN(maxw - indent - lrpad, TEXTW(highlight) - lrpad), + bh, 0, highlight, 0 + #if PANGO_PATCH + , True + #endif // PANGO_PATCH + ); + highlight[utf8charlen] = c; + i += utf8charlen; + } + highlight++; + } + return; + } + #endif // FUZZYMATCH_PATCH + strcpy(tokens, text); for (token = strtok(tokens, " "); token; token = strtok(NULL, " ")) { highlight = fstrstr(itemtext, token); @@ -57,4 +106,4 @@ drawhighlights(struct item *item, int x, int y, int maxw) highlight = fstrstr(highlight + strlen(token), token); } } -} \ No newline at end of file +} diff --git a/patch/include.c b/patch/include.c index 4240212..c8a6612 100644 --- a/patch/include.c +++ b/patch/include.c @@ -1,9 +1,7 @@ #if CENTER_PATCH #include "center.c" #endif -#if FUZZYHIGHLIGHT_PATCH -#include "fuzzyhighlight.c" -#elif HIGHLIGHT_PATCH +#if HIGHLIGHT_PATCH #include "highlight.c" #endif #if FUZZYMATCH_PATCH @@ -35,4 +33,4 @@ #endif #if XRESOURCES_PATCH #include "xresources.c" -#endif \ No newline at end of file +#endif diff --git a/patch/xresources.c b/patch/xresources.c index cdb1631..6ab60bb 100644 --- a/patch/xresources.c +++ b/patch/xresources.c @@ -43,7 +43,7 @@ readxresources(void) if (XrmGetResource(xdb, "dmenu.bordercolor", "*", &type, &xval)) colors[SchemeBorder][ColBg] = strdup(xval.addr); #endif // BORDER_PATCH - #if HIGHLIGHT_PATCH || FUZZYHIGHLIGHT_PATCH + #if HIGHLIGHT_PATCH if (XrmGetResource(xdb, "dmenu.selhlbackground", "*", &type, &xval)) colors[SchemeSelHighlight][ColBg] = strdup(xval.addr); if (XrmGetResource(xdb, "dmenu.selhlforeground", "*", &type, &xval)) @@ -52,7 +52,7 @@ readxresources(void) colors[SchemeNormHighlight][ColBg] = strdup(xval.addr); if (XrmGetResource(xdb, "dmenu.hlforeground", "*", &type, &xval)) colors[SchemeNormHighlight][ColFg] = strdup(xval.addr); - #endif // HIGHLIGHT_PATCH | FUZZYHIGHLIGHT_PATCH + #endif // HIGHLIGHT_PATCH #if HIGHPRIORITY_PATCH if (XrmGetResource(xdb, "dmenu.hpbackground", "*", &type, &xval)) colors[SchemeHp][ColBg] = strdup(xval.addr); @@ -87,4 +87,4 @@ readxresources(void) #endif // EMOJI_HIGHLIGHT_PATCH XrmDestroyDatabase(xdb); } -} \ No newline at end of file +} diff --git a/patches.def.h b/patches.def.h index be4f59f..25cbf82 100644 --- a/patches.def.h +++ b/patches.def.h @@ -57,12 +57,6 @@ */ #define EMOJI_HIGHLIGHT_PATCH 0 -/* This patch make it so that fuzzy matches gets highlighted and is therefore meant - * to be used together with the fuzzymatch patch. - * https://tools.suckless.org/dmenu/patches/fuzzyhighlight/ - */ -#define FUZZYHIGHLIGHT_PATCH 0 - /* This patch adds support for fuzzy-matching to dmenu, allowing users to type non-consecutive * portions of the string to be matched. * https://tools.suckless.org/dmenu/patches/fuzzymatch/ @@ -89,8 +83,13 @@ #define GRIDNAV_PATCH 0 /* This patch highlights the individual characters of matched text for each dmenu list entry. - * The fuzzy highlight patch takes precedence over this patch. + * If combined with the fuzzymatch patch then fuzzy highlight will be used for highlighting + * depending on whether fuzzy matching is enabled. + * + * Known issue: highlighting does not work properly when pango markup is used + * * https://tools.suckless.org/dmenu/patches/highlight/ + * https://tools.suckless.org/dmenu/patches/fuzzyhighlight/ */ #define HIGHLIGHT_PATCH 0 @@ -216,6 +215,8 @@ * A long term fix for the libXft library is pending approval of this pull request: * https://gitlab.freedesktop.org/xorg/lib/libxft/-/merge_requests/1 * + * Known issue: not compatible with the scroll patch + * * Also see: * https://developer.gnome.org/pygtk/stable/pango-markup-language.html * https://github.com/StillANixRookie/dmenu-pango @@ -302,6 +303,9 @@ /* This patch adds support for text scrolling and no longer appends '...' for long input as * it can handle long text. + * + * Known issue: not compatible with the pango patch + * * https://tools.suckless.org/dmenu/patches/scroll/ */ #define SCROLL_PATCH 0