From afe4ea4250073e482c6ec6accfc539f873df6977 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Tue, 16 Jun 2020 07:28:57 +0000
Subject: [PATCH 1/2] Correctly move to previous line when looking for previous
 word, from Derry Jing.

---
 window-copy.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/window-copy.c b/window-copy.c
index f30d241e..7b7ab72c 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -4535,14 +4535,14 @@ window_copy_cursor_previous_word_pos(struct window_mode_entry *wme,
 				    separators))
 					break;
 			} else {
-				if (data->cy == 0 &&
+				if (py == 0 ||
+				    (data->cy == 0 &&
 				    (screen_hsize(data->backing) == 0 ||
 				    data->oy >=
-				    screen_hsize(data->backing) - 1))
+				    screen_hsize(data->backing) - 1)))
 					goto out;
 
-				py = screen_hsize(data->backing) + data->cy -
-				    data->oy;
+				py--;
 				px = window_copy_find_length(wme, py);
 
 				/* Stop if separator at EOL. */

From 1bf9555e4f1ad19e1e6f97ede6fb19808ff1c267 Mon Sep 17 00:00:00 2001
From: nicm <nicm>
Date: Tue, 16 Jun 2020 08:18:34 +0000
Subject: [PATCH 2/2] d and D keys to reset to default in customize mode.

---
 cmd-set-option.c   |  11 +--
 input.c            |   8 +--
 key-bindings.c     |  49 ++++++++++++++
 mode-tree.c        |  15 ++++-
 options.c          |  21 +++++-
 tmux.1             |   2 +
 tmux.h             |   6 +-
 window-customize.c | 164 +++++++++++++++++++++++++++++++++++----------
 8 files changed, 222 insertions(+), 54 deletions(-)

