<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">[PATCH] Command argument completion callback

Add an argument completion callback to the command struct. Defaults to NULL.

If set, when tab completion is requested on the argument of a valid command,
the callback will be invoked, passing in the partial argument. The callback
then returns a (possibly partially) completed string.

The initial targets for this will be commands like map, playdemo, etc.

diff -urN b/NQ/cmd.c head/NQ/cmd.c
--- b/NQ/cmd.c	2006-02-18 10:55:26.000000000 +1030
+++ head/NQ/cmd.c	2006-02-16 16:14:10.000000000 +1030
@@ -396,6 +396,7 @@
     struct cmd_function_s *next;
     const char *name;
     xcommand_t function;
+    cmd_arg_f completion;
 } cmd_function_t;
 
 
@@ -550,12 +551,27 @@
     cmd = Hunk_Alloc(sizeof(cmd_function_t));
     cmd-&gt;name = cmd_name;
     cmd-&gt;function = function;
+    cmd-&gt;completion = NULL;
     cmd-&gt;next = cmd_functions;
     cmd_functions = cmd;
 
     insert_command_completion(cmd_name);
 }
 
+void
+Cmd_SetCompletion(const char *cmd_name, cmd_arg_f completion)
+{
+    cmd_function_t *cmd;
+
+    for (cmd = cmd_functions; cmd; cmd = cmd-&gt;next)
+	if (!strcmp(cmd_name, cmd-&gt;name)) {
+	    cmd-&gt;completion = completion;
+	    break;
+	}
+    if (!cmd)
+	Sys_Error("%s: no such command - %s", __func__, cmd_name);
+}
+
 /*
 ============
 Cmd_Exists
@@ -648,6 +664,46 @@
 	Con_Printf("Unknown command \"%s\"\n", Cmd_Argv(0));
 }
 
+/*
+ * Return a string tree with all possible argument completions of the given
+ * buffer for the given command.
+ */
+struct rb_string_root *
+Cmd_ArgCompletions(const char *name, const char *buf)
+{
+    cmd_function_t *cmd;
+    struct rb_string_root *root = NULL;
+
+    for (cmd = cmd_functions; cmd; cmd = cmd-&gt;next) {
+	if (!strcasecmp(name, cmd-&gt;name) &amp;&amp; cmd-&gt;completion) {
+	    /* FIXME - ST_AllocInit? */
+	    root = cmd-&gt;completion(buf);
+	    break;
+	}
+    }
+
+    return root;
+}
+
+/*
+ * Call the argument completion function for cmd "name".
+ * Returned result should be Z_Free'd after use.
+ */
+char *
+Cmd_ArgComplete(const char *name, const char *buf)
+{
+    char *result = NULL;
+    struct rb_string_root *root;
+
+    root = Cmd_ArgCompletions(name, buf);
+    if (root) {
+	result = ST_MaxMatch(root, buf);
+	Z_Free(root);
+    }
+
+    return result;
+}
+
 
 /*
 ================
diff -urN b/NQ/cmd.h head/NQ/cmd.h
--- b/NQ/cmd.h	2006-02-18 10:55:26.000000000 +1030
+++ head/NQ/cmd.h	2006-02-15 11:59:55.000000000 +1030
@@ -80,6 +80,14 @@
 /* Command function */
 typedef void (*xcommand_t)(void);
 
+/*
+ * Command argument completion function.
+ * Pass in the argument string
+ * Returns a string tree of possible completions
+ * Requires ST_Alloc_Init() prior to calling
+ */
+typedef struct rb_string_root *(*cmd_arg_f)(const char *);
+
 typedef enum {
     src_client,			// came in over a net connection as a clc_stringcmd
     // host_client will be valid during this state.
@@ -91,6 +99,9 @@
 void Cmd_Init(void);
 
 void Cmd_AddCommand(const char *cmd_name, xcommand_t function);
+void Cmd_SetCompletion(const char *cmd_name, cmd_arg_f completion);
+char *Cmd_ArgComplete(const char *name, const char *buf);
+struct rb_string_root *Cmd_ArgCompletions(const char *name, const char *buf);
 
 // called by the init functions of other parts of the program to
 // register commands and functions to call for them.
diff -urN b/QW/client/cmd.h head/QW/client/cmd.h
--- b/QW/client/cmd.h	2006-02-18 10:55:26.000000000 +1030
+++ head/QW/client/cmd.h	2006-02-16 17:39:20.000000000 +1030
@@ -74,9 +74,20 @@
 /* Command function */
 typedef void (*xcommand_t)(void);
 
+/*
+ * Command argument completion function.
+ * Pass in the argument string
+ * Returns a string tree of possible completions
+ * Requires ST_Alloc_Init() prior to calling
+ */
+typedef struct rb_string_root *(*cmd_arg_f)(const char *);
+
 void Cmd_Init(void);
 
 void Cmd_AddCommand(const char *cmd_name, xcommand_t function);
+void Cmd_SetCompletion(const char *cmd_name, cmd_arg_f completion);
+char *Cmd_ArgComplete(const char *name, const char *buf);
+struct rb_string_root *Cmd_ArgCompletions(const char *name, const char *buf);
 
 // called by the init functions of other parts of the program to
 // register commands and functions to call for them.
diff -urN b/QW/common/cmd.c head/QW/common/cmd.c
--- b/QW/common/cmd.c	2006-02-18 11:37:47.000000000 +1030
+++ head/QW/common/cmd.c	2006-02-18 09:36:13.000000000 +1030
@@ -406,6 +406,7 @@
     struct cmd_function_s *next;
     const char *name;
     xcommand_t function;
+    cmd_arg_f completion;
 } cmd_function_t;
 
 
