diff mbox series

[v7,7/8] sequencer.c: define get_config_from_cleanup

Message ID ced1df0fc76c97c9896b6cbb5b4154532461716d.1552275703.git.liu.denton@gmail.com (mailing list archive)
State New, archived
Headers show
Series Fix scissors bug during conflict | expand

Commit Message

Denton Liu March 11, 2019, 3:42 a.m. UTC
Define a function which allows us to get the string configuration value
of a enum commit_msg_cleanup_mode. This is done by refactoring
get_cleanup_mode such that it uses a lookup table to find the mappings
between string and enum and then using the same LUT in reverse to define
get_config_from_cleanup.

Signed-off-by: Denton Liu <liu.denton@gmail.com>
---
 sequencer.c | 67 +++++++++++++++++++++++++++++++++++++++++------------
 sequencer.h |  1 +
 2 files changed, 53 insertions(+), 15 deletions(-)

Comments

Eric Sunshine March 11, 2019, 6:19 a.m. UTC | #1
On Sun, Mar 10, 2019 at 11:42 PM Denton Liu <liu.denton@gmail.com> wrote:
> Define a function which allows us to get the string configuration value
> of a enum commit_msg_cleanup_mode. This is done by refactoring
> get_cleanup_mode such that it uses a lookup table to find the mappings
> between string and enum and then using the same LUT in reverse to define
> get_config_from_cleanup.

Aside from a missing 'static', the comments below are mostly style
suggestions to make the new code less noisy. The basic idea is to
reduce the "wordiness" of the code so that the eye can glide over it
more easily, thus allowing the reader to grasp its meaning "at a
glance", without necessarily having to read it attentively. You may or
may not consider the suggestions actionable.

> Signed-off-by: Denton Liu <liu.denton@gmail.com>
> ---
> diff --git a/sequencer.c b/sequencer.c
> @@ -160,6 +160,22 @@ static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
> +struct cleanup_config_mapping {
> +    const char *config_value;
> +    enum commit_msg_cleanup_mode editor_cleanup;
> +    enum commit_msg_cleanup_mode no_editor_cleanup;
> +};

These members are already inside a struct named
"cleanup_config_mapping", so we can drop some of the wordiness from
the member names. For instance:

    config --or-- value --or-- val
    editor
    no_editor

> +/* note that we assume that cleanup_config_mapping[0] contains the default settings */
> +struct cleanup_config_mapping cleanup_config_mappings[] = {
> +       { "default", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_SPACE },
> +       { "verbatim", COMMIT_MSG_CLEANUP_NONE, COMMIT_MSG_CLEANUP_NONE },
> +       { "whitespace", COMMIT_MSG_CLEANUP_SPACE, COMMIT_MSG_CLEANUP_SPACE },
> +       { "strip", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_ALL },
> +       { "scissors", COMMIT_MSG_CLEANUP_SCISSORS, COMMIT_MSG_CLEANUP_SPACE },
> +       { NULL, 0, 0 }
> +};

This table should be 'static'.

> @@ -504,26 +520,42 @@ static int fast_forward_to(struct repository *r,
>  enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
>         int use_editor, int die_on_error)
>  {
> +       struct cleanup_config_mapping *default_mapping = &cleanup_config_mappings[0];
> +       struct cleanup_config_mapping *current_mapping;

We can shorten these two variable names to "def" and "p",
respectively, without losing clarity; there are only two variables in
the function, so it's not difficult to remember what they are. More
importantly, the rest of the code becomes considerably shorter,
allowing the eye to glide over it while easily taking in its meaning
rather than having to spend time actively reading it.

> +       if (!cleanup_arg) {
> +               return use_editor ? default_mapping->editor_cleanup :
> +                                   default_mapping->no_editor_cleanup;
> +       }
> +
> +       for (current_mapping = cleanup_config_mappings; current_mapping->config_value; current_mapping++) {
> +               if (!strcmp(cleanup_arg, current_mapping->config_value)) {
> +                       return use_editor ? current_mapping->editor_cleanup :
> +                                           current_mapping->no_editor_cleanup;
> +               }
> +       }

For instance, with the shorter names, the above loop (while also
dropping unnecessary braces) becomes:

    for (p = cleanup_config_mappings; p->val; p++)
        if (!strcmp(cleanup_arg, p->val))
            return use_editor ? p->editor : p->no_editor;

> +       if (!die_on_error) {
>                 warning(_("Invalid cleanup mode %s, falling back to default"), cleanup_arg);
> -               return use_editor ? COMMIT_MSG_CLEANUP_ALL :
> -                                   COMMIT_MSG_CLEANUP_SPACE;
> +               return use_editor ? default_mapping->editor_cleanup :
> +                                   default_mapping->no_editor_cleanup;
>         } else
>                 die(_("Invalid cleanup mode %s"), cleanup_arg);
>  }

