commit a2e85fdc57c1322c16d120d1865acc8f13f4c8bd
parent 19b4d99293ad470c98406b94935565750339b716
Author: Tom Schwindl <schwindl@posteo.de>
Date: Thu, 29 Dec 2022 13:17:48 +0100
Introduce modes
A mode determines how keyborad input is interpreted and which keybindings
are available. Currently, there is a default- and an emacs-mode from which
the user can choose.
Diffstat:
M | Makefile | | | 7 | +++++-- |
M | lchat.c | | | 9 | ++++++--- |
M | slackline.c | | | 112 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
A | slackline_emacs.c | | | 53 | +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
4 files changed, 139 insertions(+), 42 deletions(-)
diff --git a/Makefile b/Makefile
@@ -22,8 +22,8 @@ dist:
tar -czf lchat-$(VERSION).tar.gz lchat-$(VERSION)
rm -fr lchat-$(VERSION)
-lchat: lchat.o slackline.o util.o
- $(CC) -o $@ lchat.o slackline.o util.o $(LIBS)
+lchat: lchat.o slackline.o util.o slackline_emacs.o
+ $(CC) -o $@ lchat.o slackline.o slackline_emacs.o util.o $(LIBS)
lchat.o: lchat.c
$(CC) -c $(CFLAGS) -D_BSD_SOURCE -D_XOPEN_SOURCE -D_GNU_SOURCE \
@@ -42,5 +42,8 @@ sl_test: sl_test.o slackline.o slackline.h
slackline.o: slackline.c slackline.h
$(CC) -c $(CFLAGS) -o $@ slackline.c
+slackline_emacs.o: slackline_emacs.c slackline.h
+ $(CC) -c $(CFLAGS) -o $@ slackline_emacs.c
+
util.o: util.c util.h
$(CC) -c $(CFLAGS) -D_BSD_SOURCE -o $@ util.c
diff --git a/lchat.c b/lchat.c
@@ -23,6 +23,7 @@
#include <limits.h>
#include <poll.h>
#include <signal.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -183,7 +184,7 @@ main(int argc, char *argv[])
char *in_file = NULL;
char *out_file = NULL;
- while ((ch = getopt(argc, argv, "an:i:eo:p:t:uh")) != -1) {
+ while ((ch = getopt(argc, argv, "an:i:eo:p:t:uhm:")) != -1) {
switch (ch) {
case 'a':
bell_flag = false;
@@ -217,6 +218,10 @@ main(int argc, char *argv[])
case 'u':
ucspi = true;
break;
+ case 'm':
+ if (strcmp(optarg, "emacs") == 0)
+ sl_mode(sl, SL_EMACS);
+ break;
case 'h':
default:
usage();
@@ -342,8 +347,6 @@ main(int argc, char *argv[])
return EXIT_SUCCESS;
switch (c) {
- case 4: /* eot */
- return EXIT_SUCCESS;
case 13: /* return */
if (sl->rlen == 0 && empty_line == false)
goto out;
diff --git a/slackline.c b/slackline.c
@@ -22,13 +22,14 @@
#include <grapheme.h>
+#include "slackline_internals.h"
#include "slackline.h"
-
-enum direction {LEFT, RIGHT, HOME, END};
+#include "util.h"
struct slackline *
sl_init(void)
{
+ char *mode = getenv("EDITOR");
struct slackline *sl = malloc(sizeof *sl);
if (sl == NULL)
@@ -45,6 +46,14 @@ sl_init(void)
sl_reset(sl);
+ sl->mode = SL_DEFAULT;
+ if (mode != NULL) {
+ if (strcmp(mode, "emacs") == 0)
+ sl->mode = SL_EMACS;
+ else if (strcmp(mode, "vi") == 0)
+ sl->mode = SL_VI;
+ }
+
return sl;
}
@@ -71,7 +80,13 @@ sl_reset(struct slackline *sl)
sl->ubuf_len = 0;
}
-static size_t
+void
+sl_mode(struct slackline *sl, enum mode mode)
+{
+ sl->mode = mode;
+}
+
+size_t
sl_postobyte(struct slackline *sl, size_t pos)
{
char *ptr = &sl->buf[0];
@@ -84,13 +99,13 @@ sl_postobyte(struct slackline *sl, size_t pos)
return byte;
}
-static char *
+char *
sl_postoptr(struct slackline *sl, size_t pos)
{
return &sl->buf[sl_postobyte(sl, pos)];
}
-static void
+void
sl_backspace(struct slackline *sl)
{
char *ncur;
@@ -114,7 +129,7 @@ sl_backspace(struct slackline *sl)
sl->ptr = ncur;
}
-static void
+void
sl_move(struct slackline *sl, enum direction dir)
{
switch (dir) {
@@ -139,21 +154,41 @@ sl_move(struct slackline *sl, enum direction dir)
sl->ptr = sl->buf + sl->bcur;
}
-int
-sl_keystroke(struct slackline *sl, int key)
+static void
+sl_default(struct slackline *sl, int key)
{
- uint_least32_t cp;
-
- if (sl == NULL || sl->rlen < sl->rcur)
- return -1;
+ switch (key) {
+ case 27: /* Escape */
+ sl->esc = ESC;
+ break;
+ case 21:
+ sl_reset(sl);
+ break;
+ case 23: /* ctrl+w -- erase previous word */
+ while (sl->rcur != 0 && isspace((unsigned char) *(sl->ptr-1)))
+ sl_backspace(sl);
+ while (sl->rcur != 0 && !isspace((unsigned char) *(sl->ptr-1)))
+ sl_backspace(sl);
+ break;
+ case 127: /* backspace */
+ case 8: /* backspace */
+ sl_backspace(sl);
+ break;
+ default:
+ break;
+ }
+}
+static int
+sl_esc(struct slackline *sl, int key)
+{
/* handle escape sequences */
switch (sl->esc) {
case ESC_NONE:
break;
case ESC:
sl->esc = key == '[' ? ESC_BRACKET : ESC_NONE;
- return 0;
+ return 1;
case ESC_BRACKET:
switch (key) {
case 'A': /* up */
@@ -189,10 +224,10 @@ sl_keystroke(struct slackline *sl, int key)
case '9':
sl->nummod = key;
sl->esc = ESC_BRACKET_NUM;
- return 0;
+ return 1;
}
sl->esc = ESC_NONE;
- return 0;
+ return 1;
case ESC_BRACKET_NUM:
switch(key) {
case '~':
@@ -213,35 +248,38 @@ sl_keystroke(struct slackline *sl, int key)
break;
}
sl->esc = ESC_NONE;
- return 0;
+ return 1;
}
}
- if (!iscntrl((unsigned char) key))
- goto compose;
+ return 0;
+}
- /* handle ctl keys */
- switch (key) {
- case 27: /* Escape */
- sl->esc = ESC;
- return 0;
- case 127: /* backspace */
- case 8: /* backspace */
- sl_backspace(sl);
- return 0;
- case 21: /* ctrl+u -- clearline */
- sl_reset(sl);
- return 0;
- case 23: /* ctrl+w -- erase previous word */
- while (sl->rcur != 0 && isspace((unsigned char) *(sl->ptr-1)))
- sl_backspace(sl);
+int
+sl_keystroke(struct slackline *sl, int key)
+{
+ uint_least32_t cp;
- while (sl->rcur != 0 && !isspace((unsigned char) *(sl->ptr-1)))
- sl_backspace(sl);
- return 0;
- default:
+ if (sl == NULL || sl->rlen < sl->rcur)
+ return -1;
+ if (sl_esc(sl, key))
return 0;
+ if (!iscntrl((unsigned char) key))
+ goto compose;
+
+ switch (sl->mode) {
+ case SL_DEFAULT:
+ sl_default(sl, key);
+ break;
+ case SL_EMACS:
+ sl_default(sl, key);
+ sl_emacs(sl, key);
+ break;
+ case SL_VI:
+ /* TODO: implement vi-mode */
+ break;
}
+ return 0;
compose:
/* byte-wise composing of UTF-8 runes */
diff --git a/slackline_emacs.c b/slackline_emacs.c
@@ -0,0 +1,53 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "slackline_internals.h"
+
+void
+sl_emacs(struct slackline *sl, int key)
+{
+ char tmp;
+
+ switch (key) {
+ case 27: /* Escape */
+ sl->esc = ESC;
+ break;
+ case 1: /* ctrl+a -- start of line */
+ sl_move(sl, HOME);
+ break;
+ case 2: /* ctrl+b -- previous char */
+ sl_move(sl, LEFT);
+ break;
+ case 4: /* ctrl+d -- delete char in front of the cursor or exit */
+ if (sl->rcur < sl->rlen) {
+ sl_move(sl, RIGHT);
+ sl_backspace(sl);
+ } else {
+ exit(EXIT_SUCCESS);
+ }
+ break;
+ case 5: /* ctrl+e -- end of line */
+ sl_move(sl, END);
+ break;
+ case 6: /* ctrl+f -- next char */
+ sl_move(sl, RIGHT);
+ break;
+ case 11: /* ctrl+k -- delete line from cursor to end */
+ for (int i = sl->rlen - sl->rcur; i > 0; --i) {
+ sl_move(sl, RIGHT);
+ sl_backspace(sl);
+ }
+ break;
+ case 20: /* ctrl+t -- swap last two chars */
+ if (sl->rcur >= 2) {
+ tmp = *sl_postoptr(sl, sl->rcur-1);
+ sl->buf[sl->rcur-1] = *sl_postoptr(sl, sl->rcur-2);
+ sl->buf[sl->rcur-2] = tmp;
+ }
+ break;
+ default:
+ break;
+ }
+}