readline/NSH: Extend the tab-completion logic so that NSH commands can also be completed by pressing the tab key

This commit is contained in:
Gregory Nutt 2015-07-30 12:11:58 -06:00
parent 0c85a9f4b3
commit 820c5c42dd
8 changed files with 401 additions and 36 deletions

View File

@ -1368,3 +1368,6 @@
* apps/system/readline: Add support for Unix-style tab complete toi * apps/system/readline: Add support for Unix-style tab complete toi
readline. This currently works only for built-in functions.i readline. This currently works only for built-in functions.i
Contributed by Nghia (2015-07-28). Contributed by Nghia (2015-07-28).
* apps/system/readline and apps/nshlib: Extended the tab-completion
support to also expand NSH command names (2015-07-30).

View File

@ -47,6 +47,27 @@
* Pre-processor Definitions * Pre-processor Definitions
****************************************************************************/ ****************************************************************************/
#ifndef CONFIG_READLINE_MAX_BUILTINS
# define CONFIG_READLINE_MAX_BUILTINS 64
#endif
#ifndef CONFIG_READLINE_MAX_EXTCMDS
# define CONFIG_READLINE_MAX_EXTCMDS 64
#endif
/****************************************************************************
* Public Types
****************************************************************************/
#if defined(CONFIG_READLINE_TABCOMPLETION) && defined(CONFIG_READLINE_HAVE_EXTMATCH)
struct extmatch_vtable_s
{
CODE int (*count_matches)(FAR char *name, FAR int *matches, int namelen);
CODE FAR const char *(*getname)(int index);
};
#endif
/**************************************************************************** /****************************************************************************
* Public Data * Public Data
****************************************************************************/ ****************************************************************************/
@ -72,10 +93,11 @@ extern "C"
* to reprint the prompt string. * to reprint the prompt string.
* *
* Input Parameters: * Input Parameters:
* prompt - The prompt string. * prompt - The prompt string. This function may then be
* called with that value in order to restore the previous vtable.
* *
* Returned values: * Returned values:
* None * Returns the previous value of the prompt string
* *
* Assumptions: * Assumptions:
* The prompt string is statically allocated a global. readline will * The prompt string is statically allocated a global. readline will
@ -87,11 +109,40 @@ extern "C"
**************************************************************************/ **************************************************************************/
#ifdef CONFIG_READLINE_TABCOMPLETION #ifdef CONFIG_READLINE_TABCOMPLETION
void readline_prompt(FAR const char *prompt); FAR const char *readline_prompt(FAR const char *prompt);
#else #else
# define readline_prompt(p) # define readline_prompt(p)
#endif #endif
/****************************************************************************
* Name: readline_extmatch
*
* If the applications supports a command set, then it may call this
* function in order to provide support for tab complete on these\
* "external" commands
*
* Input Parameters:
* vtbl - Callbacks to access the external names.
*
* Returned values:
* Returns the previous vtable pointer. This function may then be
* called with that value in order to restore the previous vtable.
*
* Assumptions:
* The vtbl string is statically allocated a global. readline will
* simply remember the pointer to the structure. The structure must stay
* allocated and available. Only one instance of such a structure is
* upported. If there are multiple clients of readline, they must all
* share the same tab-completion logic (with exceptions in the case of
* the kernel build).
*
**************************************************************************/
#if defined(CONFIG_READLINE_TABCOMPLETION) && defined(CONFIG_READLINE_HAVE_EXTMATCH)
FAR const struct extmatch_vtable_s *
readline_extmatch(FAR const struct extmatch_vtable_s *vtbl);
#endif
/**************************************************************************** /****************************************************************************
* Name: readline * Name: readline
* *

View File

@ -8,6 +8,7 @@ config NSH_LIBRARY
default n default n
select NETUTILS_NETLIB if NET select NETUTILS_NETLIB if NET
select LIBC_NETDB if NET select LIBC_NETDB if NET
select READLINE_HAVE_EXTMATCH
---help--- ---help---
Build the NSH support library. This is used, for example, by Build the NSH support library. This is used, for example, by
examples/nsh in order to implement the full NuttShell (NSH). examples/nsh in order to implement the full NuttShell (NSH).

View File

@ -1055,4 +1055,49 @@ void nsh_usbtrace(void);
# endif # endif
#endif #endif
/****************************************************************************
* Name: nsh_extmatch_count
*
* Description:
* This support function is used to provide support for realine tab-
* completion logic nsh_extmatch_count() counts the number of matching
* nsh command names
*
* Input Parameters:
* name - A point to the name containing the name to be matched.
* matches - A table is size CONFIG_READLINE_MAX_EXTCMDS that can
* be used to remember matching name indices.
* namelen - The lenght of the name to match
*
* Returned Values:
* The number commands that match to the first namelen characters.
*
****************************************************************************/
#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \
defined(CONFIG_READLINE_HAVE_EXTMATCH)
int nsh_extmatch_count(FAR char *name, FAR int *matches, int namelen);
#endif
/****************************************************************************
* Name: nsh_extmatch_getname
*
* Description:
* This support function is used to provide support for realine tab-
* completion logic nsh_extmatch_getname() will return the full command
* string from an index that was previously saved by nsh_exmatch_count().
*
* Input Parameters:
* index - The index of the command name to be returned.
*
* Returned Values:
* The numb
*
****************************************************************************/
#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \
defined(CONFIG_READLINE_HAVE_EXTMATCH)
FAR const char *nsh_extmatch_getname(int index);
#endif
#endif /* __APPS_NSHLIB_NSH_H */ #endif /* __APPS_NSHLIB_NSH_H */

