From 34804f2709a16dca45dc072fb53d03f79db61e51 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Mon, 13 Apr 2020 16:19:37 +0000
Subject: [PATCH] When parsing strings, put all commands in one group even if
 there are newlines. This means that for example bind q { a \n b } and bind q
 "a ; b" are the same. Also log commands in different groups separated by ;;
 rather than ; (a command list like this should never be user visible).

---
 cmd-parse.y | 21 ++++++++++++++++++---
 cmd.c       | 23 ++++++++++++++++-------
 tmux.h      |  1 +
 3 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/cmd-parse.y b/cmd-parse.y
index aabfe2d3..2ca0124a 100644
--- a/cmd-parse.y
+++ b/cmd-parse.y
@@ -700,15 +700,17 @@ cmd_parse_build_commands(struct cmd_parse_commands *cmds,
 
 	/*
 	 * Parse each command into a command list. Create a new command list
-	 * for each line so they get a new group (so the queue knows which ones
-	 * to remove if a command fails when executed).
+	 * for each line (unless the flag is set) so they get a new group (so
+	 * the queue knows which ones to remove if a command fails when
+	 * executed).
 	 */
 	result = cmd_list_new();
 	TAILQ_FOREACH(cmd, cmds, entry) {
 		log_debug("%s: %u %s", __func__, cmd->line, cmd->name);
 		cmd_log_argv(cmd->argc, cmd->argv, __func__);
 
-		if (cmdlist == NULL || cmd->line != line) {
+		if (cmdlist == NULL ||
+		    ((~pi->flags & CMD_PARSE_ONEGROUP) && cmd->line != line)) {
 			if (cmdlist != NULL) {
 				cmd_parse_print_commands(pi, line, cmdlist);
 				cmd_list_move(result, cmdlist);
@@ -775,6 +777,19 @@ cmd_parse_from_file(FILE *f, struct cmd_parse_input *pi)
 struct cmd_parse_result *
 cmd_parse_from_string(const char *s, struct cmd_parse_input *pi)
 {
+	struct cmd_parse_input	input;
+
+	if (pi == NULL) {
+		memset(&input, 0, sizeof input);
+		pi = &input;
+	}
+
+	/*
+	 * When parsing a string, put commands in one group even if there are
+	 * multiple lines. This means { a \n b } is identical to "a ; b" when
+	 * given as an argument to another command.
+	 */
+	pi->flags |= CMD_PARSE_ONEGROUP;
 	return (cmd_parse_from_buffer(s, strlen(s), pi));
 }
 
diff --git a/cmd.c b/cmd.c
index b7b144b4..bc807cbe 100644
--- a/cmd.c
+++ b/cmd.c
@@ -624,7 +624,7 @@ cmd_list_free(struct cmd_list *cmdlist)
 char *
 cmd_list_print(struct cmd_list *cmdlist, int escaped)
 {
-	struct cmd	*cmd;
+	struct cmd	*cmd, *next;
 	char		*buf, *this;
 	size_t		 len;
 
@@ -634,15 +634,24 @@ cmd_list_print(struct cmd_list *cmdlist, int escaped)
 	TAILQ_FOREACH(cmd, cmdlist->list, qentry) {
 		this = cmd_print(cmd);
 
-		len += strlen(this) + 4;
+		len += strlen(this) + 6;
 		buf = xrealloc(buf, len);
 
 		strlcat(buf, this, len);
-		if (TAILQ_NEXT(cmd, qentry) != NULL) {
-			if (escaped)
-				strlcat(buf, " \\; ", len);
-			else
-				strlcat(buf, " ; ", len);
+
+		next = TAILQ_NEXT(cmd, qentry);
+		if (next != NULL) {
+			if (cmd->group != next->group) {
+				if (escaped)
+					strlcat(buf, " \\;\\; ", len);
+				else
+					strlcat(buf, " ;; ", len);
+			} else {
+				if (escaped)
+					strlcat(buf, " \\; ", len);
+				else
+					strlcat(buf, " ; ", len);
+			}
 		}
 
 		free(this);
diff --git a/tmux.h b/tmux.h
index 74b16c89..c1b91f93 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1370,6 +1370,7 @@ struct cmd_parse_input {
 #define CMD_PARSE_PARSEONLY 0x2
 #define CMD_PARSE_NOALIAS 0x4
 #define CMD_PARSE_VERBOSE 0x8
+#define CMD_PARSE_ONEGROUP 0x10
 
 	const char		*file;
 	u_int			 line;