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