]> git.lizzy.rs Git - linenoise.git/blobdiff - linenoise.c
Use CMake
[linenoise.git] / linenoise.c
index d04ec6ce31ceef271f9b12b353c324b4e945db69..4300cb8677c5c03b7fc084b6dddbacf813edaa71 100644 (file)
 #include <sys/types.h>
 
 #include "linenoise.h"
+#ifndef STRINGBUF_H
 #include "stringbuf.h"
+#endif
+#ifndef UTF8_UTIL_H
 #include "utf8.h"
+#endif
 
 #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
 
@@ -180,6 +184,8 @@ struct current {
     int cols;   /* Size of the window, in chars */
     int nrows;  /* How many rows are being used in multiline mode (>= 1) */
     int rpos;   /* The current row containing the cursor - multiline mode only */
+    int colsright; /* refreshLine() cached cols for insert_char() optimisation */
+    int colsleft;  /* refreshLine() cached cols for remove_char() optimisation */
     const char *prompt;
     stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
     stringbuf *output;  /* used only during refreshLine() - output accumulator */
@@ -223,15 +229,17 @@ void linenoiseHistoryFree(void) {
     }
 }
 
+typedef enum {
+    EP_START,   /* looking for ESC */
+    EP_ESC,     /* looking for [ */
+    EP_DIGITS,  /* parsing digits */
+    EP_PROPS,   /* parsing digits or semicolons */
+    EP_END,     /* ok */
+    EP_ERROR,   /* error */
+} ep_state_t;
+
 struct esc_parser {
-    enum {
-        EP_START,   /* looking for ESC */
-        EP_ESC,     /* looking for [ */
-        EP_DIGITS,  /* parsing digits */
-        EP_PROPS,   /* parsing digits or semicolons */
-        EP_END,     /* ok */
-        EP_ERROR,   /* error */
-    } state;
+    ep_state_t state;
     int props[5];   /* properties are stored here */
     int maxprops;   /* size of the props[] array */
     int numprops;   /* number of properties found */
@@ -338,7 +346,7 @@ static void DRL_STR(const char *str)
     }
 }
 #else
-#define DRL(ARGS...)
+#define DRL(...)
 #define DRL_CHAR(ch)
 #define DRL_STR(str)
 #endif