Same comments apply to other new code introduced by this patch.

> +const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)
> +{
> +       struct cleanup_config_mapping *current_mapping;
> +
> +       for (current_mapping = &cleanup_config_mappings[1]; current_mapping->config_value; current_mapping++) {
> +               if (cleanup_mode == current_mapping->editor_cleanup) {
> +                       return current_mapping->config_value;
> +               }
> +       }
> +
> +       BUG(_("invalid cleanup_mode provided"));

Don't localize BUG() messages; they are intended only for developer
eyes, not end-users. So:

    BUG("invalid cleanup_mode provided");

> +}
> diff --git a/sequencer.h b/sequencer.h
> @@ -118,6 +118,7 @@ void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
>  enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
>         int use_editor, int die_on_error);
> +const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode);

A more intuitive name might be describe_cleanup_mode().
Junio C Hamano March 12, 2019, 6:29 a.m. UTC | #2
Denton Liu <liu.denton@gmail.com> writes:

> +struct cleanup_config_mapping {
> +    const char *config_value;
> +    enum commit_msg_cleanup_mode editor_cleanup;
> +    enum commit_msg_cleanup_mode no_editor_cleanup;
> +};

Is this code using 4-space indent?  Please don't.  Also, I found
that Eric's comment on naming gave a good suggestion.

Is the cleanup_config_mapping[] array we see below supposed to be
constant, or does it allow further runtime configuration?  I am
assuming the former (i.e. when the user says "default", then
editor_cleanup will always become CLEANUP_ALL and no_editor_cleanup
will always become CLEANUP_SPACE), in which case, I wonder if we can
be more explicit about constness of the table.

> +/* note that we assume that cleanup_config_mapping[0] contains the default settings */

That sounds as if it is bad to make that assumption.  Be more
positive and direct to clearly tell future programmers what rule
they need to honor, e.g.

	/* the 0th element of this array must be the "default" */