View File

@ -45,6 +45,10 @@
# include <nuttx/binfmt/builtin.h> # include <nuttx/binfmt/builtin.h>
#endif #endif
#if defined(CONFIG_SYSTEM_READLINE) && defined(CONFIG_READLINE_HAVE_EXTMATCH)
# include <apps/readline.h>
#endif
#include "nsh.h" #include "nsh.h"
#include "nsh_console.h" #include "nsh_console.h"
@ -824,3 +828,73 @@ int nsh_command(FAR struct nsh_vtbl_s *vtbl, int argc, char *argv[])
ret = handler(vtbl, argc, argv); ret = handler(vtbl, argc, argv);
return ret; return ret;
} }
/****************************************************************************
* Name: nsh_extmatch_count
*
* Description:
* This support function is used to provide support for realine tab-
* completion logic nsh_extmatch_count() counts the number of matching
* nsh command names
*
* Input Parameters:
* name - A point to the name containing the name to be matched.
* matches - A table is size CONFIG_READLINE_MAX_EXTCMDS that can
* be used to remember matching name indices.
* namelen - The lenght of the name to match
*
* Returned Values:
* The number commands that match to the first namelen characters.
*
****************************************************************************/
#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \
defined(CONFIG_READLINE_HAVE_EXTMATCH)
int nsh_extmatch_count(FAR char *name, FAR int *matches, int namelen)
{
int nr_matches = 0;
int i;
for (i = 0; i < NUM_CMDS; i++)
{
if (strncmp(name, g_cmdmap[i].cmd, namelen) == 0)
{
matches[nr_matches] = i;
nr_matches++;
if (nr_matches >= CONFIG_READLINE_MAX_EXTCMDS)
{
break;
}
}
}
return nr_matches;
}
#endif
/****************************************************************************
* Name: nsh_extmatch_getname
*
* Description:
* This support function is used to provide support for realine tab-
* completion logic nsh_extmatch_getname() will return the full command
* string from an index that was previously saved by nsh_exmatch_count().
*
* Input Parameters:
* index - The index of the command name to be returned.
*
* Returned Values:
* The numb
*
****************************************************************************/
#if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \
defined(CONFIG_READLINE_HAVE_EXTMATCH)
FAR const char *nsh_extmatch_getname(int index)
{
DEBUGASSERT(index > 0 && index <= NUM_CMDS);
return g_cmdmap[index].cmd;
}
#endif

