r514 - in trunk/code: client qcommon unix

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Sat Jan 21 20:58:51 EST 2006


Author: tma
Date: 2006-01-21 20:58:50 -0500 (Sat, 21 Jan 2006)
New Revision: 514

Modified:
   trunk/code/client/cl_keys.c
   trunk/code/qcommon/cmd.c
   trunk/code/qcommon/common.c
   trunk/code/qcommon/files.c
   trunk/code/qcommon/q_shared.c
   trunk/code/qcommon/q_shared.h
   trunk/code/qcommon/qcommon.h
   trunk/code/unix/unix_main.c
Log:
* Overhaul of console autocompletion
  - No longer does weird stuff like move the cursor inappropriately
  - Autocomplete works with compound commands
  - Special autocomplete on some commands e.g. \map, \demo
  - Removed various hacks used to counter the original autocomplete code


Modified: trunk/code/client/cl_keys.c
===================================================================
--- trunk/code/client/cl_keys.c	2006-01-21 15:09:35 UTC (rev 513)
+++ trunk/code/client/cl_keys.c	2006-01-22 01:58:50 UTC (rev 514)
@@ -483,7 +483,7 @@
 
 	// enter finishes the line
 	if ( key == K_ENTER || key == K_KP_ENTER ) {
-		// if not in the game explicitly prepent a slash if needed
+		// if not in the game explicitly prepend a slash if needed
 		if ( cls.state != CA_ACTIVE && g_consoleField.buffer[0] != '\\' 
 			&& g_consoleField.buffer[0] != '/' ) {
 			char	temp[MAX_STRING_CHARS];
@@ -528,7 +528,7 @@
 	// command completion
 
 	if (key == K_TAB) {
-		Field_CompleteCommand(&g_consoleField);
+		Field_AutoComplete(&g_consoleField);
 		return;
 	}
 

Modified: trunk/code/qcommon/cmd.c
===================================================================
--- trunk/code/qcommon/cmd.c	2006-01-21 15:09:35 UTC (rev 513)
+++ trunk/code/qcommon/cmd.c	2006-01-22 01:58:50 UTC (rev 514)
@@ -439,7 +439,7 @@
 */
 // NOTE TTimo define that to track tokenization issues
 //#define TKN_DBG
-void Cmd_TokenizeString( const char *text_in ) {
+static void Cmd_TokenizeString2( const char *text_in, qboolean ignoreQuotes ) {
 	const char	*text;
 	char	*textOut;
 
@@ -495,7 +495,7 @@
 
 		// handle quoted strings
     // NOTE TTimo this doesn't handle \" escaping
-		if ( *text == '"' ) {
+		if ( !ignoreQuotes && *text == '"' ) {
 			cmd_argv[cmd_argc] = textOut;
 			cmd_argc++;
 			text++;
@@ -516,7 +516,7 @@
 
 		// skip until whitespace, quote, or command
 		while ( *text > ' ' ) {
-			if ( text[0] == '"' ) {
+			if ( !ignoreQuotes && text[0] == '"' ) {
 				break;
 			}
 
@@ -541,9 +541,26 @@
 	
 }
 
+/*
+============
+Cmd_TokenizeString
+============
+*/
+void Cmd_TokenizeString( const char *text_in ) {
+	Cmd_TokenizeString2( text_in, qfalse );
+}
 
 /*
 ============
+Cmd_TokenizeStringIgnoreQuotes
+============
+*/
+void Cmd_TokenizeStringIgnoreQuotes( const char *text_in ) {
+	Cmd_TokenizeString2( text_in, qtrue );
+}
+
+/*
+============
 Cmd_AddCommand
 ============
 */

Modified: trunk/code/qcommon/common.c
===================================================================
--- trunk/code/qcommon/common.c	2006-01-21 15:09:35 UTC (rev 513)
+++ trunk/code/qcommon/common.c	2006-01-22 01:58:50 UTC (rev 514)
@@ -2912,7 +2912,7 @@
 static const char *completionString;
 static char shortestMatch[MAX_TOKEN_CHARS];
 static int	matchCount;
-// field we are working on, passed to Field_CompleteCommand (&g_consoleCommand for instance)
+// field we are working on, passed to Field_AutoComplete(&g_consoleCommand for instance)
 static field_t *completionField;
 
 /*
@@ -2948,11 +2948,11 @@
 
 /*
 ===============
-PrintCmdMatches
+PrintMatches
 
 ===============
 */
-static void PrintCmdMatches( const char *s ) {
+static void PrintMatches( const char *s ) {
 	if ( !Q_stricmpn( s, shortestMatch, strlen( shortestMatch ) ) ) {
 		Com_Printf( "    %s\n", s );
 	}
@@ -2970,96 +2970,231 @@
 	}
 }
 
-static void keyConcatArgs( void ) {
-	int		i;
-	char	*arg;
+/*
+===============
+Field_FindFirstSeparator
+===============
+*/
+static char *Field_FindFirstSeparator( char *s )
+{
+	int i;
 
-	for ( i = 1 ; i < Cmd_Argc() ; i++ ) {
-		Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
-		arg = Cmd_Argv( i );
-		while (*arg) {
-			if (*arg == ' ') {
-				Q_strcat( completionField->buffer, sizeof( completionField->buffer ),  "\"");
-				break;
-			}
-			arg++;
-		}
-		Q_strcat( completionField->buffer, sizeof( completionField->buffer ),  Cmd_Argv( i ) );
-		if (*arg == ' ') {
-			Q_strcat( completionField->buffer, sizeof( completionField->buffer ),  "\"");
-		}
+	for( i = 0; i < strlen( s ); i++ )
+	{
+		if( s[ i ] == ';' )
+			return &s[ i ];
 	}
+
+	return NULL;
 }
 
-static void ConcatRemaining( const char *src, const char *start ) {
-	char *str;
+/*
+===============
+Field_CompleteFilename
+===============
+*/
+static void Field_CompleteFilename( const char *dir,
+		const char *ext, qboolean stripExt )
+{
+	matchCount = 0;
+	shortestMatch[ 0 ] = 0;
 
-	str = strstr(src, start);
-	if (!str) {
-		keyConcatArgs();
+	FS_FilenameCompletion( dir, ext, stripExt, FindMatches );
+
+	if( matchCount == 0 )
 		return;
+
+	Q_strcat( completionField->buffer, sizeof( completionField->buffer ),
+			shortestMatch + strlen( completionString ) );
+	completionField->cursor = strlen( completionField->buffer );
+
+	if( matchCount == 1 )
+	{
+		Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
+		completionField->cursor++;
+		return;
 	}
 
-	str += strlen(start);
-	Q_strcat( completionField->buffer, sizeof( completionField->buffer ), str);
+	Com_Printf( "]%s\n", completionField->buffer );
+	
+	FS_FilenameCompletion( dir, ext, stripExt, PrintMatches );
 }
 
 /*
 ===============
 Field_CompleteCommand
-
-perform Tab expansion
-NOTE TTimo this was originally client code only
-  moved to common code when writing tty console for *nix dedicated server
 ===============
 */
-void Field_CompleteCommand( field_t *field ) {
-	field_t		temp;
+static void Field_CompleteCommand( char *cmd,
+		qboolean doCommands, qboolean doCvars )
+{
+	int		completionArgument = 0;
+	char	*p;
 
-	completionField = field;
+	// Skip leading whitespace and quotes
+	cmd = Com_SkipCharset( cmd, " \"" );
 
-	// only look at the first token for completion purposes
-	Cmd_TokenizeString( completionField->buffer );
+	Cmd_TokenizeStringIgnoreQuotes( cmd );
+	completionArgument = Cmd_Argc( );
 
-	completionString = Cmd_Argv(0);
-	if ( completionString[0] == '\\' || completionString[0] == '/' ) {
-		completionString++;
+	// If there is trailing whitespace on the cmd
+	if( *( cmd + strlen( cmd ) - 1 ) == ' ' )
+	{
+		completionString = "";
+		completionArgument++;
 	}
-	matchCount = 0;
-	shortestMatch[0] = 0;
+	else
+		completionString = Cmd_Argv( completionArgument - 1 );
 
-	if ( strlen( completionString ) == 0 ) {
-		return;
-	}
+	if( completionArgument > 1 )
+	{
+		const char *baseCmd = Cmd_Argv( 0 );
 
-	Cmd_CommandCompletion( FindMatches );
-	Cvar_CommandCompletion( FindMatches );
+#ifndef DEDICATED
+		// If the very first token does not have a leading \ or /,
+		// refuse to autocomplete
+		if( cmd == completionField->buffer )
+		{
+			if( baseCmd[ 0 ] != '\\' && baseCmd[ 0 ] != '/' )
+				return;
 
-	if ( matchCount == 0 ) {
-		return;	// no matches
+			baseCmd++;
+		}
+#endif
+
+		if( ( p = Field_FindFirstSeparator( cmd ) ) )
+		{
+			// Compound command
+			Field_CompleteCommand( p + 1, qtrue, qtrue );
+		}
+		else
+		{
+			// FIXME: all this junk should really be associated with the respective
+			// commands, instead of being hard coded here
+			if( ( !Q_stricmp( baseCmd, "map" ) ||
+						!Q_stricmp( baseCmd, "devmap" ) ||
+						!Q_stricmp( baseCmd, "spmap" ) ||
+						!Q_stricmp( baseCmd, "spdevmap" ) ) &&
+					completionArgument == 2 )
+			{
+				Field_CompleteFilename( "maps", "bsp", qtrue );
+			}
+			else if( ( !Q_stricmp( baseCmd, "exec" ) ||
+						!Q_stricmp( baseCmd, "writeconfig" ) ) &&
+					completionArgument == 2 )
+			{
+				Field_CompleteFilename( "", "cfg", qfalse );
+			}
+			else if( !Q_stricmp( baseCmd, "condump" ) &&
+					completionArgument == 2 )
+			{
+				Field_CompleteFilename( "", "txt", qfalse );
+			}
+			else if( !Q_stricmp( baseCmd, "demo" ) && completionArgument == 2 )
+			{
+				char demoExt[ 16 ];
+
+				Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION );
+				Field_CompleteFilename( "demos", demoExt, qtrue );
+			}
+			else if( ( !Q_stricmp( baseCmd, "toggle" ) ||
+						!Q_stricmp( baseCmd, "vstr" ) ||
+						!Q_stricmp( baseCmd, "set" ) ||
+						!Q_stricmp( baseCmd, "seta" ) ||
+						!Q_stricmp( baseCmd, "setu" ) ||
+						!Q_stricmp( baseCmd, "sets" ) ) &&
+					completionArgument == 2 )
+			{
+				// Skip "<cmd> "
+				p = Com_SkipTokens( cmd, 1, " " );
+
+				if( p > cmd )
+					Field_CompleteCommand( p, qfalse, qtrue );
+			}
+			else if( !Q_stricmp( baseCmd, "rcon" ) && completionArgument == 2 )
+			{
+				// Skip "rcon "
+				p = Com_SkipTokens( cmd, 1, " " );
+
+				if( p > cmd )
+					Field_CompleteCommand( p, qtrue, qtrue );
+			}
+			else if( !Q_stricmp( baseCmd, "bind" ) && completionArgument >= 3 )
+			{
+				// Skip "bind <key> "
+				p = Com_SkipTokens( cmd, 2, " " );
+
+				if( p > cmd )
+					Field_CompleteCommand( p, qtrue, qtrue );
+			}
+		}
 	}
+	else
+	{
+		if( completionString[0] == '\\' || completionString[0] == '/' )
+			completionString++;
 
-	Com_Memcpy(&temp, completionField, sizeof(field_t));
+		matchCount = 0;
+		shortestMatch[ 0 ] = 0;
 
-	if ( matchCount == 1 ) {
-		Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch );
-		if ( Cmd_Argc() == 1 ) {
+		if( strlen( completionString ) == 0 )
+			return;
+
+		if( doCommands )
+			Cmd_CommandCompletion( FindMatches );
+
+		if( doCvars )
+			Cvar_CommandCompletion( FindMatches );
+
+		if( matchCount == 0 )
+			return;	// no matches
+
+		if( cmd == completionField->buffer )
+		{
+#ifndef DEDICATED
+			Com_sprintf( completionField->buffer,
+					sizeof( completionField->buffer ), "\\%s", shortestMatch );
+#else
+			Com_sprintf( completionField->buffer,
+					sizeof( completionField->buffer ), "%s", shortestMatch );
+#endif
+		}
+		else
+		{
+			Q_strcat( completionField->buffer, sizeof( completionField->buffer ),
+					shortestMatch + strlen( completionString ) );
+		}
+
+		completionField->cursor = strlen( completionField->buffer );
+
+		if( matchCount == 1 )
+		{
 			Q_strcat( completionField->buffer, sizeof( completionField->buffer ), " " );
-		} else {
-			ConcatRemaining( temp.buffer, completionString );
+			completionField->cursor++;
+			return;
 		}
-		completionField->cursor = strlen( completionField->buffer );
-		return;
+
+		Com_Printf( "]%s\n", completionField->buffer );
+
+		// run through again, printing matches
+		if( doCommands )
+			Cmd_CommandCompletion( PrintMatches );
+
+		if( doCvars )
+			Cvar_CommandCompletion( PrintCvarMatches );
 	}
+}
 
-	// multiple matches, complete to shortest
-	Com_sprintf( completionField->buffer, sizeof( completionField->buffer ), "\\%s", shortestMatch );
-	completionField->cursor = strlen( completionField->buffer );
-	ConcatRemaining( temp.buffer, completionString );
+/*
+===============
+Field_AutoComplete
 
-	Com_Printf( "]%s\n", completionField->buffer );
+Perform Tab expansion
+===============
+*/
+void Field_AutoComplete( field_t *field )
+{
+	completionField = field;
 
-	// run through again, printing matches
-	Cmd_CommandCompletion( PrintCmdMatches );
-	Cvar_CommandCompletion( PrintCvarMatches );
+	Field_CompleteCommand( completionField->buffer, qtrue, qtrue );
 }

Modified: trunk/code/qcommon/files.c
===================================================================
--- trunk/code/qcommon/files.c	2006-01-21 15:09:35 UTC (rev 513)
+++ trunk/code/qcommon/files.c	2006-01-22 01:58:50 UTC (rev 514)
@@ -3426,3 +3426,26 @@
 	fflush(fsh[f].handleFiles.file.o);
 }
 
+void	FS_FilenameCompletion( const char *dir, const char *ext,
+		qboolean stripExt, void(*callback)(const char *s) ) {
+	char	**filenames;
+	int		nfiles;
+	int		i;
+	char	filename[ MAX_STRING_CHARS ];
+
+	filenames = FS_ListFilteredFiles( dir, ext, NULL, &nfiles );
+
+	FS_SortFileList( filenames, nfiles );
+
+	for( i = 0; i < nfiles; i++ ) {
+		FS_ConvertPath( filenames[ i ] );
+		Q_strncpyz( filename, filenames[ i ], MAX_STRING_CHARS );
+
+		if( stripExt ) {
+			COM_StripExtension( filename, filename );
+		}
+
+		callback( filename );
+	}
+	FS_FreeFileList( filenames );
+}

Modified: trunk/code/qcommon/q_shared.c
===================================================================
--- trunk/code/qcommon/q_shared.c	2006-01-21 15:09:35 UTC (rev 513)
+++ trunk/code/qcommon/q_shared.c	2006-01-22 01:58:50 UTC (rev 514)
@@ -59,10 +59,19 @@
 ============
 */
 void COM_StripExtension( const char *in, char *out ) {
-	while ( *in && *in != '.' ) {
-		*out++ = *in++;
+	int             length;
+
+	strcpy( out, in );
+
+	length = strlen(out)-1;
+	while (length > 0 && out[length] != '.')
+	{
+		length--;
+		if (out[length] == '/')
+			return;		// no extension
 	}
-	*out = 0;
+	if (length)
+		out[length] = 0;
 }
 
 
@@ -1249,4 +1258,68 @@
 
 //====================================================================
 
+/*
+==================
+Com_CharIsOneOfCharset
+==================
+*/
+static qboolean Com_CharIsOneOfCharset( char c, char *set )
+{
+	int i;
 
+	for( i = 0; i < strlen( set ); i++ )
+	{
+		if( set[ i ] == c )
+			return qtrue;
+	}
+
+	return qfalse;
+}
+
+/*
+==================
+Com_SkipCharset
+==================
+*/
+char *Com_SkipCharset( char *s, char *sep )
+{
+	char	*p = s;
+
+	while( p )
+	{
+		if( Com_CharIsOneOfCharset( *p, sep ) )
+			p++;
+		else
+			break;
+	}
+
+	return p;
+}
+
+/*
+==================
+Com_SkipTokens
+==================
+*/
+char *Com_SkipTokens( char *s, int numTokens, char *sep )
+{
+	int		sepCount = 0;
+	char	*p = s;
+
+	while( sepCount < numTokens )
+	{
+		if( Com_CharIsOneOfCharset( *p++, sep ) )
+		{
+			sepCount++;
+			while( Com_CharIsOneOfCharset( *p, sep ) )
+				p++;
+		}
+		else if( *p == '\0' )
+			break;
+	}
+
+	if( sepCount == numTokens )
+		return p;
+	else
+		return s;
+}

Modified: trunk/code/qcommon/q_shared.h
===================================================================
--- trunk/code/qcommon/q_shared.h	2006-01-21 15:09:35 UTC (rev 513)
+++ trunk/code/qcommon/q_shared.h	2006-01-22 01:58:50 UTC (rev 514)
@@ -610,6 +610,8 @@
 
 void	QDECL Com_sprintf (char *dest, int size, const char *fmt, ...);
 
+char *Com_SkipTokens( char *s, int numTokens, char *sep );
+char *Com_SkipCharset( char *s, char *sep );
 
 // mode parm for FS_FOpenFile
 typedef enum {

Modified: trunk/code/qcommon/qcommon.h
===================================================================
--- trunk/code/qcommon/qcommon.h	2006-01-21 15:09:35 UTC (rev 513)
+++ trunk/code/qcommon/qcommon.h	2006-01-22 01:58:50 UTC (rev 514)
@@ -415,6 +415,7 @@
 // if arg > argc, so string operations are allways safe.
 
 void	Cmd_TokenizeString( const char *text );
+void	Cmd_TokenizeStringIgnoreQuotes( const char *text_in );
 // Takes a null terminated string.  Does not need to be /n terminated.
 // breaks the string up into arg tokens.
 
@@ -657,6 +658,8 @@
 void FS_Remove( const char *osPath );
 void FS_HomeRemove( const char *homePath );
 
+void	FS_FilenameCompletion( const char *dir, const char *ext,
+		qboolean stripExt, void(*callback)(const char *s) );
 /*
 ==============================================================
 
@@ -674,7 +677,7 @@
 } field_t;
 
 void Field_Clear( field_t *edit );
-void Field_CompleteCommand( field_t *edit );
+void Field_AutoComplete( field_t *edit );
 
 /*
 ==============================================================

Modified: trunk/code/unix/unix_main.c
===================================================================
--- trunk/code/unix/unix_main.c	2006-01-21 15:09:35 UTC (rev 513)
+++ trunk/code/unix/unix_main.c	2006-01-22 01:58:50 UTC (rev 514)
@@ -549,7 +549,6 @@
 {
   // we use this when sending back commands
   static char text[256];
-  int i;
   int avail;
   char key;
   field_t *history;
@@ -588,22 +587,7 @@
         if (key == '\t')
         {
           tty_Hide();
-          Field_CompleteCommand( &tty_con );
-          // Field_CompleteCommand does weird things to the string, do a cleanup
-          //   it adds a '\' at the beginning of the string
-          //   cursor doesn't reflect actual length of the string that's sent back
-          tty_con.cursor = strlen(tty_con.buffer);
-          if (tty_con.cursor>0)
-          {
-            if (tty_con.buffer[0] == '\\')
-            {
-              for (i=0; i<=tty_con.cursor; i++)
-              {
-                tty_con.buffer[i] = tty_con.buffer[i+1];
-              }
-              tty_con.cursor--;
-            }
-          }
+          Field_AutoComplete( &tty_con );
           tty_Show();
           return NULL;
         }




More information about the quake3-commits mailing list