diff --git a/Makefile b/Makefile index 83ead95..9e6392b 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ all: catgirl catgirl: ${OBJS} ${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@ -${OBJS}: chat.h +${OBJS}: chat.h edit.h tags: *.[ch] ctags -w *.[ch] diff --git a/chat.c b/chat.c index 4898411..454ae31 100644 --- a/chat.c +++ b/chat.c @@ -371,7 +371,6 @@ int main(int argc, char *argv[]) { set(&network.name, host); set(&self.nick, "*"); - editCompleteAdd(); commandCompleteAdd(); ircConfig(insecure, trust, cert, priv); diff --git a/chat.h b/chat.h index 753d1a3..8a9a48f 100644 --- a/chat.h +++ b/chat.h @@ -341,31 +341,6 @@ int bufferReflow( struct Buffer *buffer, int cols, enum Heat thresh, size_t tail ); -enum Edit { - EditHead, - EditTail, - EditPrev, - EditNext, - EditPrevWord, - EditNextWord, - EditDeleteHead, - EditDeleteTail, - EditDeletePrev, - EditDeleteNext, - EditDeletePrevWord, - EditDeleteNextWord, - EditPaste, - EditTranspose, - EditCollapse, - EditInsert, - EditComplete, - EditExpand, - EditEnter, -}; -void edit(uint id, enum Edit op, wchar_t ch); -char *editBuffer(size_t *pos); -void editCompleteAdd(void); - const char *complete(uint id, const char *prefix); const char *completeSubstr(uint id, const char *substr); void completeAccept(void); diff --git a/edit.c b/edit.c index 66b5302..21061d7 100644 --- a/edit.c +++ b/edit.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 C. McEnroe +/* Copyright (C) 2020, 2022 June 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 @@ -13,8 +13,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * Additional permission under GNU GPL version 3 section 7: - * * If you modify this Program, or any covered work, by linking or * combining it with OpenSSL (or a modified version of that library), * containing parts covered by the terms of the OpenSSL License and the @@ -25,282 +23,272 @@ * covered work. */ -#include +#include #include #include -#include #include #include #include -#include "chat.h" - -enum { Cap = 1024 }; -static wchar_t buf[Cap]; -static size_t len; -static size_t pos; - -char *editBuffer(size_t *mbsPos) { - static char mbs[MB_LEN_MAX * Cap]; - - const wchar_t *ptr = buf; - size_t mbsLen = wcsnrtombs(mbs, &ptr, pos, sizeof(mbs) - 1, NULL); - assert(mbsLen != (size_t)-1); - if (mbsPos) *mbsPos = mbsLen; - - ptr = &buf[pos]; - size_t n = wcsnrtombs( - &mbs[mbsLen], &ptr, len - pos, sizeof(mbs) - mbsLen - 1, NULL - ); - assert(n != (size_t)-1); - mbsLen += n; - - mbs[mbsLen] = '\0'; - return mbs; -} - -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; - wmemmove(&buf[index + count], &buf[index], len - index); - len += count; - return true; -} - -static void delete(bool copy, size_t index, size_t count) { - if (index + count > len) return; - if (copy) { - wmemcpy(cut.buf, &buf[index], count); - cut.len = count; - } - wmemmove(&buf[index], &buf[index + count], len - index - count); - len -= count; -} - -static const struct { - const wchar_t *name; - const wchar_t *string; -} Macros[] = { - { L"\\banhammer", L"▬▬▬▬▬▬▬▋ Ò╭╮Ó" }, - { L"\\bear", L"ʕっ•ᴥ•ʔっ" }, - { L"\\blush", L"(˶′◡‵˶)" }, - { L"\\com", L"\0038,4\2 ☭ " }, - { L"\\cool", L"(⌐■_■)" }, - { L"\\flip", L"(╯°□°)╯︵ ┻━┻" }, - { L"\\gary", L"ᕕ( ᐛ )ᕗ" }, - { L"\\hug", L"(っ・∀・)っ" }, - { L"\\lenny", L"( ͡° ͜ʖ ͡°)" }, - { L"\\look", L"ಠ_ಠ" }, - { L"\\shrug", L"¯\\_(ツ)_/¯" }, - { L"\\unflip", L"┬─┬ノ(º_ºノ)" }, - { L"\\wave", L"ヾ(^∇^)" }, -}; - -void editCompleteAdd(void) { - char mbs[256]; - for (size_t i = 0; i < ARRAY_LEN(Macros); ++i) { - size_t n = wcstombs(mbs, Macros[i].name, sizeof(mbs)); - assert(n != (size_t)-1); - completeAdd(None, mbs, Default); - } -} - -static void macroExpand(void) { - size_t macro = pos; - while (macro && buf[macro] != L'\\') macro--; - if (macro == pos) return; - for (size_t i = 0; i < ARRAY_LEN(Macros); ++i) { - if (wcsncmp(Macros[i].name, &buf[macro], pos - macro)) continue; - if (wcstombs(NULL, Macros[i].string, 0) == (size_t)-1) continue; - delete(false, macro, pos - macro); - pos = macro; - size_t expand = wcslen(Macros[i].string); - if (reserve(macro, expand)) { - wcsncpy(&buf[macro], Macros[i].string, expand); - pos += expand; - } - } -} - -static struct { - size_t pos; - size_t pre; - size_t len; - bool suffix; -} tab; - -static void tabComplete(uint id) { - if (!tab.len) { - tab.pos = pos; - while (tab.pos && !iswspace(buf[tab.pos - 1])) tab.pos--; - if (tab.pos == pos) return; - tab.pre = pos - tab.pos; - tab.len = tab.pre; - tab.suffix = true; - } - - 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); - mbs[n] = '\0'; - - const char *comp = complete(id, mbs); - if (!comp) { - comp = complete(id, mbs); - tab.suffix ^= true; - } - if (!comp) { - tab.len = 0; - return; - } - - wchar_t wcs[Cap]; - n = mbstowcs(wcs, comp, Cap); - assert(n != (size_t)-1); - if (tab.pos + n + 2 > Cap) { - completeReject(); - tab.len = 0; - return; - } - - bool colon = (tab.len >= 2 && buf[tab.pos + tab.len - 2] == L':'); - - delete(false, tab.pos, tab.len); - tab.len = n; - if (wcs[0] == L'\\' || wcschr(wcs, L' ')) { - reserve(tab.pos, tab.len); - } else if (wcs[0] != L'/' && tab.suffix && (!tab.pos || colon)) { - tab.len += 2; - reserve(tab.pos, tab.len); - buf[tab.pos + n + 0] = L':'; - buf[tab.pos + n + 1] = L' '; - } else if (tab.suffix && tab.pos >= 2 && buf[tab.pos - 2] == L':') { - tab.len += 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++; - reserve(tab.pos, tab.len); - if (!tab.suffix && tab.pos >= 2 && buf[tab.pos - 2] == L',') { - buf[tab.pos - 2] = L':'; - } - buf[tab.pos + n] = L' '; - } - wmemcpy(&buf[tab.pos], wcs, n); - pos = tab.pos + tab.len; -} - -static void tabAccept(void) { - completeAccept(); - tab.len = 0; -} - -static void tabReject(void) { - completeReject(); - tab.len = 0; -} +#include "edit.h" static bool isword(wchar_t ch) { return !iswspace(ch) && !iswpunct(ch); } -void edit(uint id, enum Edit op, wchar_t ch) { - size_t init = pos; - switch (op) { - break; case EditHead: pos = 0; - break; case EditTail: pos = len; - break; case EditPrev: if (pos) pos--; - break; case EditNext: if (pos < len) pos++; +void editFree(struct Edit *e) { + free(e->buf); + free(e->cut.buf); + free(e->mbs.buf); + e->pos = e->len = e->cap = 0; + e->cut.len = 0; + e->mbs.pos = e->mbs.len = 0; +} + +char *editString(struct Edit *e) { + size_t cap = e->len * MB_CUR_MAX + 1; + char *buf = realloc(e->mbs.buf, cap); + if (!buf) return NULL; + e->mbs.buf = buf; + + const wchar_t *ptr = e->buf; + e->mbs.len = wcsnrtombs(e->mbs.buf, &ptr, e->pos, cap-1, NULL); + if (e->mbs.len == (size_t)-1) return NULL; + e->mbs.pos = e->mbs.len; + + ptr = &e->buf[e->pos]; + size_t n = wcsnrtombs( + &e->mbs.buf[e->mbs.len], &ptr, e->len - e->pos, + cap-1 - e->mbs.len, NULL + ); + if (n == (size_t)-1) return NULL; + e->mbs.len += n; + + e->mbs.buf[e->mbs.len] = '\0'; + return e->mbs.buf; +} + +int editReserve(struct Edit *e, size_t index, size_t count) { + if (index > e->len) { + errno = EINVAL; + return -1; + } + if (e->len + count > e->cap) { + size_t cap = (e->cap ? e->cap * 2 : 256); + wchar_t *buf = realloc(e->buf, sizeof(*buf) * cap); + if (!buf) return -1; + e->buf = buf; + e->cap = cap; + } + wmemmove(&e->buf[index + count], &e->buf[index], e->len - index); + e->len += count; + return 0; +} + +int editCopy(struct Edit *e, size_t index, size_t count) { + if (index + count > e->len) { + errno = EINVAL; + return -1; + } + wchar_t *buf = realloc(e->cut.buf, sizeof(*buf) * count); + if (!buf) return -1; + e->cut.buf = buf; + wmemcpy(e->cut.buf, &e->buf[index], count); + e->cut.len = count; + return 0; +} + +int editDelete(struct Edit *e, bool cut, size_t index, size_t count) { + if (index + count > e->len) { + errno = EINVAL; + return -1; + } + if (cut && editCopy(e, index, count) < 0) return -1; + wmemmove(&e->buf[index], &e->buf[index + count], e->len - index - count); + e->len -= count; + if (e->pos > e->len) e->pos = e->len; + return 0; +} + +int editFn(struct Edit *e, enum EditFn fn) { + int ret = 0; + switch (fn) { + break; case EditHead: e->pos = 0; + break; case EditTail: e->pos = e->len; + break; case EditPrev: if (e->pos) e->pos--; + break; case EditNext: if (e->pos < e->len) e->pos++; break; case EditPrevWord: { - while (pos && !isword(buf[pos - 1])) pos--; - while (pos && isword(buf[pos - 1])) pos--; + while (e->pos && !isword(e->buf[e->pos-1])) e->pos--; + while (e->pos && isword(e->buf[e->pos-1])) e->pos--; } break; case EditNextWord: { - while (pos < len && isword(buf[pos])) pos++; - while (pos < len && !isword(buf[pos])) pos++; + while (e->pos < e->len && isword(e->buf[e->pos])) e->pos++; + while (e->pos < e->len && !isword(e->buf[e->pos])) e->pos++; } - break; case EditDeleteHead: delete(true, 0, pos); pos = 0; - break; case EditDeleteTail: delete(true, pos, len - pos); - break; case EditDeletePrev: if (pos) delete(false, --pos, 1); - break; case EditDeleteNext: delete(false, pos, 1); + break; case EditDeleteHead: { + ret = editDelete(e, true, 0, e->pos); + e->pos = 0; + } + break; case EditDeleteTail: { + ret = editDelete(e, true, e->pos, e->len - e->pos); + } + break; case EditDeletePrev: { + if (e->pos) editDelete(e, false, --e->pos, 1); + } + break; case EditDeleteNext: { + editDelete(e, false, e->pos, 1); + } break; case EditDeletePrevWord: { - if (!pos) break; - size_t word = pos; - while (word && !isword(buf[word - 1])) word--; - while (word && isword(buf[word - 1])) word--; - delete(true, word, pos - word); - pos = word; + if (!e->pos) break; + size_t word = e->pos; + while (word && !isword(e->buf[word-1])) word--; + while (word && isword(e->buf[word-1])) word--; + ret = editDelete(e, true, word, e->pos - word); + e->pos = word; } break; case EditDeleteNextWord: { - if (pos == len) break; - size_t word = pos; - while (word < len && !isword(buf[word])) word++; - while (word < len && isword(buf[word])) word++; - delete(true, pos, word - pos); - } - break; case EditPaste: { - if (reserve(pos, cut.len)) { - wmemcpy(&buf[pos], cut.buf, cut.len); - pos += cut.len; - } + if (e->pos == e->len) break; + size_t word = e->pos; + while (word < e->len && !isword(e->buf[word])) word++; + while (word < e->len && isword(e->buf[word])) word++; + ret = editDelete(e, true, e->pos, word - e->pos); } + break; case EditPaste: { + ret = editReserve(e, e->pos, e->cut.len); + if (ret == 0) { + wmemcpy(&e->buf[e->pos], e->cut.buf, e->cut.len); + e->pos += e->cut.len; + } + } break; case EditTranspose: { - if (!pos || len < 2) break; - if (pos == len) pos--; - wchar_t t = buf[pos - 1]; - buf[pos - 1] = buf[pos]; - buf[pos++] = t; + if (e->len < 2) break; + if (!e->pos) e->pos++; + if (e->pos == e->len) e->pos--; + wchar_t x = e->buf[e->pos-1]; + e->buf[e->pos-1] = e->buf[e->pos]; + e->buf[e->pos++] = x; } break; case EditCollapse: { size_t ws; - for (pos = 0; pos < len;) { - for (; pos < len && !iswspace(buf[pos]); ++pos); - for (ws = pos; ws < len && iswspace(buf[ws]); ++ws); - if (pos && ws < len) { - delete(false, pos, ws - pos - 1); - buf[pos++] = L' '; + for (e->pos = 0; e->pos < e->len;) { + for (; e->pos < e->len && !iswspace(e->buf[e->pos]); ++e->pos); + for (ws = e->pos; ws < e->len && iswspace(e->buf[ws]); ++ws); + if (e->pos && ws < e->len) { + editDelete(e, false, e->pos, ws - e->pos - 1); + e->buf[e->pos++] = L' '; } else { - delete(false, pos, ws - pos); + editDelete(e, false, e->pos, ws - e->pos); } } } - break; case EditInsert: { - char mb[MB_LEN_MAX]; - if (wctomb(mb, ch) < 0) return; - if (reserve(pos, 1)) { - buf[pos++] = ch; - } - } - break; case EditComplete: { - tabComplete(id); - return; - } - break; case EditExpand: { - macroExpand(); - tabAccept(); - return; - } - break; case EditEnter: { - tabAccept(); - command(id, editBuffer(NULL)); - len = pos = 0; - return; - } + break; case EditClear: e->len = e->pos = 0; } + return ret; +} - if (pos < init) { - tabReject(); - } else { - tabAccept(); +int editInsert(struct Edit *e, wchar_t ch) { + char mb[MB_LEN_MAX]; + if (wctomb(mb, ch) < 0) return -1; + if (editReserve(e, e->pos, 1) < 0) return -1; + e->buf[e->pos++] = ch; + return 0; +} + +#ifdef TEST +#undef NDEBUG +#include +#include + +static void fix(struct Edit *e, const char *str) { + editFn(e, EditClear); + for (const char *ch = str; *ch; ++ch) { + editInsert(e, (wchar_t)*ch); } } + +static bool eq(struct Edit *e, const char *str1) { + const char *str2 = &str1[strlen(str1) + 1]; + const char *buf = editString(e); + return e->mbs.pos == strlen(str1) + && !strncmp(buf, str1, e->mbs.pos) + && !strcmp(&buf[e->mbs.pos], str2); +} + +int main(void) { + struct Edit e = { .mode = EditEmacs }; + + fix(&e, "foo bar"); + editFn(&e, EditHead); + assert(eq(&e, "\0foo bar")); + editFn(&e, EditTail); + assert(eq(&e, "foo bar\0")); + editFn(&e, EditPrev); + assert(eq(&e, "foo ba\0r")); + editFn(&e, EditNext); + assert(eq(&e, "foo bar\0")); + + fix(&e, "foo, bar"); + editFn(&e, EditPrevWord); + assert(eq(&e, "foo, \0bar")); + editFn(&e, EditPrevWord); + assert(eq(&e, "\0foo, bar")); + editFn(&e, EditNextWord); + assert(eq(&e, "foo, \0bar")); + editFn(&e, EditNextWord); + assert(eq(&e, "foo, bar\0")); + + fix(&e, "foo bar"); + editFn(&e, EditPrevWord); + editFn(&e, EditDeleteHead); + assert(eq(&e, "\0bar")); + + fix(&e, "foo bar"); + editFn(&e, EditPrevWord); + editFn(&e, EditDeleteTail); + assert(eq(&e, "foo \0")); + + fix(&e, "foo bar"); + editFn(&e, EditDeletePrev); + assert(eq(&e, "foo ba\0")); + editFn(&e, EditHead); + editFn(&e, EditDeleteNext); + assert(eq(&e, "\0oo ba")); + + fix(&e, "foo, bar"); + editFn(&e, EditDeletePrevWord); + assert(eq(&e, "foo, \0")); + editFn(&e, EditDeletePrevWord); + assert(eq(&e, "\0")); + + fix(&e, "foo, bar"); + editFn(&e, EditHead); + editFn(&e, EditDeleteNextWord); + assert(eq(&e, "\0, bar")); + editFn(&e, EditDeleteNextWord); + assert(eq(&e, "\0")); + + fix(&e, "foo bar"); + editFn(&e, EditDeletePrevWord); + editFn(&e, EditPaste); + assert(eq(&e, "foo bar\0")); + editFn(&e, EditPaste); + assert(eq(&e, "foo barbar\0")); + + fix(&e, "bar"); + editFn(&e, EditTranspose); + assert(eq(&e, "bra\0")); + editFn(&e, EditHead); + editFn(&e, EditTranspose); + assert(eq(&e, "rb\0a")); + editFn(&e, EditTranspose); + assert(eq(&e, "rab\0")); + + fix(&e, " foo bar "); + editFn(&e, EditCollapse); + assert(eq(&e, "foo bar\0")); +} + +#endif /* TEST */ diff --git a/edit.h b/edit.h new file mode 100644 index 0000000..57edfc1 --- /dev/null +++ b/edit.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2022 June 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 . + * + * If you modify this Program, or any covered work, by linking or + * combining it with OpenSSL (or a modified version of that library), + * containing parts covered by the terms of the OpenSSL License and the + * original SSLeay license, the licensors of this Program grant you + * additional permission to convey the resulting work. Corresponding + * Source for a non-source form of such a combination shall include the + * source code for the parts of OpenSSL used as well as that of the + * covered work. + */ + +#include +#include + +enum EditMode { + EditEmacs, +}; + +struct Edit { + enum EditMode mode; + wchar_t *buf; + size_t pos; + size_t len; + size_t cap; + struct { + wchar_t *buf; + size_t len; + } cut; + struct { + char *buf; + size_t pos; + size_t len; + } mbs; +}; + +enum EditFn { + EditHead, + EditTail, + EditPrev, + EditNext, + EditPrevWord, + EditNextWord, + EditDeleteHead, + EditDeleteTail, + EditDeletePrev, + EditDeleteNext, + EditDeletePrevWord, + EditDeleteNextWord, + EditPaste, + EditTranspose, + EditCollapse, + EditClear, +}; + +// Perform an editing function. +int editFn(struct Edit *e, enum EditFn fn); + +// Perform a vi-mode editing function. +int editVi(struct Edit *e, wchar_t ch); + +// Insert a character at the cursor. +int editInsert(struct Edit *e, wchar_t ch); + +// Convert the buffer to a multi-byte string stored in e->mbs. +char *editString(struct Edit *e); + +// Free all buffers. +void editFree(struct Edit *e); + +// Reserve a range in the buffer. +int editReserve(struct Edit *e, size_t index, size_t count); + +// Copy a range of the buffer into e->cut. +int editCopy(struct Edit *e, size_t index, size_t count); + +// Delete a range from the buffer. If cut is true, copy the deleted portion. +int editDelete(struct Edit *e, bool cut, size_t index, size_t count); diff --git a/ui.c b/ui.c index 3df0cd6..212d205 100644 --- a/ui.c +++ b/ui.c @@ -54,6 +54,7 @@ #endif #include "chat.h" +#include "edit.h" // Annoying stuff from : #undef lines @@ -752,9 +753,10 @@ static char *inputStop( return stop; } +static struct Edit edit; + static void inputUpdate(void) { - size_t pos; - char *buf = editBuffer(&pos); + char *buf = editString(&edit); struct Window *window = windows.ptrs[windows.show]; const char *prefix = ""; @@ -786,7 +788,7 @@ static void inputUpdate(void) { } else { prompt = ""; } - if (skip > &buf[pos]) { + if (skip > &buf[edit.mbs.pos]) { prefix = prompt = suffix = ""; skip = buf; } @@ -803,10 +805,10 @@ static void inputUpdate(void) { waddstr(input, suffix); getyx(input, y, x); - int posx; + int pos; struct Style style = styleInput; - inputStop(styleInput, &style, skip, &buf[pos]); - getyx(input, y, posx); + inputStop(styleInput, &style, skip, &buf[edit.mbs.pos]); + getyx(input, y, pos); wmove(input, y, x); style = styleInput; @@ -818,7 +820,7 @@ static void inputUpdate(void) { } inputAdd(styleInput, &style, ptr); wclrtoeol(input); - wmove(input, y, posx); + wmove(input, y, pos); } void uiWindows(void) { @@ -965,6 +967,11 @@ static void showAuto(void) { } } +static void inputEnter(uint id) { + command(id, editString(&edit)); + editFn(&edit, EditClear); +} + static void keyCode(int code) { struct Window *window = windows.ptrs[windows.show]; uint id = window->id; @@ -973,7 +980,7 @@ static void keyCode(int code) { break; case KeyFocusIn: unmark(window); break; case KeyFocusOut: mark(window); - break; case KeyMetaEnter: edit(id, EditInsert, L'\n'); + break; case KeyMetaEnter: editInsert(&edit, L'\n'); break; case KeyMetaEqual: window->mute ^= true; statusUpdate(); break; case KeyMetaMinus: incThresh(window, -1); break; case KeyMetaPlus: incThresh(window, +1); @@ -984,32 +991,32 @@ static void keyCode(int code) { break; case KeyMeta0 ... KeyMeta9: uiShowNum(code - KeyMeta0); break; case KeyMetaA: showAuto(); - break; case KeyMetaB: edit(id, EditPrevWord, 0); - break; case KeyMetaD: edit(id, EditDeleteNextWord, 0); - break; case KeyMetaF: edit(id, EditNextWord, 0); + break; case KeyMetaB: editFn(&edit, EditPrevWord); + break; case KeyMetaD: editFn(&edit, EditDeleteNextWord); + break; case KeyMetaF: editFn(&edit, EditNextWord); break; case KeyMetaL: windowList(window); break; case KeyMetaM: uiWrite(id, Warm, NULL, ""); break; case KeyMetaN: scrollHot(window, +1); break; case KeyMetaP: scrollHot(window, -1); - break; case KeyMetaQ: edit(id, EditCollapse, 0); + break; case KeyMetaQ: editFn(&edit, EditCollapse); break; case KeyMetaS: spoilerReveal ^= true; mainUpdate(); break; case KeyMetaT: toggleTime(window); break; case KeyMetaU: scrollTo(window, window->unreadHard); break; case KeyMetaV: scrollPage(window, +1); - break; case KeyCtrlLeft: edit(id, EditPrevWord, 0); - break; case KeyCtrlRight: edit(id, EditNextWord, 0); + break; case KeyCtrlLeft: editFn(&edit, EditPrevWord); + break; case KeyCtrlRight: editFn(&edit, EditNextWord); - break; case KEY_BACKSPACE: edit(id, EditDeletePrev, 0); - break; case KEY_DC: edit(id, EditDeleteNext, 0); + break; case KEY_BACKSPACE: editFn(&edit, EditDeletePrev); + break; case KEY_DC: editFn(&edit, EditDeleteNext); break; case KEY_DOWN: windowScroll(window, -1); - break; case KEY_END: edit(id, EditTail, 0); - break; case KEY_ENTER: edit(id, EditEnter, 0); - break; case KEY_HOME: edit(id, EditHead, 0); - break; case KEY_LEFT: edit(id, EditPrev, 0); + break; case KEY_END: editFn(&edit, EditTail); + break; case KEY_ENTER: inputEnter(id); + break; case KEY_HOME: editFn(&edit, EditHead); + break; case KEY_LEFT: editFn(&edit, EditPrev); break; case KEY_NPAGE: scrollPage(window, -1); break; case KEY_PPAGE: scrollPage(window, +1); - break; case KEY_RIGHT: edit(id, EditNext, 0); + break; case KEY_RIGHT: editFn(&edit, EditNext); break; case KEY_SEND: scrollTo(window, 0); break; case KEY_SHOME: scrollTo(window, BufferCap); break; case KEY_UP: windowScroll(window, +1); @@ -1020,33 +1027,30 @@ static void keyCtrl(wchar_t ch) { struct Window *window = windows.ptrs[windows.show]; uint id = window->id; switch (ch ^ L'@') { - 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'?': editFn(&edit, EditDeletePrev); + break; case L'A': editFn(&edit, EditHead); + break; case L'B': editFn(&edit, EditPrev); break; case L'C': raise(SIGINT); - 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, EditDeleteTail, 0); + break; case L'D': editFn(&edit, EditDeleteNext); + break; case L'E': editFn(&edit, EditTail); + break; case L'F': editFn(&edit, EditNext); + break; case L'H': editFn(&edit, EditDeletePrev); + break; case L'J': inputEnter(id); + break; case L'K': editFn(&edit, EditDeleteTail); break; case L'L': clearok(curscr, true); break; case L'N': uiShowNum(windows.show + 1); break; case L'P': uiShowNum(windows.show - 1); - break; case L'R': scrollSearch(window, editBuffer(NULL), -1); - break; case L'S': scrollSearch(window, editBuffer(NULL), +1); - break; case L'T': edit(id, EditTranspose, 0); - break; case L'U': edit(id, EditDeleteHead, 0); + break; case L'R': scrollSearch(window, editString(&edit), -1); + break; case L'S': scrollSearch(window, editString(&edit), +1); + break; case L'T': editFn(&edit, EditTranspose); + break; case L'U': editFn(&edit, EditDeleteHead); break; case L'V': scrollPage(window, -1); - break; case L'W': edit(id, EditDeletePrevWord, 0); - break; case L'X': edit(id, EditExpand, 0); - break; case L'Y': edit(id, EditPaste, 0); + break; case L'W': editFn(&edit, EditDeletePrevWord); + break; case L'Y': editFn(&edit, EditPaste); } } static void keyStyle(wchar_t ch) { - uint id = windows.ptrs[windows.show]->id; if (iswcntrl(ch)) ch = towlower(ch ^ L'@'); char buf[8] = {0}; enum Color color = Default; @@ -1077,7 +1081,7 @@ static void keyStyle(wchar_t ch) { snprintf(buf, sizeof(buf), "%c%02d", C, color); } for (char *ch = buf; *ch; ++ch) { - edit(id, EditInsert, *ch); + editInsert(&edit, *ch); } } @@ -1103,7 +1107,7 @@ void uiRead(void) { } else if (ret == KEY_CODE_YES && ch == KeyPasteManual) { paste ^= true; } else if (paste || literal) { - edit(windows.ptrs[windows.show]->id, EditInsert, ch); + editInsert(&edit, ch); } else if (ret == KEY_CODE_YES) { keyCode(ch); } else if (ch == (L'Z' ^ L'@')) { @@ -1117,7 +1121,7 @@ void uiRead(void) { } else if (iswcntrl(ch)) { keyCtrl(ch); } else { - edit(windows.ptrs[windows.show]->id, EditInsert, ch); + editInsert(&edit, ch); } style = false; literal = false;