View File

@ -40,6 +40,7 @@
#include <nuttx/config.h> #include <nuttx/config.h>
#include <sys/boardctl.h> #include <sys/boardctl.h>
#include <apps/readline.h> #include <apps/readline.h>
#include <apps/nsh.h> #include <apps/nsh.h>
@ -61,7 +62,16 @@
* Private Data * Private Data
****************************************************************************/ ****************************************************************************/
/**************************************************************************** #if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION) && \
defined(CONFIG_READLINE_HAVE_EXTMATCH)
static const struct extmatch_vtable_s g_nsh_extmatch =
{
nsh_extmatch_count, /* count_matches */
nsh_extmatch_getname /* getname */
};
#endif
/**************************************************************************
* Public Data * Public Data
****************************************************************************/ ****************************************************************************/
@ -105,9 +115,15 @@ void nsh_initialize(void)
(void)nsh_netinit(); (void)nsh_netinit();
#ifdef CONFIG_READLINE_TABCOMPLETION #if defined(CONFIG_NSH_READLINE) && defined(CONFIG_READLINE_TABCOMPLETION)
/* Configure the NSH prompt */ /* Configure the NSH prompt */
readline_prompt(g_nshprompt); (void)readline_prompt(g_nshprompt);
#ifdef CONFIG_READLINE_HAVE_EXTMATCH
/* Set up for tab completion on NSH commands */
(void)readline_extmatch(&g_nsh_extmatch);
#endif
#endif #endif
} }

View File

@ -9,6 +9,10 @@ menuconfig SYSTEM_READLINE
---help--- ---help---
Enable support for the readline() function. Enable support for the readline() function.
config READLINE_HAVE_EXTMATCH
bool
default n
if SYSTEM_READLINE if SYSTEM_READLINE
config READLINE_ECHO config READLINE_ECHO
@ -23,9 +27,28 @@ config READLINE_ECHO
config READLINE_TABCOMPLETION config READLINE_TABCOMPLETION
bool "Tab completion" bool "Tab completion"
default n default n
depends on BUILD_FLAT && BUILTIN depends on (BUILD_FLAT && BUILTIN) || READLINE_HAVE_EXTMATCH
---help--- ---help---
Build in support for Unix-style tab completion. This feature was Build in support for Unix-style tab completion. This feature was
provided by Nghia. originally provided by Nghia.
endif if READLINE_TABCOMPLETION
config READLINE_MAX_BUILTINS
int "Maximum built-in matches"
default 64
depends on BUILTIN
---help---
This the maximum number of matching names of builtin commands that
will be displayed.
config READLINE_MAX_EXTCMDS
int "Maximum built-in matches"
default 64
depends on READLINE_HAVE_EXTMATCH
---help---
This the maximum number of matching names of builtin commands that
will be displayed.
endif # READLINE_TABCOMPLETION
endif # SYSTEM_READLINE

View File

