sites

public wiki contents of suckless.org
git clone git://git.suckless.org/sites
Log | Files | Refs

st-clickurl-0.8.5.diff (5778B)


      1 From d5b492049f48dc411b0dd7dc01a403304c20438d Mon Sep 17 00:00:00 2001
      2 From: Jishnu Sen <jishnu1@gmail.com>
      3 Date: Sun, 7 Apr 2024 22:54:46 -0700
      4 Subject: [PATCH] Highlight URLs with control and follow with click
      5 
      6 ---
      7  config.def.h | 11 +++++++
      8  st.c         | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++
      9  st.h         |  9 ++++++
     10  x.c          | 24 +++++++++++++-
     11  4 files changed, 132 insertions(+), 1 deletion(-)
     12 
     13 diff --git a/config.def.h b/config.def.h
     14 index 91ab8ca..4961830 100644
     15 --- a/config.def.h
     16 +++ b/config.def.h
     17 @@ -472,3 +472,14 @@ static char ascii_printable[] =
     18  	" !\"#$%&'()*+,-./0123456789:;<=>?"
     19  	"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
     20  	"`abcdefghijklmnopqrstuvwxyz{|}~";
     21 +
     22 +/*
     23 + * Open urls starting with urlprefixes, contatining urlchars
     24 + * by passing as ARG1 to urlhandler.
     25 + */
     26 +char* urlhandler = "xdg-open";
     27 +char urlchars[] =
     28 +	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     29 +	"abcdefghijklmnopqrstuvwxyz"
     30 +	"0123456789-._~:/?#@!$&'*+,;=%";
     31 +char* urlprefixes[] = {"http://", "https://", NULL};
     32 diff --git a/st.c b/st.c
     33 index 51049ba..8f2156c 100644
     34 --- a/st.c
     35 +++ b/st.c
     36 @@ -643,6 +643,95 @@ getsel(void)
     37  	return str;
     38  }
     39  
     40 +char *
     41 +strstrany(char* s, char** strs) {
     42 +	char *match;
     43 +	for (int i = 0; strs[i]; i++) {
     44 +		if ((match = strstr(s, strs[i]))) {
     45 +			return match;
     46 +		}
     47 +	}
     48 +	return NULL;
     49 +}
     50 +
     51 +void
     52 +highlighturls(void)
     53 +{
     54 +	char *match;
     55 +	char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */
     56 +	for (int i = term.top; i < term.bot; i++) {
     57 +		int url_start = -1;
     58 +		for (int j = 0; j < term.col; j++) {
     59 +			if (term.line[i][j].u < 127) {
     60 +				linestr[j] = term.line[i][j].u;
     61 +			}
     62 +			linestr[term.col] = '\0';
     63 +		}
     64 +		while ((match = strstrany(linestr + url_start + 1, urlprefixes))) {
     65 +			url_start = match - linestr;
     66 +			for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) {
     67 +				term.line[i][c].mode |= ATTR_URL;
     68 +				tsetdirt(i, c);
     69 +			}
     70 +		}
     71 +	}
     72 +	free(linestr);
     73 +}
     74 +
     75 +void
     76 +unhighlighturls(void)
     77 +{
     78 +	for (int i = term.top; i < term.bot; i++) {
     79 +		for (int j = 0; j < term.col; j++) {
     80 +			Glyph* g = &term.line[i][j];
     81 +			if (g->mode & ATTR_URL) {
     82 +				g->mode &= ~ATTR_URL;
     83 +				tsetdirt(i, j);
     84 +			}
     85 +		}
     86 +	}
     87 +	return;
     88 +}
     89 +
     90 +void
     91 +followurl(int x, int y) {
     92 +	char *linestr = calloc(sizeof(char), term.col+1); /* assume ascii */
     93 +	char *match;
     94 +	for (int i = 0; i < term.col; i++) {
     95 +		if (term.line[x][i].u < 127) {
     96 +			linestr[i] = term.line[x][i].u;
     97 +		}
     98 +		linestr[term.col] = '\0';
     99 +	}
    100 +	int url_start = -1;
    101 +	while ((match = strstrany(linestr + url_start + 1, urlprefixes))) {
    102 +		url_start = match - linestr;
    103 +		int url_end = url_start;
    104 +		for (int c = url_start; c < term.col && strchr(urlchars, linestr[c]); c++) {
    105 +			url_end++;
    106 +		}
    107 +		if (url_start <= y && y < url_end) {
    108 +			linestr[url_end] = '\0';
    109 +			break;
    110 +		}
    111 +	}
    112 +	if (url_start == -1) {
    113 +		free(linestr);
    114 +		return;
    115 +	}
    116 +
    117 +	pid_t chpid;
    118 +	if ((chpid = fork()) == 0) {
    119 +		if (fork() == 0)
    120 +			execlp(urlhandler, urlhandler, linestr + url_start, NULL);
    121 +		exit(1);
    122 +	}
    123 +	if (chpid > 0)
    124 +		waitpid(chpid, NULL, 0);
    125 +	free(linestr);
    126 +	unhighlighturls();
    127 +}
    128 +
    129  void
    130  selclear(void)
    131  {
    132 diff --git a/st.h b/st.h
    133 index 519b9bd..354e7f9 100644
    134 --- a/st.h
    135 +++ b/st.h
    136 @@ -34,6 +34,7 @@ enum glyph_attribute {
    137  	ATTR_WIDE       = 1 << 9,
    138  	ATTR_WDUMMY     = 1 << 10,
    139  	ATTR_BOLD_FAINT = ATTR_BOLD | ATTR_FAINT,
    140 +	ATTR_URL	= 1 << 14,
    141  };
    142  
    143  enum selection_mode {
    144 @@ -105,6 +106,10 @@ void selextend(int, int, int, int);
    145  int selected(int, int);
    146  char *getsel(void);
    147  
    148 +void highlighturls(void);
    149 +void unhighlighturls(void);
    150 +void followurl(int, int);
    151 +
    152  size_t utf8encode(Rune, char *);
    153  
    154  void *xmalloc(size_t);
    155 @@ -126,3 +131,7 @@ extern unsigned int tabspaces;
    156  extern unsigned int defaultfg;
    157  extern unsigned int defaultbg;
    158  extern unsigned int defaultcs;
    159 +extern char *urlhandler;
    160 +extern char urlchars[];
    161 +extern char *urlprefixes[];
    162 +extern int nurlprefixes;
    163 diff --git a/x.c b/x.c
    164 index 8a16faa..13f68e4 100644
    165 --- a/x.c
    166 +++ b/x.c
    167 @@ -191,6 +191,7 @@ static void usage(void);
    168  
    169  static void (*handler[LASTEvent])(XEvent *) = {
    170  	[KeyPress] = kpress,
    171 +	[KeyRelease] = kpress,
    172  	[ClientMessage] = cmessage,
    173  	[ConfigureNotify] = resize,
    174  	[VisibilityNotify] = visibility,
    175 @@ -445,6 +446,15 @@ mouseaction(XEvent *e, uint release)
    176  	/* ignore Button<N>mask for Button<N> - it's set on release */
    177  	uint state = e->xbutton.state & ~buttonmask(e->xbutton.button);
    178  
    179 +	if (release == 0 &&
    180 +	    e->xbutton.button == Button1 &&
    181 +	    (match(ControlMask, state) ||
    182 +	     match(ControlMask, state & ~forcemousemod))) {
    183 +		followurl(evrow(e), evcol(e));
    184 +		return 1;
    185 +	}
    186 +
    187 +
    188  	for (ms = mshortcuts; ms < mshortcuts + LEN(mshortcuts); ms++) {
    189  		if (ms->release == release &&
    190  		    ms->button == e->xbutton.button &&
    191 @@ -1476,7 +1486,7 @@ xdrawglyphfontspecs(const XftGlyphFontSpec *specs, Glyph base, int len, int x, i
    192  	XftDrawGlyphFontSpec(xw.draw, fg, specs, len);
    193  
    194  	/* Render underline and strikethrough. */
    195 -	if (base.mode & ATTR_UNDERLINE) {
    196 +	if (base.mode & ATTR_UNDERLINE || base.mode & ATTR_URL) {
    197  		XftDrawRect(xw.draw, fg, winx, winy + dc.font.ascent + 1,
    198  				width, 1);
    199  	}
    200 @@ -1831,6 +1841,18 @@ kpress(XEvent *ev)
    201  		len = XmbLookupString(xw.ime.xic, e, buf, sizeof buf, &ksym, &status);
    202  	else
    203  		len = XLookupString(e, buf, sizeof buf, &ksym, NULL);
    204 +
    205 +        /* 0. highlight URLs when control held */
    206 +	if (ksym == XK_Control_L) {
    207 +		highlighturls();
    208 +	} else if (ev->type == KeyRelease && e->keycode == XKeysymToKeycode(e->display, XK_Control_L)) {
    209 +		unhighlighturls();
    210 +	}
    211 +
    212 +	/* KeyRelease not relevant to shortcuts */
    213 +	if (ev->type == KeyRelease)
    214 +		return;
    215 +
    216  	/* 1. shortcuts */
    217  	for (bp = shortcuts; bp < shortcuts + LEN(shortcuts); bp++) {
    218  		if (ksym == bp->keysym && match(bp->mod, e->state)) {
    219 -- 
    220 2.44.0
    221