Skip to content

Commit

Permalink
add linenoise.readLineStatus to get status (eg: ctrl-D or ctrl-C) (ni…
Browse files Browse the repository at this point in the history
…m-lang#16977)

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

* changelog
  • Loading branch information
timotheecour authored and ardek66 committed Mar 26, 2021
1 parent 46dea72 commit f09cc7f
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 15 deletions.
2 changes: 2 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,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

0 comments on commit f09cc7f

Please sign in to comment.