diff --git a/cmd-set-option.c b/cmd-set-option.c
index 36579f29..0df12aa0 100644
--- a/cmd-set-option.c
+++ b/cmd-set-option.c
@@ -151,16 +151,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
 	if (args_has(args, 'u')) {
 		if (o == NULL)
 			goto out;
-		if (idx == -1) {
-			if (*name == '@')
-				options_remove(o);
-			else if (oo == global_options ||
-			    oo == global_s_options ||
-			    oo == global_w_options)
-				options_default(oo, options_table_entry(o));
-			else
-				options_remove(o);
-		} else if (options_array_set(o, idx, NULL, 0, &cause) != 0) {
+		if (options_remove_or_default(o, idx, &cause) != 0) {
 			cmdq_error(item, "%s", cause);
 			free(cause);
 			goto fail;
diff --git a/input.c b/input.c
index 46d8734d..a3850371 100644
--- a/input.c
+++ b/input.c
@@ -2347,7 +2347,7 @@ static void
 input_exit_rename(struct input_ctx *ictx)
 {
 	struct window_pane	*wp = ictx->wp;
-	struct options_entry	*oe;
+	struct options_entry	*o;
 
 	if (wp == NULL)
 		return;
@@ -2361,9 +2361,9 @@ input_exit_rename(struct input_ctx *ictx)
 		return;
 
 	if (ictx->input_len == 0) {
-		oe = options_get_only(wp->window->options, "automatic-rename");
-		if (oe != NULL)
-			options_remove(oe);
+		o = options_get_only(wp->window->options, "automatic-rename");
+		if (o != NULL)
+			options_remove_or_default(o, -1, NULL);
 		return;
 	}
 	window_set_name(wp->window, ictx->input_buf);
diff --git a/key-bindings.c b/key-bindings.c
index 9e198123..f11bb430 100644
--- a/key-bindings.c
+++ b/key-bindings.c
@@ -231,6 +231,38 @@ key_bindings_remove(const char *name, key_code key)
 	}
 }
 
+void
+key_bindings_reset(const char *name, key_code key)
+{
+	struct key_table	*table;
+	struct key_binding	*bd, *dd;
+
+	table = key_bindings_get_table(name, 0);
+	if (table == NULL)
+		return;
+
+	bd = key_bindings_get(table, key & ~KEYC_MASK_FLAGS);
+	if (bd == NULL)
+		return;
+
+	dd = key_bindings_get_default(table, bd->key);
+	if (dd == NULL) {
+		key_bindings_remove(name, bd->key);
+		return;
+	}
+
+	cmd_list_free(bd->cmdlist);
+	bd->cmdlist = dd->cmdlist;
+	bd->cmdlist->references++;
+
+	free((void *)bd->note);
+	if (dd->note != NULL)
+		bd->note = xstrdup(dd->note);
+	else
+		bd->note = NULL;
+	bd->flags = dd->flags;
+}
+
 void
 key_bindings_remove_table(const char *name)
 {
@@ -248,6 +280,23 @@ key_bindings_remove_table(const char *name)
 	}
 }
 
+void
+key_bindings_reset_table(const char *name)
+{
+	struct key_table	*table;
+	struct key_binding	*bd, *bd1;
+
+	table = key_bindings_get_table(name, 0);
+	if (table == NULL)
+		return;
+	if (RB_EMPTY(&table->default_key_bindings)) {
+		key_bindings_remove_table(name);
+		return;
+	}
+	RB_FOREACH_SAFE(bd, key_bindings, &table->key_bindings, bd1)
+		key_bindings_reset(name, bd->key);
+}
+
 static enum cmd_retval
 key_bindings_init_done(__unused struct cmdq_item *item, __unused void *data)
 {
diff --git a/mode-tree.c b/mode-tree.c
index 993070ec..c4b776f9 100644
--- a/mode-tree.c
+++ b/mode-tree.c
@@ -80,7 +80,9 @@ struct mode_tree_item {
 
 	int				 expanded;
 	int				 tagged;
+
 	int				 draw_as_parent;
+	int				 no_tag;
 
 	struct mode_tree_list		 children;
 	TAILQ_ENTRY(mode_tree_item)	 entry;
@@ -565,6 +567,12 @@ mode_tree_draw_as_parent(struct mode_tree_item *mti)
 	mti->draw_as_parent = 1;
 }
 
+void
+mode_tree_no_tag(struct mode_tree_item *mti)
+{
+	mti->no_tag = 1;
+}
+
 void
 mode_tree_remove(struct mode_tree_data *mtd, struct mode_tree_item *mti)
 {
@@ -1053,6 +1061,8 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
 		 * Do not allow parents and children to both be tagged: untag
 		 * all parents and children of current.
 		 */
+		if (current->no_tag)
+			break;
 		if (!current->tagged) {
 			parent = current->parent;
 			while (parent != NULL) {
@@ -1072,7 +1082,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
 		break;
 	case '\024': /* C-t */
 		for (i = 0; i < mtd->line_size; i++) {
-			if (mtd->line_list[i].item->parent == NULL)
+			if ((mtd->line_list[i].item->parent == NULL &&
+			    !mtd->line_list[i].item->no_tag) ||
+			    (mtd->line_list[i].item->parent != NULL &&
+			    mtd->line_list[i].item->parent->no_tag))
 				mtd->line_list[i].item->tagged = 1;
 			else
 				mtd->line_list[i].item->tagged = 0;
diff --git a/options.c b/options.c
index 6ed38bcd..336eb732 100644
--- a/options.c
+++ b/options.c
@@ -66,6 +66,7 @@ struct options {
 };
 
 static struct options_entry	*options_add(struct options *, const char *);
+static void			 options_remove(struct options_entry *);
 
 #define OPTIONS_IS_STRING(o)						\
 	((o)->tableentry == NULL ||					\
@@ -315,7 +316,7 @@ options_add(struct options *oo, const char *name)
 	return (o);
 }
 
-void
+static void
 options_remove(struct options_entry *o)
 {
 	struct options	*oo = o->owner;
@@ -1106,3 +1107,21 @@ options_push_changes(const char *name)
 			server_redraw_client(loop);
 	}
 }
+
+int
+options_remove_or_default(struct options_entry *o, int idx, char **cause)
+{
+	struct options	*oo = o->owner;
+
+	if (idx == -1) {
+		if (o->tableentry != NULL &&
+		    (oo == global_options ||
+		    oo == global_s_options ||
+		    oo == global_w_options))
+			options_default(oo, o->tableentry);
+		else
+			options_remove(o);
+	} else if (options_array_set(o, idx, NULL, 0, cause) != 0)
+		return (-1);
+	return (0);
+}
diff --git a/tmux.1 b/tmux.1
index d6fd1c85..3c017e64 100644
--- a/tmux.1
+++ b/tmux.1
@@ -2031,6 +2031,8 @@ The following keys may be used in customize mode:
 .It Li "s" Ta "Set option value or key attribute"
 .It Li "S" Ta "Set global option value"
 .It Li "w" Ta "Set window option value, if option is for pane and window"
+.It Li "d" Ta "Set an option or key to the default"
+.It Li "D" Ta "Set tagged options and tagged keys to the default"
 .It Li "u" Ta "Unset an option (set to default value if global) or unbind a key"
 .It Li "U" Ta "Unset tagged options and unbind tagged keys"
 .It Li "C-s" Ta "Search by name"
diff --git a/tmux.h b/tmux.h
index 9bc89628..837566ca 100644
--- a/tmux.h
+++ b/tmux.h
@@ -1988,7 +1988,6 @@ struct options	*options_owner(struct options_entry *);
 const struct options_table_entry *options_table_entry(struct options_entry *);
 struct options_entry *options_get_only(struct options *, const char *);
 struct options_entry *options_get(struct options *, const char *);
-void		 options_remove(struct options_entry *);
 void		 options_array_clear(struct options_entry *);
 union options_value *options_array_get(struct options_entry *, u_int);
 int		 options_array_set(struct options_entry *, u_int, const char *,
@@ -2025,6 +2024,8 @@ int		 options_from_string(struct options *,
 		     const struct options_table_entry *, const char *,
 		     const char *, int, char **);
 void		 options_push_changes(const char *);
+int		 options_remove_or_default(struct options_entry *, int,
+		     char **);
 
 /* options-table.c */
 extern const struct options_table_entry options_table[];
@@ -2325,7 +2326,9 @@ struct key_binding *key_bindings_next(struct key_table *, struct key_binding *);
 void	 key_bindings_add(const char *, key_code, const char *, int,
 	     struct cmd_list *);
 void	 key_bindings_remove(const char *, key_code);
+void	 key_bindings_reset(const char *, key_code);
 void	 key_bindings_remove_table(const char *);
+void	 key_bindings_reset_table(const char *);
 void	 key_bindings_init(void);
 struct cmdq_item *key_bindings_dispatch(struct key_binding *,
 	     struct cmdq_item *, struct client *, struct key_event *,
@@ -2802,6 +2805,7 @@ struct mode_tree_item *mode_tree_add(struct mode_tree_data *,
 	     struct mode_tree_item *, void *, uint64_t, const char *,
 	     const char *, int);
 void	 mode_tree_draw_as_parent(struct mode_tree_item *);
+void	 mode_tree_no_tag(struct mode_tree_item *);
 void	 mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
 void	 mode_tree_draw(struct mode_tree_data *);
 int	 mode_tree_key(struct mode_tree_data *, struct client *, key_code *,
diff --git a/window-customize.c b/window-customize.c
index 093ebbe4..f60d2e6e 100644
--- a/window-customize.c
+++ b/window-customize.c
@@ -76,6 +76,11 @@ enum window_customize_scope {
 	WINDOW_CUSTOMIZE_PANE
 };
 
+enum window_customize_change {
+	WINDOW_CUSTOMIZE_UNSET,
+	WINDOW_CUSTOMIZE_RESET,
+};
+
 struct window_customize_itemdata {
 	struct window_customize_modedata	*data;
 	enum window_customize_scope		 scope;
@@ -101,6 +106,7 @@ struct window_customize_modedata {
 	u_int					  item_size;
 
 	struct cmd_find_state			  fs;
+	enum window_customize_change		  change;
 };
 
 static uint64_t
@@ -380,6 +386,7 @@ window_customize_build_options(struct window_customize_modedata *data,
 	enum window_customize_scope	  scope;
 
 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
+	mode_tree_no_tag(top);
 
 	/*
 	 * We get the options from the first tree, but build it using the
@@ -452,6 +459,7 @@ window_customize_build_keys(struct window_customize_modedata *data,
 
 	xasprintf(&title, "Key Table - %s", kt->name);
 	top = mode_tree_add(data->data, NULL, NULL, tag, title, NULL, 0);
+	mode_tree_no_tag(top);
 	free(title);
 
 	ft = format_create_from_state(NULL, NULL, fs);
@@ -476,6 +484,8 @@ window_customize_build_keys(struct window_customize_modedata *data,
 		item->scope = WINDOW_CUSTOMIZE_KEY;
 		item->table = xstrdup(kt->name);
 		item->key = bd->key;
+		item->name = xstrdup(key_string_lookup_key(item->key, 0));
+		item->idx = -1;
 
 		expanded = format_expand(ft, data->format);
 		child = mode_tree_add(data->data, top, item, (uint64_t)bd,
@@ -488,6 +498,7 @@ window_customize_build_keys(struct window_customize_modedata *data,
 		mti = mode_tree_add(data->data, child, item,
 		    tag|(bd->key << 3)|(0 << 1)|1, "Command", text, -1);
 		mode_tree_draw_as_parent(mti);
+		mode_tree_no_tag(mti);
 		free(text);
 
 		if (bd->note != NULL)
@@ -497,6 +508,7 @@ window_customize_build_keys(struct window_customize_modedata *data,
 		mti = mode_tree_add(data->data, child, item,
 		    tag|(bd->key << 3)|(1 << 1)|1, "Note", text, -1);
 		mode_tree_draw_as_parent(mti);
+		mode_tree_no_tag(mti);
 		free(text);
 
 		if (bd->flags & KEY_BINDING_REPEAT)
@@ -506,6 +518,7 @@ window_customize_build_keys(struct window_customize_modedata *data,
 		mti = mode_tree_add(data->data, child, item,
 		    tag|(bd->key << 3)|(2 << 1)|1, "Repeat", flag, -1);
 		mode_tree_draw_as_parent(mti);
+		mode_tree_no_tag(mti);
 
 		bd = key_bindings_next(kt, bd);
 	}
@@ -1125,8 +1138,7 @@ static void
 window_customize_unset_option(struct window_customize_modedata *data,
     struct window_customize_itemdata *item)
 {
-	struct options_entry			*o;
-	const struct options_table_entry	*oe;
+	struct options_entry	*o;
 
 	if (item == NULL || !window_customize_check_item(data, item, NULL))
 		return;
@@ -1134,20 +1146,30 @@ window_customize_unset_option(struct window_customize_modedata *data,
 	o = options_get(item->oo, item->name);
 	if (o == NULL)
 		return;
-	if (item->idx != -1) {
-		if (item == mode_tree_get_current(data->data))
-			mode_tree_up(data->data, 0);
-		options_array_set(o, item->idx, NULL, 0, NULL);
+	if (item->idx != -1 && item == mode_tree_get_current(data->data))
+		mode_tree_up(data->data, 0);
+	options_remove_or_default(o, item->idx, NULL);
+}
+
+static void
+window_customize_reset_option(struct window_customize_modedata *data,
+    struct window_customize_itemdata *item)
+{
+	struct options		*oo;
+	struct options_entry	*o;
+
+	if (item == NULL || !window_customize_check_item(data, item, NULL))
 		return;
+	if (item->idx != -1)
+		return;
+
+	oo = item->oo;
+	while (oo != NULL) {
+		o = options_get_only(item->oo, item->name);
+		if (o != NULL)
+			options_remove_or_default(o, -1, NULL);
+		oo = options_get_parent(oo);
 	}
-	oe = options_table_entry(o);
-	if (oe != NULL &&
-	    options_owner(o) != global_options &&
-	    options_owner(o) != global_s_options &&
-	    options_owner(o) != global_w_options)
-		options_remove(o);
-	else
-		options_default(options_owner(o), oe);
 }
 
 static int
@@ -1286,21 +1308,52 @@ window_customize_unset_key(struct window_customize_modedata *data,
 }
 
 static void
-window_customize_unset_each(void *modedata, void *itemdata,
+window_customize_reset_key(struct window_customize_modedata *data,
+    struct window_customize_itemdata *item)
+{
+	struct key_table	*kt;
+	struct key_binding	*dd, *bd;
+
+	if (item == NULL || !window_customize_get_key(item, &kt, &bd))
+		return;
+
+	dd = key_bindings_get_default(kt, bd->key);
+	if (dd != NULL && bd->cmdlist == dd->cmdlist)
+		return;
+	if (dd == NULL && item == mode_tree_get_current(data->data)) {
+		mode_tree_collapse_current(data->data);
+		mode_tree_up(data->data, 0);
+	}
+	key_bindings_reset(kt->name, bd->key);
+}
+
+static void
+window_customize_change_each(void *modedata, void *itemdata,
     __unused struct client *c, __unused key_code key)
 {
+	struct window_customize_modedata	*data = modedata;
 	struct window_customize_itemdata	*item = itemdata;
 
-	if (item->scope == WINDOW_CUSTOMIZE_KEY)
-		window_customize_unset_key(modedata, item);
-	else {
-		window_customize_unset_option(modedata, item);
-		options_push_changes(item->name);
+	switch (data->change) {
+	case WINDOW_CUSTOMIZE_UNSET:
+		if (item->scope == WINDOW_CUSTOMIZE_KEY)
+			window_customize_unset_key(data, item);
+		else
+			window_customize_unset_option(data, item);
+		break;
+	case WINDOW_CUSTOMIZE_RESET:
+		if (item->scope == WINDOW_CUSTOMIZE_KEY)
+			window_customize_reset_key(data, item);
+		else
+			window_customize_reset_option(data, item);
+		break;
 	}
+	if (item->scope != WINDOW_CUSTOMIZE_KEY)
+		options_push_changes(item->name);
 }
 
 static int
-window_customize_unset_current_callback(__unused struct client *c,
+window_customize_change_current_callback(__unused struct client *c,
     void *modedata, const char *s, __unused int done)
 {
 	struct window_customize_modedata	*data = modedata;
@@ -1312,12 +1365,22 @@ window_customize_unset_current_callback(__unused struct client *c,
 		return (0);
 
 	item = mode_tree_get_current(data->data);
-	if (item->scope == WINDOW_CUSTOMIZE_KEY)
-		window_customize_unset_key(data, item);
-	else {
-		window_customize_unset_option(data, item);
-		options_push_changes(item->name);
+	switch (data->change) {
+	case WINDOW_CUSTOMIZE_UNSET:
+		if (item->scope == WINDOW_CUSTOMIZE_KEY)
+			window_customize_unset_key(data, item);
+		else
+			window_customize_unset_option(data, item);
+		break;
+	case WINDOW_CUSTOMIZE_RESET:
+		if (item->scope == WINDOW_CUSTOMIZE_KEY)
+			window_customize_reset_key(data, item);
+		else
+			window_customize_reset_option(data, item);
+		break;
 	}
+	if (item->scope != WINDOW_CUSTOMIZE_KEY)
+		options_push_changes(item->name);
 	mode_tree_build(data->data);
 	mode_tree_draw(data->data);
 	data->wp->flags |= PANE_REDRAW;
@@ -1326,7 +1389,7 @@ window_customize_unset_current_callback(__unused struct client *c,
 }
 
 static int
-window_customize_unset_tagged_callback(struct client *c, void *modedata,
+window_customize_change_tagged_callback(struct client *c, void *modedata,
     const char *s, __unused int done)
 {
 	struct window_customize_modedata	*data = modedata;
@@ -1336,7 +1399,7 @@ window_customize_unset_tagged_callback(struct client *c, void *modedata,
 	if (tolower((u_char) s[0]) != 'y' || s[1] != '\0')
 		return (0);
 
-	mode_tree_each_tagged(data->data, window_customize_unset_each, c,
+	mode_tree_each_tagged(data->data, window_customize_change_each, c,
 	    KEYC_NONE, 0);
 	mode_tree_build(data->data);
 	mode_tree_draw(data->data);
@@ -1353,7 +1416,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
 	struct window_pane			*wp = wme->wp;
 	struct window_customize_modedata	*data = wme->data;
 	struct window_customize_itemdata	*item, *new_item;
-	int					 finished;
+	int					 finished, idx;
 	char					*prompt;
 	u_int					 tagged;
 
@@ -1390,17 +1453,43 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
 		options_push_changes(item->name);
 		mode_tree_build(data->data);
 		break;
+	case 'd':
+		if (item == NULL || item->idx != -1)
+			break;
+		xasprintf(&prompt, "Reset %s to default? ", item->name);
+		data->references++;
+		data->change = WINDOW_CUSTOMIZE_RESET;
+		status_prompt_set(c, NULL, prompt, "",
+		    window_customize_change_current_callback,
+		    window_customize_free_callback, data,
+		    PROMPT_SINGLE|PROMPT_NOFORMAT);
+		free(prompt);
+		break;
+	case 'D':
+		tagged = mode_tree_count_tagged(data->data);
+		if (tagged == 0)
+			break;
+		xasprintf(&prompt, "Reset %u tagged to default? ", tagged);
+		data->references++;
+		data->change = WINDOW_CUSTOMIZE_RESET;
+		status_prompt_set(c, NULL, prompt, "",
+		    window_customize_change_tagged_callback,
+		    window_customize_free_callback, data,
+		    PROMPT_SINGLE|PROMPT_NOFORMAT);
+		free(prompt);
+		break;
 	case 'u':
 		if (item == NULL)
 			break;
-		if (item->scope == WINDOW_CUSTOMIZE_KEY) {
-			xasprintf(&prompt, "Unbind key %s? ",
-			    key_string_lookup_key(item->key, 0));
-		} else
-			xasprintf(&prompt, "Unset option %s? ", item->name);
+		idx = item->idx;
+		if (idx != -1)
+			xasprintf(&prompt, "Unset %s[%d]? ", item->name, idx);
+		else
+			xasprintf(&prompt, "Unset %s? ", item->name);
 		data->references++;
+		data->change = WINDOW_CUSTOMIZE_UNSET;
 		status_prompt_set(c, NULL, prompt, "",
-		    window_customize_unset_current_callback,
+		    window_customize_change_current_callback,
 		    window_customize_free_callback, data,
 		    PROMPT_SINGLE|PROMPT_NOFORMAT);
 		free(prompt);
@@ -1409,10 +1498,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
 		tagged = mode_tree_count_tagged(data->data);
 		if (tagged == 0)
 			break;
-		xasprintf(&prompt, "Unset or unbind %u tagged? ", tagged);
+		xasprintf(&prompt, "Unset %u tagged? ", tagged);
 		data->references++;
+		data->change = WINDOW_CUSTOMIZE_UNSET;
 		status_prompt_set(c, NULL, prompt, "",
-		    window_customize_unset_tagged_callback,
+		    window_customize_change_tagged_callback,
 		    window_customize_free_callback, data,
 		    PROMPT_SINGLE|PROMPT_NOFORMAT);
 		free(prompt);