Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ctrl-D #1402

Merged
merged 1 commit into from
Mar 23, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 54 additions & 90 deletions tools/shell/linenoise.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ struct linenoiseState {
size_t pos; /* Current cursor position. */
size_t oldpos; /* Previous refresh cursor position. */
size_t len; /* Current edited line length. */
size_t chars; /* Number of utf-8 chars in buffer. */
size_t totalUTF8Chars; /* Number of utf-8 chars in buffer. */
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. */
Expand Down Expand Up @@ -190,8 +190,6 @@ static void refreshLine(struct linenoiseState* l);
std::string oldInput = "";
bool inputLeft;

std::string utf8store = "";

/* Debugging macro. */
#if 0
FILE *lndebug_fp = NULL;
Expand Down Expand Up @@ -569,19 +567,6 @@ uint32_t linenoiseComputeRenderWidth(const char* buf, size_t len) {
return renderWidth;
}

int getBufCharPos(struct linenoiseState* l) {
if (l->pos == l->chars) {
return l->len;
}
size_t idx = 0;
size_t ctr = 0;
while (ctr < l->pos) {
idx = utf8proc_next_grapheme(l->buf, l->len, idx);
ctr++;
}
return idx;
}

/* Single line low level line refresh.
*
* Rewrite the currently edited line accordingly to the buffer content,
Expand All @@ -593,7 +578,7 @@ static void refreshSingleLine(struct linenoiseState* l) {
char buf[LINENOISE_MAX_LINE];
size_t len = l->len;
size_t pos = l->pos;
size_t chars = l->chars;
size_t chars = l->totalUTF8Chars;
struct abuf ab;
uint32_t renderWidth = 0;
uint32_t renderPos = 0;
Expand All @@ -613,7 +598,7 @@ static void refreshSingleLine(struct linenoiseState* l) {
posCounter++;
totalRenderWidth += charRenderWidth;
if (totalRenderWidth >= remainingRenderWidth) {
if (prevPos >= getBufCharPos(l)) {
if (prevPos >= pos) {
// We passed the cursor: break, we no longer need to render.
charPos = prevPos;
break;
Expand Down Expand Up @@ -791,91 +776,68 @@ bool pastedInput(int ifd) {
* On error writing to the terminal -1 is returned, otherwise 0. */
int linenoiseEditInsert(struct linenoiseState* l, char c) {
if (l->len < l->buflen) {
if (l->chars == l->pos) {
l->buf[l->len] = c;
if (l->len == l->pos) {
l->buf[l->pos] = c;
l->pos++;
l->len++;
l->buf[l->len] = '\0';

if ((!mlmode && !hintsCallback)) {
char d = (maskmode == 1) ? '*' : c;
if (!Utf8Proc::isValid(&d, 1)) {
utf8store += d;
if (Utf8Proc::isValid(utf8store.c_str(), utf8store.length())) {
utf8store = "";
l->chars++;
l->pos++;
}
} else {
if (l->plen + l->chars < l->cols) {
if (write(l->ofd, &d, 1) == -1)
return -1;
}
l->chars++;
l->pos++;
}
}
if (!pastedInput(l->ifd) && !inputLeft) {
if ((!mlmode && l->plen + l->len < l->cols && !hintsCallback)) {
/* Avoid a full update of the line in the
* trivial case. */
if (write(l->ofd, &c, 1) == -1)
return -1;
} else {
refreshLine(l);
}
} else {
if (!Utf8Proc::isValid(&c, 1)) {
utf8store += c;
l->len++;
}
if (Utf8Proc::isValid(utf8store.c_str(), utf8store.length())) {
uint32_t charLoc = getBufCharPos(l);
uint32_t charWidth = std::max((uint32_t)utf8store.length(), 1u);
memmove(l->buf + charLoc + charWidth, l->buf + charLoc, l->len - charLoc);
if (utf8store != "") {
for (auto i = 0u; i < utf8store.length(); i++) {
l->buf[charLoc + i] = utf8store[i];
}
utf8store = "";
l->pos++;
l->chars++;
l->buf[l->len] = '\0';
refreshLine(l);
} else {
l->buf[l->pos] = c;
l->len++;
l->pos++;
l->chars++;
l->buf[l->len] = '\0';
refreshLine(l);
}
}
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);
}
}
refreshLine(l);
return 0;
}

static uint32_t prevChar(struct linenoiseState* l) {
return Utf8Proc::previousGraphemeCluster(l->buf, l->len, getBufCharPos(l));
return Utf8Proc::previousGraphemeCluster(l->buf, l->len, l->pos);
}

static uint32_t nextChar(struct linenoiseState* l) {
return utf8proc_next_grapheme(l->buf, l->len, getBufCharPos(l));
return utf8proc_next_grapheme(l->buf, l->len, l->pos);
}

/* Move cursor on the left. */
void linenoiseEditMoveLeft(struct linenoiseState* l) {
if (l->pos > 0) {
l->pos--;
l->pos = prevChar(l);
refreshLine(l);
}
}

/* Move cursor on the right. */
void linenoiseEditMoveRight(struct linenoiseState* l) {
if (l->pos != l->chars) {
l->pos++;
if (l->pos != l->len) {
l->pos = nextChar(l);
refreshLine(l);
}
}

/* Checks to see if character defines a separation between words */
bool isWordSeparator(char c) {
return (c == 0x20);
if (c >= 'a' && c <= 'z') {
return false;
}
if (c >= 'A' && c <= 'Z') {
return false;
}
if (c >= '0' && c <= '9') {
return false;
}
return true;
}

/* Move cursor one word to the left */
Expand All @@ -884,19 +846,19 @@ void linenoiseEditMoveWordLeft(struct linenoiseState* l) {
return;
}
do {
l->pos--;
} while (l->pos > 0 && !isWordSeparator(l->buf[getBufCharPos(l)]));
l->pos = prevChar(l);
} while (l->pos > 0 && !isWordSeparator(l->buf[l->pos]));
refreshLine(l);
}

/* Move cursor one word to the right */
void linenoiseEditMoveWordRight(struct linenoiseState* l) {
if (l->pos == l->chars) {
if (l->pos == l->len) {
return;
}
do {
l->pos++;
} while (l->pos != l->chars && !isWordSeparator(l->buf[getBufCharPos(l)]));
l->pos = nextChar(l);
} while (l->pos != l->len && !isWordSeparator(l->buf[l->pos]));
refreshLine(l);
}

Expand All @@ -910,8 +872,8 @@ void linenoiseEditMoveHome(struct linenoiseState* l) {

/* Move cursor to the end of the line. */
void linenoiseEditMoveEnd(struct linenoiseState* l) {
if (l->pos != l->chars) {
l->pos = l->chars;
if (l->pos != l->len) {
l->pos = l->len;
refreshLine(l);
}
}
Expand Down Expand Up @@ -945,13 +907,14 @@ void linenoiseEditHistoryNext(struct linenoiseState* l, int dir) {
/* Delete the character at the right of the cursor without altering the cursor
* position. Basically this is what happens with the "Delete" keyboard key. */
void linenoiseEditDelete(struct linenoiseState* l) {
if (l->len > 0 && l->pos < l->chars) {
if (l->len > 0 && l->pos < l->len) {
uint32_t newPos = nextChar(l);
uint32_t oldPos = getBufCharPos(l);
uint32_t charSize = newPos - oldPos;
memmove(l->buf + oldPos, l->buf + newPos, l->len - oldPos);
uint32_t charSize = newPos - l->pos;
memmove(l->buf + l->pos, l->buf + newPos, l->len - newPos);
l->len -= charSize;
l->chars--;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@anuchak We should have a mechanism to check whether the character to delete is a utf-8 character or not.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the name of this variable to totalUTF8Chars and the mechanism is simple, just check if total size of the character > 1.

Also I'm not sure if this variable is even required or not, because I checked DuckDB's codebase and they have no such tracking variable, don't think it is required at all.

if(charSize > 1) {
l->totalUTF8Chars--;
}
l->buf[l->len] = '\0';
refreshLine(l);
}
Expand All @@ -961,12 +924,13 @@ void linenoiseEditDelete(struct linenoiseState* l) {
void linenoiseEditBackspace(struct linenoiseState* l) {
if (l->pos > 0 && l->len > 0) {
uint32_t newPos = prevChar(l);
uint32_t oldPos = getBufCharPos(l);
uint32_t charSize = oldPos - newPos;
memmove(l->buf + newPos, l->buf + oldPos, l->len - newPos);
uint32_t charSize = l->pos - newPos;
memmove(l->buf + newPos, l->buf + l->pos, l->len - l->pos);
l->len -= charSize;
l->pos--;
l->chars--;
l->pos = newPos;
if(charSize > 1) {
l->totalUTF8Chars--;
}
l->buf[l->len] = '\0';
refreshLine(l);
}
Expand Down Expand Up @@ -1010,7 +974,7 @@ static int linenoiseEdit(
l.plen = strlen(prompt);
l.oldpos = l.pos = 0;
l.len = 0;
l.chars = 0;
l.totalUTF8Chars = 0;
l.cols = getColumns(stdin_fd, stdout_fd);
l.maxrows = 0;
l.history_index = 0;
Expand Down