From: taniwha Date: Fri, 20 Jul 2001 20:24:36 +0000 (+0000) Subject: very nice command line tab completeion from Shawn Walker X-Git-Tag: RELEASE_0_2_0_RC1~794 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fdarkplaces.git;a=commitdiff_plain;h=beba2f9a1752c871a39303e5d559e779fac69271 very nice command line tab completeion from Shawn Walker with my later modifications so that commands are completed as much as possible. git-svn-id: svn://svn.icculus.org/twilight/trunk/darkplaces@269 d7cf8633-e32d-0410-b094-e92efae38249 --- diff --git a/cmd.c b/cmd.c index ae328726..a8349082 100644 --- a/cmd.c +++ b/cmd.c @@ -418,6 +418,45 @@ cmd_source_t cmd_source; static cmd_function_t *cmd_functions; // possible commands to execute +/* +======== +Cmd_List + + CmdList Added by EvilTypeGuy eviltypeguy@qeradiant.com + Thanks to Matthias "Maddes" Buecher, http://www.inside3d.com/qip/ + +======== +*/ +void Cmd_List_f (void) +{ + cmd_function_t *cmd; + char *partial; + int len; + int count; + + if (Cmd_Argc() > 1) { + partial = Cmd_Argv (1); + len = strlen(partial); + } else { + partial = NULL; + len = 0; + } + + count = 0; + for (cmd = cmd_functions; cmd; cmd = cmd->next) { + if (partial && strncmp(partial, cmd->name, len)) + continue; + Con_Printf ("%s\n", cmd->name); + count++; + } + + Con_Printf ("%i Command%s", count, (count > 1) ? "s" : ""); + if (partial) + Con_Printf(" beginning with \"%s\"", partial); + + Con_Printf ("\n\n"); +} + /* ============ Cmd_Init @@ -434,6 +473,9 @@ void Cmd_Init (void) Cmd_AddCommand ("alias",Cmd_Alias_f); Cmd_AddCommand ("cmd", Cmd_ForwardToServer); Cmd_AddCommand ("wait", Cmd_Wait_f); + Cmd_AddCommand ("cmdlist", Cmd_List_f); // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com + Cmd_AddCommand ("cvarlist", Cvar_List_f); // 2000-01-09 CmdList, CvarList commands + // By Matthias "Maddes" Buecher } /* @@ -463,7 +505,7 @@ char *Cmd_Argv (int arg) Cmd_Args ============ */ -char *Cmd_Args (void) +char *Cmd_Args (void) { return cmd_args; } @@ -594,13 +636,160 @@ char *Cmd_CompleteCommand (char *partial) return NULL; // check functions - for (cmd=cmd_functions ; cmd ; cmd=cmd->next) - if (!strncmp (partial,cmd->name, len)) + for (cmd = cmd_functions; cmd; cmd = cmd->next) + if (!strncmp(partial, cmd->name, len)) return cmd->name; return NULL; } +/* + Cmd_CompleteCountPossible + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + Thanks to taniwha + +*/ +int +Cmd_CompleteCountPossible (char *partial) +{ + cmd_function_t *cmd; + int len; + int h; + + h = 0; + len = strlen(partial); + + if (!len) + return 0; + + // Loop through the command list and count all partial matches + for (cmd = cmd_functions; cmd; cmd = cmd->next) + if (!strncasecmp(partial, cmd->name, len)) + h++; + + return h; +} + +/* + Cmd_CompleteBuildList + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + Thanks to taniwha + +*/ +char ** +Cmd_CompleteBuildList (char *partial) +{ + cmd_function_t *cmd; + int len = 0; + int bpos = 0; + int sizeofbuf = (Cmd_CompleteCountPossible (partial) + 1) * sizeof (char *); + char **buf; + + len = strlen(partial); + buf = malloc(sizeofbuf + sizeof (char *)); + // Loop through the alias list and print all matches + for (cmd = cmd_functions; cmd; cmd = cmd->next) + if (!strncasecmp(partial, cmd->name, len)) + buf[bpos++] = cmd->name; + + buf[bpos] = NULL; + return buf; +} + +/* + Cmd_CompleteAlias + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + Thanks to taniwha + +*/ +char +*Cmd_CompleteAlias (char * partial) +{ + cmdalias_t *alias; + int len; + + len = strlen(partial); + + if (!len) + return NULL; + + // Check functions + for (alias = cmd_alias; alias; alias = alias->next) + if (!strncasecmp(partial, alias->name, len)) + return alias->name; + + return NULL; +} + +/* + Cmd_CompleteAliasCountPossible + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + Thanks to taniwha + +*/ +int +Cmd_CompleteAliasCountPossible (char *partial) +{ + cmdalias_t *alias; + int len; + int h; + + h = 0; + + len = strlen(partial); + + if (!len) + return 0; + + // Loop through the command list and count all partial matches + for (alias = cmd_alias; alias; alias = alias->next) + if (!strncasecmp(partial, alias->name, len)) + h++; + + return h; +} + +/* + Cmd_CompleteAliasBuildList + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + Thanks to taniwha + +*/ +char ** +Cmd_CompleteAliasBuildList (char *partial) +{ + cmdalias_t *alias; + int len = 0; + int bpos = 0; + int sizeofbuf = (Cmd_CompleteAliasCountPossible (partial) + 1) * sizeof (char *); + char **buf; + + len = strlen(partial); + buf = malloc(sizeofbuf + sizeof (char *)); + // Loop through the alias list and print all matches + for (alias = cmd_alias; alias; alias = alias->next) + if (!strncasecmp(partial, alias->name, len)) + buf[bpos++] = alias->name; + + buf[bpos] = NULL; + return buf; +} + /* ============ Cmd_ExecuteString @@ -701,3 +890,4 @@ int Cmd_CheckParm (char *parm) return 0; } + diff --git a/cmd.h b/cmd.h index da1daa65..5583b329 100644 --- a/cmd.h +++ b/cmd.h @@ -91,6 +91,14 @@ char *Cmd_CompleteCommand (char *partial); // attempts to match a partial command for automatic command line completion // returns NULL if nothing fits +int Cmd_CompleteAliasCountPossible (char *partial); +char **Cmd_CompleteAliasBuildList (char *partial); +int Cmd_CompleteCountPossible (char *partial); +char **Cmd_CompleteBuildList (char *partial); +char *Cmd_CompleteAlias (char *partial); +// Enhanced console completion by Fett erich@heintz.com +// Added by EvilTypeGuy eviltypeguy@qeradiant.com + int Cmd_Argc (void); char *Cmd_Argv (int arg); char *Cmd_Args (void); diff --git a/console.c b/console.c index 2c8939b1..a034a298 100644 --- a/console.c +++ b/console.c @@ -45,28 +45,29 @@ int con_totallines; // total lines in console scrollback int con_backscroll; // lines up from bottom to display int con_current; // where next message will be printed int con_x; // offset in current line for next print -char *con_text=0; +char *con_text = 0; -cvar_t con_notifytime = {"con_notifytime","3"}; //seconds +cvar_t con_notifytime = {"con_notifytime","3"}; //seconds cvar_t logfile = {"logfile","0"}; #define NUM_CON_TIMES 4 float con_times[NUM_CON_TIMES]; // realtime time the line was generated - // for transparent notify lines + // for transparent notify lines int con_vislines; qboolean con_debuglog; -#define MAXCMDLINE 256 +#define MAXCMDLINE 256 extern char key_lines[32][MAXCMDLINE]; extern int edit_line; extern int key_linepos; +extern int key_insert; qboolean con_initialized; -int con_notifylines; // scan lines to clear for notify lines +int con_notifylines; // scan lines to clear for notify lines extern void M_Menu_Main_f (void); @@ -487,39 +488,53 @@ DRAWING Con_DrawInput The input line scrolls horizontally if typing goes beyond the right edge + +Modified by EvilTypeGuy eviltypeguy@qeradiant.com ================ */ void Con_DrawInput (void) { int y; char *text; + char editlinecopy[256]; if (key_dest != key_console && !con_forcedup) return; // don't draw anything - text = key_lines[edit_line]; + text = strcpy(editlinecopy, key_lines[edit_line]); + y = strlen(text); + + // Advanced Console Editing by Radix radix@planetquake.com + // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com + // use strlen of edit_line instead of key_linepos to allow editing + // of early characters w/o erasing + + // add the cursor frame + if ((int)(realtime*con_cursorspeed) & 1) // cursor is visible + text[key_linepos] = 11 + 130 * key_insert; // either solid or triangle facing right -// add the cursor frame - text[key_linepos] = 10+((int)(realtime*con_cursorspeed)&1); + text[key_linepos + 1] = 0; // LordHavoc: null terminate, rather than padding with spaces + // text[key_linepos] = 10 + ((int)(realtime*con_cursorspeed) & 1); - text[key_linepos+1] = 0; // LordHavoc: null terminate, rather than padding with spaces -// fill out remainder with spaces -// for (i=key_linepos+1 ; i< con_linewidth ; i++) -// text[i] = ' '; + + // fill out remainder with spaces + // for (i=key_linepos+1 ; i< con_linewidth ; i++) + // text[i] = ' '; -// prestep if horizontally scrolling + // prestep if horizontally scrolling if (key_linepos >= con_linewidth) text += 1 + key_linepos - con_linewidth; -// draw it - y = con_vislines-16; + // draw it + y = con_vislines - 16; + + // for (i=0 ; i maxlen) + maxlen = len; + walk++; + } + maxlen += 1; + + while (*list) { + len = strlen(*list); + if (pos + maxlen >= width) { + Con_Printf("\n"); + pos = 0; + } + + Con_Printf("%s", *list); + for (i = 0; i < (maxlen - len); i++) + Con_Printf(" "); + + pos += maxlen; + list++; + } + + if (pos) + Con_Printf("\n\n"); +} + +/* + Con_CompleteCommandLine + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + Thanks to taniwha + +*/ +void +Con_CompleteCommandLine (void) +{ + char *cmd = ""; + char *s; + int c, v, a, i; + int cmd_len; + char **list[3] = {0, 0, 0}; + + s = key_lines[edit_line] + 1; + // Count number of possible matches + c = Cmd_CompleteCountPossible(s); + v = Cvar_CompleteCountPossible(s); + a = Cmd_CompleteAliasCountPossible(s); + + if (!(c + v + a)) // No possible matches + return; + + if (c + v + a == 1) { + if (c) + list[0] = Cmd_CompleteBuildList(s); + else if (v) + list[0] = Cvar_CompleteBuildList(s); + else + list[0] = Cmd_CompleteAliasBuildList(s); + cmd = *list[0]; + cmd_len = strlen (cmd); + } else { + if (c) + cmd = *(list[0] = Cmd_CompleteBuildList(s)); + if (v) + cmd = *(list[1] = Cvar_CompleteBuildList(s)); + if (a) + cmd = *(list[2] = Cmd_CompleteAliasBuildList(s)); + + cmd_len = strlen (s); + do { + for (i = 0; i < 3; i++) { + char ch = cmd[cmd_len]; + char **l = list[i]; + if (l) { + while (*l && (*l)[cmd_len] == ch) + l++; + if (*l) + break; + } + } + if (i == 3) + cmd_len++; + } while (i == 3); + // 'quakebar' + Con_Printf("\n\35"); + for (i = 0; i < con_linewidth - 4; i++) + Con_Printf("\36"); + Con_Printf("\37\n"); + + // Print Possible Commands + if (c) { + Con_Printf("%i possible command%s\n", c, (c > 1) ? "s: " : ":"); + Con_DisplayList(list[0]); + } + + if (v) { + Con_Printf("%i possible variable%s\n", v, (v > 1) ? "s: " : ":"); + Con_DisplayList(list[1]); + } + + if (a) { + Con_Printf("%i possible aliases%s\n", a, (a > 1) ? "s: " : ":"); + Con_DisplayList(list[2]); + } + return; + } + + if (cmd) { + strncpy(key_lines[edit_line] + 2, cmd, cmd_len); + key_linepos = cmd_len + 2; + if (c + v + a == 1) { + key_lines[edit_line][key_linepos] = ' '; + key_linepos++; + } + key_lines[edit_line][key_linepos] = 0; + } + for (i = 0; i < 3; i++) + if (list[i]) + free (list[i]); +} + diff --git a/console.h b/console.h index 58273125..1ba74e1e 100644 --- a/console.h +++ b/console.h @@ -41,3 +41,14 @@ void Con_Clear_f (void); void Con_DrawNotify (void); void Con_ClearNotify (void); void Con_ToggleConsole_f (void); + +// wrapper function to attempt to either complete the command line +// or to list possible matches grouped by type +// (i.e. will display possible variables, aliases, commands +// that match what they've typed so far) +void Con_CompleteCommandLine(void); + +// Generic libs/util/console.c function to display a list +// formatted in columns on the console +void Con_DisplayList(char **list); + diff --git a/cvar.c b/cvar.c index 0f8ccd4d..318adc45 100644 --- a/cvar.c +++ b/cvar.c @@ -96,6 +96,64 @@ char *Cvar_CompleteVariable (char *partial) } +/* + CVar_CompleteCountPossible + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + +*/ +int +Cvar_CompleteCountPossible (char *partial) +{ + cvar_t *cvar; + int len; + int h; + + h = 0; + len = strlen(partial); + + if (!len) + return 0; + + // Loop through the cvars and count all possible matches + for (cvar = cvar_vars; cvar; cvar = cvar->next) + if (!strncasecmp(partial, cvar->name, len)) + h++; + + return h; +} + +/* + CVar_CompleteBuildList + + New function for tab-completion system + Added by EvilTypeGuy + Thanks to Fett erich@heintz.com + Thanks to taniwha + +*/ +char ** +Cvar_CompleteBuildList (char *partial) +{ + cvar_t *cvar; + int len = 0; + int bpos = 0; + int sizeofbuf = (Cvar_CompleteCountPossible (partial) + 1) * sizeof (char *); + char **buf; + + len = strlen(partial); + buf = malloc(sizeofbuf + sizeof (char *)); + // Loop through the alias list and print all matches + for (cvar = cvar_vars; cvar; cvar = cvar->next) + if (!strncasecmp(partial, cvar->name, len)) + buf[bpos++] = cvar->name; + + buf[bpos] = NULL; + return buf; +} + /* ============ Cvar_Set @@ -223,3 +281,41 @@ void Cvar_WriteVariables (QFile *f) Qprintf (f, "%s \"%s\"\n", var->name, var->string); } + +// Added by EvilTypeGuy eviltypeguy@qeradiant.com +// 2000-01-09 CvarList command By Matthias "Maddes" Buecher, http://www.inside3d.com/qip/ +/* +========= +Cvar_List +========= +*/ +void Cvar_List_f (void) +{ + cvar_t *cvar; + char *partial; + int len; + int count; + + if (Cmd_Argc() > 1) { + partial = Cmd_Argv (1); + len = strlen(partial); + } else { + partial = NULL; + len = 0; + } + + count = 0; + for (cvar = cvar_vars; cvar; cvar = cvar->next) { + if (partial && strncmp (partial,cvar->name,len)) + continue; + + Con_Printf ("%s is \"%s\"\n", cvar->name, cvar->string); + count++; + } + + Con_Printf ("%i cvar(s)", count); + if (partial) + Con_Printf (" beginning with \"%s\"", partial); + Con_Printf ("\n"); +} +// 2000-01-09 CvarList command by Maddes diff --git a/cvar.h b/cvar.h index 6aa1ad14..1c73c39c 100644 --- a/cvar.h +++ b/cvar.h @@ -95,3 +95,16 @@ void Cvar_WriteVariables (QFile *f); cvar_t *Cvar_FindVar (char *var_name); extern cvar_t *cvar_vars; + +int Cvar_CompleteCountPossible (char *partial); +char **Cvar_CompleteBuildList (char *partial); +// Added by EvilTypeGuy - functions for tab completion system +// Thanks to Fett erich@heintz.com +// Thanks to taniwha + +void Cvar_List_f (void); +// Prints a list of Cvars including a count of them to the user console +// Referenced in cmd.c in Cmd_Init hence it's inclusion here +// Added by EvilTypeGuy eviltypeguy@qeradiant.com +// Thanks to Matthias "Maddes" Buecher, http://www.inside3d.com/qip/ + diff --git a/keys.c b/keys.c index 1929b68d..a3d1f158 100644 --- a/keys.c +++ b/keys.c @@ -29,11 +29,12 @@ key up events are sent even if in console mode #define MAXCMDLINE 256 char key_lines[32][MAXCMDLINE]; int key_linepos; -int shift_down=false; +int shift_down = false; int key_lastpress; +int key_insert; // insert key toggle (for editing) -int edit_line=0; -int history_line=0; +int edit_line = 0; +int history_line = 0; keydest_t key_dest; @@ -158,8 +159,6 @@ Interactive line editing and console scrollback */ void Key_Console (int key) { - char *cmd; - if (key == K_ENTER) { Cbuf_AddText (key_lines[edit_line]+1); // skip the > @@ -168,36 +167,79 @@ void Key_Console (int key) edit_line = (edit_line + 1) & 31; history_line = edit_line; key_lines[edit_line][0] = ']'; + key_lines[edit_line][1] = 0; // EvilTypeGuy: null terminate key_linepos = 1; if (cls.state == ca_disconnected) SCR_UpdateScreen (); // force an update, because the command - // may take some time + // may take some time return; } if (key == K_TAB) - { // command completion - cmd = Cmd_CompleteCommand (key_lines[edit_line]+1); - if (!cmd) - cmd = Cvar_CompleteVariable (key_lines[edit_line]+1); - if (cmd) - { - strcpy (key_lines[edit_line]+1, cmd); - key_linepos = strlen(cmd)+1; - key_lines[edit_line][key_linepos] = ' '; - key_linepos++; - key_lines[edit_line][key_linepos] = 0; - return; - } + { + // Enhanced command completion + // by EvilTypeGuy eviltypeguy@qeradiant.com + // Thanks to Fett, Taniwha + Con_CompleteCommandLine(); } - if (key == K_BACKSPACE || key == K_LEFTARROW) + // Advanced Console Editing by Radix radix@planetquake.com + // Added/Modified by EvilTypeGuy eviltypeguy@qeradiant.com + + // left arrow will just move left one without erasing, backspace will + // actually erase charcter + if (key == K_LEFTARROW) + { + if (key_linepos > 1) + key_linepos--; + return; + } + + if (key == K_BACKSPACE) // delete char before cursor { if (key_linepos > 1) + { + strcpy(key_lines[edit_line] + key_linepos - 1, key_lines[edit_line] + key_linepos); key_linepos--; + } + return; + } + + if (key == K_DEL) // delete char on cursor + { + if (key_linepos < strlen(key_lines[edit_line])) + strcpy(key_lines[edit_line] + key_linepos, key_lines[edit_line] + key_linepos + 1); + return; + } + + + // if we're at the end, get one character from previous line, + // otherwise just go right one + if (key == K_RIGHTARROW) + { + if (strlen(key_lines[edit_line]) == key_linepos) + { + if (strlen(key_lines[(edit_line + 31) & 31]) <= key_linepos) + return; // no character to get + + key_lines[edit_line][key_linepos] = key_lines[(edit_line + 31) & 31][key_linepos]; + key_linepos++; + key_lines[edit_line][key_linepos] = 0; + } + else + key_linepos++; + + return; + } + + if (key == K_INS) // toggle insert mode + { + key_insert ^= 1; return; } + // End Advanced Console Editing + if (key == K_UPARROW) { do @@ -265,13 +307,32 @@ void Key_Console (int key) if (key < 32 || key > 127) return; // non printable + + if (key_linepos < MAXCMDLINE-1) { + int i; + + if (key_insert) // check insert mode + { + // can't do strcpy to move string to right + i = strlen(key_lines[edit_line]) - 1; + + if (i == 254) + i--; + + for (; i >= key_linepos; i--) + key_lines[edit_line][i + 1] = key_lines[edit_line][i]; + } + + // only null terminate if at the end + i = key_lines[edit_line][key_linepos]; key_lines[edit_line][key_linepos] = key; key_linepos++; - key_lines[edit_line][key_linepos] = 0; - } + if (!i) + key_lines[edit_line][key_linepos] = 0; + } } //============================================================================ @@ -542,6 +603,8 @@ void Key_Init (void) consolekeys[K_UPARROW] = true; consolekeys[K_DOWNARROW] = true; consolekeys[K_BACKSPACE] = true; + consolekeys[K_DEL] = true; + consolekeys[K_INS] = true; consolekeys[K_PGUP] = true; consolekeys[K_PGDN] = true; consolekeys[K_SHIFT] = true;