@ -1,7 +1,7 @@
/**************************************************************************** /****************************************************************************
* apps/system/readline/readline_common.c * apps/system/readline/readline_common.c
* *
* Copyright (C) 2007-2008, 2011-2013 Gregory Nutt. All rights reserved. * Copyright (C) 2007-2008, 2011-2013, 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org> * Author: Gregory Nutt <gnutt@nuttx.org>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -64,12 +64,56 @@ static const char g_erasetoeol[] = VT100_CLEAREOL;
/* Prompt string to present at the beginning of the line */ /* Prompt string to present at the beginning of the line */
static FAR const char *g_readline_prompt = NULL; static FAR const char *g_readline_prompt = NULL;
#ifdef CONFIG_READLINE_HAVE_EXTMATCH
static FAR const struct extmatch_vtable_s *g_extmatch_vtbl = NULL;
#endif
#endif #endif
/**************************************************************************** /****************************************************************************
* Private Functions * Private Functions
****************************************************************************/ ****************************************************************************/
/****************************************************************************
* Name: count_builtin_maches
*
* Description:
* Count the number of builtin commands
*
* Input Parameters:
* matches - Array to save builtin command index.
* len - The length of the matching name to try
*
* Returned Value:
* The number of matching names
*
**************************************************************************/
#if defined(CONFIG_READLINE_TABCOMPLETION) && defined(CONFIG_BUILTIN)
static int count_builtin_maches(FAR char *buf, FAR int *matches, int namelen)
{
FAR const char *name;
int nr_matches = 0;
int i;
for (i = 0; (name = builtin_getname(i)) != NULL; i++)
{
if (strncmp(buf, name, namelen) == 0)
{
matches[nr_matches] = i;
nr_matches++;
if (nr_matches >= CONFIG_READLINE_MAX_BUILTINS)
{
break;
}
}
}
return nr_matches;
}
#endif
/**************************************************************************** /****************************************************************************
* Name: tab_completion * Name: tab_completion
* *
@ -87,36 +131,83 @@ static FAR const char *g_readline_prompt = NULL;
**************************************************************************/ **************************************************************************/
#ifdef CONFIG_READLINE_TABCOMPLETION #ifdef CONFIG_READLINE_TABCOMPLETION
void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch) static void tab_completion(FAR struct rl_common_s *vtbl, char *buf,
int *nch)
{ {
FAR const char *name = NULL; FAR const char *name = NULL;
int num_matches = 0; #ifdef CONFIG_BUILTIN
int matches[128]; int nr_builtin_matches = 0;
int builtin_matches[CONFIG_READLINE_MAX_BUILTINS];
#endif
#ifdef CONFIG_BUILTIN
int nr_ext_matches = 0;
int ext_matches[CONFIG_READLINE_MAX_EXTCMDS];
#endif
int nr_matches;
int len = *nch; int len = *nch;
int i; int i;
int j; int j;
if (len >= 1) if (len >= 1)
{ {
for (i = 0; (name = builtin_getname(i)) != NULL; i++) #ifdef CONFIG_BUILTIN
{ /* Count the matching builtin commands */
if (!strncmp(buf, name, len))
{
matches[num_matches] = i;
num_matches++;
if (num_matches >= sizeof(matches) / sizeof(int)) nr_builtin_matches = count_builtin_maches(buf, builtin_matches, len);
nr_matches = nr_builtin_matches;
#else
nr_matches = 0;
#endif
#ifdef CONFIG_READLINE_HAVE_EXTMATCH
/* Is there registered external handling logic? */
nr_ext_matches = 0;
if (g_extmatch_vtbl != NULL)
{ {
break; /* Count the number of external commands */
}
} nr_ext_matches = g_extmatch_vtbl->count_matches(buf, ext_matches, len);
nr_matches += nr_ext_matches;
} }
if (num_matches == 1) #endif
{
name = builtin_getname(matches[0]);
int name_len = strlen(name); /* Is there only one matching name? */
if (nr_matches == 1)
{
int name_len;
/* Yes... that that is the one we want. Was it a match with a
* builtin command? Or with an external command.
*/
#ifdef CONFIG_BUILTIN
#ifdef CONFIG_READLINE_HAVE_EXTMATCH
if (nr_builtin_matches == 1)
#endif
{
/* It is a match with a builtin command */
name = builtin_getname(builtin_matches[0]);
}
#endif
#ifdef CONFIG_READLINE_HAVE_EXTMATCH
#ifdef CONFIG_BUILTIN
else
#endif
{
/* It is a match with an external command */
name = g_extmatch_vtbl->getname(ext_matches[0]);
}
#endif
/* Copy the name to the command buffer and to the display. */
name_len = strlen(name);
for (j = len; j < name_len; j++) for (j = len; j < name_len; j++)
{ {
@ -124,22 +215,26 @@ void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch)
RL_PUTC(vtbl, name[j]); RL_PUTC(vtbl, name[j]);
} }
/* Don't remove extra characters after the completed word, if any */ /* Don't remove extra characters after the completed word, if any. */
if (len < name_len) if (len < name_len)
{ {
*nch = name_len; *nch = name_len;
} }
} }
else if (num_matches > 1)
/* Are there multiple matching names? */
else if (nr_matches > 1)
{ {
RL_PUTC(vtbl, '\n'); RL_PUTC(vtbl, '\n');
/* possible completion */ #ifdef CONFIG_READLINE_HAVE_EXTMATCH
/* Show the possible external completions */
for (i = 0; i < num_matches; i++) for (i = 0; i < nr_ext_matches; i++)
{ {
name = builtin_getname(matches[i]); name = g_extmatch_vtbl->getname(ext_matches[i]);
RL_PUTC(vtbl, ' '); RL_PUTC(vtbl, ' ');
RL_PUTC(vtbl, ' '); RL_PUTC(vtbl, ' ');
@ -151,6 +246,26 @@ void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch)
RL_PUTC(vtbl, '\n'); RL_PUTC(vtbl, '\n');
} }
#endif
#ifdef CONFIG_BUILTIN
/* Show the possible builtin completions */
for (i = 0; i < nr_builtin_matches; i++)
{
name = builtin_getname(builtin_matches[i]);
RL_PUTC(vtbl, ' ');
RL_PUTC(vtbl, ' ');
for (j = 0; j < strlen(name); j++)
{
RL_PUTC(vtbl, name[j]);
}
RL_PUTC(vtbl, '\n');
}
#endif
/* Output the original prompt */ /* Output the original prompt */
@ -184,10 +299,11 @@ void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch)
* to reprint the prompt string. * to reprint the prompt string.
* *
* Input Parameters: * Input Parameters:
* prompt - The prompt string. * prompt - The prompt string. This function may then be
* called with that value in order to restore the previous vtable.
* *
* Returned values: * Returned values:
* None * Returns the previous value of the prompt string
* *
* Assumptions: * Assumptions:
* The prompt string is statically allocated a global. readline will * The prompt string is statically allocated a global. readline will
@ -199,9 +315,45 @@ void tab_completion(FAR struct rl_common_s *vtbl, char *buf, int *nch)
**************************************************************************/ **************************************************************************/
#ifdef CONFIG_READLINE_TABCOMPLETION #ifdef CONFIG_READLINE_TABCOMPLETION
void readline_prompt(FAR const char *prompt) FAR const char *readline_prompt(FAR const char *prompt)
{ {
FAR const char *ret = g_readline_prompt;
g_readline_prompt = prompt; g_readline_prompt = prompt;
return ret;
}
#endif
/****************************************************************************
* Name: readline_extmatch
*
* If the applications supports a command set, then it may call this
* function in order to provide support for tab complete on these\
* "external" commands
*
* Input Parameters:
* vtbl - Callbacks to access the external names.
*
* Returned values:
* Returns the previous vtable pointer. This function may then be
* called with that value in order to restore the previous vtable.
*
* Assumptions:
* The vtbl string is statically allocated a global. readline will
* simply remember the pointer to the structure. The structure must stay
* allocated and available. Only one instance of such a structure is
* upported. If there are multiple clients of readline, they must all
* share the same tab-completion logic (with exceptions in the case of
* the kernel build).
*
**************************************************************************/
#if defined(CONFIG_READLINE_TABCOMPLETION) && defined(CONFIG_READLINE_HAVE_EXTMATCH)
FAR const struct extmatch_vtable_s *
readline_extmatch(FAR const struct extmatch_vtable_s *vtbl)
{
FAR const struct extmatch_vtable_s *ret = g_extmatch_vtbl;
g_extmatch_vtbl = vtbl;
return ret;
} }
#endif #endif