> +struct cleanup_config_mapping cleanup_config_mappings[] = {

Do not give a plural name to an array.  Access to an element in a
array "type thing[]" can be written thing[4] and can be more
naturally read as "the fourth thing" (you do not say "the fourth
things") that way.

An exception is when you very often pass around the array as a whole
as one unit of datum across callchains.  

> +	struct cleanup_config_mapping *default_mapping = &cleanup_config_mappings[0];
> +	struct cleanup_config_mapping *current_mapping;
> +
> +	if (!cleanup_arg) {
> +		return use_editor ? default_mapping->editor_cleanup :
> +				    default_mapping->no_editor_cleanup;
> +	}

No need for extra {}.

> +
> +	for (current_mapping = cleanup_config_mappings; current_mapping->config_value; current_mapping++) {
> +		if (!strcmp(cleanup_arg, current_mapping->config_value)) {
> +			return use_editor ? current_mapping->editor_cleanup :
> +					    current_mapping->no_editor_cleanup;
> +		}
> +	}

Ditto.  In addition, perhaps split the for (...) like so:

	for (current_mapping = cleanup_config_mappings;
             current_mapping->config_value;
             current_mapping++)
		if (...)
			return ...;

> +const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)

static???

Is this really getting "config" from "cleanup"?  It rather smells
backwards i.e. grabbing the clean-up settings from the config system
to me.

> +{
> +	struct cleanup_config_mapping *current_mapping;
> +
> +	for (current_mapping = &cleanup_config_mappings[1]; current_mapping->config_value; current_mapping++) {
> +		if (cleanup_mode == current_mapping->editor_cleanup) {
> +			return current_mapping->config_value;
> +		}
> +	}
> +
> +	BUG(_("invalid cleanup_mode provided"));

Is that a bug (i.e. programming error) or a bad configuration file?
I think you meant the former, but then I do not think we want _()
around the message.  Instead, however, we may want to show the
cleanup_mode that was provided, possibly with the available values
in the .editor_cleanup field of cleanup_config_mapping[] entries.
diff mbox series

Patch

diff --git a/sequencer.c b/sequencer.c
index 612621f221..5d94e2c865 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -160,6 +160,22 @@  static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
 static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
 static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec")
 
+struct cleanup_config_mapping {
+    const char *config_value;
+    enum commit_msg_cleanup_mode editor_cleanup;
+    enum commit_msg_cleanup_mode no_editor_cleanup;
+};
+
+/* note that we assume that cleanup_config_mapping[0] contains the default settings */
+struct cleanup_config_mapping cleanup_config_mappings[] = {
+	{ "default", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_SPACE },
+	{ "verbatim", COMMIT_MSG_CLEANUP_NONE, COMMIT_MSG_CLEANUP_NONE },
+	{ "whitespace", COMMIT_MSG_CLEANUP_SPACE, COMMIT_MSG_CLEANUP_SPACE },
+	{ "strip", COMMIT_MSG_CLEANUP_ALL, COMMIT_MSG_CLEANUP_ALL },
+	{ "scissors", COMMIT_MSG_CLEANUP_SCISSORS, COMMIT_MSG_CLEANUP_SPACE },
+	{ NULL, 0, 0 }
+};
+
 static int git_sequencer_config(const char *k, const char *v, void *cb)
 {
 	struct replay_opts *opts = cb;
@@ -504,26 +520,42 @@  static int fast_forward_to(struct repository *r,
 enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
 	int use_editor, int die_on_error)
 {
-	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
-		return use_editor ? COMMIT_MSG_CLEANUP_ALL :
-				    COMMIT_MSG_CLEANUP_SPACE;
-	else if (!strcmp(cleanup_arg, "verbatim"))
-		return COMMIT_MSG_CLEANUP_NONE;
-	else if (!strcmp(cleanup_arg, "whitespace"))
-		return COMMIT_MSG_CLEANUP_SPACE;
-	else if (!strcmp(cleanup_arg, "strip"))
-		return COMMIT_MSG_CLEANUP_ALL;
-	else if (!strcmp(cleanup_arg, "scissors"))
-		return use_editor ? COMMIT_MSG_CLEANUP_SCISSORS :
-				    COMMIT_MSG_CLEANUP_SPACE;
-	else if (!die_on_error) {
+	struct cleanup_config_mapping *default_mapping = &cleanup_config_mappings[0];
+	struct cleanup_config_mapping *current_mapping;
+
+	if (!cleanup_arg) {
+		return use_editor ? default_mapping->editor_cleanup :
+				    default_mapping->no_editor_cleanup;
+	}
+
+	for (current_mapping = cleanup_config_mappings; current_mapping->config_value; current_mapping++) {
+		if (!strcmp(cleanup_arg, current_mapping->config_value)) {
+			return use_editor ? current_mapping->editor_cleanup :
+					    current_mapping->no_editor_cleanup;
+		}
+	}
+
+	if (!die_on_error) {
 		warning(_("Invalid cleanup mode %s, falling back to default"), cleanup_arg);
-		return use_editor ? COMMIT_MSG_CLEANUP_ALL :
-				    COMMIT_MSG_CLEANUP_SPACE;
+		return use_editor ? default_mapping->editor_cleanup :
+				    default_mapping->no_editor_cleanup;
 	} else
 		die(_("Invalid cleanup mode %s"), cleanup_arg);
 }
 
+const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode)
+{
+	struct cleanup_config_mapping *current_mapping;
+
+	for (current_mapping = &cleanup_config_mappings[1]; current_mapping->config_value; current_mapping++) {
+		if (cleanup_mode == current_mapping->editor_cleanup) {
+			return current_mapping->config_value;
+		}
+	}
+
+	BUG(_("invalid cleanup_mode provided"));
+}
+
 void append_conflicts_hint(struct index_state *istate,
 			   struct strbuf *msgbuf)
 {
@@ -2350,6 +2382,8 @@  static int populate_opts_cb(const char *key, const char *value, void *data)
 		opts->allow_rerere_auto =
 			git_config_bool_or_int(key, value, &error_flag) ?
 				RERERE_AUTOUPDATE : RERERE_NOAUTOUPDATE;
+	else if (!strcmp(key, "options.default-msg-cleanup"))
+		opts->default_msg_cleanup = get_cleanup_mode(value, 1, 1);
 	else
 		return error(_("invalid key: %s"), key);
 
@@ -2754,6 +2788,9 @@  static int save_opts(struct replay_opts *opts)
 		res |= git_config_set_in_file_gently(opts_file, "options.allow-rerere-auto",
 						     opts->allow_rerere_auto == RERERE_AUTOUPDATE ?
 						     "true" : "false");
+
+	res |= git_config_set_in_file_gently(opts_file, "options.default-msg-cleanup",
+					     get_config_from_cleanup(opts->default_msg_cleanup));
 	return res;
 }
 
diff --git a/sequencer.h b/sequencer.h
index e7908f558e..e3c1f44807 100644
--- a/sequencer.h
+++ b/sequencer.h
@@ -118,6 +118,7 @@  void append_signoff(struct strbuf *msgbuf, size_t ignore_footer, unsigned flag);
 void append_conflicts_hint(struct index_state *istate, struct strbuf *msgbuf);
 enum commit_msg_cleanup_mode get_cleanup_mode(const char *cleanup_arg,
 	int use_editor, int die_on_error);
+const char *get_config_from_cleanup(enum commit_msg_cleanup_mode cleanup_mode);
 
 void cleanup_message(struct strbuf *msgbuf,
 	enum commit_msg_cleanup_mode cleanup_mode, int verbose);