engine: console: implement autocomplete for any arg count, add autocomplete for bind for both keys and commands

This commit is contained in:
Alibek Omarov 2019-07-09 15:08:00 +03:00
parent 62fb743a20
commit 7aa163f713
2 changed files with 175 additions and 55 deletions

View File

@ -782,7 +782,7 @@ void pfnResetTutorMessageDecayData( void );
//
// con_utils.c
//
qboolean Cmd_AutocompleteName( const char *source, char *buffer, size_t bufsize );
qboolean Cmd_AutocompleteName( const char *source, int arg, char *buffer, size_t bufsize );
void Con_CompleteCommand( field_t *field );
void Cmd_AutoComplete( char *complete_string );
void Cmd_AutoCompleteClear( void );

View File

@ -25,6 +25,7 @@ extern convar_t *con_gamemaps;
typedef struct autocomplete_list_s
{
const char *name;
int arg; // argument number to handle
qboolean (*func)( const char *s, char *name, int length );
} autocomplete_list_t;
@ -528,7 +529,137 @@ qboolean Cmd_GetItemsList( const char *s, char *completedname, int length )
}
return true;
}
#endif
/*
=====================================
Cmd_GetKeysList
Autocomplete for bind command
=====================================
*/
qboolean Cmd_GetKeysList( const char *s, char *completedname, int length )
{
size_t i, numkeys;
string keys[256];
string matchbuf;
// compare keys list with current keyword
for( i = 0, numkeys = 0; i < 255; i++ )
{
const char *keyname = Key_KeynumToString( i );
if(( *s == '*' ) || !Q_strnicmp( keyname, s, Q_strlen( s )))
Q_strcpy( keys[numkeys++], keyname );
}
if( !numkeys ) return false;
Q_strncpy( matchbuf, keys[0], sizeof( matchbuf ));
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
if( numkeys == 1 ) return true;
for( i = 0; i < numkeys; i++ )
{
Q_strncpy( matchbuf, keys[i], sizeof( matchbuf ));
Con_Printf( "%16s\n", matchbuf );
}
Con_Printf( "\n^3 %i keys found.\n", numkeys );
if( completedname && length )
{
for( i = 0; matchbuf[i]; i++ )
{
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
completedname[i] = 0;
}
}
return true;
}
#endif // XASH_DEDICATED
/*
===============
Con_AddCommandToList
===============
*/
static void Con_AddCommandToList( const char *s, const char *unused1, const char *unused2, void *_autocompleteList )
{
con_autocomplete_t *list = (con_autocomplete_t*)_autocompleteList;
if( *s == '@' ) return; // never show system cvars or cmds
if( list->matchCount >= CON_MAXCMDS ) return; // list is full
if( Q_strnicmp( s, list->completionString, Q_strlen( list->completionString ) ) )
return; // no match
list->cmds[list->matchCount++] = copystring( s );
}
/*
=====================================
Cmd_GetCommandsList
Autocomplete for bind command
=====================================
*/
qboolean Cmd_GetCommandsList( const char *s, char *completedname, int length )
{
size_t i;
string matchbuf;
con_autocomplete_t list; // local autocomplete list
memset( &list, 0, sizeof( list ));
list.completionString = s;
// skip backslash
while( *list.completionString && (*list.completionString == '\\' || *list.completionString == '/') )
list.completionString++;
if( !Q_strlen( list.completionString ) )
return false;
// find matching commands and variables
Cmd_LookupCmds( NULL, &list, (setpair_t)Con_AddCommandToList );
Cvar_LookupVars( 0, NULL, &list, (setpair_t)Con_AddCommandToList );
if( !list.matchCount ) return false;
Q_strncpy( matchbuf, list.cmds[0], sizeof( matchbuf ));
if( completedname && length )
Q_strncpy( completedname, matchbuf, length );
if( list.matchCount == 1 ) return true;
for( i = 0; i < list.matchCount; i++ )
{
Q_strncpy( matchbuf, list.cmds[i], sizeof( matchbuf ));
Con_Printf( "%16s\n", matchbuf );
}
Con_Printf( "\n^3 %i commands found.\n", list.matchCount );
if( completedname && length )
{
for( i = 0; matchbuf[i]; i++ )
{
if( Q_tolower( completedname[i] ) != Q_tolower( matchbuf[i] ))
completedname[i] = 0;
}
}
for( i = 0; i < list.matchCount; i++ )
{
if( list.cmds[i] != NULL )
{
Mem_Free( list.cmds[i] );
}
}
return true;
}
/*
=====================================
@ -831,27 +962,30 @@ int Cmd_CheckMapsList( int fRefresh )
autocomplete_list_t cmd_list[] =
{
{ "map_background", Cmd_GetMapList },
{ "changelevel2", Cmd_GetMapList },
{ "changelevel", Cmd_GetMapList },
{ "playdemo", Cmd_GetDemoList, },
{ "timedemo", Cmd_GetDemoList, },
{ "playvol", Cmd_GetSoundList },
{ "hpkval", Cmd_GetCustomList },
{ "entpatch", Cmd_GetMapList },
{ "music", Cmd_GetMusicList, },
{ "movie", Cmd_GetMovieList },
{ "exec", Cmd_GetConfigList },
{ "map_background", 1, Cmd_GetMapList },
{ "changelevel2", 1, Cmd_GetMapList },
{ "changelevel", 1, Cmd_GetMapList },
{ "playdemo", 1, Cmd_GetDemoList, },
{ "timedemo", 1, Cmd_GetDemoList, },
{ "playvol", 1, Cmd_GetSoundList },
{ "hpkval", 1, Cmd_GetCustomList },
{ "entpatch", 1, Cmd_GetMapList },
{ "music", 1, Cmd_GetMusicList, },
{ "movie", 1, Cmd_GetMovieList },
{ "exec", 1, Cmd_GetConfigList },
#ifndef XASH_DEDICATED
{ "give", Cmd_GetItemsList },
{ "drop", Cmd_GetItemsList },
{ "give", 1, Cmd_GetItemsList },
{ "drop", 1, Cmd_GetItemsList },
{ "bind", 1, Cmd_GetKeysList },
{ "unbind", 1, Cmd_GetKeysList },
{ "bind", 2, Cmd_GetCommandsList },
#endif
{ "game", Cmd_GetGamesList },
{ "save", Cmd_GetSavesList },
{ "load", Cmd_GetSavesList },
{ "play", Cmd_GetSoundList },
{ "map", Cmd_GetMapList },
{ "cd", Cmd_GetCDList },
{ "game", 1, Cmd_GetGamesList },
{ "save", 1, Cmd_GetSavesList },
{ "load", 1, Cmd_GetSavesList },
{ "play", 1, Cmd_GetSoundList },
{ "map", 1, Cmd_GetMapList },
{ "cd", 1, Cmd_GetCDList },
{ NULL }, // termiantor
};
@ -879,36 +1013,19 @@ Autocomplete filename
for various cmds
============
*/
qboolean Cmd_AutocompleteName( const char *source, char *buffer, size_t bufsize )
qboolean Cmd_AutocompleteName( const char *source, int arg, char *buffer, size_t bufsize )
{
autocomplete_list_t *list;
for( list = cmd_list; list->name; list++ )
{
if( Cmd_CheckName( list->name ))
if( list->arg == arg && Cmd_CheckName( list->name ))
return list->func( source, buffer, bufsize );
}
return false;
}
/*
===============
Con_AddCommandToList
===============
*/
static void Con_AddCommandToList( const char *s, const char *unused1, const char *unused2, void *unused3 )
{
if( *s == '@' ) return; // never show system cvars or cmds
if( con.matchCount >= CON_MAXCMDS ) return; // list is full
if( Q_strnicmp( s, con.completionString, Q_strlen( con.completionString ) ) )
return; // no match
con.cmds[con.matchCount++] = copystring( s );
}
/*
=================
Con_SortCmds
@ -1008,16 +1125,11 @@ void Con_CompleteCommand( field_t *field )
nextcmd = (con.completionField->buffer[Q_strlen( con.completionField->buffer ) - 1] == ' ') ? true : false;
con.completionString = Cmd_Argv( 0 );
con.completionBuffer = Cmd_Argv( 1 );
// skip backslash
while( *con.completionString && (*con.completionString == '\\' || *con.completionString == '/') )
con.completionString++;
// skip backslash
while( *con.completionBuffer && (*con.completionBuffer == '\\' || *con.completionBuffer == '/') )
con.completionBuffer++;
if( !Q_strlen( con.completionString ) )
return;
@ -1035,33 +1147,41 @@ void Con_CompleteCommand( field_t *field )
con.shortestMatch[0] = 0;
// find matching commands and variables
Cmd_LookupCmds( NULL, NULL, (setpair_t)Con_AddCommandToList );
Cvar_LookupVars( 0, NULL, NULL, (setpair_t)Con_AddCommandToList );
Cmd_LookupCmds( NULL, &con, (setpair_t)Con_AddCommandToList );
Cvar_LookupVars( 0, NULL, &con, (setpair_t)Con_AddCommandToList );
if( !con.matchCount ) return; // no matches
memcpy( &temp, con.completionField, sizeof( field_t ) );
// autocomplete second arg
if( (Cmd_Argc() == 2) || ((Cmd_Argc() == 1) && nextcmd) )
if( (Cmd_Argc() >= 2) || ((Cmd_Argc() == 1) && nextcmd) )
{
con.completionBuffer = Cmd_Argv( Cmd_Argc() - 1 );
// skip backslash
while( *con.completionBuffer && (*con.completionBuffer == '\\' || *con.completionBuffer == '/') )
con.completionBuffer++;
if( !Q_strlen( con.completionBuffer ) )
return;
if( Cmd_AutocompleteName( con.completionBuffer, filename, sizeof( filename ) ) )
if( Cmd_AutocompleteName( con.completionBuffer, Cmd_Argc() - 1, filename, sizeof( filename ) ) )
{
Q_sprintf( con.completionField->buffer, "%s %s", Cmd_Argv( 0 ), filename );
con.completionField->buffer[0] = 0;
for( i = 0; i < Cmd_Argc() - 1; i++ )
{
Q_strncat( con.completionField->buffer, Cmd_Argv( i ), sizeof( con.completionField->buffer ));
Q_strncat( con.completionField->buffer, " ", sizeof( con.completionField->buffer ));
}
Q_strncat( con.completionField->buffer, filename, sizeof( con.completionField->buffer ));
con.completionField->cursor = Q_strlen( con.completionField->buffer );
}
// don't adjusting cursor pos if we nothing found
return;
}
else if( Cmd_Argc() >= 3 )
{
// disable autocomplete for all next args
return;
}
if( con.matchCount == 1 )
{