Refactoring: code split into sections. Chars insertion as a function.

This commit is contained in:
antirez
2013-02-05 19:51:03 +01:00
parent b7a76cd8a5
commit 9334052613

View File

@@ -50,7 +50,6 @@
* - Win32 support
*
* Bloat:
* - Completion?
* - History search like Ctrl+r in readline?
*
* List of escape sequences used by this program, we do everything just
@@ -126,6 +125,11 @@ struct linenoiseState {
static void linenoiseAtExit(void);
int linenoiseHistoryAdd(const char *line);
static void refreshLine(struct linenoiseState *l);
static void refreshLineRaw(int fd, const char *prompt, char *buf, size_t len,
size_t pos, size_t cols);
/* ======================= Low level terminal handling ====================== */
/* Return true if the terminal name is in the list of terminals we know are
* not able to understand basic escape sequences. */
@@ -139,18 +143,6 @@ static int isUnsupportedTerm(void) {
return 0;
}
/* Free the history, but does not reset it. Only used when we have to
* exit() to avoid memory leaks are reported by valgrind & co. */
static void freeHistory(void) {
if (history) {
int j;
for (j = 0; j < history_len; j++)
free(history[j]);
free(history);
}
}
/* Raw mode: 1960 magic shit. */
static int enableRawMode(int fd) {
struct termios raw;
@@ -193,12 +185,6 @@ static void disableRawMode(int fd) {
rawmode = 0;
}
/* At exit we'll try to fix the terminal to the initial conditions. */
static void linenoiseAtExit(void) {
disableRawMode(STDIN_FILENO);
freeHistory();
}
/* Try to get the number of columns in the current terminal, or assume 80
* if it fails. */
static int getColumns(void) {
@@ -208,6 +194,109 @@ static int getColumns(void) {
return ws.ws_col;
}
/* Clear the screen. Used to handle ctrl+l */
void linenoiseClearScreen(void) {
if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
/* nothing to do, just to avoid warning. */
}
}
/* Beep, used for completion when there is nothing to complete or when all
* the choices were already shown. */
static void linenoiseBeep(void) {
fprintf(stderr, "\x7");
fflush(stderr);
}
/* ============================== Completion ================================ */
/* Free a list of completion option populated by linenoiseAddCompletion(). */
static void freeCompletions(linenoiseCompletions *lc) {
size_t i;
for (i = 0; i < lc->len; i++)
free(lc->cvec[i]);
if (lc->cvec != NULL)
free(lc->cvec);
}
/* This is an helper function for linenoiseEdit() and is called when the
* user types the <tab> key in order to complete the string currently in the
* input.
*
* The state of the editing is encapsulated into the pointed linenoiseState
* structure as described in the structure definition. */
static int completeLine(struct linenoiseState *ls) {
linenoiseCompletions lc = { 0, NULL };
int nread, nwritten;
char c = 0;
completionCallback(ls->buf,&lc);
if (lc.len == 0) {
linenoiseBeep();
} else {
size_t stop = 0, i = 0;
size_t clen;
while(!stop) {
/* Show completion or original buffer */
if (i < lc.len) {
clen = strlen(lc.cvec[i]);
refreshLineRaw(ls->fd,ls->prompt,lc.cvec[i],clen,clen,ls->cols);
} else {
refreshLine(ls);
}
nread = read(ls->fd,&c,1);
if (nread <= 0) {
freeCompletions(&lc);
return -1;
}
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;
}
}
}
freeCompletions(&lc);
return c; /* Return last read character */
}
/* Register a callback function to be called for tab-completion. */
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
completionCallback = fn;
}
/* This function is used by the callback function registered by the user
* in order to add completion options given the input string when the
* user typed <tab>. See the example.c source code for a very easy to
* understand example. */
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
size_t len = strlen(str);
char *copy = malloc(len+1);
memcpy(copy,str,len+1);
lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
lc->cvec[lc->len++] = copy;
}
/* =========================== Line editing ================================= */
/* Rewrite the currently edited line accordingly to the buffer content,
* cursor position, and number of columns of the terminal. */
static void refreshLineRaw(int fd, const char *prompt, char *buf, size_t len,
@@ -245,86 +334,33 @@ static void refreshLine(struct linenoiseState *l) {
refreshLineRaw(l->fd, l->prompt, l->buf, l->len, l->pos, l->cols);
}
/* Beep, used for completion when there is nothing to complete or when all
* the choices were already shown. */
static void beep() {
fprintf(stderr, "\x7");
fflush(stderr);
}
/* Free a list of completion option populated by linenoiseAddCompletion(). */
static void freeCompletions(linenoiseCompletions *lc) {
size_t i;
for (i = 0; i < lc->len; i++)
free(lc->cvec[i]);
if (lc->cvec != NULL)
free(lc->cvec);
}
/* This is an helper function for linenoiseEdit() and is called when the
* user types the <tab> key in order to complete the string currently in the
* input.
*
* The state of the editing is encapsulated into the pointed linenoiseState
* structure as described in the structure definition. */
static int completeLine(struct linenoiseState *ls) {
linenoiseCompletions lc = { 0, NULL };
int nread, nwritten;
char c = 0;
completionCallback(ls->buf,&lc);
if (lc.len == 0) {
beep();
} else {
size_t stop = 0, i = 0;
size_t clen;
while(!stop) {
/* Show completion or original buffer */
if (i < lc.len) {
clen = strlen(lc.cvec[i]);
refreshLineRaw(ls->fd,ls->prompt,lc.cvec[i],clen,clen,ls->cols);
/* Insert the character 'c' at cursor current position.
*
* On error writing to the terminal -1 is returned, otherwise 0. */
int linenoiseEditInsert(struct linenoiseState *l, int c) {
if (l->len < l->buflen) {
if (l->len == l->pos) {
l->buf[l->pos] = c;
l->pos++;
l->len++;
l->buf[l->len] = '\0';
if (l->plen+l->len < l->cols) {
/* Avoid a full update of the line in the
* trivial case. */
if (write(l->fd,&c,1) == -1) return -1;
} else {
refreshLine(ls);
}
nread = read(ls->fd,&c,1);
if (nread <= 0) {
freeCompletions(&lc);
return -1;
}
switch(c) {
case 9: /* tab */
i = (i+1) % (lc.len+1);
if (i == lc.len) beep();
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;
refreshLine(l);
}
} else {
memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);
l->buf[l->pos] = c;
l->len++;
l->pos++;
l->buf[l->len] = '\0';
refreshLine(l);
}
}
freeCompletions(&lc);
return c; /* Return last read character */
}
/* Clear the screen. Used to handle ctrl+l */
void linenoiseClearScreen(void) {
if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
/* nothing to do, just to avoid warning. */
}
return 0;
}
/* This function is the core of the line editing capability of linenoise.
@@ -484,28 +520,7 @@ up_down_arrow:
}
break;
default:
if (l.len < buflen) {
if (l.len == l.pos) {
buf[l.pos] = c;
l.pos++;
l.len++;
buf[l.len] = '\0';
if (l.plen+l.len < l.cols) {
/* Avoid a full update of the line in the
* trivial case. */
if (write(fd,&c,1) == -1) return -1;
} else {
refreshLine(&l);
}
} else {
memmove(buf+l.pos+1,buf+l.pos,l.len-l.pos);
buf[l.pos] = c;
l.len++;
l.pos++;
buf[l.len] = '\0';
refreshLine(&l);
}
}
if (linenoiseEditInsert(&l,c)) return -1;
break;
case 21: /* Ctrl+u, delete the whole line. */
buf[0] = '\0';
@@ -599,21 +614,24 @@ char *linenoise(const char *prompt) {
}
}
/* Register a callback function to be called for tab-completion. */
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
completionCallback = fn;
/* ================================ History ================================= */
/* Free the history, but does not reset it. Only used when we have to
* exit() to avoid memory leaks are reported by valgrind & co. */
static void freeHistory(void) {
if (history) {
int j;
for (j = 0; j < history_len; j++)
free(history[j]);
free(history);
}
}
/* This function is used by the callback function registered by the user
* in order to add completion options given the input string when the
* user typed <tab>. See the example.c source code for a very easy to
* understand example. */
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
size_t len = strlen(str);
char *copy = malloc(len+1);
memcpy(copy,str,len+1);
lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
lc->cvec[lc->len++] = copy;
/* At exit we'll try to fix the terminal to the initial conditions. */
static void linenoiseAtExit(void) {
disableRawMode(STDIN_FILENO);
freeHistory();
}
/* Using a circular buffer is smarter, but a bit more complex to handle. */