From 4c12ac9fb80f8f8d2096ba2463b448e9865b70cd Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Mon, 27 Jan 2025 09:16:05 +0000
Subject: [PATCH] Make list-commands command show only one command if an
 argument is given, from Ilya Grigoriev in GitHub issue 4352.

---
 cmd-list-keys.c | 68 +++++++++++++++++++++++++++++--------------------
 cmd.c           |  2 +-
 tmux.h          |  1 +
 3 files changed, 43 insertions(+), 28 deletions(-)

diff --git a/cmd-list-keys.c b/cmd-list-keys.c
index 395b147c..b5050f09 100644
--- a/cmd-list-keys.c
+++ b/cmd-list-keys.c
@@ -91,7 +91,7 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args,
 	struct key_binding	*bd;
 	const char		*key;
 	char			*tmp, *note;
-	int	                 found = 0;
+	int			 found = 0;
 
 	table = key_bindings_get_table(tablename, 0);
 	if (table == NULL)
@@ -321,6 +321,31 @@ out:
 	return (CMD_RETURN_NORMAL);
 }
 
+static void
+cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft,
+    const char *template, struct cmdq_item *item)
+{
+	const char	*s;
+	char		*line;
+
+	format_add(ft, "command_list_name", "%s", entry->name);
+	if (entry->alias != NULL)
+		s = entry->alias;
+	else
+		s = "";
+	format_add(ft, "command_list_alias", "%s", s);
+	if (entry->usage != NULL)
+		s = entry->usage;
+	else
+		s = "";
+	format_add(ft, "command_list_usage", "%s", s);
+
+	line = format_expand(ft, template);
+	if (*line != '\0')
+		cmdq_print(item, "%s", line);
+	free(line);
+}
+
 static enum cmd_retval
 cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
 {
@@ -328,8 +353,8 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
 	const struct cmd_entry	**entryp;
 	const struct cmd_entry	 *entry;
 	struct format_tree	 *ft;
-	const char		 *template, *s, *command;
-	char			 *line;
+	const char		 *template,  *command;
+	char			 *cause;
 
 	if ((template = args_get(args, 'F')) == NULL) {
 		template = "#{command_list_name}"
@@ -341,30 +366,19 @@ cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item)
 	format_defaults(ft, NULL, NULL, NULL, NULL);
 
 	command = args_string(args, 0);
-	for (entryp = cmd_table; *entryp != NULL; entryp++) {
-		entry = *entryp;
-		if (command != NULL &&
-		    (strcmp(entry->name, command) != 0 &&
-		    (entry->alias == NULL ||
-		    strcmp(entry->alias, command) != 0)))
-		    continue;
-
-		format_add(ft, "command_list_name", "%s", entry->name);
-		if (entry->alias != NULL)
-			s = entry->alias;
-		else
-			s = "";
-		format_add(ft, "command_list_alias", "%s", s);
-		if (entry->usage != NULL)
-			s = entry->usage;
-		else
-			s = "";
-		format_add(ft, "command_list_usage", "%s", s);
-
-		line = format_expand(ft, template);
-		if (*line != '\0')
-			cmdq_print(item, "%s", line);
-		free(line);
+	if (command == NULL) {
+		for (entryp = cmd_table; *entryp != NULL; entryp++)
+			cmd_list_single_command(*entryp, ft, template, item);
+	} else {
+		entry = cmd_find(command, &cause);
+		if (entry != NULL)
+			cmd_list_single_command(entry, ft, template, item);
+		else {
+			cmdq_error(item, "%s", cause);
+			free(cause);
+			format_free(ft);
+			return (CMD_RETURN_ERROR);
+		}
 	}
 
 	format_free(ft);
diff --git a/cmd.c b/cmd.c
index 9e2ce036..64f63c92 100644
--- a/cmd.c
+++ b/cmd.c
@@ -445,7 +445,7 @@ cmd_get_alias(const char *name)
 }
 
 /* Look up a command entry by name. */
-static const struct cmd_entry *
+const struct cmd_entry *
 cmd_find(const char *name, char **cause)
 {
 	const struct cmd_entry	**loop, *entry, *found = NULL;
diff --git a/tmux.h b/tmux.h
index 558bc009..1b6d889a 100644
--- a/tmux.h
+++ b/tmux.h
@@ -2597,6 +2597,7 @@ int		 cmd_find_from_nothing(struct cmd_find_state *, int);
 
 /* cmd.c */
 extern const struct cmd_entry *cmd_table[];
+const struct cmd_entry *cmd_find(const char *, char **);
 void printflike(3, 4) cmd_log_argv(int, char **, const char *, ...);
 void		 cmd_prepend_argv(int *, char ***, const char *);
 void		 cmd_append_argv(int *, char ***, const char *);