@@ -560,12 +561,27 @@
     cmd = Hunk_Alloc(sizeof(cmd_function_t));
     cmd-&gt;name = cmd_name;
     cmd-&gt;function = function;
+    cmd-&gt;completion = NULL;
     cmd-&gt;next = cmd_functions;
     cmd_functions = cmd;
 
     insert_command_completion(cmd_name);
 }
 
+void
+Cmd_SetCompletion(const char *cmd_name, cmd_arg_f completion)
+{
+    cmd_function_t *cmd;
+
+    for (cmd = cmd_functions; cmd; cmd = cmd-&gt;next)
+	if (!strcmp(cmd_name, cmd-&gt;name)) {
+	    cmd-&gt;completion = completion;
+	    break;
+	}
+    if (!cmd)
+	Sys_Error("%s: no such command - %s", __func__, cmd_name);
+}
+
 /*
 ============
 Cmd_Exists
@@ -688,6 +704,46 @@
 
 }
 
+/*
+ * Return a string tree with all possible argument completions of the given
+ * buffer for the given command.
+ */
+struct rb_string_root *
+Cmd_ArgCompletions(const char *name, const char *buf)
+{
+    cmd_function_t *cmd;
+    struct rb_string_root *root = NULL;
+
+    for (cmd = cmd_functions; cmd; cmd = cmd-&gt;next) {
+	if (!strcasecmp(name, cmd-&gt;name) &amp;&amp; cmd-&gt;completion) {
+	    /* FIXME - ST_AllocInit? */
+	    root = cmd-&gt;completion(buf);
+	    break;
+	}
+    }
+
+    return root;
+}
+
+/*
+ * Call the argument completion function for cmd "name".
+ * Returned result should be Z_Free'd after use.
+ */
+char *
+Cmd_ArgComplete(const char *name, const char *buf)
+{
+    char *result = NULL;
+    struct rb_string_root *root;
+
+    root = Cmd_ArgCompletions(name, buf);
+    if (root) {
+	result = ST_MaxMatch(root, buf);
+	Z_Free(root);
+    }
+
+    return result;
+}
+
 
 /*
 ================
diff -urN b/common/keys.c head/common/keys.c
--- b/common/keys.c	2006-02-18 12:02:44.000000000 +1030
+++ head/common/keys.c	2006-02-15 14:44:59.000000000 +1030
@@ -211,6 +211,8 @@
 {
     char *cmd;
     char *s;
+    char *completion;
+    int len;
 
     s = GetCommandPos(key_lines[edit_line] + 1);
     cmd = find_completion(s);
@@ -229,6 +231,29 @@
 
 	key_lines[edit_line][key_linepos] = 0;
 	Z_Free(cmd);
+    } else {
+	/* Try argument completion? */
+	cmd = strchr(s, ' ');
+	if (cmd) {
+	    len = cmd - s;
+	    cmd = Z_Malloc(len + 1);
+	    strncpy(cmd, s, len);
+	    cmd[len] = 0;
+
+	    if (command_exists(cmd)) {
+		s += len;
+		while (*s == ' ')
+		    s++;
+		completion = Cmd_ArgComplete(cmd, s);
+		if (completion) {
+		    key_linepos = s - key_lines[edit_line];
+		    strcpy(s, completion);
+		    key_linepos += strlen(completion);
+		    Z_Free(completion);
+		}
+	    }
+	    Z_Free(cmd);
+	}
     }
 }
 
@@ -249,6 +274,29 @@
 		maxlen = len;
 	}
 	Con_ShowList(completions_list, cnt, maxlen);
+    } else {
+	char *cmd = strchr(s, ' ');
+	if (cmd) {
+	    len = cmd - s;
+	    cmd = Z_Malloc(len + 1);
+	    strncpy(cmd, s, len);
+	    cmd[len] = 0;
+
+	    if (command_exists(cmd)) {
+		struct rb_string_root *root;
+
+		s += len;
+		while (*s == ' ')
+		    s++;
+
+		root = Cmd_ArgCompletions(cmd, s);
+		if (root &amp;&amp; root-&gt;entries) {
+		    Con_ShowTree(root);
+		    Z_Free(root);
+		}
+	    }
+	    Z_Free(cmd);
+	}
     }
 }
 
</pre></body></html>