From: Michael D. Lowis Date: Fri, 26 Apr 2019 20:06:34 +0000 (-0400) Subject: added rctags, tsed, updated TODO, added loading message to pick X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=14a46da9b09f42665fce049a827c94d3a5bc50e3;p=projs%2Ftide.git added rctags, tsed, updated TODO, added loading message to pick --- diff --git a/Makefile b/Makefile index 5be212d..756b79e 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ CC = ./acc INCS = -Iinc/ -BINS = bin/tide bin/registrar bin/edit bin/fetch bin/pick +BINS = bin/tide bin/registrar bin/edit bin/fetch bin/pick bin/tsed TEST_BINS = tests/libedit MAN1 = docs/tide.1 diff --git a/TODO.md b/TODO.md index a325c53..fcfe8a5 100644 --- a/TODO.md +++ b/TODO.md @@ -7,6 +7,7 @@ ## STAGING * tide: gap buffer does not handle UTF-8 currently +* tide: arrow keys on reverse selection shrink the wrong way ## BACKLOG diff --git a/bin/rctags b/bin/rctags new file mode 100755 index 0000000..73709a7 --- /dev/null +++ b/bin/rctags @@ -0,0 +1,2 @@ +#!/bin/sh +ctags -R --exclude="mock*" --exclude="test_*" diff --git a/foo.md b/foo.md deleted file mode 100644 index 06c59fc..0000000 --- a/foo.md +++ /dev/null @@ -1,10 +0,0 @@ -# Ctrl+D - -1) Tag: "Get", Edit: nil -2) Tag: "Get", Edit: "Makefile" -3) Tag: "Get Makefile", Edit: nil -4) Tag: nil, Edit: "Get" -5) Tag: "Makefile", Edit: "Get" -6) Tag: nil, Edit: "Get Makefile" - -# Tag Execution \ No newline at end of file diff --git a/src/pick.c b/src/pick.c index 4dd6de8..9c525cd 100644 --- a/src/pick.c +++ b/src/pick.c @@ -240,10 +240,14 @@ static void redraw(XConf* x) { x11_draw_string(x, x->font, offset + 2, fheight, Palette[TagsFg], Query); /* draw the scored and sorted results */ - for (int i = Offset, y = 2 * fheight + 4; ((size_t)i < vec_size(&Choices)) && ((size_t)i <= Offset+nlines); i++, y += fheight) { - if ((size_t)i == ChoiceIdx) - x11_draw_rect(x, Palette[EditSel], ScrollWidth+3, y - x->font->ascent, x->width, fheight); - x11_draw_string(x, x->font, ScrollWidth+3, y, Palette[TagsFg], ((Choice*)vec_at(&Choices, i))->string); + if (vec_size(&Choices)) { + for (int i = Offset, y = 2 * fheight + 4; ((size_t)i < vec_size(&Choices)) && ((size_t)i <= Offset+nlines); i++, y += fheight) { + if ((size_t)i == ChoiceIdx) + x11_draw_rect(x, Palette[EditSel], ScrollWidth+3, y - x->font->ascent, x->width, fheight); + x11_draw_string(x, x->font, ScrollWidth+3, y, Palette[TagsFg], ((Choice*)vec_at(&Choices, i))->string); + } + } else { + x11_draw_string(x, x->font, ScrollWidth+3, 2 * fheight + 4, Palette[TagsFg], "Loading..."); } x11_flip(x); } diff --git a/src/tsed.c b/src/tsed.c new file mode 100644 index 0000000..457f04f --- /dev/null +++ b/src/tsed.c @@ -0,0 +1,236 @@ +#include +#include +#include +#include +#include + +/* Line buffer flags */ +enum { + LB_NEWLINE = (1 << 0), + LB_DELETE = (1 << 1) +}; + +typedef struct { + int flags; + size_t capacity; + ssize_t length; + char* buffer; +} LineBuf; + +/* Command flags */ +enum { + SUB_GLOBAL = (1 << 0), + SUB_PRINT = (1 << 1), + IN_RANGE = (1 << 2), +}; + +typedef struct { + enum { NONE, LINE, REGEX } type; + union { + regex_t* regex; + size_t line; + } u; +} Addr; + +typedef struct { + int type; + int flags; + Addr addr[2]; + regex_t* regex; + char* text; +} Cmd; + +typedef struct { + size_t ncmds; + size_t line; + Cmd cmds[]; +} Prog; + +typedef void (*CmdFn)(Cmd* cmd, LineBuf* buf); + +static void cmd_d(Cmd* cmd, LineBuf* lbuf) { + (void)cmd; + lbuf->flags |= LB_DELETE; +} + +static void cmd_s(Cmd* cmd, LineBuf* lbuf) { + regmatch_t match[10] = {0}; + if (!regexec(cmd->regex, lbuf->buffer, 10, match, 0)) { +// lbuf->flags |= LB_DELETE; + } +} + +static const CmdFn Commands[] = { + ['d'] = cmd_d, + ['s'] = cmd_s +}; + +static int match_addr(Prog* prog, Addr* addr, LineBuf* lbuf) { + if (addr->type == NONE) + return 1; + else if (addr->type == LINE) + return (addr->u.line == prog->line); + else if (addr->type == REGEX) + return (regexec(addr->u.regex, lbuf->buffer, 0, 0, 0) == 0); + else + return 0; +} + +static char* rdline(LineBuf* buf, FILE* file) { + buf->length = getline(&(buf->buffer), &(buf->capacity), file); + buf->flags = 0; + if (buf->length <= 0) { + free(buf->buffer); + buf->buffer = NULL; + } else if (buf->buffer[buf->length-1] == '\n') { + buf->flags |= LB_NEWLINE; + buf->buffer[buf->length-1] = '\0'; + } + return buf->buffer; +} + +static void parse_fail(char* msg) { + printf("parse error: %s\n", msg); + exit(1); +} + +static char* parse_till(char* script, int term, char** str) { + if (!*script) return script; + char *sbeg = script, *send = script; + for (; *send && *send != term; send++) + if (*send == '\\') send++; + *str = strndup(sbeg, send - sbeg); + return (*send ? send+1 : send); +} + +static char* parse_addr(char* script, Addr* addr) { + if (isdigit(*script)) { + addr->type = LINE; + for (; *script && isdigit(*script); script++) + addr->u.line = (addr->u.line * 10) + (*script - '0'); + } else if (*script == '/') { + addr->type = REGEX; + char* rstr = NULL; + script = parse_till(++script, '/', &rstr); + addr->u.regex = calloc(1, sizeof(regex_t)); + if (regcomp(addr->u.regex, rstr, REG_EXTENDED|REG_NOSUB) != 0) + parse_fail("failed to compile regex"); + } + return script; +} + +static char* parse_sub(char* script, Cmd* cmd) { + int sep = *script++; + if (!sep) return (script-1); + + /* parse out the regex and template */ + char *rstr = NULL, *tstr = NULL; + script = parse_till(script, sep, &rstr); + script = parse_till(script, sep, &tstr); + + /* parse the flags */ + for (;*script && !isspace(*script); script++) { + switch (*script) { + case 'g': cmd->flags |= SUB_GLOBAL; break; + case 'p': cmd->flags |= SUB_PRINT; break; + default: parse_fail("bad substitute flag"); + } + } + + /* compile the regex and setup the command */ + cmd->text = tstr; + cmd->regex = calloc(1, sizeof(regex_t)); + if (regcomp(cmd->regex, rstr, REG_EXTENDED|REG_NEWLINE) < 0) + parse_fail("failed to compile regex"); + free(rstr); + + return script; +} + +static Prog* prog_grow(Prog* prog, Cmd** lastcmd) { + prog = realloc(prog, sizeof(Prog) + ((prog->ncmds + 1) * sizeof(Cmd))); + *lastcmd = &(prog->cmds[prog->ncmds]); + prog->ncmds++; + memset(*lastcmd, 0, sizeof(Cmd)); + return prog; +} + +Prog* prog_parse(char* script) { + Cmd* cmd = NULL; + Prog* prog = calloc(1, sizeof(Prog)); + while (*script) { + while (isspace(*script)) script++; + if (!*script) break; + + /* allocate a new command */ + prog = prog_grow(prog, &cmd); + + /* parse the addresses */ + script = parse_addr(script, &(cmd->addr[0])); + if (*script == ',') + script = parse_addr(++script, &(cmd->addr[1])); + + /* parse the command */ + cmd->type = *script; + switch (*script++) { + case 'd': + /* handled above */ + break; + case 's': + script = parse_sub(script, cmd); + break; + case '\0': + parse_fail("unexpected end of string"); + break; + default: + parse_fail("unexpected char"); + exit(1); + break; + } + } + return prog; +} + +void prog_free(Prog* prog) { +#define FREE_REGEX(regex) do { regfree(regex); free(regex); } while(0) + for (size_t i = 0; i < prog->ncmds; i++) { + if (prog->cmds[i].addr[0].type == REGEX) FREE_REGEX(prog->cmds[i].addr[0].u.regex); + if (prog->cmds[i].addr[1].type == REGEX) FREE_REGEX(prog->cmds[i].addr[1].u.regex); + if (prog->cmds[i].regex) FREE_REGEX(prog->cmds[i].regex); + if (prog->cmds[i].text) free(prog->cmds[i].text); + } +#undef FREE_REGEX + free(prog); +} + +void prog_exec(Prog* prog, LineBuf* lbuf) { + prog->line++; + for (size_t i = 0; i < prog->ncmds; i++) { + Cmd* cmd = &(prog->cmds[i]); + if ((cmd->flags & IN_RANGE) ||match_addr(prog, &(cmd->addr[0]), lbuf)) { + if (cmd->addr[1].type != NONE) { + cmd->flags |= IN_RANGE; + if (match_addr(prog, &(cmd->addr[1]), lbuf)) + cmd->flags &= ~IN_RANGE; + } + Commands[cmd->type](cmd, lbuf); + } + } +} + +int main(int argc, char** argv) { + if (argc < 2) return 1; + + LineBuf buf = {0}; + Prog* prog = prog_parse(argv[1]); + while (rdline(&buf, stdin)) { + prog_exec(prog, &buf); + if (buf.flags & LB_DELETE) continue; + fwrite(buf.buffer, 1u, buf.length, stdout); + if (buf.flags & LB_NEWLINE) + fwrite("\n", 1u, 1u, stdout); + } + prog_free(prog); + + return 0; +}