com_completionStyle 1 for cycling auto-completion results line in ET

This commit is contained in:
myT 2017-07-05 05:53:05 +02:00
parent 9b776ab343
commit 47a566f9cc
11 changed files with 211 additions and 68 deletions

View file

@ -1,6 +1,10 @@
DD Mmm 17 - 1.49
add: com_completionStyle to select the auto-completion style
com_completionStyle 0 (default) = always print all results
com_completionStyle 1 = cycle results like Enemy Territory's console
chg: cvars and commands are alphabetically sorted
fix: commands registered by cgame no longer auto-complete after the cgame qvm has shut down

View file

@ -69,8 +69,10 @@ static console_t con;
int g_console_field_width = CONSOLE_WIDTH;
void Con_ToggleConsole_f( void )
void Con_ToggleConsole_f()
{
g_consoleField.acOffset = 0;
// closing a full screen console restarts the demo loop
if ( cls.state == CA_DISCONNECTED && cls.keyCatchers == KEYCATCH_CONSOLE ) {
CL_StartDemoLoop();
@ -404,7 +406,18 @@ static void Con_DrawInput()
return;
}
float y = con.y - (con.ch * 1.5);
float y = con.y - (con.ch * 1.5f);
// highlight the currently auto-completed part of the edit line
if ( g_consoleField.acOffset > 0 ) {
const int length = g_consoleField.acLength;
if ( length > 0 ) {
const vec4_t highlightColor = { 0.5f, 0.5f, 0.2f, 0.45f };
const int offset = g_consoleField.acOffset;
re.SetColor( highlightColor );
re.DrawStretchPic( con.xadjust + con.cw + offset * con.cw, y, length * con.cw, con.ch, 0, 0, 0, 0, cls.whiteShader );
}
}
re.SetColor( colorBlack );
SCR_DrawChar( con.xadjust + 1, y + 1, con.cw, con.ch, ']' );

View file

@ -435,6 +435,10 @@ CONSOLE LINE EDITING
static void Console_Key( int key )
{
// clear auto-completion buffer when not pressing tab
if ( key != K_TAB )
g_consoleField.acOffset = 0;
// ctrl-L clears screen
if ( key == 'l' && keys[K_CTRL].down ) {
Cbuf_AddText( "clear\n" );
@ -443,6 +447,8 @@ static void Console_Key( int key )
// enter finishes the line
if ( key == K_ENTER || key == K_KP_ENTER ) {
g_consoleField.acOffset = 0;
// if not in the game explicitly prepend a slash if needed
if ( (cls.state != CA_ACTIVE) && g_consoleField.buffer[0]
&& (g_consoleField.buffer[0] != '\\') && (g_consoleField.buffer[0] != '/') ) {
@ -492,12 +498,14 @@ static void Console_Key( int key )
if ( ( key == K_UPARROW ) ||
( ( tolower(key) == 'p' ) && keys[K_CTRL].down ) ) {
g_consoleField.acOffset = 0;
History_GetPreviousCommand( &g_consoleField, &g_history );
return;
}
if ( ( key == K_DOWNARROW ) ||
( ( tolower(key) == 'n' ) && keys[K_CTRL].down ) ) {
g_consoleField.acOffset = 0;
History_GetNextCommand( &g_consoleField, &g_history, g_console_field_width );
return;
}
@ -829,7 +837,7 @@ static void Key_Bind_f()
static void Key_CompleteBind_f( int startArg, int compArg )
{
if ( compArg == startArg + 1 )
Field_AutoCompleteKeyName( startArg, compArg );
Field_AutoCompleteCustom( startArg, compArg, &Field_AutoCompleteKeyName );
else if ( compArg >= startArg + 2 )
Field_AutoCompleteFrom( compArg, compArg, qtrue, qtrue );
}
@ -838,7 +846,7 @@ static void Key_CompleteBind_f( int startArg, int compArg )
static void Key_CompleteUnbind_f( int startArg, int compArg )
{
if ( compArg == startArg + 1 )
Field_AutoCompleteKeyName( startArg, compArg );
Field_AutoCompleteCustom( startArg, compArg, &Field_AutoCompleteKeyName );
}

View file

@ -262,7 +262,7 @@ static void CL_Record_f()
static void CL_CompleteDemoRecord_f( int startArg, int compArg )
{
if ( startArg + 1 == compArg )
Field_AutoCompleteDemoNameWrite( startArg, compArg );
Field_AutoCompleteCustom( startArg, compArg, &Field_AutoCompleteDemoNameWrite );
}
@ -431,7 +431,7 @@ void CL_PlayDemo_f()
static void CL_CompleteDemoPlay_f( int startArg, int compArg )
{
if ( startArg + 1 == compArg )
Field_AutoCompleteDemoNameRead( startArg, compArg );
Field_AutoCompleteCustom( startArg, compArg, &Field_AutoCompleteDemoNameRead );
}
@ -1899,7 +1899,7 @@ static void CL_CallVote_f()
static void CL_CompleteCallVote_f( int startArg, int compArg )
{
if ( compArg == startArg + 2 && !Q_stricmp( Cmd_Argv( startArg + 1 ), "map" ) )
Field_AutoCompleteMapName( startArg, compArg );
Field_AutoCompleteCustom( startArg, compArg, &Field_AutoCompleteMapName );
}

View file

@ -394,7 +394,7 @@ qbool CL_UpdateVisiblePings_f( int source );
// console
//
void CL_ConInit();
void Con_ToggleConsole_f( void );
void Con_ToggleConsole_f();
void Con_ClearNotify();
void Con_RunConsole();
void Con_DrawConsole();

View file

@ -187,7 +187,7 @@ static void Cmd_Exec_f()
static void Cmd_CompleteExec_f( int startArg, int compArg )
{
if ( startArg + 1 == compArg )
Field_AutoCompleteConfigName( startArg, compArg );
Field_AutoCompleteCustom( startArg, compArg, &Field_AutoCompleteConfigName );
}

View file

@ -84,6 +84,8 @@ cvar_t *sv_packetdelay = 0;
cvar_t *com_noErrorInterrupt;
#endif
static cvar_t *com_completionStyle; // 0 = legacy, 1 = ET-style
// com_speeds times
int time_game;
int time_frontend; // renderer frontend time
@ -2217,6 +2219,8 @@ void Com_Init( char *commandLine )
com_noErrorInterrupt = Cvar_Get( "com_noErrorInterrupt", "0", 0 );
#endif
com_completionStyle = Cvar_Get( "com_completionStyle", "0", CVAR_ARCHIVE );
if ( com_dedicated->integer ) {
if ( !com_viewlog->integer ) {
Cvar_Set( "viewlog", "1" );
@ -2331,7 +2335,7 @@ static void Com_WriteConfig_f()
static void Com_CompleteWriteConfig_f( int startArg, int compArg )
{
if ( startArg + 1 == compArg )
Field_AutoCompleteConfigName( startArg, compArg );
Field_AutoCompleteCustom( startArg, compArg, &Field_AutoCompleteConfigName );
}
@ -2625,11 +2629,14 @@ void Field_Clear( field_t* edit )
edit->scroll = 0;
}
static const char* completionString;
static char shortestMatch[MAX_TOKEN_CHARS];
static int matchCount;
// field we are working on, passed to Field_AutoComplete(&g_consoleCommand for instance)
static field_t* completionField;
static const char* completionString;
static char shortestMatch[MAX_TOKEN_CHARS];
static char fullMatch[MAX_TOKEN_CHARS];
static int matchCount;
static int matchIndex;
static qbool findIndexOnly; // for ET-style completion of command arguments
static field_t* completionField; // field we are working on, passed to Field_AutoComplete
static void FindMatches( const char *s )
{
@ -2658,6 +2665,21 @@ static void FindMatches( const char *s )
}
static int findMatchIndex;
static void FindIndexMatch( const char *s )
{
if ( Q_stricmpn( s, completionString, strlen( completionString ) ) ) {
return;
}
if ( findMatchIndex == matchIndex ) {
Q_strncpyz( fullMatch, s, sizeof( fullMatch ) );
}
findMatchIndex++;
}
static void PrintMatches( const char *s )
{
if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
@ -2761,6 +2783,42 @@ static void Field_AutoCompleteCmdOrVarName( int startArg, int compArg, qbool sea
if ( !searchCmds && !searchVars )
return;
field_t* const field = completionField;
if ( field->acOffset > 0 ) {
if ( matchCount > 1 ) {
// find the next match
completionString = shortestMatch;
findMatchIndex = 0;
if ( searchCmds )
Cmd_CommandCompletion( FindIndexMatch );
if ( searchVars )
Cvar_CommandCompletion( FindIndexMatch );
matchIndex = ( matchIndex + 1 ) % matchCount;
// insert it in the edit field
if ( compArg == 0 ) {
Q_strncpyz( field->buffer, fullMatch, sizeof(field->buffer) );
field->cursor = strlen( field->buffer );
Field_AppendLastArgs( field, 1 );
} else {
field->buffer[0] = '\0';
Field_AppendFirstArgs( field, compArg );
Q_strcat( field->buffer, sizeof(field->buffer), " " );
Q_strcat( field->buffer, sizeof(field->buffer), fullMatch );
field->cursor = strlen( field->buffer );
Field_AppendLastArgs( field, compArg + 1 );
}
const int delta = String_HasLeadingSlash( field->buffer ) ? 0 : 1;
field->acLength = field->cursor + delta - field->acOffset;
}
return;
}
*shortestMatch = '\0';
matchCount = 0;
matchIndex = 0;
if ( searchCmds )
Cmd_CommandCompletion( FindMatches );
if ( searchVars )
@ -2769,6 +2827,15 @@ static void Field_AutoCompleteCmdOrVarName( int startArg, int compArg, qbool sea
if ( !Field_CompleteShortestMatch( startArg, compArg ) )
return;
// we found 2+ matches
if ( com_completionStyle->integer ) {
const int delta = String_HasLeadingSlash( field->buffer ) ? 0 : 1;
field->acStartArg = startArg;
field->acCompArg = compArg;
field->acOffset = field->cursor + delta;
field->acLength = 0;
}
if ( searchCmds )
Cmd_CommandCompletion( PrintMatches );
if ( searchVars )
@ -2778,29 +2845,65 @@ static void Field_AutoCompleteCmdOrVarName( int startArg, int compArg, qbool sea
static void Field_AutoCompleteCommandArgument( int startArg, int compArg )
{
field_t* const field = completionField;
if ( field->acOffset == 0 ) {
*shortestMatch = '\0';
matchCount = 0;
matchIndex = 0;
}
const char* cmdName = Cmd_Argv( startArg );
if ( String_HasLeadingSlash( cmdName ) )
cmdName++;
if ( *cmdName == '\0' )
return;
if ( field->acStartArg == startArg && field->acOffset > 0 ) {
if ( matchCount > 1 ) {
// find the next match
completionString = shortestMatch;
findMatchIndex = 0;
findIndexOnly = qtrue;
Cmd_AutoCompleteArgument( cmdName, startArg, compArg );
findIndexOnly = qfalse;
matchIndex = ( matchIndex + 1 ) % matchCount;
// insert it in the edit field
field->buffer[0] = '\0';
Field_AppendFirstArgs( field, compArg );
Q_strcat( field->buffer, sizeof(field->buffer), " " );
Q_strcat( field->buffer, sizeof(field->buffer), fullMatch );
field->cursor = strlen( field->buffer );
Field_AppendLastArgs( field, compArg + 1 );
const int delta = String_HasLeadingSlash( field->buffer ) ? 0 : 1;
field->acLength = field->cursor + delta - field->acOffset;
}
return;
}
Cmd_AutoCompleteArgument( cmdName, startArg, compArg );
// we found 2+ matches
if ( field->acOffset == 0 && matchCount >= 2 && com_completionStyle->integer ) {
const int delta = String_HasLeadingSlash( field->buffer ) ? 0 : 1;
field->acStartArg = startArg;
field->acCompArg = compArg;
field->acOffset = field->cursor + delta;
field->acLength = 0;
}
}
void Field_AutoCompleteFrom( int startArg, int compArg, qbool searchCmds, qbool searchVars )
{
// clear results
matchCount = 0;
*shortestMatch = '\0';
// For the first argument, we always check both variables and commands.
// For other arguments, we run a custom auto-completion handler
// registered by the command if one was provided.
if ( compArg == startArg )
if ( compArg == startArg ) {
Field_AutoCompleteCmdOrVarName( startArg, compArg, searchCmds, searchVars );
else
} else {
Field_AutoCompleteCommandArgument( startArg, compArg );
}
}
@ -2874,57 +2977,50 @@ void Field_AutoComplete( field_t *field, qbool insertBackslash )
}
void Field_AutoCompleteMapName( int startArg, int compArg )
void Field_AutoCompleteCustom( int startArg, int compArg, fieldCompletionHandler_t callback )
{
FS_FilenameCompletion( "maps", "bsp", qtrue, FindMatches, 0 );
if ( Field_CompleteShortestMatch( startArg, compArg ) )
FS_FilenameCompletion( "maps", "bsp", qtrue, PrintMatches, 0 );
}
void Field_AutoCompleteConfigName( int startArg, int compArg )
{
FS_FilenameCompletion( "", "cfg", qtrue, FindMatches, FS_FILTER_INPAK );
if ( Field_CompleteShortestMatch( startArg, compArg ) )
FS_FilenameCompletion( "", "cfg", qtrue, PrintMatches, FS_FILTER_INPAK );
}
#define DEMO_EXT "dm_"STRINGIZE(PROTOCOL_VERSION)
void Field_AutoCompleteDemoNameRead( int startArg, int compArg )
{
FS_FilenameCompletion( "demos", "dm_66", qtrue, FindMatches, 0 );
FS_FilenameCompletion( "demos", "dm_67", qtrue, FindMatches, 0 );
FS_FilenameCompletion( "demos", DEMO_EXT, qtrue, FindMatches, 0 );
if ( Field_CompleteShortestMatch( startArg, compArg ) )
{
FS_FilenameCompletion( "demos", "dm_66", qtrue, PrintMatches, 0 );
FS_FilenameCompletion( "demos", "dm_67", qtrue, PrintMatches, 0 );
FS_FilenameCompletion( "demos", DEMO_EXT, qtrue, PrintMatches, 0 );
if ( findIndexOnly ) {
( *callback )( FindIndexMatch );
return;
}
}
void Field_AutoCompleteDemoNameWrite( int startArg, int compArg )
{
FS_FilenameCompletion( "demos", DEMO_EXT, qtrue, FindMatches, FS_FILTER_INPAK );
( *callback )( FindMatches );
if ( Field_CompleteShortestMatch( startArg, compArg ) )
FS_FilenameCompletion( "demos", DEMO_EXT, qtrue, PrintMatches, FS_FILTER_INPAK );
( *callback )( PrintMatches );
}
#undef DEMO_EXT
void Field_AutoCompleteMapName( fieldCallback_t callback )
{
FS_FilenameCompletion( "maps", "bsp", qtrue, callback, 0 );
}
void Field_AutoCompleteConfigName( fieldCallback_t callback )
{
FS_FilenameCompletion( "", "cfg", qtrue, callback, FS_FILTER_INPAK );
}
void Field_AutoCompleteDemoNameRead( fieldCallback_t callback )
{
FS_FilenameCompletion( "demos", "dm_66", qtrue, callback, 0 );
FS_FilenameCompletion( "demos", "dm_67", qtrue, callback, 0 );
FS_FilenameCompletion( "demos", "dm_68", qtrue, callback, 0 );
}
void Field_AutoCompleteDemoNameWrite( fieldCallback_t callback )
{
FS_FilenameCompletion( "demos", "dm_68", qtrue, callback, FS_FILTER_INPAK );
}
#ifndef DEDICATED
void Field_AutoCompleteKeyName( int startArg, int compArg )
void Field_AutoCompleteKeyName( fieldCallback_t callback )
{
Key_KeyNameCompletion( FindMatches );
if ( Field_CompleteShortestMatch( startArg, compArg ) )
Key_KeyNameCompletion( PrintMatches );
Key_KeyNameCompletion( callback );
}
#endif

View file

@ -651,22 +651,34 @@ Edit fields and command line history/completion
#define MAX_EDIT_LINE 256
typedef struct {
char buffer[MAX_EDIT_LINE];
int cursor;
int scroll;
int widthInChars;
char buffer[MAX_EDIT_LINE];
int acOffset; // auto-completion letter index with the leading slash present
int acLength; // auto-completion letter count
int acStartArg; // auto-completion command token index
int acCompArg; // auto-completion argument token index
} field_t;
void Field_Clear( field_t *edit );
void Field_AutoComplete( field_t *edit, qbool insertBackslash ); // should only be called by Console_Key
// these are the functions you can use from your own command argument auto-completion callbacks
typedef void (*fieldCallback_t)( const char* );
typedef void (*fieldCompletionHandler_t)( fieldCallback_t );
// these are the only Field_ functions you can call from a Cmd_SetAutoCompletion callback
void Field_AutoCompleteFrom( int startArg, int compArg, qbool searchCmds, qbool searchVars );
void Field_AutoCompleteMapName( int startArg, int compArg );
void Field_AutoCompleteConfigName( int startArg, int compArg );
void Field_AutoCompleteDemoNameRead( int startArg, int compArg );
void Field_AutoCompleteDemoNameWrite( int startArg, int compArg );
void Field_AutoCompleteKeyName( int startArg, int compArg );
void Field_AutoCompleteCustom( int startArg, int compArg, fieldCompletionHandler_t callback );
// these should only be passed as an argument to Field_AutoCompleteCustom
void Field_AutoCompleteMapName( fieldCallback_t callback );
void Field_AutoCompleteConfigName( fieldCallback_t callback );
void Field_AutoCompleteDemoNameRead( fieldCallback_t callback );
void Field_AutoCompleteDemoNameWrite( fieldCallback_t callback );
#ifndef DEDICATED
void Field_AutoCompleteKeyName( fieldCallback_t callback );
#endif
#define COMMAND_HISTORY 32
typedef struct {

View file

@ -201,7 +201,7 @@ static void SV_DevMap_f( )
static void SV_CompleteMap_f( int startArg, int compArg )
{
if ( startArg + 1 == compArg )
Field_AutoCompleteMapName( startArg, compArg );
Field_AutoCompleteCustom( startArg, compArg, &Field_AutoCompleteMapName );
}

View file

@ -240,6 +240,10 @@ const char* Sys_ConsoleInput()
if (avail != -1)
{
// we have something
if (key != '\t')
tty_con.acOffset = 0;
if (key == tty_erase || key == 127 || key == 8) // backspace
{
const int length = strlen(tty_con.buffer);

View file

@ -285,6 +285,9 @@ LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
break;
case WM_KEYDOWN:
if ( wParam != VK_TAB )
s_wcd.inputField.acOffset = 0;
if ( wParam == VK_UP )
{
field_t field;
@ -313,6 +316,9 @@ LONG WINAPI InputLineWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
break;
case WM_CHAR:
if ( wParam != VK_TAB )
s_wcd.inputField.acOffset = 0;
if ( wParam == VK_RETURN )
{
GetWindowText( s_wcd.hwndInputLine, inputBuffer, sizeof( inputBuffer ) );