From 9944dc484bf8cc7d5f1c506610a0593202bb5f92 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Tue, 4 Feb 2020 20:23:55 -0500 Subject: Split showing style codes and word wrapping --- edit.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 edit.c (limited to 'edit.c') diff --git a/edit.c b/edit.c new file mode 100644 index 0000000..446e0e9 --- /dev/null +++ b/edit.c @@ -0,0 +1,30 @@ +/* Copyright (C) 2020 C. McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "chat.h" + +const char *editHead(void) { + return "foo\0033bar"; +} + +const char *editTail(void) { + return "baz\3"; +} -- cgit 1.4.1-2-gfad0 From 0df8bd51aa70cfa66951e8f08a6a3dce3fd45dec Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Tue, 4 Feb 2020 21:27:52 -0500 Subject: Convert editHead and editTail from wchar_t --- edit.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'edit.c') diff --git a/edit.c b/edit.c index 446e0e9..e142507 100644 --- a/edit.c +++ b/edit.c @@ -14,17 +14,31 @@ * along with this program. If not, see . */ -#include +#include +#include #include #include -#include +#include #include "chat.h" +enum { Cap = 512 }; +static wchar_t buf[Cap] = L"foo\0033bar\3baz"; +static size_t len = 12; +static size_t pos = 6; + const char *editHead(void) { - return "foo\0033bar"; + static char mbs[MB_LEN_MAX * Cap]; + const wchar_t *ptr = buf; + size_t n = wcsnrtombs(mbs, &ptr, pos, sizeof(mbs), NULL); + assert(n != (size_t)-1); + return mbs; } const char *editTail(void) { - return "baz\3"; + static char mbs[MB_LEN_MAX * Cap]; + const wchar_t *ptr = &buf[pos]; + size_t n = wcsnrtombs(mbs, &ptr, len - pos, sizeof(mbs), NULL); + assert(n != (size_t)-1); + return mbs; } -- cgit 1.4.1-2-gfad0 From 4cce893eab7403821ff211f64a7df05051fd6f52 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Wed, 5 Feb 2020 00:20:39 -0500 Subject: Add extremely basic editing and message sending --- Makefile | 1 + chat.h | 12 ++++++++++-- command.c | 32 ++++++++++++++++++++++++++++++++ edit.c | 32 +++++++++++++++++++++++++------- ui.c | 5 ++++- 5 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 command.c (limited to 'edit.c') diff --git a/Makefile b/Makefile index 63f719d..05f8bb8 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ CFLAGS += -std=c11 -Wall -Wextra -Wpedantic LDLIBS = -lcurses -lcrypto -ltls OBJS += chat.o +OBJS += command.o OBJS += edit.o OBJS += handle.o OBJS += irc.o diff --git a/chat.h b/chat.h index c754357..c8b31c2 100644 --- a/chat.h +++ b/chat.h @@ -20,6 +20,7 @@ #include #include #include +#include #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0])) #define BIT(x) x##Bit, x = 1 << x##Bit, x##Bit_ = x##Bit @@ -109,6 +110,7 @@ void ircFormat(const char *format, ...) __attribute__((format(printf, 1, 2))); void handle(struct Message msg); +void command(size_t id, char *input); enum Heat { Cold, Warm, Hot }; void uiInit(void); @@ -122,8 +124,14 @@ void uiFormat( size_t id, enum Heat heat, const time_t *time, const char *format, ... ) __attribute__((format(printf, 4, 5))); -const char *editHead(void); -const char *editTail(void); +enum Edit { + EditKill, + EditInsert, + EditEnter, +}; +void edit(size_t id, enum Edit op, wchar_t ch); +char *editHead(void); +char *editTail(void); static inline enum Color hash(const char *str) { if (*str == '~') str++; diff --git a/command.c b/command.c new file mode 100644 index 0000000..ab05587 --- /dev/null +++ b/command.c @@ -0,0 +1,32 @@ +/* Copyright (C) 2020 C. McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "chat.h" + +void command(size_t id, char *input) { + ircFormat("PRIVMSG %s :%s\r\n", idNames[id], input); + struct Message msg = { + .nick = self.nick, + // TODO: .user, + .cmd = "PRIVMSG", + .params[0] = idNames[id], + .params[1] = input, + }; + handle(msg); +} diff --git a/edit.c b/edit.c index e142507..68593d1 100644 --- a/edit.c +++ b/edit.c @@ -23,22 +23,40 @@ #include "chat.h" enum { Cap = 512 }; -static wchar_t buf[Cap] = L"foo\0033bar\3baz"; -static size_t len = 12; -static size_t pos = 6; +static wchar_t buf[Cap]; +static size_t len; +static size_t pos; -const char *editHead(void) { +char *editHead(void) { static char mbs[MB_LEN_MAX * Cap]; const wchar_t *ptr = buf; - size_t n = wcsnrtombs(mbs, &ptr, pos, sizeof(mbs), NULL); + size_t n = wcsnrtombs(mbs, &ptr, pos, sizeof(mbs) - 1, NULL); assert(n != (size_t)-1); + mbs[n] = '\0'; return mbs; } -const char *editTail(void) { +char *editTail(void) { static char mbs[MB_LEN_MAX * Cap]; const wchar_t *ptr = &buf[pos]; - size_t n = wcsnrtombs(mbs, &ptr, len - pos, sizeof(mbs), NULL); + size_t n = wcsnrtombs(mbs, &ptr, len - pos, sizeof(mbs) - 1, NULL); assert(n != (size_t)-1); + mbs[n] = '\0'; return mbs; } + +void edit(size_t id, enum Edit op, wchar_t ch) { + switch (op) { + break; case EditKill: len = pos = 0; + break; case EditInsert: { + if (len == Cap) break; + buf[pos++] = ch; + len++; + } + break; case EditEnter: { + pos = 0; + command(id, editTail()); + len = 0; + } + } +} diff --git a/ui.c b/ui.c index 8f813b4..b764f84 100644 --- a/ui.c +++ b/ui.c @@ -498,8 +498,11 @@ static void keyMeta(wchar_t ch) { } static void keyCtrl(wchar_t ch) { + size_t id = windows.active->id; switch (ch) { + break; case L'J': edit(id, EditEnter, 0); break; case L'L': clearok(curscr, true); + break; case L'U': edit(id, EditKill, 0); } } @@ -518,7 +521,7 @@ void uiRead(void) { } else if (iswcntrl(ch)) { keyCtrl(ch ^ L'@'); } else { - // TODO: Insert. + edit(windows.active->id, EditInsert, ch); } meta = false; } -- cgit 1.4.1-2-gfad0 From af244ad3cd19fb50bf9b9855f02d81e61441ab50 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Fri, 7 Feb 2020 01:55:26 -0500 Subject: Add some real line editing operations --- chat.h | 5 +++++ edit.c | 28 ++++++++++++++++++++++++---- ui.c | 12 ++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) (limited to 'edit.c') diff --git a/chat.h b/chat.h index 8279d1b..a327620 100644 --- a/chat.h +++ b/chat.h @@ -133,7 +133,12 @@ void uiFormat( ) __attribute__((format(printf, 4, 5))); enum Edit { + EditHome, + EditEnd, + EditLeft, + EditRight, EditKill, + EditErase, EditInsert, EditEnter, }; diff --git a/edit.c b/edit.c index 68593d1..b6edb98 100644 --- a/edit.c +++ b/edit.c @@ -45,13 +45,33 @@ char *editTail(void) { return mbs; } +static void reserve(size_t index, size_t count) { + if (len + count > Cap) return; + memmove(&buf[index + count], &buf[index], sizeof(*buf) * (len - index)); + len += count; +} + +static void delete(size_t index, size_t count) { + if (index + count > len) return; + memmove( + &buf[index], &buf[index + count], sizeof(*buf) * (len - index - count) + ); + len -= count; +} + void edit(size_t id, enum Edit op, wchar_t ch) { switch (op) { - break; case EditKill: len = pos = 0; + break; case EditHome: pos = 0; + break; case EditEnd: pos = len; + break; case EditLeft: if (pos) pos--; + break; case EditRight: if (pos < len) pos++; + + break; case EditKill: len = pos = 0; + break; case EditErase: if (pos) delete(--pos, 1); + break; case EditInsert: { - if (len == Cap) break; - buf[pos++] = ch; - len++; + reserve(pos, 1); + if (pos < Cap) buf[pos++] = ch; } break; case EditEnter: { pos = 0; diff --git a/ui.c b/ui.c index ffe8748..f73020a 100644 --- a/ui.c +++ b/ui.c @@ -546,12 +546,20 @@ void uiShowNum(size_t num) { } static void keyCode(int code) { + size_t id = windows.active->id; switch (code) { break; case KEY_RESIZE: resize(); break; case KeyFocusIn: unmark(); break; case KeyFocusOut: windows.active->mark = true; break; case KeyPasteOn:; // TODO break; case KeyPasteOff:; // TODO + + break; case KEY_BACKSPACE: edit(id, EditErase, 0); + break; case KEY_END: edit(id, EditEnd, 0); + break; case KEY_ENTER: edit(id, EditEnter, 0); + break; case KEY_HOME: edit(id, EditHome, 0); + break; case KEY_LEFT: edit(id, EditLeft, 0); + break; case KEY_RIGHT: edit(id, EditRight, 0); } } @@ -567,6 +575,10 @@ static void keyMeta(wchar_t ch) { static void keyCtrl(wchar_t ch) { size_t id = windows.active->id; switch (ch) { + break; case L'?': edit(id, EditErase, 0); + break; case L'A': edit(id, EditHome, 0); + break; case L'E': edit(id, EditEnd, 0); + break; case L'H': edit(id, EditErase, 0); break; case L'J': edit(id, EditEnter, 0); break; case L'L': clearok(curscr, true); break; case L'U': edit(id, EditKill, 0); -- cgit 1.4.1-2-gfad0 From fe5fd897052abd1909d1536056936a0417666459 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Fri, 7 Feb 2020 21:30:25 -0500 Subject: Populate completion with commands --- Makefile | 1 + chat.c | 1 + chat.h | 8 +++++ command.c | 6 ++++ complete.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ edit.c | 3 ++ ui.c | 1 + 7 files changed, 130 insertions(+) create mode 100644 complete.c (limited to 'edit.c') diff --git a/Makefile b/Makefile index 5380d20..48aba7b 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,7 @@ LDLIBS = -lcrypto -ltls -lncursesw OBJS += chat.o OBJS += command.o +OBJS += complete.o OBJS += config.o OBJS += edit.o OBJS += handle.o diff --git a/chat.c b/chat.c index c487722..91da6e3 100644 --- a/chat.c +++ b/chat.c @@ -110,6 +110,7 @@ int main(int argc, char *argv[]) { set(&self.network, host); set(&self.chanTypes, "#&"); set(&self.prefixes, "@+"); + commandComplete(); FILE *certFile = NULL; FILE *privFile = NULL; diff --git a/chat.h b/chat.h index a327620..f164e7a 100644 --- a/chat.h +++ b/chat.h @@ -118,6 +118,7 @@ void command(size_t id, char *input); const char *commandIsPrivmsg(size_t id, const char *input); const char *commandIsNotice(size_t id, const char *input); const char *commandIsAction(size_t id, const char *input); +void commandComplete(void); enum Heat { Cold, Warm, Hot }; void uiInit(void); @@ -140,12 +141,19 @@ enum Edit { EditKill, EditErase, EditInsert, + EditComplete, EditEnter, }; void edit(size_t id, enum Edit op, wchar_t ch); char *editHead(void); char *editTail(void); +const char *complete(size_t id, const char *prefix); +void completeAccept(void); +void completeReject(void); +void completeAdd(size_t id, const char *str, enum Color color); +void completeTouch(size_t id, const char *str, enum Color color); + FILE *configOpen(const char *path, const char *mode); int getopt_config( int argc, char *const *argv, diff --git a/command.c b/command.c index 3215322..41aacc9 100644 --- a/command.c +++ b/command.c @@ -136,3 +136,9 @@ void command(size_t id, char *input) { } } } + +void commandComplete(void) { + for (size_t i = 0; i < ARRAY_LEN(Commands); ++i) { + completeAdd(None, Commands[i].cmd, Default); + } +} diff --git a/complete.c b/complete.c new file mode 100644 index 0000000..b8f2dfc --- /dev/null +++ b/complete.c @@ -0,0 +1,110 @@ +/* Copyright (C) 2020 C. McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "chat.h" + +struct Node { + size_t id; + char *str; + enum Color color; + struct Node *prev; + struct Node *next; +}; + +static struct Node *alloc(size_t id, const char *str, enum Color color) { + struct Node *node = malloc(sizeof(*node)); + if (!node) err(EX_OSERR, "malloc"); + node->id = id; + node->str = strdup(str); + if (!node->str) err(EX_OSERR, "strdup"); + node->color = color; + node->prev = NULL; + node->next = NULL; + return node; +} + +static struct Node *head; +static struct Node *tail; + +static struct Node *detach(struct Node *node) { + if (node->prev) node->prev->next = node->next; + if (node->next) node->next->prev = node->prev; + if (head == node) head = node->next; + if (tail == node) tail = node->prev; + node->prev = NULL; + node->next = NULL; + return node; +} + +static struct Node *prepend(struct Node *node) { + node->prev = NULL; + node->next = head; + if (head) head->prev = node; + head = node; + if (!tail) tail = node; + return node; +} + +static struct Node *append(struct Node *node) { + node->next = NULL; + node->prev = tail; + if (tail) tail->next = node; + tail = node; + if (!head) head = node; + return node; +} + +static struct Node *find(size_t id, const char *str) { + for (struct Node *node = head; node; node = node->next) { + if (node->id == id && !strcmp(node->str, str)) return node; + } + return NULL; +} + +void completeAdd(size_t id, const char *str, enum Color color) { + if (!find(id, str)) append(alloc(id, str, color)); +} + +void completeTouch(size_t id, const char *str, enum Color color) { + struct Node *node = find(id, str); + prepend(node ? detach(node) : alloc(id, str, color)); +} + +static struct Node *match; + +const char *complete(size_t id, const char *prefix) { + for (match = (match ? match->next : head); match; match = match->next) { + if (match->id && match->id != id) continue; + if (strncasecmp(match->str, prefix, strlen(prefix))) continue; + return match->str; + } + return NULL; +} + +void completeAccept(void) { + if (match) prepend(detach(match)); + match = NULL; +} + +void completeReject(void) { + match = NULL; +} diff --git a/edit.c b/edit.c index b6edb98..0c50f33 100644 --- a/edit.c +++ b/edit.c @@ -73,6 +73,9 @@ void edit(size_t id, enum Edit op, wchar_t ch) { reserve(pos, 1); if (pos < Cap) buf[pos++] = ch; } + break; case EditComplete: { + // TODO + } break; case EditEnter: { pos = 0; command(id, editTail()); diff --git a/ui.c b/ui.c index 147381e..5a8f155 100644 --- a/ui.c +++ b/ui.c @@ -596,6 +596,7 @@ static void keyCtrl(wchar_t ch) { break; case L'A': edit(id, EditHome, 0); break; case L'E': edit(id, EditEnd, 0); break; case L'H': edit(id, EditErase, 0); + break; case L'I': edit(id, EditComplete, 0); break; case L'J': edit(id, EditEnter, 0); break; case L'L': clearok(curscr, true); break; case L'U': edit(id, EditKill, 0); -- cgit 1.4.1-2-gfad0 From 2d62ea9e30e7249e7f3e5bc3e60f5c8e97b3e2cc Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 01:28:24 -0500 Subject: Simplify edit buffer conversion and input rendering --- chat.h | 3 +-- edit.c | 25 +++++++++++----------- ui.c | 76 ++++++++++++++++++++++++++++++++++++------------------------------ 3 files changed, 55 insertions(+), 49 deletions(-) (limited to 'edit.c') diff --git a/chat.h b/chat.h index 896549e..914fde6 100644 --- a/chat.h +++ b/chat.h @@ -156,8 +156,7 @@ enum Edit { EditEnter, }; void edit(size_t id, enum Edit op, wchar_t ch); -char *editHead(void); -char *editTail(void); +char *editBuffer(size_t *pos); const char *complete(size_t id, const char *prefix); void completeAccept(void); diff --git a/edit.c b/edit.c index 0c50f33..38dadcd 100644 --- a/edit.c +++ b/edit.c @@ -27,21 +27,22 @@ static wchar_t buf[Cap]; static size_t len; static size_t pos; -char *editHead(void) { +char *editBuffer(size_t *mbsPos) { static char mbs[MB_LEN_MAX * Cap]; + const wchar_t *ptr = buf; - size_t n = wcsnrtombs(mbs, &ptr, pos, sizeof(mbs) - 1, NULL); - assert(n != (size_t)-1); - mbs[n] = '\0'; - return mbs; -} + size_t mbsLen = wcsnrtombs(mbs, &ptr, pos, sizeof(mbs) - 1, NULL); + assert(mbsLen != (size_t)-1); + if (mbsPos) *mbsPos = mbsLen; -char *editTail(void) { - static char mbs[MB_LEN_MAX * Cap]; - const wchar_t *ptr = &buf[pos]; - size_t n = wcsnrtombs(mbs, &ptr, len - pos, sizeof(mbs) - 1, NULL); + ptr = &buf[pos]; + size_t n = wcsnrtombs( + &mbs[mbsLen], &ptr, len - pos, sizeof(mbs) - mbsLen - 1, NULL + ); assert(n != (size_t)-1); - mbs[n] = '\0'; + mbsLen += n; + + mbs[mbsLen] = '\0'; return mbs; } @@ -78,7 +79,7 @@ void edit(size_t id, enum Edit op, wchar_t ch) { } break; case EditEnter: { pos = 0; - command(id, editTail()); + command(id, editBuffer(NULL)); len = 0; } } diff --git a/ui.c b/ui.c index 23bf929..c342339 100644 --- a/ui.c +++ b/ui.c @@ -507,48 +507,54 @@ static void inputAdd(struct Style *style, const char *str) { static void inputUpdate(void) { size_t id = windows.active->id; - const char *nick = self.nick; - const char *head = editHead(); + size_t pos; + char *buf = editBuffer(&pos); + const char *skip = NULL; - const char *pre = ""; - const char *suf = " "; - struct Style style = { .fg = self.color, .bg = Default }; - struct Style reset = Reset; - if (NULL != (skip = commandIsPrivmsg(id, head))) { - pre = "<"; - suf = "> "; - } else if (NULL != (skip = commandIsNotice(id, head))) { - pre = "-"; - suf = "- "; - reset.fg = LightGray; - } else if (NULL != (skip = commandIsAction(id, head))) { - style.attr |= A_ITALIC; - pre = "* "; - reset.attr |= A_ITALIC; + struct Style init = { .fg = self.color, .bg = Default }; + struct Style rest = Reset; + const char *prefix = ""; + const char *prompt = (self.nick ? self.nick : ""); + const char *suffix = ""; + if (NULL != (skip = commandIsPrivmsg(id, buf))) { + prefix = "<"; suffix = "> "; + } else if (NULL != (skip = commandIsNotice(id, buf))) { + prefix = "-"; suffix = "- "; + rest.fg = LightGray; + } else if (NULL != (skip = commandIsAction(id, buf))) { + init.attr |= A_ITALIC; + prefix = "* "; suffix = " "; + rest.attr |= A_ITALIC; } else if (id == Debug) { - skip = head; - style.fg = Gray; - pre = "<<"; - nick = NULL; + skip = buf; + init.fg = Gray; + prompt = "<< "; + } else { + prompt = ""; + } + if (skip && skip > &buf[pos]) { + skip = NULL; + prefix = prompt = suffix = ""; } int y, x; wmove(input, 0, 0); - if (skip) { - wattr_set( - input, - style.attr | colorAttr(mapColor(style.fg)), - colorPair(mapColor(style.fg), mapColor(style.bg)), - NULL - ); - waddstr(input, pre); - if (nick) waddstr(input, nick); - waddstr(input, suf); - } - style = reset; - inputAdd(&style, (skip ? skip : head)); + wattr_set( + input, + init.attr | colorAttr(mapColor(init.fg)), + colorPair(mapColor(init.fg), mapColor(init.bg)), + NULL + ); + waddstr(input, prefix); + waddstr(input, prompt); + waddstr(input, suffix); + struct Style style = rest; + char p = buf[pos]; + buf[pos] = '\0'; + inputAdd(&style, (skip ? skip : buf)); getyx(input, y, x); - inputAdd(&style, editTail()); + buf[pos] = p; + inputAdd(&style, &buf[pos]); wclrtoeol(input); wmove(input, y, x); } -- cgit 1.4.1-2-gfad0 From 40e362f505b2b48656791b25193d67ff4af9a11b Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 02:33:53 -0500 Subject: Hook up tab-complete --- edit.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 3 deletions(-) (limited to 'edit.c') diff --git a/edit.c b/edit.c index 38dadcd..f058f0a 100644 --- a/edit.c +++ b/edit.c @@ -60,7 +60,78 @@ static void delete(size_t index, size_t count) { len -= count; } +static struct { + size_t pos; + size_t pre; + size_t len; +} tab; + +static void tabComplete(size_t id) { + if (!tab.len) { + tab.pos = pos; + while (tab.pos && buf[tab.pos - 1] != L' ') tab.pos--; + if (tab.pos == pos) return; + tab.pre = pos - tab.pos; + tab.len = tab.pre; + } + + char mbs[MB_LEN_MAX * tab.pre + 1]; + const wchar_t *ptr = &buf[tab.pos]; + size_t n = wcsnrtombs(mbs, &ptr, tab.pre, sizeof(mbs) - 1, NULL); + assert(n != (size_t)-1); + mbs[n] = '\0'; + + const char *comp = complete(id, mbs); + if (!comp) comp = complete(id, mbs); + if (!comp) { + tab.len = 0; + return; + } + + wchar_t wcs[strlen(comp) + 1]; + n = mbstowcs(wcs, comp, sizeof(wcs)); + assert(n != (size_t)-1); + if (tab.pos + n + 2 > Cap) { + completeReject(); + tab.len = 0; + return; + } + + delete(tab.pos, tab.len); + if (wcs[0] != L'/' && !tab.pos) { + tab.len = n + 2; + reserve(tab.pos, tab.len); + buf[tab.pos + n + 0] = L':'; + buf[tab.pos + n + 1] = L' '; + } else if ( + tab.pos >= 2 && (buf[tab.pos - 2] == L':' || buf[tab.pos - 2] == L',') + ) { + tab.len = n + 2; + reserve(tab.pos, tab.len); + buf[tab.pos - 2] = L','; + buf[tab.pos + n + 0] = L':'; + buf[tab.pos + n + 1] = L' '; + } else { + tab.len = n + 1; + reserve(tab.pos, tab.len); + buf[tab.pos + n] = L' '; + } + memcpy(&buf[tab.pos], wcs, sizeof(*wcs) * n); + pos = tab.pos + tab.len; +} + +static void tabAccept(void) { + completeAccept(); + tab.len = 0; +} + +static void tabReject(void) { + completeReject(); + tab.len = 0; +} + void edit(size_t id, enum Edit op, wchar_t ch) { + size_t init = pos; switch (op) { break; case EditHome: pos = 0; break; case EditEnd: pos = len; @@ -75,12 +146,20 @@ void edit(size_t id, enum Edit op, wchar_t ch) { if (pos < Cap) buf[pos++] = ch; } break; case EditComplete: { - // TODO + tabComplete(id); + return; } break; case EditEnter: { - pos = 0; + tabAccept(); command(id, editBuffer(NULL)); - len = 0; + len = pos = 0; + return; } } + + if (pos < init) { + tabReject(); + } else { + tabAccept(); + } } -- cgit 1.4.1-2-gfad0 From 282de9af30793f9935fe521ad95ffc253bd4f474 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 03:45:44 -0500 Subject: Add C-d --- chat.h | 1 + edit.c | 5 +++-- ui.c | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'edit.c') diff --git a/chat.h b/chat.h index 914fde6..d6d9e1c 100644 --- a/chat.h +++ b/chat.h @@ -151,6 +151,7 @@ enum Edit { EditRight, EditKill, EditErase, + EditDelete, EditInsert, EditComplete, EditEnter, diff --git a/edit.c b/edit.c index f058f0a..c30e725 100644 --- a/edit.c +++ b/edit.c @@ -138,8 +138,9 @@ void edit(size_t id, enum Edit op, wchar_t ch) { break; case EditLeft: if (pos) pos--; break; case EditRight: if (pos < len) pos++; - break; case EditKill: len = pos = 0; - break; case EditErase: if (pos) delete(--pos, 1); + break; case EditKill: len = pos = 0; + break; case EditErase: if (pos) delete(--pos, 1); + break; case EditDelete: delete(pos, 1); break; case EditInsert: { reserve(pos, 1); diff --git a/ui.c b/ui.c index 4478478..8e502ca 100644 --- a/ui.c +++ b/ui.c @@ -625,6 +625,7 @@ static void keyCode(int code) { break; case KeyMetaM: waddch(windows.active->pad, '\n'); break; case KEY_BACKSPACE: edit(id, EditErase, 0); + break; case KEY_DC: edit(id, EditDelete, 0); break; case KEY_END: edit(id, EditEnd, 0); break; case KEY_ENTER: edit(id, EditEnter, 0); break; case KEY_HOME: edit(id, EditHome, 0); @@ -645,6 +646,7 @@ static void keyCtrl(wchar_t ch) { break; case L'?': edit(id, EditErase, 0); break; case L'A': edit(id, EditHome, 0); break; case L'B': edit(id, EditLeft, 0); + break; case L'D': edit(id, EditDelete, 0); break; case L'E': edit(id, EditEnd, 0); break; case L'F': edit(id, EditRight, 0); break; case L'H': edit(id, EditErase, 0); -- cgit 1.4.1-2-gfad0 From d7c96fc81b71b77b30511d6526fe3acaa84c39ee Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 03:56:18 -0500 Subject: Add C-k Also rename all the edit ops to something consistent. --- catgirl.1 | 6 +++++- chat.h | 15 ++++++++------- edit.c | 17 +++++++++-------- ui.c | 29 +++++++++++++++-------------- 4 files changed, 37 insertions(+), 30 deletions(-) (limited to 'edit.c') diff --git a/catgirl.1 b/catgirl.1 index f68e6c3..a356fe0 100644 --- a/catgirl.1 +++ b/catgirl.1 @@ -201,12 +201,16 @@ Switch to window by number. Move to beginning of line. .It Ic C-b Move left. +.It Ic C-d +Delete next character. .It Ic C-e Move to end of line. .It Ic C-f Move right. +.It Ic C-k +Delete to end of line. .It Ic C-u -Delete line. +Delete to beginning of line. .El . .Ss Window Keys diff --git a/chat.h b/chat.h index d6d9e1c..aa1bcc1 100644 --- a/chat.h +++ b/chat.h @@ -145,13 +145,14 @@ void uiFormat( ) __attribute__((format(printf, 4, 5))); enum Edit { - EditHome, - EditEnd, - EditLeft, - EditRight, - EditKill, - EditErase, - EditDelete, + EditHead, + EditTail, + EditPrev, + EditNext, + EditKillPrev, + EditKillNext, + EditDeletePrev, + EditDeleteNext, EditInsert, EditComplete, EditEnter, diff --git a/edit.c b/edit.c index c30e725..7fcff40 100644 --- a/edit.c +++ b/edit.c @@ -133,14 +133,15 @@ static void tabReject(void) { void edit(size_t id, enum Edit op, wchar_t ch) { size_t init = pos; switch (op) { - break; case EditHome: pos = 0; - break; case EditEnd: pos = len; - break; case EditLeft: if (pos) pos--; - break; case EditRight: if (pos < len) pos++; - - break; case EditKill: len = pos = 0; - break; case EditErase: if (pos) delete(--pos, 1); - break; case EditDelete: delete(pos, 1); + break; case EditHead: pos = 0; + break; case EditTail: pos = len; + break; case EditPrev: if (pos) pos--; + break; case EditNext: if (pos < len) pos++; + + break; case EditDeletePrev: if (pos) delete(--pos, 1); + break; case EditDeleteNext: delete(pos, 1); + break; case EditKillPrev: delete(0, pos); pos = 0; + break; case EditKillNext: delete(pos, len - pos); break; case EditInsert: { reserve(pos, 1); diff --git a/ui.c b/ui.c index 8e502ca..d83a1f3 100644 --- a/ui.c +++ b/ui.c @@ -624,13 +624,13 @@ static void keyCode(int code) { break; case KeyMetaM: waddch(windows.active->pad, '\n'); - break; case KEY_BACKSPACE: edit(id, EditErase, 0); - break; case KEY_DC: edit(id, EditDelete, 0); - break; case KEY_END: edit(id, EditEnd, 0); + break; case KEY_BACKSPACE: edit(id, EditDeletePrev, 0); + break; case KEY_DC: edit(id, EditDeleteNext, 0); + break; case KEY_END: edit(id, EditTail, 0); break; case KEY_ENTER: edit(id, EditEnter, 0); - break; case KEY_HOME: edit(id, EditHome, 0); - break; case KEY_LEFT: edit(id, EditLeft, 0); - break; case KEY_RIGHT: edit(id, EditRight, 0); + break; case KEY_HOME: edit(id, EditHead, 0); + break; case KEY_LEFT: edit(id, EditPrev, 0); + break; case KEY_RIGHT: edit(id, EditNext, 0); break; default: { if (code >= KeyMeta0 && code <= KeyMeta9) { @@ -643,17 +643,18 @@ static void keyCode(int code) { static void keyCtrl(wchar_t ch) { size_t id = windows.active->id; switch (ch ^ L'@') { - break; case L'?': edit(id, EditErase, 0); - break; case L'A': edit(id, EditHome, 0); - break; case L'B': edit(id, EditLeft, 0); - break; case L'D': edit(id, EditDelete, 0); - break; case L'E': edit(id, EditEnd, 0); - break; case L'F': edit(id, EditRight, 0); - break; case L'H': edit(id, EditErase, 0); + break; case L'?': edit(id, EditDeletePrev, 0); + break; case L'A': edit(id, EditHead, 0); + break; case L'B': edit(id, EditPrev, 0); + break; case L'D': edit(id, EditDeleteNext, 0); + break; case L'E': edit(id, EditTail, 0); + break; case L'F': edit(id, EditNext, 0); + break; case L'H': edit(id, EditDeletePrev, 0); break; case L'I': edit(id, EditComplete, 0); break; case L'J': edit(id, EditEnter, 0); + break; case L'K': edit(id, EditKillNext, 0); break; case L'L': clearok(curscr, true); - break; case L'U': edit(id, EditKill, 0); + break; case L'U': edit(id, EditKillPrev, 0); } } -- cgit 1.4.1-2-gfad0 From b08c2d03efa08bd319a0665d12bef34df08ab283 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 04:20:07 -0500 Subject: Add M-b and M-f --- catgirl.1 | 4 ++++ chat.h | 2 ++ edit.c | 8 ++++++++ ui.c | 4 ++++ 4 files changed, 18 insertions(+) (limited to 'edit.c') diff --git a/catgirl.1 b/catgirl.1 index a356fe0..9cb208e 100644 --- a/catgirl.1 +++ b/catgirl.1 @@ -211,6 +211,10 @@ Move right. Delete to end of line. .It Ic C-u Delete to beginning of line. +.It Ic M-b +Move to previous word. +.It Ic M-f +Move to next word. .El . .Ss Window Keys diff --git a/chat.h b/chat.h index aa1bcc1..6b68eae 100644 --- a/chat.h +++ b/chat.h @@ -149,6 +149,8 @@ enum Edit { EditTail, EditPrev, EditNext, + EditPrevWord, + EditNextWord, EditKillPrev, EditKillNext, EditDeletePrev, diff --git a/edit.c b/edit.c index 7fcff40..38b2dea 100644 --- a/edit.c +++ b/edit.c @@ -137,6 +137,14 @@ void edit(size_t id, enum Edit op, wchar_t ch) { break; case EditTail: pos = len; break; case EditPrev: if (pos) pos--; break; case EditNext: if (pos < len) pos++; + break; case EditPrevWord: { + if (pos) pos--; + while (pos && buf[pos - 1] != L' ') pos--; + } + break; case EditNextWord: { + if (pos < len) pos++; + while (pos < len && buf[pos] != L' ') pos++; + } break; case EditDeletePrev: if (pos) delete(--pos, 1); break; case EditDeleteNext: delete(pos, 1); diff --git a/ui.c b/ui.c index d83a1f3..7811e88 100644 --- a/ui.c +++ b/ui.c @@ -192,6 +192,8 @@ static void errExit(void) { X(KeyMeta7, "\0337") \ X(KeyMeta8, "\0338") \ X(KeyMeta9, "\0339") \ + X(KeyMetaB, "\033b") \ + X(KeyMetaF, "\033f") \ X(KeyMetaM, "\33m") \ X(KeyFocusIn, "\33[I") \ X(KeyFocusOut, "\33[O") \ @@ -622,6 +624,8 @@ static void keyCode(int code) { break; case KeyPasteOn:; // TODO break; case KeyPasteOff:; // TODO + break; case KeyMetaB: edit(id, EditPrevWord, 0); + break; case KeyMetaF: edit(id, EditNextWord, 0); break; case KeyMetaM: waddch(windows.active->pad, '\n'); break; case KEY_BACKSPACE: edit(id, EditDeletePrev, 0); -- cgit 1.4.1-2-gfad0 From 3cd830681e25022a8a3936ca9fe58d149fbe493a Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 04:22:41 -0500 Subject: Rename kill ops --- chat.h | 4 ++-- edit.c | 4 ++-- ui.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'edit.c') diff --git a/chat.h b/chat.h index 6b68eae..ac56f51 100644 --- a/chat.h +++ b/chat.h @@ -151,8 +151,8 @@ enum Edit { EditNext, EditPrevWord, EditNextWord, - EditKillPrev, - EditKillNext, + EditDeleteHead, + EditDeleteTail, EditDeletePrev, EditDeleteNext, EditInsert, diff --git a/edit.c b/edit.c index 38b2dea..7b20079 100644 --- a/edit.c +++ b/edit.c @@ -148,8 +148,8 @@ void edit(size_t id, enum Edit op, wchar_t ch) { break; case EditDeletePrev: if (pos) delete(--pos, 1); break; case EditDeleteNext: delete(pos, 1); - break; case EditKillPrev: delete(0, pos); pos = 0; - break; case EditKillNext: delete(pos, len - pos); + break; case EditDeleteHead: delete(0, pos); pos = 0; + break; case EditDeleteTail: delete(pos, len - pos); break; case EditInsert: { reserve(pos, 1); diff --git a/ui.c b/ui.c index 7811e88..e3b9cb5 100644 --- a/ui.c +++ b/ui.c @@ -656,9 +656,9 @@ static void keyCtrl(wchar_t ch) { break; case L'H': edit(id, EditDeletePrev, 0); break; case L'I': edit(id, EditComplete, 0); break; case L'J': edit(id, EditEnter, 0); - break; case L'K': edit(id, EditKillNext, 0); + break; case L'K': edit(id, EditDeleteTail, 0); break; case L'L': clearok(curscr, true); - break; case L'U': edit(id, EditKillPrev, 0); + break; case L'U': edit(id, EditDeleteHead, 0); } } -- cgit 1.4.1-2-gfad0 From 5e637324c9f2b16a602c1b66081390624598c703 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 04:32:32 -0500 Subject: Add C-w and M-d --- catgirl.1 | 4 ++++ chat.h | 2 ++ edit.c | 17 +++++++++++++++-- ui.c | 7 +++++-- 4 files changed, 26 insertions(+), 4 deletions(-) (limited to 'edit.c') diff --git a/catgirl.1 b/catgirl.1 index 9cb208e..6f8256b 100644 --- a/catgirl.1 +++ b/catgirl.1 @@ -211,8 +211,12 @@ Move right. Delete to end of line. .It Ic C-u Delete to beginning of line. +.It Ic C-w +Delete previous word. .It Ic M-b Move to previous word. +.It Ic M-d +Delete next word. .It Ic M-f Move to next word. .El diff --git a/chat.h b/chat.h index ac56f51..fc18b15 100644 --- a/chat.h +++ b/chat.h @@ -155,6 +155,8 @@ enum Edit { EditDeleteTail, EditDeletePrev, EditDeleteNext, + EditDeletePrevWord, + EditDeleteNextWord, EditInsert, EditComplete, EditEnter, diff --git a/edit.c b/edit.c index 7b20079..47478ec 100644 --- a/edit.c +++ b/edit.c @@ -146,10 +146,23 @@ void edit(size_t id, enum Edit op, wchar_t ch) { while (pos < len && buf[pos] != L' ') pos++; } - break; case EditDeletePrev: if (pos) delete(--pos, 1); - break; case EditDeleteNext: delete(pos, 1); break; case EditDeleteHead: delete(0, pos); pos = 0; break; case EditDeleteTail: delete(pos, len - pos); + break; case EditDeletePrev: if (pos) delete(--pos, 1); + break; case EditDeleteNext: delete(pos, 1); + break; case EditDeletePrevWord: { + if (!pos) break; + size_t word = pos - 1; + while (word && buf[word - 1] != L' ') word--; + delete(word, pos - word); + pos = word; + } + break; case EditDeleteNextWord: { + if (pos == len) break; + size_t word = pos + 1; + while (word < len && buf[word] != L' ') word++; + delete(pos, word - pos); + } break; case EditInsert: { reserve(pos, 1); diff --git a/ui.c b/ui.c index e3b9cb5..65b4760 100644 --- a/ui.c +++ b/ui.c @@ -192,8 +192,9 @@ static void errExit(void) { X(KeyMeta7, "\0337") \ X(KeyMeta8, "\0338") \ X(KeyMeta9, "\0339") \ - X(KeyMetaB, "\033b") \ - X(KeyMetaF, "\033f") \ + X(KeyMetaB, "\33b") \ + X(KeyMetaD, "\33d") \ + X(KeyMetaF, "\33f") \ X(KeyMetaM, "\33m") \ X(KeyFocusIn, "\33[I") \ X(KeyFocusOut, "\33[O") \ @@ -625,6 +626,7 @@ static void keyCode(int code) { break; case KeyPasteOff:; // TODO break; case KeyMetaB: edit(id, EditPrevWord, 0); + break; case KeyMetaD: edit(id, EditDeleteNextWord, 0); break; case KeyMetaF: edit(id, EditNextWord, 0); break; case KeyMetaM: waddch(windows.active->pad, '\n'); @@ -659,6 +661,7 @@ static void keyCtrl(wchar_t ch) { break; case L'K': edit(id, EditDeleteTail, 0); break; case L'L': clearok(curscr, true); break; case L'U': edit(id, EditDeleteHead, 0); + break; case L'W': edit(id, EditDeletePrevWord, 0); } } -- cgit 1.4.1-2-gfad0 From 2aa2005339750e64a587f6117ae21960e975e211 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 07:09:51 -0500 Subject: Add C-y This is weechat's binding for it. --- catgirl.1 | 2 ++ chat.h | 1 + edit.c | 26 ++++++++++++++++++++++---- ui.c | 7 +++++-- 4 files changed, 30 insertions(+), 6 deletions(-) (limited to 'edit.c') diff --git a/catgirl.1 b/catgirl.1 index 752a9d2..2a3828d 100644 --- a/catgirl.1 +++ b/catgirl.1 @@ -226,6 +226,8 @@ Delete to end of line. Delete to beginning of line. .It Ic C-w Delete previous word. +.It Ic C-y +Paste previously deleted text. .It Ic M-b Move to previous word. .It Ic M-d diff --git a/chat.h b/chat.h index fc18b15..24360f0 100644 --- a/chat.h +++ b/chat.h @@ -157,6 +157,7 @@ enum Edit { EditDeleteNext, EditDeletePrevWord, EditDeleteNextWord, + EditPaste, EditInsert, EditComplete, EditEnter, diff --git a/edit.c b/edit.c index 47478ec..16fa910 100644 --- a/edit.c +++ b/edit.c @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -46,14 +47,24 @@ char *editBuffer(size_t *mbsPos) { return mbs; } -static void reserve(size_t index, size_t count) { - if (len + count > Cap) return; +static struct { + wchar_t buf[Cap]; + size_t len; +} cut; + +static bool reserve(size_t index, size_t count) { + if (len + count > Cap) return false; memmove(&buf[index + count], &buf[index], sizeof(*buf) * (len - index)); len += count; + return true; } static void delete(size_t index, size_t count) { if (index + count > len) return; + if (count > 1) { + memcpy(cut.buf, &buf[index], sizeof(*buf) * count); + cut.len = count; + } memmove( &buf[index], &buf[index + count], sizeof(*buf) * (len - index - count) ); @@ -163,10 +174,17 @@ void edit(size_t id, enum Edit op, wchar_t ch) { while (word < len && buf[word] != L' ') word++; delete(pos, word - pos); } + break; case EditPaste: { + if (reserve(pos, cut.len)) { + memcpy(&buf[pos], cut.buf, sizeof(*buf) * cut.len); + pos += cut.len; + } + } break; case EditInsert: { - reserve(pos, 1); - if (pos < Cap) buf[pos++] = ch; + if (reserve(pos, 1)) { + buf[pos++] = ch; + } } break; case EditComplete: { tabComplete(id); diff --git a/ui.c b/ui.c index 65b4760..d946854 100644 --- a/ui.c +++ b/ui.c @@ -166,12 +166,14 @@ void uiHide(void) { endwin(); } -static void disableFlowControl(void) { +// Gain use of C-q, C-s, C-z, C-y, C-o. +static void acquireKeys(void) { struct termios term; int error = tcgetattr(STDOUT_FILENO, &term); if (error) err(EX_OSERR, "tcgetattr"); term.c_iflag &= ~IXON; term.c_cc[VSUSP] = _POSIX_VDISABLE; + term.c_cc[VDSUSP] = _POSIX_VDISABLE; term.c_cc[VDISCARD] = _POSIX_VDISABLE; error = tcsetattr(STDOUT_FILENO, TCSADRAIN, &term); if (error) err(EX_OSERR, "tcsetattr"); @@ -212,7 +214,7 @@ void uiInit(void) { initscr(); cbreak(); noecho(); - disableFlowControl(); + acquireKeys(); def_prog_mode(); atexit(errExit); colorInit(); @@ -662,6 +664,7 @@ static void keyCtrl(wchar_t ch) { break; case L'L': clearok(curscr, true); break; case L'U': edit(id, EditDeleteHead, 0); break; case L'W': edit(id, EditDeletePrevWord, 0); + break; case L'Y': edit(id, EditPaste, 0); } } -- cgit 1.4.1-2-gfad0 From 02ca8e972bbf1b3a5b66df9ba0bd0e77903ba6d8 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 07:32:35 -0500 Subject: Use iswspace for word movement --- edit.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'edit.c') diff --git a/edit.c b/edit.c index 16fa910..fe79e76 100644 --- a/edit.c +++ b/edit.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "chat.h" @@ -150,11 +151,11 @@ void edit(size_t id, enum Edit op, wchar_t ch) { break; case EditNext: if (pos < len) pos++; break; case EditPrevWord: { if (pos) pos--; - while (pos && buf[pos - 1] != L' ') pos--; + while (pos && !iswspace(buf[pos - 1])) pos--; } break; case EditNextWord: { if (pos < len) pos++; - while (pos < len && buf[pos] != L' ') pos++; + while (pos < len && !iswspace(buf[pos])) pos++; } break; case EditDeleteHead: delete(0, pos); pos = 0; @@ -164,14 +165,14 @@ void edit(size_t id, enum Edit op, wchar_t ch) { break; case EditDeletePrevWord: { if (!pos) break; size_t word = pos - 1; - while (word && buf[word - 1] != L' ') word--; + while (word && !iswspace(buf[word - 1])) word--; delete(word, pos - word); pos = word; } break; case EditDeleteNextWord: { if (pos == len) break; size_t word = pos + 1; - while (word < len && buf[word] != L' ') word++; + while (word < len && !iswspace(buf[word])) word++; delete(pos, word - pos); } break; case EditPaste: { -- cgit 1.4.1-2-gfad0 From 0dd8ac36f4f02a9c50fe4919719ca02955e61782 Mon Sep 17 00:00:00 2001 From: C. McEnroe Date: Sun, 9 Feb 2020 14:17:04 -0500 Subject: Avoid VLAs in tab complete --- edit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'edit.c') diff --git a/edit.c b/edit.c index fe79e76..d90d558 100644 --- a/edit.c +++ b/edit.c @@ -87,7 +87,7 @@ static void tabComplete(size_t id) { tab.len = tab.pre; } - char mbs[MB_LEN_MAX * tab.pre + 1]; + char mbs[MB_LEN_MAX * Cap]; const wchar_t *ptr = &buf[tab.pos]; size_t n = wcsnrtombs(mbs, &ptr, tab.pre, sizeof(mbs) - 1, NULL); assert(n != (size_t)-1); @@ -100,7 +100,7 @@ static void tabComplete(size_t id) { return; } - wchar_t wcs[strlen(comp) + 1]; + wchar_t wcs[Cap]; n = mbstowcs(wcs, comp, sizeof(wcs)); assert(n != (size_t)-1); if (tab.pos + n + 2 > Cap) { -- cgit 1.4.1-2-gfad0