dst/source/patch/openurlonclick.d

237 lines
7.5 KiB
D
Raw Permalink Normal View History

2025-06-26 18:47:07 +00:00
module patch.openurlonclick;
import core.stdc.string : strchr, strncmp;
import core.sys.posix.unistd : pid_t;
import std.algorithm : min, max;
import std.string : toStringz, fromStringz;
import patches : isPatchEnabled;
import st : term, Rune, Line, ATTR_WRAP;
import st : TLINE;
// Define ATTR_SET if not already defined
enum ATTR_SET = 1 << 15;
enum MODE_ALTSCREEN = 1 << 2;
// Define IS_SET helper
bool IS_SET(uint flag) {
import st : win;
return (win.mode & flag) != 0;
}
static if (isPatchEnabled!"OPENURLONCLICK_PATCH") {
static if (!isPatchEnabled!"REFLOW_PATCH") {
static if (isPatchEnabled!"SCROLLBACK_PATCH") {
alias TLINEURL = TLINE;
} else {
auto TLINEURL(int y) { return term.line[y]; }
}
}
extern(C) export __gshared int url_x1, url_y1, url_x2, url_y2 = -1;
extern(C) export __gshared int url_draw, url_click, url_maxcol;
private int isvalidurlchar(Rune u) {
static immutable string urlchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" ~
"abcdefghijklmnopqrstuvwxyz" ~
"0123456789-._~:/?#@!$&'*+,;=%";
return u < 128 && strchr(urlchars.ptr, cast(int)u) != null;
}
static if (isPatchEnabled!"REFLOW_PATCH") {
private int findeowl(Line line) {
int i = term.col - 1;
do {
if (line[i].mode & ATTR_WRAP)
return i;
} while (!(line[i].mode & ATTR_SET) && --i >= 0);
return -1;
}
} else {
private int findeowl(int row) {
static if (isPatchEnabled!"COLUMNS_PATCH") {
int col = term.maxcol - 1;
} else {
int col = term.col - 1;
}
do {
if (TLINEURL(row)[col].mode & ATTR_WRAP)
return col;
} while (TLINEURL(row)[col].u == ' ' && --col >= 0);
return -1;
}
}
void clearurl() {
while (url_y1 <= url_y2 && url_y1 < term.row)
term.dirty[url_y1++] = 1;
url_y2 = -1;
}
static if (isPatchEnabled!"REFLOW_PATCH") {
char* detecturl(int col, int row, int draw) {
static char[2048] url;
Line line;
int x1, y1, x2, y2;
int i = url.length/2+1, j = cast(int)(url.length/2);
int row_start = row, col_start = col;
int minrow = IS_SET(MODE_ALTSCREEN) ? 0 : term.scr - term.histf;
int maxrow = IS_SET(MODE_ALTSCREEN) ? term.row - 1 : term.scr + term.row - 1;
if (draw)
clearurl();
url_maxcol = 0;
line = TLINE(row);
if (!isvalidurlchar(line[col].u))
return null;
do {
x1 = col_start;
y1 = row_start;
url_maxcol = max(url_maxcol, x1);
url[--i] = cast(char)line[col_start].u;
if (--col_start < 0) {
if (--row_start < minrow || (col_start = findeowl(TLINE(row_start))) < 0)
break;
line = TLINE(row_start);
}
} while (isvalidurlchar(line[col_start].u) && i > 0);
if (url[i] != 'h')
return null;
line = TLINE(row);
do {
x2 = col;
y2 = row;
url_maxcol = max(url_maxcol, x2);
url[j++] = cast(char)line[col].u;
if (line[col++].mode & ATTR_WRAP) {
if (++row > maxrow)
break;
col = 0;
line = TLINE(row);
}
} while (col < term.col && isvalidurlchar(line[col].u) && j < url.length-1);
url[j] = 0;
if (strncmp("https://".ptr, &url[i], 8) && strncmp("http://".ptr, &url[i], 7))
return null;
if (strchr(",.;:?!".ptr, cast(int)(url[j-1])) != null) {
x2 = max(x2-1, 0);
url[j-1] = 0;
}
if (draw) {
url_x1 = (y1 >= 0) ? x1 : 0;
url_x2 = (y2 < term.row) ? x2 : url_maxcol;
url_y1 = max(y1, 0);
url_y2 = min(y2, term.row-1);
url_draw = 1;
for (y1 = url_y1; y1 <= url_y2; y1++)
term.dirty[y1] = 1;
}
return &url[i];
}
} else {
char* detecturl(int col, int row, int draw) {
static char[2048] url;
int x1, y1, x2, y2, wrapped;
int row_start = row;
int col_start = col;
int i = url.length/2+1, j = cast(int)(url.length/2);
static if (isPatchEnabled!"SCROLLBACK_PATCH") {
int minrow = term.scr - term.histn, maxrow = term.scr + term.row - 1;
if ((term.mode & (1 << 2)) != 0)
minrow = 0, maxrow = term.row - 1;
} else {
int minrow = 0, maxrow = term.row - 1;
}
url_maxcol = 0;
if (draw)
clearurl();
if (!isvalidurlchar(TLINEURL(row)[col].u))
return null;
do {
x1 = col_start;
y1 = row_start;
url_maxcol = max(url_maxcol, x1);
url[--i] = cast(char)TLINEURL(row_start)[col_start].u;
if (--col_start < 0) {
if (--row_start < minrow || (col_start = findeowl(row_start)) < 0)
break;
}
} while (i > 0 && isvalidurlchar(TLINEURL(row_start)[col_start].u));
if (url[i] != 'h')
return null;
do {
x2 = col;
y2 = row;
url_maxcol = max(url_maxcol, x2);
url[j++] = cast(char)TLINEURL(row)[col].u;
wrapped = TLINEURL(row)[col].mode & ATTR_WRAP;
static if (isPatchEnabled!"COLUMNS_PATCH") {
if (++col >= term.maxcol || wrapped) {
col = 0;
if (++row > maxrow || !wrapped)
break;
}
} else {
if (++col >= term.col || wrapped) {
col = 0;
if (++row > maxrow || !wrapped)
break;
}
}
} while (j < url.length-1 && isvalidurlchar(TLINEURL(row)[col].u));
url[j] = 0;
if (strncmp("https://".ptr, &url[i], 8) && strncmp("http://".ptr, &url[i], 7))
return null;
if (draw) {
url_x1 = (y1 >= 0) ? x1 : 0;
url_x2 = (y2 < term.row) ? x2 : url_maxcol;
url_y1 = max(y1, 0);
url_y2 = min(y2, term.row-1);
url_draw = 1;
for (y1 = url_y1; y1 <= url_y2; y1++)
term.dirty[y1] = 1;
}
return &url[i];
}
}
void openUrlOnClick(int col, int row, char* url_opener) {
char* url = detecturl(col, row, 1);
if (url) {
import core.stdc.stdlib : system;
import std.format : format;
import std.process : spawnProcess;
// Use spawnProcess instead of posix_spawnp
string[] args = [cast(string)fromStringz(url_opener), cast(string)fromStringz(url)];
try {
spawnProcess(args);
} catch (Exception e) {
// Ignore errors
}
}
}
}