add linenoise.readLineStatus to get status (eg: ctrl-D or ctrl-C) (#16977)
* add linenoise.readLineStatus to get status (eg: ctrl-D or ctrl-C) * changelog
This commit is contained in:
parent
49b64e8dc7
commit
630f867bd7
5 changed files with 77 additions and 15 deletions
|
|
@ -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 `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
|
## Language changes
|
||||||
|
|
||||||
- `nimscript` now handles `except Exception as e`.
|
- `nimscript` now handles `except Exception as e`.
|
||||||
|
|
|
||||||
|
|
@ -12,16 +12,18 @@
|
||||||
## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine``
|
## (e.g. you can navigate with the arrow keys). On Windows ``system.readLine``
|
||||||
## is used. This suffices because Windows' console already provides the
|
## is used. This suffices because Windows' console already provides the
|
||||||
## wanted functionality.
|
## 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 {.
|
proc readLineFromStdin*(prompt: string): string {.
|
||||||
tags: [ReadIOEffect, WriteIOEffect].} =
|
tags: [ReadIOEffect, WriteIOEffect].} =
|
||||||
## Reads a line from stdin.
|
## Reads a line from stdin.
|
||||||
|
|
|
||||||
|
|
@ -765,7 +765,7 @@ void linenoiseEditDeletePrevWord(struct linenoiseState *l) {
|
||||||
* when ctrl+d is typed.
|
* when ctrl+d is typed.
|
||||||
*
|
*
|
||||||
* The function returns the length of the current buffer. */
|
* 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;
|
struct linenoiseState l;
|
||||||
|
|
||||||
|
|
@ -827,6 +827,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||||
return (int)l.len;
|
return (int)l.len;
|
||||||
case CTRL_C: /* ctrl-c */
|
case CTRL_C: /* ctrl-c */
|
||||||
errno = EAGAIN;
|
errno = EAGAIN;
|
||||||
|
data->status = linenoiseStatus_ctrl_C;
|
||||||
return -1;
|
return -1;
|
||||||
case BACKSPACE: /* backspace */
|
case BACKSPACE: /* backspace */
|
||||||
case 8: /* ctrl-h */
|
case 8: /* ctrl-h */
|
||||||
|
|
@ -839,6 +840,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
||||||
} else {
|
} else {
|
||||||
history_len--;
|
history_len--;
|
||||||
free(history[history_len]);
|
free(history[history_len]);
|
||||||
|
data->status = linenoiseStatus_ctrl_D;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -979,7 +981,7 @@ void linenoisePrintKeyCodes(void) {
|
||||||
|
|
||||||
/* This function calls the line editing function linenoiseEdit() using
|
/* This function calls the line editing function linenoiseEdit() using
|
||||||
* the STDIN file descriptor set in raw mode. */
|
* 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;
|
int count;
|
||||||
|
|
||||||
if (buflen == 0) {
|
if (buflen == 0) {
|
||||||
|
|
@ -988,7 +990,7 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enableRawMode(STDIN_FILENO) == -1) return -1;
|
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);
|
disableRawMode(STDIN_FILENO);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
return count;
|
return count;
|
||||||
|
|
@ -1035,7 +1037,7 @@ static char *linenoiseNoTTY(void) {
|
||||||
* for a blacklist of stupid terminals, and later either calls the line
|
* 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
|
* editing function or uses dummy fgets() so that you will be able to type
|
||||||
* something even in the most desperate of the conditions. */
|
* 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];
|
char buf[LINENOISE_MAX_LINE];
|
||||||
int count;
|
int count;
|
||||||
|
|
||||||
|
|
@ -1056,12 +1058,18 @@ char *linenoise(const char *prompt) {
|
||||||
}
|
}
|
||||||
return strdup(buf);
|
return strdup(buf);
|
||||||
} else {
|
} else {
|
||||||
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
|
count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt, data);
|
||||||
if (count == -1) return NULL;
|
if (count == -1) return NULL;
|
||||||
return strdup(buf);
|
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
|
/* 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
|
* the linenoise returned buffer is freed with the same allocator it was
|
||||||
* created with. Useful when the main program is using an alternative
|
* created with. Useful when the main program is using an alternative
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,16 @@ typedef struct linenoiseCompletions {
|
||||||
char **cvec;
|
char **cvec;
|
||||||
} linenoiseCompletions;
|
} 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 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 *);
|
||||||
|
|
@ -57,6 +67,7 @@ void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||||
|
|
||||||
char *linenoise(const char *prompt);
|
char *linenoise(const char *prompt);
|
||||||
|
char *linenoiseExtra(const char *prompt, linenoiseData* data);
|
||||||
void linenoiseFree(void *ptr);
|
void linenoiseFree(void *ptr);
|
||||||
int linenoiseHistoryAdd(const char *line);
|
int linenoiseHistoryAdd(const char *line);
|
||||||
int linenoiseHistorySetMaxLen(int len);
|
int linenoiseHistorySetMaxLen(int len);
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
type
|
type
|
||||||
Completions* = object
|
Completions* = object
|
||||||
len*: csize
|
len*: csize_t
|
||||||
cvec*: cstringArray
|
cvec*: cstringArray
|
||||||
|
|
||||||
CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.}
|
CompletionCallback* = proc (a2: cstring; a3: ptr Completions) {.cdecl.}
|
||||||
|
|
@ -32,3 +32,42 @@ proc printKeyCodes*() {.importc: "linenoisePrintKeyCodes".}
|
||||||
|
|
||||||
proc free*(s: cstring) {.importc: "free", header: "<stdlib.h>".}
|
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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue