Commit a1d8e181c2c62fcee37af6dbdd41ef82c927d752

antirez 2023-03-27T09:21:31

Multiplexing: make completion non-blocking as well.

diff --git a/linenoise.c b/linenoise.c
index 58c1b05..0eab3df 100644
--- a/linenoise.c
+++ b/linenoise.c
@@ -355,57 +355,57 @@ static void freeCompletions(linenoiseCompletions *lc) {
  * the input was consumed by the completeLine() function to navigate the
  * possible completions, and the caller should read for the next characters
  * from stdin. */
-static int completeLine(struct linenoiseState *ls) {
+static int completeLine(struct linenoiseState *ls, int keypressed) {
     linenoiseCompletions lc = { 0, NULL };
-    int nread, nwritten;
-    char c = 0;
+    int nwritten;
+    char c = keypressed;
 
     completionCallback(ls->buf,&lc);
     if (lc.len == 0) {
         linenoiseBeep();
+        ls->in_completion = 0;
     } else {
-        size_t stop = 0, i = 0;
-
-        while(!stop) {
-            /* Show completion or original buffer */
-            if (i < lc.len) {
-                struct linenoiseState saved = *ls;
-
-                ls->len = ls->pos = strlen(lc.cvec[i]);
-                ls->buf = lc.cvec[i];
-                refreshLine(ls);
-                ls->len = saved.len;
-                ls->pos = saved.pos;
-                ls->buf = saved.buf;
-            } else {
-                refreshLine(ls);
-            }
+        switch(c) {
+            case 9: /* tab */
+                if (ls->in_completion == 0) {
+                    ls->in_completion = 1;
+                    ls->completion_idx = 0;
+                } else {
+                    ls->completion_idx = (ls->completion_idx+1) % (lc.len+1);
+                    if (ls->completion_idx == lc.len) linenoiseBeep();
+                }
+                c = 0;
+                break;
+            case 27: /* escape */
+                /* Re-show original buffer */
+                if (ls->completion_idx < lc.len) refreshLine(ls);
+                ls->in_completion = 0;
+                c = 0;
+                break;
+            default:
+                /* Update buffer and return */
+                if (ls->completion_idx < lc.len) {
+                    nwritten = snprintf(ls->buf,ls->buflen,"%s",
+                        lc.cvec[ls->completion_idx]);
+                    ls->len = ls->pos = nwritten;
+                    c = 0;
+                }
+                ls->in_completion = 0;
+                break;
+        }
 
-            nread = read(ls->ifd,&c,1);
-            if (nread <= 0) {
-                freeCompletions(&lc);
-                return -1;
-            }
+        /* Show completion or original buffer */
+        if (ls->in_completion && ls->completion_idx < lc.len) {
+            struct linenoiseState saved = *ls;
 
-            switch(c) {
-                case 9: /* tab */
-                    i = (i+1) % (lc.len+1);
-                    if (i == lc.len) linenoiseBeep();
-                    break;
-                case 27: /* escape */
-                    /* Re-show original buffer */
-                    if (i < lc.len) refreshLine(ls);
-                    stop = 1;
-                    break;
-                default:
-                    /* Update buffer and return */
-                    if (i < lc.len) {
-                        nwritten = snprintf(ls->buf,ls->buflen,"%s",lc.cvec[i]);
-                        ls->len = ls->pos = nwritten;
-                    }
-                    stop = 1;
-                    break;
-            }
+            ls->len = ls->pos = strlen(lc.cvec[ls->completion_idx]);
+            ls->buf = lc.cvec[ls->completion_idx];
+            refreshLine(ls);
+            ls->len = saved.len;
+            ls->pos = saved.pos;
+            ls->buf = saved.buf;
+        } else {
+            refreshLine(ls);
         }
     }
 
@@ -841,6 +841,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
 int linenoiseEditStart(struct linenoiseState *l, int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) {
     /* Populate the linenoise state that we pass to functions implementing
      * specific editing functionalities. */
+    l->in_completion = 0;
     l->ifd = stdin_fd != -1 ? stdin_fd : STDIN_FILENO;
     l->ofd = stdout_fd != -1 ? stdout_fd : STDOUT_FILENO;
     l->buf = buf;
@@ -908,8 +909,8 @@ char *linenoiseEditFeed(struct linenoiseState *l) {
     /* Only autocomplete when the callback is set. It returns < 0 when
      * there was an error reading from fd. Otherwise it will return the
      * character that should be handled next. */
-    if (c == 9 && completionCallback != NULL) {
-        c = completeLine(l);
+    if ((l->in_completion || c == 9) && completionCallback != NULL) {
+        c = completeLine(l,c);
         /* Return on errors */
         if (c < 0) return NULL;
         /* Read next character when 0 */
diff --git a/linenoise.h b/linenoise.h
index 6aac92d..fdb4319 100644
--- a/linenoise.h
+++ b/linenoise.h
@@ -51,6 +51,9 @@ extern char *linenoiseEditMore;
  * We pass this state to functions implementing specific editing
  * functionalities. */
 struct linenoiseState {
+    int in_completion;  /* The user pressed TAB and we are now in completion
+                         * mode, so input is handled by completeLine(). */
+    size_t completion_idx; /* Index of next completion to propose. */
     int ifd;            /* Terminal stdin file descriptor. */
     int ofd;            /* Terminal stdout file descriptor. */
     char *buf;          /* Edited line buffer. */