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

add linenoise.readLineStatus to get status (eg: ctrl-D or ctrl-C) #16977

Merged
merged 2 commits into from
Feb 9, 2021
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior.

- Added `random.initRand()` overload with no argument which uses the current time as a seed.

- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C).

## Language changes

- `nimscript` now handles `except Exception as e`.
Expand Down
20 changes: 11 additions & 9 deletions lib/impure/rdstdin.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@
## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine``
## is used. This suffices because Windows' console already provides the
## wanted functionality.
##
## **Examples:**
##
## .. code-block:: nim
## echo readLineFromStdin("Is Nim awesome? (Y/n):")
## var userResponse: string
## doAssert readLineFromStdin("How are you?:", line = userResponse)
## echo userResponse

when defined(Windows):
runnableExamples:
if false:
echo readLineFromStdin("Is Nim awesome? (Y/n): ")
var line: string
while true:
let ok = readLineFromStdin("How are you? ", line)
if not ok: break # ctrl-C or ctrl-D will cause a break
if line.len > 0: echo line
echo "exiting"

when defined(windows):
proc readLineFromStdin*(prompt: string): string {.
tags: [ReadIOEffect, WriteIOEffect].} =
## Reads a line from stdin.
Expand Down
18 changes: 13 additions & 5 deletions lib/wrappers/linenoise/linenoise.c
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
* when ctrl+d is typed.
*
* The function returns the length of the current buffer. */
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt, linenoiseData* data)
{
struct linenoiseState l;

Expand Down Expand Up @@ -827,6 +827,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
return (int)l.len;
case CTRL_C: /* ctrl-c */
errno = EAGAIN;
data->status = linenoiseStatus_ctrl_C;
return -1;
case BACKSPACE: /* backspace */
case 8: /* ctrl-h */
Expand All @@ -839,6 +840,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
} else {
history_len--;
free(history[history_len]);
data->status = linenoiseStatus_ctrl_D;
return -1;
}
break;
Expand Down Expand Up @@ -979,7 +981,7 @@ void linenoisePrintKeyCodes(void) {

/* This function calls the line editing function linenoiseEdit() using
* the STDIN file descriptor set in raw mode. */
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
static int linenoiseRaw(char *buf, size_t buflen, const char *prompt, linenoiseData* data) {
int count;

if (buflen == 0) {
Expand All @@ -988,7 +990,7 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
}

if (enableRawMode(STDIN_FILENO) == -1) return -1;
count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt, data);
disableRawMode(STDIN_FILENO);
printf("\n");
return count;
Expand Down Expand Up @@ -1035,7 +1037,7 @@ static char *linenoiseNoTTY(void) {
* for a blacklist of stupid terminals, and later either calls the line
* editing function or uses dummy fgets() so that you will be able to type
* something even in the most desperate of the conditions. */
char *linenoise(const char *prompt) {
char *linenoiseExtra(const char *prompt, linenoiseData* data) {
char buf[LINENOISE_MAX_LINE];
int count;

Expand All @@ -1056,12 +1058,18 @@ char *linenoise(const char *prompt) {
}
return strdup(buf);
} else {
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt, data);
if (count == -1) return NULL;
return strdup(buf);
}
}

char *linenoise(const char *prompt) {
linenoiseData data;
data.status = linenoiseStatus_ctrl_unknown;
return linenoiseExtra(prompt, &data);
}

/* This is just a wrapper the user may want to call in order to make sure
* the linenoise returned buffer is freed with the same allocator it was
* created with. Useful when the main program is using an alternative
Expand Down
11 changes: 11 additions & 0 deletions lib/wrappers/linenoise/linenoise.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ typedef struct linenoiseCompletions {
char **cvec;
} linenoiseCompletions;

typedef enum linenoiseStatus {
linenoiseStatus_ctrl_unknown,
linenoiseStatus_ctrl_C,
linenoiseStatus_ctrl_D
} linenoiseStatus;

typedef struct linenoiseData {
linenoiseStatus status;
} linenoiseData;

typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
typedef void(linenoiseFreeHintsCallback)(void *);
Expand All @@ -57,6 +67,7 @@ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, const char *);

char *linenoise(const char *prompt);
char *linenoiseExtra(const char *prompt, linenoiseData* data);
void linenoiseFree(void *ptr);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
Expand Down
41 changes: 40 additions & 1 deletion lib/wrappers/linenoise/linenoise.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

type
Completions* = object
len*: csize
len*: csize_t
cvec*: cstringArray

CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.}
Expand All @@ -32,3 +32,42 @@ proc printKeyCodes*() {.importc: "linenoisePrintKeyCodes".}

proc free*(s: cstring) {.importc: "free", header: "<stdlib.h>".}

when defined nimExperimentalLinenoiseExtra:
# C interface
type linenoiseStatus = enum
linenoiseStatus_ctrl_unknown
linenoiseStatus_ctrl_C
linenoiseStatus_ctrl_D

type linenoiseData* = object
status: linenoiseStatus

proc linenoiseExtra(prompt: cstring, data: ptr linenoiseData): cstring {.importc.}

# stable nim interface
type Status* = enum
lnCtrlUnkown
lnCtrlC
lnCtrlD

type ReadLineResult* = object
line*: string
status*: Status

proc readLineStatus*(prompt: string, result: var ReadLineResult) =
## line editing API that allows returning the line entered and an indicator
## of which control key was entered, allowing user to distinguish between
## for example ctrl-C vs ctrl-D.
runnableExamples("-d:nimExperimentalLinenoiseExtra"):
if false:
var ret: ReadLineResult
while true:
readLineStatus("name: ", ret) # ctrl-D will exit, ctrl-C will go to next prompt
if ret.line.len > 0: echo ret.line
if ret.status == lnCtrlD: break
echo "exiting"
var data: linenoiseData
let buf = linenoiseExtra(prompt, data.addr)
result.line = $buf
free(buf)
result.status = data.status.ord.Status