Multiplexing: hide/show current line + remove debug + a few fixes

from orig commit c9123ec
This commit is contained in:
Данила Горнушко 2023-11-23 00:44:22 +03:00
parent d015138236
commit 05ece3c1ed
2 changed files with 153 additions and 169 deletions

View file

@ -10,7 +10,8 @@
* *
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* *
* Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2023-2023, Danila Gornushko <d3g3v3 at gmail dot com>
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
@ -128,21 +129,6 @@ static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
static int history_len = 0; static int history_len = 0;
static char **history = NULL; static char **history = NULL;
/* The linenoiseState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
struct linenoiseState {
char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */
const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t len; /* Current edited line length. */
size_t cols; /* Number of columns in terminal. */
size_t maxrows; /* Maximum num of rows used so far (multiline mode) */
int history_index; /* The history index we are currently editing. */
};
enum KEY_ACTION{ enum KEY_ACTION{
KEY_NULL = 0, /* NULL */ KEY_NULL = 0, /* NULL */
@ -167,27 +153,11 @@ enum KEY_ACTION{
}; };
int linenoiseHistoryAdd(const char *line); int linenoiseHistoryAdd(const char *line);
#define REFRESH_CLEAN (1<<0) // Clean the old prompt from the screen
#define REFRESH_WRITE (1<<1) // Rewrite the prompt on the screen.
#define REFRESH_ALL (REFRESH_CLEAN|REFRESH_WRITE) // Do both.
static void refreshLine(struct linenoiseState *l); static void refreshLine(struct linenoiseState *l);
/* Debugging macro. */
#if 0
FILE *lndebug_fp = NULL;
#define lndebug(...) \
do { \
if (lndebug_fp == NULL) { \
lndebug_fp = fopen("/tmp/lndebug.txt","a"); \
fprintf(lndebug_fp, \
"[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \
(int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \
(int)l->maxrows,old_rows); \
} \
fprintf(lndebug_fp, ", " __VA_ARGS__); \
fflush(lndebug_fp); \
} while (0)
#else
#define lndebug(fmt, ...)
#endif
/* ======================= Low level terminal handling ====================== */ /* ======================= Low level terminal handling ====================== */
/* Enable "mask mode". When it is enabled, instead of the input that /* Enable "mask mode". When it is enabled, instead of the input that
@ -440,8 +410,11 @@ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
/* Single line low level line refresh. /* Single line low level line refresh.
* *
* Rewrite the currently edited line accordingly to the buffer content, * Rewrite the currently edited line accordingly to the buffer content,
* cursor position, and number of columns of the terminal. */ * cursor position, and number of columns of the terminal.
static void refreshSingleLine(struct linenoiseState *l) { *
* Flags is REFRESH_* macros. The function can just remove the old
* prompt, just write it, or both. */
static void refreshSingleLine(struct linenoiseState *l, int flags) {
char seq[64]; char seq[64];
size_t plen = l->plen; size_t plen = l->plen;
char *buf = l->buf; char *buf = l->buf;
@ -460,8 +433,10 @@ static void refreshSingleLine(struct linenoiseState *l) {
abInit(&ab); abInit(&ab);
/* Cursor to left edge */ /* Cursor to left edge */
snprintf(seq,64,"\r"); snprintf(seq,sizeof(seq),"\r");
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
if (flags & REFRESH_WRITE) {
/* Write the prompt and the current buffer content */ /* Write the prompt and the current buffer content */
abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,l->prompt,strlen(l->prompt));
if (maskmode == 1) { if (maskmode == 1) {
@ -471,12 +446,17 @@ static void refreshSingleLine(struct linenoiseState *l) {
} }
/* Show hits if any. */ /* Show hits if any. */
refreshShowHints(&ab,l,plen); refreshShowHints(&ab,l,plen);
}
/* Erase to right */ /* Erase to right */
snprintf(seq,64,"\x1b[0K"); snprintf(seq,sizeof(seq),"\x1b[0K");
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
if (flags & REFRESH_WRITE) {
/* Move cursor to original position. */ /* Move cursor to original position. */
snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen)); snprintf(seq,sizeof(seq),"\r\x1b[%dC", (int)(pos+plen));
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
}
if (fwrite(ab.b, ab.len, 1, stdout) == -1) {} /* Can't recover from write error. */ if (fwrite(ab.b, ab.len, 1, stdout) == -1) {} /* Can't recover from write error. */
abFree(&ab); abFree(&ab);
} }
@ -484,8 +464,11 @@ static void refreshSingleLine(struct linenoiseState *l) {
/* Multi line low level line refresh. /* Multi line low level line refresh.
* *
* Rewrite the currently edited line accordingly to the buffer content, * Rewrite the currently edited line accordingly to the buffer content,
* cursor position, and number of columns of the terminal. */ * cursor position, and number of columns of the terminal.
static void refreshMultiLine(struct linenoiseState *l) { *
* Flags is REFRESH_* macros. The function can just remove the old
* prompt, just write it, or both. */
static void refreshMultiLine(struct linenoiseState *l, int flags) {
char seq[64]; char seq[64];
int plen = l->plen; int plen = l->plen;
int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */
@ -502,31 +485,34 @@ static void refreshMultiLine(struct linenoiseState *l) {
/* First step: clear all the lines used before. To do so start by /* First step: clear all the lines used before. To do so start by
* going to the last row. */ * going to the last row. */
abInit(&ab); abInit(&ab);
if (flags & REFRESH_CLEAN) {
if (old_rows-rpos > 0) { if (old_rows-rpos > 0) {
lndebug("go down %d", old_rows-rpos);
snprintf(seq,64,"\x1b[%dB", old_rows-rpos); snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
} }
/* Now for every row clear it, go up. */ /* Now for every row clear it, go up. */
for (j = 0; j < old_rows-1; j++) { for (j = 0; j < old_rows-1; j++) {
lndebug("clear+up");
snprintf(seq,64,"\r\x1b[0K\x1b[1A"); snprintf(seq,64,"\r\x1b[0K\x1b[1A");
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
} }
/* Clean the top line. */ /* Clean the top line. */
lndebug("clear");
snprintf(seq,64,"\r\x1b[0K"); snprintf(seq,64,"\r\x1b[0K");
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
}
if (flags & REFRESH_WRITE) {
/* Write the prompt and the current buffer content */ /* Write the prompt and the current buffer content */
abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,l->prompt,strlen(l->prompt));
if (maskmode == 1) { if (maskmode == 1) {
for (uint i = 0; i < l->len; i++) abAppend(&ab,"*",1); unsigned int i;
for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
} else { } else {
abAppend(&ab,l->buf,l->len); abAppend(&ab,l->buf,l->len);
} }
/* Show hits if any. */ /* Show hits if any. */
refreshShowHints(&ab,l,plen); refreshShowHints(&ab,l,plen);
@ -536,7 +522,6 @@ static void refreshMultiLine(struct linenoiseState *l) {
l->pos == l->len && l->pos == l->len &&
(l->pos+plen) % l->cols == 0) (l->pos+plen) % l->cols == 0)
{ {
lndebug("<newline>");
abAppend(&ab,"\n",1); abAppend(&ab,"\n",1);
snprintf(seq,64,"\r"); snprintf(seq,64,"\r");
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
@ -545,29 +530,26 @@ static void refreshMultiLine(struct linenoiseState *l) {
} }
/* Move cursor to right position. */ /* Move cursor to right position. */
rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ rpos2 = (plen+l->pos+l->cols)/l->cols; /* Current cursor relative row */
lndebug("rpos2 %d", rpos2);
/* Go up till we reach the expected positon. */ /* Go up till we reach the expected positon. */
if (rows-rpos2 > 0) { if (rows-rpos2 > 0) {
lndebug("go-up %d", rows-rpos2);
snprintf(seq,64,"\x1b[%dA", rows-rpos2); snprintf(seq,64,"\x1b[%dA", rows-rpos2);
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
} }
/* Set column. */ /* Set column. */
col = (plen+(int)l->pos) % (int)l->cols; col = (plen+(int)l->pos) % (int)l->cols;
lndebug("set col %d", 1+col);
if (col) if (col)
snprintf(seq,64,"\r\x1b[%dC", col); snprintf(seq,64,"\r\x1b[%dC", col);
else else
snprintf(seq,64,"\r"); snprintf(seq,64,"\r");
abAppend(&ab,seq,strlen(seq)); abAppend(&ab,seq,strlen(seq));
}
lndebug("\n");
l->oldpos = l->pos; l->oldpos = l->pos;
if (fwrite(ab.b,ab.len,1,stdout) == -1) {} /* Can't recover from write error. */ if (fwrite(ab.b, ab.len, 1, stdout) == -1) {} /* Can't recover from write error. */
abFree(&ab); abFree(&ab);
} }
@ -575,9 +557,25 @@ static void refreshMultiLine(struct linenoiseState *l) {
* refreshMultiLine() according to the selected mode. */ * refreshMultiLine() according to the selected mode. */
static void refreshLine(struct linenoiseState *l) { static void refreshLine(struct linenoiseState *l) {
if (mlmode) if (mlmode)
refreshMultiLine(l); refreshMultiLine(l,REFRESH_ALL);
else else
refreshSingleLine(l); refreshSingleLine(l,REFRESH_ALL);
}
/* Hide the current line, when using the multiplexing API. */
void linenoiseHide(struct linenoiseState *l) {
if (mlmode)
refreshMultiLine(l,REFRESH_CLEAN);
else
refreshSingleLine(l,REFRESH_CLEAN);
}
/* Show the current line, when using the multiplexing API. */
void linenoiseShow(struct linenoiseState *l) {
if (mlmode)
refreshMultiLine(l,REFRESH_WRITE);
else
refreshSingleLine(l,REFRESH_WRITE);
} }
/* Insert the character 'c' at cursor current position. /* Insert the character 'c' at cursor current position.
@ -751,60 +749,12 @@ int linenoiseEditStart(struct linenoiseState *l, char *buf, size_t buflen, const
* initially is just an empty string. */ * initially is just an empty string. */
linenoiseHistoryAdd(""); linenoiseHistoryAdd("");
int pos1 = getCursorPosition();
if (fwrite(prompt,l->plen,1,stdout) == -1) return -1; if (fwrite(prompt,l->plen,1,stdout) == -1) return -1;
return 0; int pos2 = getCursorPosition();
} if (pos1 >= 0 && pos2 >= 0) {
// l->plen = pos2 - pos1;
// int pos1 = getCursorPosition(); }
// if (fwrite(prompt,l.plen,1,stdout) == -1) return -1;
// int pos2 = getCursorPosition();
// if (pos1 >= 0 && pos2 >= 0) {
// l.plen = pos2 - pos1;
// }
/* This function is part of the multiplexed API of linenoise, see the top
* comment on linenoiseEditStart() for more information. Call this function
* each time there is some data to read from the standard input file
* descriptor. In the case of blocking operations, this function can just be
* called in a loop, and block.
*
* The function returns NULL to signal that line editing is still in progress,
* that is, the user didn't yet pressed enter / CTRL-D. Otherwise the function
* returns the pointer to the buffer and populates '*len' with the current
* buffer length. If '*len' is set to -1, some special condition occurred, and
* the caller may want to check 'errno':
*
* EAGAIN if the user pressed Ctrl-C
* ENOENT if the user pressed Ctrl-D
*/
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->ifd = stdin_fd != -1 ? stdin_fd : STDIN_FILENO;
l->ofd = stdout_fd != -1 ? stdout_fd : STDOUT_FILENO;
l->buf = buf;
l->buflen = buflen;
l->prompt = prompt;
l->plen = strlen(prompt);
l->oldpos = l->pos = 0;
l->len = 0;
l->cols = getColumns(stdin_fd, stdout_fd);
l->maxrows = 0;
l->history_index = 0;
/* Buffer starts empty. */
l->buf[0] = '\0';
l->buflen--; /* Make sure there is always space for the nulterm */
/* Enter raw mode. */
if (enableRawMode(l->ifd) == -1) return -1;
/* The latest history entry is always our current buffer, that
* initially is just an empty string. */
linenoiseHistoryAdd("");
if (write(l->ofd,prompt,l->plen) == -1) return -1;
return 0; return 0;
} }

View file

@ -7,7 +7,8 @@
* *
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* *
* Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2023-2023, Danila Gornushko <d3g3v3 at gmail dot com>
* Copyright (c) 2010-2023, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
* *
* All rights reserved. * All rights reserved.
@ -43,11 +44,43 @@
extern "C" { extern "C" {
#endif #endif
#include <stddef.h> /* For size_t. */
/* The linenoiseState structure represents the state during line editing.
* We pass this state to functions implementing specific editing
* functionalities. */
struct linenoiseState {
char *buf; /* Edited line buffer. */
size_t buflen; /* Edited line buffer size. */
const char *prompt; /* Prompt to display. */
size_t plen; /* Prompt length. */
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t len; /* Current edited line length. */
size_t cols; /* Number of columns in terminal. */
size_t maxrows; /* Maximum num of rows used so far (multiline mode) */
int history_index; /* The history index we are currently editing. */
};
typedef struct linenoiseCompletions { typedef struct linenoiseCompletions {
size_t len; size_t len;
char **cvec; char **cvec;
} linenoiseCompletions; } linenoiseCompletions;
/* Non blocking API. */
int linenoiseEditStart(struct linenoiseState *l, char *buf, size_t buflen, const char *prompt);
char *linenoiseEditFeed(struct linenoiseState *l, int *len);
void linenoiseEditStop(struct linenoiseState *l);
void linenoiseHide(struct linenoiseState *l);
void linenoiseShow(struct linenoiseState *l);
/* Blocking API. */
char *linenoise(const char *prompt);
void linenoiseFree(void *ptr);
/* Completion API. */
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *); typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold); typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *); typedef void(linenoiseFreeHintsCallback)(void *);
@ -56,17 +89,18 @@ void linenoiseSetHintsCallback(linenoiseHintsCallback *);
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *); void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *); void linenoiseAddCompletion(linenoiseCompletions *, const char *);
char *linenoise(const char *prompt); /* History API. */
void linenoiseFree(void *ptr);
int linenoiseHistoryAdd(const char *line); int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len); int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename); int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename); int linenoiseHistoryLoad(const char *filename);
void linenoiseHistoryFree(); void linenoiseHistoryFree();
/* Other utilities. */
void linenoiseClearScreen(void); void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml); void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void); void linenoisePrintKeyCodes(void);
void linenoiseMaskModeEnable(void); void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(void); void linenoiseMaskModeDisable(void);