Merge pull request #2726 from alexmiller-apple/fdbcli-hints
Update LineNoise, and use hints to provide inline argument descriptions.
This commit is contained in:
commit
b44747fe12
|
@ -113,7 +113,7 @@ LineNoise::LineNoise(
|
|||
for( auto const& c : completions )
|
||||
linenoiseAddCompletion( lc, c.c_str() );
|
||||
});
|
||||
/*linenoiseSetHintsCallback( [](const char* line, int* color, int*bold) -> const char* {
|
||||
linenoiseSetHintsCallback( [](const char* line, int* color, int*bold) -> char* {
|
||||
Hint h = onMainThread( [line]() -> Future<Hint> {
|
||||
return hint_callback(line);
|
||||
}).getBlocking();
|
||||
|
@ -122,7 +122,7 @@ LineNoise::LineNoise(
|
|||
*bold = h.bold;
|
||||
return strdup( h.text.c_str() );
|
||||
});
|
||||
linenoiseSetFreeHintsCallback( free );*/
|
||||
linenoiseSetFreeHintsCallback( free );
|
||||
#endif
|
||||
|
||||
threadPool->addThread(reader);
|
||||
|
|
|
@ -67,6 +67,7 @@ enum {
|
|||
OPT_TIMEOUT,
|
||||
OPT_EXEC,
|
||||
OPT_NO_STATUS,
|
||||
OPT_NO_HINTS,
|
||||
OPT_STATUS_FROM_JSON,
|
||||
OPT_VERSION,
|
||||
OPT_TRACE_FORMAT,
|
||||
|
@ -81,6 +82,7 @@ CSimpleOpt::SOption g_rgOptions[] = { { OPT_CONNFILE, "-C", SO_REQ_SEP },
|
|||
{ OPT_TIMEOUT, "--timeout", SO_REQ_SEP },
|
||||
{ OPT_EXEC, "--exec", SO_REQ_SEP },
|
||||
{ OPT_NO_STATUS, "--no-status", SO_NONE },
|
||||
{ OPT_NO_HINTS, "--no-hints", SO_NONE },
|
||||
{ OPT_HELP, "-?", SO_NONE },
|
||||
{ OPT_HELP, "-h", SO_NONE },
|
||||
{ OPT_HELP, "--help", SO_NONE },
|
||||
|
@ -493,7 +495,7 @@ void initHelp() {
|
|||
"change the class of a process",
|
||||
"If no address and class are specified, lists the classes of all servers.\n\nSetting the class to `default' resets the process class to the class specified on the command line.");
|
||||
helpMap["status"] = CommandHelp(
|
||||
"status [minimal] [details] [json]",
|
||||
"status [minimal|details|json]",
|
||||
"get the status of a FoundationDB cluster",
|
||||
"If the cluster is down, this command will print a diagnostic which may be useful in figuring out what is wrong. If the cluster is running, this command will print cluster statistics.\n\nSpecifying 'minimal' will provide a minimal description of the status of your database.\n\nSpecifying 'details' will provide load information for individual workers.\n\nSpecifying 'json' will provide status information in a machine readable JSON format.");
|
||||
helpMap["exit"] = CommandHelp("exit", "exit the CLI", "");
|
||||
|
@ -544,7 +546,7 @@ void initHelp() {
|
|||
"attempts to kill one or more processes in the cluster",
|
||||
"If no addresses are specified, populates the list of processes which can be killed. Processes cannot be killed before this list has been populated.\n\nIf `all' is specified, attempts to kill all known processes.\n\nIf `list' is specified, displays all known processes. This is only useful when the database is unresponsive.\n\nFor each IP:port pair in <ADDRESS>*, attempt to kill the specified process.");
|
||||
helpMap["profile"] = CommandHelp(
|
||||
"<type> <action> <ARGS>",
|
||||
"profile <client|list|flow|heap> <action> <ARGS>",
|
||||
"namespace for all the profiling-related commands.",
|
||||
"Different types support different actions. Run `profile` to get a list of types, and iteratively explore the help.\n");
|
||||
helpMap["force_recovery_with_data_loss"] = CommandHelp(
|
||||
|
@ -2466,17 +2468,18 @@ void LogCommand(std::string line, UID randomID, std::string errMsg) {
|
|||
|
||||
struct CLIOptions {
|
||||
std::string program_name;
|
||||
int exit_code;
|
||||
int exit_code = -1;
|
||||
|
||||
std::string commandLine;
|
||||
|
||||
std::string clusterFile;
|
||||
bool trace;
|
||||
bool trace = false;
|
||||
std::string traceDir;
|
||||
std::string traceFormat;
|
||||
int exit_timeout;
|
||||
int exit_timeout = 0;
|
||||
Optional<std::string> exec;
|
||||
bool initialStatusCheck;
|
||||
bool initialStatusCheck = true;
|
||||
bool cliHints = true;
|
||||
std::string tlsCertPath;
|
||||
std::string tlsKeyPath;
|
||||
std::string tlsVerifyPeers;
|
||||
|
@ -2486,10 +2489,6 @@ struct CLIOptions {
|
|||
std::vector<std::pair<std::string, std::string>> knobs;
|
||||
|
||||
CLIOptions( int argc, char* argv[] )
|
||||
: trace(false),
|
||||
exit_timeout(0),
|
||||
initialStatusCheck(true),
|
||||
exit_code(-1)
|
||||
{
|
||||
program_name = argv[0];
|
||||
for (int a = 0; a<argc; a++) {
|
||||
|
@ -2573,6 +2572,8 @@ struct CLIOptions {
|
|||
case OPT_NO_STATUS:
|
||||
initialStatusCheck = false;
|
||||
break;
|
||||
case OPT_NO_HINTS:
|
||||
cliHints = false;
|
||||
|
||||
#ifndef TLS_DISABLED
|
||||
// TLS Options
|
||||
|
@ -3702,8 +3703,37 @@ ACTOR Future<int> runCli(CLIOptions opt) {
|
|||
[](std::string const& line, std::vector<std::string>& completions) {
|
||||
fdbcli_comp_cmd(line, completions);
|
||||
},
|
||||
[](std::string const& line)->LineNoise::Hint {
|
||||
return LineNoise::Hint();
|
||||
[enabled=opt.cliHints](std::string const& line)->LineNoise::Hint {
|
||||
if (!enabled) {
|
||||
return LineNoise::Hint();
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
bool partial = false;
|
||||
std::string linecopy = line;
|
||||
std::vector<std::vector<StringRef>> parsed = parseLine(linecopy, error, partial);
|
||||
if (parsed.size() == 0 || parsed.back().size() == 0) return LineNoise::Hint();
|
||||
StringRef command = parsed.back().front();
|
||||
int finishedParameters = parsed.back().size() + error;
|
||||
|
||||
// As a user is typing an escaped character, e.g. \", after the \ and before the " is typed
|
||||
// the string will be a parse error. Ignore this parse error to avoid flipping the hint to
|
||||
// {malformed escape sequence} and back to the original hint for the span of one character
|
||||
// being entered.
|
||||
if (error && line.back() != '\\') return LineNoise::Hint(std::string(" {malformed escape sequence}"), 90, false);
|
||||
|
||||
auto iter = helpMap.find(command.toString());
|
||||
if (iter != helpMap.end()) {
|
||||
std::string helpLine = iter->second.usage;
|
||||
std::vector<std::vector<StringRef>> parsedHelp = parseLine(helpLine, error, partial);
|
||||
std::string hintLine = (*(line.end() - 1) == ' ' ? "" : " ");
|
||||
for (int i = finishedParameters; i < parsedHelp.back().size(); i++) {
|
||||
hintLine = hintLine + parsedHelp.back()[i].toString() + " ";
|
||||
}
|
||||
return LineNoise::Hint(hintLine, 90, false);
|
||||
} else {
|
||||
return LineNoise::Hint();
|
||||
}
|
||||
},
|
||||
1000,
|
||||
false);
|
||||
|
|
|
@ -111,6 +111,7 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
@ -120,6 +121,8 @@
|
|||
#define LINENOISE_MAX_LINE 4096
|
||||
static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
|
||||
static linenoiseCompletionCallback *completionCallback = NULL;
|
||||
static linenoiseHintsCallback *hintsCallback = NULL;
|
||||
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;
|
||||
|
||||
static struct termios orig_termios; /* In order to restore at exit.*/
|
||||
static int rawmode = 0; /* For atexit() function to check if restore is needed*/
|
||||
|
@ -407,6 +410,18 @@ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
|
|||
completionCallback = fn;
|
||||
}
|
||||
|
||||
/* Register a hits function to be called to show hits to the user at the
|
||||
* right of the prompt. */
|
||||
void linenoiseSetHintsCallback(linenoiseHintsCallback *fn) {
|
||||
hintsCallback = fn;
|
||||
}
|
||||
|
||||
/* Register a function to free the hints returned by the hints callback
|
||||
* registered with linenoiseSetHintsCallback(). */
|
||||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *fn) {
|
||||
freeHintsCallback = 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
|
||||
|
@ -456,6 +471,32 @@ static void abFree(struct abuf *ab) {
|
|||
free(ab->b);
|
||||
}
|
||||
|
||||
/* Helper of refreshSingleLine() and refreshMultiLine() to show hints
|
||||
* to the right of the prompt. */
|
||||
void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
|
||||
char seq[64];
|
||||
if (hintsCallback && plen+l->len < l->cols) {
|
||||
int color = -1, bold = 0;
|
||||
char *hint = hintsCallback(l->buf,&color,&bold);
|
||||
if (hint) {
|
||||
int hintlen = strlen(hint);
|
||||
int hintmaxlen = l->cols-(plen+l->len);
|
||||
if (hintlen > hintmaxlen) hintlen = hintmaxlen;
|
||||
if (bold == 1 && color == -1) color = 37;
|
||||
if (color != -1 || bold != 0)
|
||||
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
|
||||
else
|
||||
seq[0] = '\0';
|
||||
abAppend(ab,seq,strlen(seq));
|
||||
abAppend(ab,hint,hintlen);
|
||||
if (color != -1 || bold != 0)
|
||||
abAppend(ab,"\033[0m",4);
|
||||
/* Call the function to free the hint returned. */
|
||||
if (freeHintsCallback) freeHintsCallback(hint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Single line low level line refresh.
|
||||
*
|
||||
* Rewrite the currently edited line accordingly to the buffer content,
|
||||
|
@ -485,6 +526,8 @@ static void refreshSingleLine(struct linenoiseState *l) {
|
|||
/* Write the prompt and the current buffer content */
|
||||
abAppend(&ab,l->prompt,strlen(l->prompt));
|
||||
abAppend(&ab,buf,len);
|
||||
/* Show hits if any. */
|
||||
refreshShowHints(&ab,l,plen);
|
||||
/* Erase to right */
|
||||
snprintf(seq,64,"\x1b[0K");
|
||||
abAppend(&ab,seq,strlen(seq));
|
||||
|
@ -538,6 +581,9 @@ static void refreshMultiLine(struct linenoiseState *l) {
|
|||
abAppend(&ab,l->prompt,strlen(l->prompt));
|
||||
abAppend(&ab,l->buf,l->len);
|
||||
|
||||
/* Show hits if any. */
|
||||
refreshShowHints(&ab,l,plen);
|
||||
|
||||
/* If we are at the very end of the screen with our prompt, we need to
|
||||
* emit a newline and move the prompt to the first column. */
|
||||
if (l->pos &&
|
||||
|
@ -598,7 +644,7 @@ int linenoiseEditInsert(struct linenoiseState *l, char c) {
|
|||
l->pos++;
|
||||
l->len++;
|
||||
l->buf[l->len] = '\0';
|
||||
if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) {
|
||||
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;
|
||||
|
@ -772,6 +818,14 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
|
|||
history_len--;
|
||||
free(history[history_len]);
|
||||
if (mlmode) linenoiseEditMoveEnd(&l);
|
||||
if (hintsCallback) {
|
||||
/* Force a refresh without hints to leave the previous
|
||||
* line as the user typed it after a newline. */
|
||||
linenoiseHintsCallback *hc = hintsCallback;
|
||||
hintsCallback = NULL;
|
||||
refreshLine(&l);
|
||||
hintsCallback = hc;
|
||||
}
|
||||
return (int)l.len;
|
||||
case CTRL_C: /* ctrl-c */
|
||||
errno = EAGAIN;
|
||||
|
@ -1010,6 +1064,14 @@ char *linenoise(const char *prompt) {
|
|||
}
|
||||
}
|
||||
|
||||
/* 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
|
||||
* allocator. */
|
||||
void linenoiseFree(void *ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/* ================================ History ================================= */
|
||||
|
||||
/* Free the history, but does not reset it. Only used when we have to
|
||||
|
@ -1101,10 +1163,14 @@ int linenoiseHistorySetMaxLen(int len) {
|
|||
/* Save the history in the specified file. On success 0 is returned
|
||||
* otherwise -1 is returned. */
|
||||
int linenoiseHistorySave(const char *filename) {
|
||||
FILE *fp = fopen(filename,"w");
|
||||
mode_t old_umask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
|
||||
FILE *fp;
|
||||
int j;
|
||||
|
||||
fp = fopen(filename,"w");
|
||||
umask(old_umask);
|
||||
if (fp == NULL) return -1;
|
||||
chmod(filename,S_IRUSR|S_IWUSR);
|
||||
for (j = 0; j < history_len; j++)
|
||||
fprintf(fp,"%s\n",history[j]);
|
||||
fclose(fp);
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
#ifndef __LINENOISE_H
|
||||
#define __LINENOISE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
@ -49,10 +51,15 @@ typedef struct linenoiseCompletions {
|
|||
} linenoiseCompletions;
|
||||
|
||||
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
|
||||
typedef char*(linenoiseHintsCallback)(const char *, int *color, int *bold);
|
||||
typedef void(linenoiseFreeHintsCallback)(void *);
|
||||
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
||||
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
|
||||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
|
||||
char *linenoise(const char *prompt);
|
||||
void linenoiseFree(void *ptr);
|
||||
int linenoiseHistoryAdd(const char *line);
|
||||
int linenoiseHistorySetMaxLen(int len);
|
||||
int linenoiseHistorySave(const char *filename);
|
||||
|
|
Loading…
Reference in New Issue