@@ -507,7 +515,7 @@ static void cursorDown(struct current *current, int n)
 
 void linenoiseClearScreen(void)
 {
-    write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7);
+    IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
 }
 
 /**
@@ -946,43 +954,52 @@ void linenoiseSetMultiLine(int enableml)
 }
 
 /* Helper of refreshSingleLine() and refreshMultiLine() to show hints
- * to the right of the prompt. */
-static void refreshShowHints(struct current *current, const char *buf, int availcols) {
+ * to the right of the prompt.
+ * Returns 1 if a hint was shown, or 0 if not
+ * If 'display' is 0, does no output. Just returns the appropriate return code.
+ */
+static int refreshShowHints(struct current *current, const char *buf, int availcols, int display)
+{
+    int rc = 0;
     if (showhints && hintsCallback && availcols > 0) {
         int bold = 0;
         int color = -1;
         char *hint = hintsCallback(buf, &color, &bold, hintsUserdata);
         if (hint) {
-            const char *pt;
-            if (bold == 1 && color == -1) color = 37;
-            if (bold || color > 0) {
-                int props[3] = { bold, color, 49 }; /* bold, color, fgnormal */
-                setOutputHighlight(current, props, 3);
-            }
-            DRL("<hint bold=%d,color=%d>", bold, color);
-            pt = hint;
-            while (*pt) {
-                int ch;
-                int n = utf8_tounicode(pt, &ch);
-                int width = char_display_width(ch);
-
-                if (width >= availcols) {
-                    DRL("<hinteol>");
-                    break;
+            rc = 1;
+            if (display) {
+                const char *pt;
+                if (bold == 1 && color == -1) color = 37;
+                if (bold || color > 0) {
+                    int props[3] = { bold, color, 49 }; /* bold, color, fgnormal */
+                    setOutputHighlight(current, props, 3);
                 }
-                DRL_CHAR(ch);
+                DRL("<hint bold=%d,color=%d>", bold, color);
+                pt = hint;
+                while (*pt) {
+                    int ch;
+                    int n = utf8_tounicode(pt, &ch);
+                    int width = char_display_width(ch);
+
+                    if (width >= availcols) {
+                        DRL("<hinteol>");
+                        break;
+                    }
+                    DRL_CHAR(ch);
 
-                availcols -= width;
-                outputChars(current, pt, n);
-                pt += n;
-            }
-            if (bold || color > 0) {
-                clearOutputHighlight(current);
+                    availcols -= width;
+                    outputChars(current, pt, n);
+                    pt += n;
+                }
+                if (bold || color > 0) {
+                    clearOutputHighlight(current);
+                }
+                /* Call the function to free the hint returned. */
+                if (freeHintsCallback) freeHintsCallback(hint, hintsUserdata);
             }
-            /* Call the function to free the hint returned. */
-            if (freeHintsCallback) freeHintsCallback(hint, hintsUserdata);
         }
     }
+    return rc;
 }
 
 #ifdef USE_TERMIOS
@@ -1003,6 +1020,7 @@ static void refreshEnd(struct current *current)
 
 static void refreshStartChars(struct current *current)
 {
+    (void)current;
 }
 
 static void refreshNewline(struct current *current)
@@ -1013,6 +1031,7 @@ static void refreshNewline(struct current *current)
 
 static void refreshEndChars(struct current *current)
 {
+    (void)current;
 }
 #endif
 
@@ -1027,6 +1046,7 @@ static void refreshLineAlt(struct current *current, const char *prompt, const ch
     int notecursor;
     int cursorcol = 0;
     int cursorrow = 0;
+    int hint;
     struct esc_parser parser;
 
 #ifdef DEBUG_REFRESHLINE
@@ -1204,10 +1224,22 @@ static void refreshLineAlt(struct current *current, const char *prompt, const ch
         cursorrow = displayrow;
     }
 
-    DRL("\nafter buf: displaycol=%d, displayrow=%d, cursorcol=%d, cursorrow=%d\n\n", displaycol, displayrow, cursorcol, cursorrow);
+    DRL("\nafter buf: displaycol=%d, displayrow=%d, cursorcol=%d, cursorrow=%d\n", displaycol, displayrow, cursorcol, cursorrow);
 
     /* (f) show hints */
-    refreshShowHints(current, buf, current->cols - displaycol);
+    hint = refreshShowHints(current, buf, current->cols - displaycol, 1);
+
+    /* Remember how many many cols are available for insert optimisation */
+    if (prompt == current->prompt && hint == 0) {
+        current->colsright = current->cols - displaycol;
+        current->colsleft = displaycol;
+    }
+    else {
+        /* Can't optimise */
+        current->colsright = 0;
+        current->colsleft = 0;
+    }
+    DRL("\nafter hints: colsleft=%d, colsright=%d\n\n", current->colsleft, current->colsright);
 
     refreshEndChars(current);
 
@@ -1252,16 +1284,48 @@ static int remove_char(struct current *current, int pos)
     if (pos >= 0 && pos < sb_chars(current->buf)) {
         int offset = utf8_index(sb_str(current->buf), pos);
         int nbytes = utf8_index(sb_str(current->buf) + offset, 1);
-
-        /* Note that we no longer try to optimise the remove-at-end case
-         * since control characters and wide characters mess
-         * up the simple count
+        int rc = 1;
+
+        /* Now we try to optimise in the simple but very common case that:
+         * - outputChars() can be used directly (not win32)
+         * - we are removing the char at EOL
+         * - the buffer is not empty
+         * - there are columns available to the left
+         * - the char being deleted is not a wide or utf-8 character
+         * - no hints are being shown
          */
+        if (current->output && current->pos == pos + 1 && current->pos == sb_chars(current->buf) && pos > 0) {
+#ifdef USE_UTF8
+            /* Could implement utf8_prev_len() but simplest just to not optimise this case */
+            char last = sb_str(current->buf)[offset];
+#else
+            char last = 0;
+#endif
+            if (current->colsleft > 0 && (last & 0x80) == 0) {
+                /* Have cols on the left and not a UTF-8 char or continuation */
+                /* Yes, can optimise */
+                current->colsleft--;
+                current->colsright++;
+                rc = 2;
+            }
+        }
+
         sb_delete(current->buf, offset, nbytes);
 
         if (current->pos > pos) {
             current->pos--;
         }
+        if (rc == 2) {
+            if (refreshShowHints(current, sb_str(current->buf), current->colsright, 0)) {
+                /* A hint needs to be shown, so can't optimise after all */
+                rc = 1;
+            }
+            else {
+                /* optimised output */
+                outputChars(current, "\b \b", 3);
+            }
+        }
+        return rc;
         return 1;
     }
     return 0;
@@ -1279,17 +1343,41 @@ static int insert_char(struct current *current, int pos, int ch)
         char buf[MAX_UTF8_LEN + 1];
         int offset = utf8_index(sb_str(current->buf), pos);
         int n = utf8_getchars(buf, ch);
+        int rc = 1;
 
         /* null terminate since sb_insert() requires it */
         buf[n] = 0;
 
-        /* Optimisation removed - see reason in remove_char() */
-
+        /* Now we try to optimise in the simple but very common case that:
+         * - outputChars() can be used directly (not win32)
+         * - we are inserting at EOL
+         * - there are enough columns available
+         * - no hints are being shown
+         */
+        if (current->output && pos == current->pos && pos == sb_chars(current->buf)) {
+            int width = char_display_width(ch);
+            if (current->colsright > width) {
+                /* Yes, can optimise */
+                current->colsright -= width;
+                current->colsleft -= width;
+                rc = 2;
+            }
+        }
         sb_insert(current->buf, offset, buf);
         if (current->pos >= pos) {
             current->pos++;
         }
-        return 1;
+        if (rc == 2) {
+            if (refreshShowHints(current, sb_str(current->buf), current->colsright, 0)) {
+                /* A hint needs to be shown, so can't optimise after all */
+                rc = 1;
+            }
+            else {
+                /* optimised output */
+                outputChars(current, buf, n);
+            }
+        }
+        return rc;
     }
     return 0;
 }
@@ -1305,7 +1393,7 @@ static void capture_chars(struct current *current, int pos, int nchars)
         int offset = utf8_index(sb_str(current->buf), pos);
         int nbytes = utf8_index(sb_str(current->buf) + offset, nchars);
 
-        if (nbytes) {
+        if (nbytes > 0) {
             if (current->capture) {
                 sb_clear(current->capture);
             }
@@ -1381,8 +1469,8 @@ static int reverseIncrementalSearch(struct current *current)
         c = fd_read(current);
         if (c == ctrl('H') || c == CHAR_DELETE) {
             if (rchars) {
-                int p = utf8_index(rbuf, --rchars);
-                rbuf[p] = 0;
+                int p_ind = utf8_index(rbuf, --rchars);
+                rbuf[p_ind] = 0;
                 rlen = strlen(rbuf);
             }
             continue;
@@ -1504,7 +1592,8 @@ static int linenoiseEdit(struct current *current) {
         switch(c) {
         case SPECIAL_NONE:
             break;
-        case '\r':    /* enter */
+        case '\r':    /* enter/CR */
+        case '\n':    /* LF */
             history_len--;
             free(history[history_len]);
             current->pos = sb_chars(current->buf);
@@ -1688,6 +1777,7 @@ history_navigation:
 int linenoiseColumns(void)
 {
     struct current current;
+    current.output = NULL;
     enableRawMode (&current);
     getWindowSize (&current);
     disableRawMode (&current);