From: Michael D. Lowis Date: Fri, 20 Apr 2018 20:10:25 +0000 (-0400) Subject: first attempt to map clicks to buffer position. horribly broken X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=996aafc32ec96779e63d9ec6ef7fc339d9d0ce4b;p=projs%2Ftide.git first attempt to map clicks to buffer position. horribly broken --- diff --git a/inc/edit.h b/inc/edit.h index 3c0ec6b..f5f5bf3 100644 --- a/inc/edit.h +++ b/inc/edit.h @@ -73,6 +73,7 @@ char* buf_fetch(Buf* buf, bool (*isword)(Rune), size_t off); /* Screen management functions *****************************************************************************/ typedef struct { + size_t off; /* offset of the rune in the buffer */ size_t width; /* width of the glyph on screen */ Rune rune; /* rune value for the cell */ } UGlyph; diff --git a/lib/view.c b/lib/view.c index 7b3d464..31485b0 100644 --- a/lib/view.c +++ b/lib/view.c @@ -124,6 +124,7 @@ static void resize(View* view, size_t width, size_t nrows, size_t off) { view->rows[view->nrows-1]->len = len; view->rows[view->nrows-1]->cols[len-1].rune = rune; view->rows[view->nrows-1]->cols[len-1].width = rwidth; + view->rows[view->nrows-1]->cols[len-1].off = off; off = buf_byrune(&(view->buffer), off, RIGHT); } } @@ -160,6 +161,16 @@ void view_byline(View* view, int move, bool extsel) { } void view_setcursor(View* view, size_t row, size_t col, bool extsel) { + size_t i = 0, y = 0, idx = view->index + row; + if (idx >= view->nrows) return; + printf("row: %ld %ld\n", row, idx); + Row* selrow = view->rows[idx]; + for (; i < selrow->len; i++) { + y += selrow->cols[i].width; + if (col < y) break; + } + getsel(view)->end = selrow[i].off; + printf("clicked: %ld\n", getsel(view)->end); } void view_selword(View* view, size_t row, size_t col) { diff --git a/lib/x11.c b/lib/x11.c index 90d913f..74a7105 100644 --- a/lib/x11.c +++ b/lib/x11.c @@ -102,7 +102,7 @@ static void get_position(WinRegion id, int x, int y, size_t* row, size_t* col) { int starty = (id == EDIT ? Divider+3 : 0); int startx = (id == EDIT ? ScrollWidth+3 : 0); *row = (y - starty) / X.font->height; - *col = (x - startx) / font_width(); + *col = x - startx; } static struct XSel* selfetch(Atom atom) { diff --git a/reader.c b/reader.c new file mode 100644 index 0000000..2d16962 --- /dev/null +++ b/reader.c @@ -0,0 +1,258 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define nelem(x) (sizeof(x)/sizeof((x)[0])) + +#ifdef __MACH__ + #define OPENCMD "open" +#else + #define OPENCMD "xdg-open" +#endif + +typedef struct { + enum { + COMPLETE=0, MATCHES, IS, ISSET, ISDIR, ISFILE, + SET, UNSET, FINDFILE, EXEC, LAUNCH + } type; + char* arg1; + char* arg2; +} Rule; + +char* Matches[10]; + +Rule* BuiltinRules[] = { + (Rule[]){ // Look up .c or .h files in Code/ + { ISSET, "EDITOR", NULL }, + { MATCHES, "data", "\\.[ch]$" }, + { ISDIR, "Code", NULL }, + { EXEC, "[[ $(find Code -type f -name '*$data') ]]", NULL }, + { LAUNCH, "find Code -type f -name '*$data' | xargs -r $EDITOR", NULL }, + { COMPLETE, NULL, NULL } + }, + (Rule[]){ // Match URLS and open them with the browser + { ISSET, "BROWSER", NULL }, + { MATCHES, "data", "^(https?|ftp)://.*" }, + { LAUNCH, "$BROWSER $0", NULL }, + { COMPLETE, NULL, NULL } + }, + (Rule[]){ // Open files with addresses in the editor + { ISSET, "EDITOR", NULL }, + { MATCHES, "data", "^([^:]+):([0-9]+)" }, + { ISFILE, "$1", NULL }, + { LAUNCH, "tctl $0", NULL }, + { COMPLETE, NULL, NULL } + }, + (Rule[]){ // If it's an existing text file, open it with editor + { ISSET, "EDITOR", NULL }, + { ISFILE, "$data", NULL }, + { EXEC, "file --mime '$file' | grep -q 'text/'", NULL }, + { LAUNCH, "$EDITOR '$file'", NULL }, + { COMPLETE, NULL, NULL } + }, + (Rule[]){ // Look it up in ctags database + { ISSET, "EDITOR", NULL }, + { ISFILE, "tags", NULL }, + { EXEC, "grep -q '^$data\\s\\+' tags", NULL }, + { LAUNCH, "picktag fetch tags '$data' | xargs -r tide", NULL }, + { COMPLETE, NULL, NULL } + }, + (Rule[]){ // If it's an existing directory, open it with system default + { ISDIR, "$data", NULL }, + { LAUNCH, OPENCMD " $data", NULL }, + { COMPLETE, NULL, NULL } + }, +}; + +/******************************************************************************/ + +char* getvar(char* val) { + if (strlen(val) == 1 && isdigit(*val)) + val = Matches[*val - '0']; + else + val = getenv(val); + return (val ? val : ""); +} + +char* eval(char* str) { + static bool inited = false; + static char* patt = "\\$([a-zA-Z0-9_]+)"; + static regex_t regex; + + if (!inited && (regcomp(®ex, patt, REG_EXTENDED) < 0)) { + perror("regcomp() :"); + exit(1); + } + + regmatch_t matches[2] = {0}; + if (regexec(®ex, str, nelem(matches), matches, 0) < 0) { + return str; + } else if (matches[1].rm_so > 0) { + char* var = strndup(str+matches[1].rm_so, matches[1].rm_eo-matches[1].rm_so); + char* val = getvar(var); + size_t sz = strlen(str) + strlen(val); + char* exp = calloc(1, sz); + strncat(exp, str, matches[0].rm_so); + strcat(exp, val); + strcat(exp, str + matches[0].rm_eo); + return eval(exp); + } else { + return str; + } +} + +/******************************************************************************/ + +bool matches(char* var, char* patt) { + regex_t regex = {0}; + regmatch_t matches[10] = {0}; + if (regcomp(®ex, patt, REG_EXTENDED) == 0) { + var = getvar(var); + memset(Matches, 0, sizeof(Matches)); + int err = regexec(®ex, var, nelem(matches), matches, 0); + for (int i = 0; i < 10 && matches[i].rm_so >= 0; i++) { + Matches[i] = strndup(var+matches[i].rm_so, matches[i].rm_eo-matches[i].rm_so); + } + return (err == 0); + } + return false; +} + +bool var_is(char* var, char* val) { + return (strcmp(getvar(var), eval(val)) == 0); +} + +bool var_isset(char* var) { + return (getenv(var) != NULL); +} + +bool var_isdir(char* var) { + struct stat st = {0}; + char* path = eval(var); + if ((stat(path, &st) < 0) && (errno == ENOENT)) { + return false; + } else if (S_ISDIR(st.st_mode)) { + setenv("dir", var, 1); + return true; + } else { + return false; + } +} + +bool var_isfile(char* var) { + struct stat st = {0}; + char* path = eval(var); + if ((stat(eval(var), &st) < 0) && (errno == ENOENT)) { + return false; + } else if (!S_ISDIR(st.st_mode)) { + setenv("file", path, 1); + return true; + } else { + return false; + } +} + +bool var_set(char* var, char* val) { + return (setenv(var, eval(val), 1) == 0); +} + +bool var_unset(char* var) { + return (unsetenv(var) == 0); +} + +bool find_file(char* file) { + return false; +} + +void runcmd(char* cmd) { + char* shellcmd[] = { getvar("SHELL"), "-c", NULL, NULL }; + if (!shellcmd[0]) shellcmd[0] = "/bin/sh"; + shellcmd[2] = eval(cmd); + _exit(execvp(shellcmd[0], shellcmd)); +} + +bool exec(char* cmd) { + int pid, status, outpipe[2]; + if ((pid = fork()) < 0) return false; + if (pid == 0) { + runcmd(cmd); + } else { + waitpid(pid, &status, 0); + return (status == 0); + } + return false; +} + +bool launch(char* cmd) { + int pid = fork(); + if (pid > 0) + return true; + else if (pid == 0) + runcmd(cmd); + return false; +} + +bool apply_rule(Rule* rule) { + switch (rule->type) { + case COMPLETE: exit(0); + case MATCHES: return matches(rule->arg1, rule->arg2); + case IS: return var_is(rule->arg1, rule->arg2); + case ISSET: return var_isset(rule->arg1); + case ISDIR: return var_isdir(rule->arg1); + case ISFILE: return var_isfile(rule->arg1); + case SET: return var_set(rule->arg1, rule->arg2); + case UNSET: return var_unset(rule->arg1); + case FINDFILE: return find_file(rule->arg1); + case EXEC: return exec(rule->arg1); + case LAUNCH: return launch(rule->arg1); + } + return false; +} + +void apply_ruleset(char* text) { + setenv("data", text, 1); + for (int i = 0; i < nelem(BuiltinRules); i++) { + Rule* rule = BuiltinRules[i]; + for (; rule->type != COMPLETE; rule++) + if (!apply_rule(rule)) + break; + if (rule->type == COMPLETE) + exit(0); + } +} + +/******************************************************************************/ + +int fifo_open(char* path, mode_t mode) { + mkfifo(path, mode); + int fd = open(path, O_WRONLY|O_NONBLOCK); + if (fd >= 0) { close(fd); return -1; } + return open(path, O_RDONLY); +} + +int main(int argc, char** argv) { + int ret = 0, nread, fd = fifo_open("./myfifo", 0655); + if (fd >= 0) { + char buf[1024]; + while ((nread = read(fd, buf, sizeof(buf)-1)) >= 0) { + if (nread == 0) continue; + buf[nread] = '\0'; + printf("fetch: '%s'\n", buf); + if (fork() == 0) apply_ruleset(buf); + } + } else { + perror("open()"); + ret = 1; + } + return ret; +} diff --git a/rules b/rules new file mode 100644 index 0000000..6bf8751 --- /dev/null +++ b/rules @@ -0,0 +1,60 @@ +# This is a list of sequentially applied rule blocks that attempt to +# determine what should be done with a piece of text stored in $data +# Processing starts at the top of the file and proceeds to the bottom +# or until a block executes the finish command. If any comman in the +# block is unsuccessful, that block should terminate and the next block +# should be evaluated. +# +# Builtin Commands: +# is_set Determines if an env var is set +# is_dir Determines if path is a directory +# is_file Determines if path is a file +# matches Determines if text matches the regex +# exec_cmd Executes cmd and fails the rule if unsuccessful +# launch_cmd Executes cmd without regard to exit status + +{ # Look up .c or .h files in Code/ + is_set EDITOR + matches "$data" "\\.[ch]$" + is_dir "Code" + exec_cmd "[[ $(find Code -type f -name '*$data') ]]" + launch_cmd "find Code -type f -name '*$data' | xargs -r $EDITOR" + finish +} + +{ # Match URLS and open them with the browser + is_set BROWSER + matches "$data" "^(https?|ftp)://.*" + launch_cmd "$BROWSER $0" + finish +} + +{ # Open files with addresses in the editor + is_set EDITOR + matches "$data" "^([^:]+):([0-9]+)" + is_file "$1" + launch_cmd "tctl $0" + finish +} + +{ # If it's an existing text file, open it with editor + is_set EDITOR + is_file "$data" + exec_cmd "file --mime '$file' | grep -q 'text/'" + launch_cmd "$EDITOR" "$file" + finish +} + +{ # Look it up in ctags database + is_set EDITOR + is_file tags + exec_cmd grep -q "^$data\\s\\+" tags + launch_cmd picktag fetch tags "$data" | xargs -r tide + finish +} + +{ # If it's an existing directory, open it with system default + is_dir "$data" + launch_cmd "open '$data'" + finish +}