From 93af9c4cfbf383f5cf146a95711008dcb052b278 Mon Sep 17 00:00:00 2001 From: Steve Bennett Date: Fri, 15 Sep 2017 11:53:03 +1000 Subject: [PATCH] history: Add autocompletion support With 'history completion ' Signed-off-by: Steve Bennett --- examples/jtclsh.tcl | 2 ++ jim-history.c | 15 +++++++++++- jim-interactive.c | 70 +++++++++++++++++++++++++++++++++++++++++------------ jim.h | 3 ++- 4 files changed, 72 insertions(+), 18 deletions(-) diff --git a/examples/jtclsh.tcl b/examples/jtclsh.tcl index b384717..772b327 100644 --- a/examples/jtclsh.tcl +++ b/examples/jtclsh.tcl @@ -8,6 +8,8 @@ package require history set histfile [env HOME]/.jtclsh history load $histfile +# Use the standard Tcl autocompletion +history completion tcl::autocomplete set prefix "" while {1} { # Read a complete line (script) diff --git a/jim-history.c b/jim-history.c index 9a56e04..fe6e628 100644 --- a/jim-history.c +++ b/jim-history.c @@ -9,7 +9,7 @@ static int history_cmd_getline(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_Obj *objPtr; - char *line = Jim_HistoryGetline(Jim_String(argv[0])); + char *line = Jim_HistoryGetline(interp, Jim_String(argv[0])); /* On EOF returns -1 if varName was specified; otherwise the empty string. */ if (line == NULL) { @@ -35,6 +35,12 @@ static int history_cmd_getline(Jim_Interp *interp, int argc, Jim_Obj *const *arg return JIM_OK; } +static int history_cmd_setcompletion(Jim_Interp *interp, int argc, Jim_Obj *const *argv) +{ + Jim_HistorySetCompletion(interp, Jim_Length(argv[0]) ? argv[0] : NULL); + return JIM_OK; +} + static int history_cmd_load(Jim_Interp *interp, int argc, Jim_Obj *const *argv) { Jim_HistoryLoad(Jim_String(argv[0])); @@ -67,6 +73,13 @@ static const jim_subcmd_type history_command_table[] = { 2, /* Description: Reads one line from the user. Similar to gets. */ }, + { "completion", + "command", + history_cmd_setcompletion, + 1, + 1, + /* Description: Sets an autocompletion callback command, or none if "" */ + }, { "load", "filename", history_cmd_load, diff --git a/jim-interactive.c b/jim-interactive.c index 8a18092..8a163c8 100644 --- a/jim-interactive.c +++ b/jim-interactive.c @@ -16,13 +16,29 @@ #define MAX_LINE_LEN 512 #endif +#ifdef USE_LINENOISE +static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata); +static const char completion_callback_assoc_key[] = "interactive-completion"; +#endif + /** * Returns an allocated line, or NULL if EOF. */ -char *Jim_HistoryGetline(const char *prompt) +char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt) { #ifdef USE_LINENOISE - return linenoise(prompt); + struct JimCompletionInfo *compinfo = Jim_GetAssocData(interp, completion_callback_assoc_key); + char *result; + /* Set any completion callback just during the call to linenoise() + * to allow for per-interp settings + */ + if (compinfo) { + linenoiseSetCompletionCallback(JimCompletionCallback, compinfo); + } + result = linenoise(prompt); + /* unset the callback */ + linenoiseSetCompletionCallback(NULL, NULL); + return result; #else int len; char *line = malloc(MAX_LINE_LEN); @@ -90,7 +106,7 @@ struct JimCompletionInfo { Jim_Obj *command; }; -void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata) +static void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void *userdata) { struct JimCompletionInfo *info = (struct JimCompletionInfo *)userdata; Jim_Obj *objv[2]; @@ -111,15 +127,47 @@ void JimCompletionCallback(const char *prefix, linenoiseCompletions *comp, void } } } + +static void JimHistoryFreeCompletion(Jim_Interp *interp, void *data) +{ + struct JimCompletionInfo *compinfo = data; + + Jim_DecrRefCount(interp, compinfo->command); + + Jim_Free(compinfo); +} #endif +/** + * Sets a completion command to be used with Jim_HistoryGetline() + * If commandObj is NULL, deletes any existing completion command. + */ +void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj) +{ +#ifdef USE_LINENOISE + if (commandObj) { + /* Increment now in case the existing object is the same */ + Jim_IncrRefCount(commandObj); + } + + Jim_DeleteAssocData(interp, completion_callback_assoc_key); + + if (commandObj) { + struct JimCompletionInfo *compinfo = Jim_Alloc(sizeof(*compinfo)); + compinfo->interp = interp; + compinfo->command = commandObj; + + Jim_SetAssocData(interp, completion_callback_assoc_key, JimHistoryFreeCompletion, compinfo); + } +#endif +} + int Jim_InteractivePrompt(Jim_Interp *interp) { int retcode = JIM_OK; char *history_file = NULL; #ifdef USE_LINENOISE const char *home; - struct JimCompletionInfo compinfo; home = getenv("HOME"); if (home && isatty(STDIN_FILENO)) { @@ -129,12 +177,7 @@ int Jim_InteractivePrompt(Jim_Interp *interp) Jim_HistoryLoad(history_file); } - compinfo.interp = interp; - compinfo.command = Jim_NewStringObj(interp, "tcl::autocomplete", -1); - Jim_IncrRefCount(compinfo.command); - - /* Register a callback function for tab-completion. */ - linenoiseSetCompletionCallback(JimCompletionCallback, &compinfo); + Jim_HistorySetCompletion(interp, Jim_NewStringObj(interp, "tcl::autocomplete", -1)); #endif printf("Welcome to Jim version %d.%d\n", @@ -167,7 +210,7 @@ int Jim_InteractivePrompt(Jim_Interp *interp) char state; char *line; - line = Jim_HistoryGetline(prompt); + line = Jim_HistoryGetline(interp, prompt); if (line == NULL) { if (errno == EINTR) { continue; @@ -217,10 +260,5 @@ int Jim_InteractivePrompt(Jim_Interp *interp) out: Jim_Free(history_file); -#ifdef USE_LINENOISE - Jim_DecrRefCount(interp, compinfo.command); - linenoiseSetCompletionCallback(NULL, NULL); -#endif - return retcode; } diff --git a/jim.h b/jim.h index 9338f86..bf8acb2 100644 --- a/jim.h +++ b/jim.h @@ -880,7 +880,8 @@ JIM_EXPORT void Jim_MakeErrorMessage (Jim_Interp *interp); JIM_EXPORT int Jim_InteractivePrompt (Jim_Interp *interp); JIM_EXPORT void Jim_HistoryLoad(const char *filename); JIM_EXPORT void Jim_HistorySave(const char *filename); -JIM_EXPORT char *Jim_HistoryGetline(const char *prompt); +JIM_EXPORT char *Jim_HistoryGetline(Jim_Interp *interp, const char *prompt); +JIM_EXPORT void Jim_HistorySetCompletion(Jim_Interp *interp, Jim_Obj *commandObj); JIM_EXPORT void Jim_HistoryAdd(const char *line); JIM_EXPORT void Jim_HistoryShow(void); -- 2.11.4.GIT