Author: julianfoad
Date: Mon Dec  3 16:19:44 2012
New Revision: 1416578

URL: http://svn.apache.org/viewvc?rev=1416578&view=rev
Log:
A bit of table-driven goodness for the interactive conflict resolver.

* subversion/svn/conflict-callbacks.c
  (MAX_PROMPT_WIDTH): New constant.
  (resolver_option_t): New struct.
  (text_conflict_options, prop_conflict_options, obstructed_add_options,
   tree_conflict_options): New arrays of option definitions.
  (find_option, prompt_string, help_string): New functions.
  (handle_text_conflict, handle_prop_conflict,
   svn_cl__conflict_func_interactive): Generate the prompt string and the
    help string from the arrays of option definitions.

Modified:
    subversion/trunk/subversion/svn/conflict-callbacks.c

Modified: subversion/trunk/subversion/svn/conflict-callbacks.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/conflict-callbacks.c?rev=1416578&r1=1416577&r2=1416578&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/conflict-callbacks.c (original)
+++ subversion/trunk/subversion/svn/conflict-callbacks.c Mon Dec  3 16:19:44 
2012
@@ -281,6 +281,159 @@ launch_resolver(svn_boolean_t *performed
   return SVN_NO_ERROR;
 }
 
+
+/* Maximum line length for the prompt string. */
+#define MAX_PROMPT_WIDTH 70
+
+/* Description of a resolver option */
+typedef struct resolver_option_t
+{
+  const char *code;        /* one or two characters */
+  const char *short_desc;  /* short description */
+  const char *long_desc;   /* longer description */
+} resolver_option_t;
+
+/* Resolver options for a text conflict */
+static const resolver_option_t text_conflict_options[] =
+{
+  { "e",  "edit",             N_("change merged file in an editor") },
+  { "df", "diff-full",        N_("show all changes made to merged file") },
+  { "r",  "resolved",         N_("accept merged version of file") },
+  { "" },
+  { "dc", "display-conflict", N_("show all conflicts (ignoring merged 
version)") },
+  { "mc", "mine-conflict",    N_("accept my version for all conflicts (same)") 
},
+  { "tc", "theirs-conflict",  N_("accept their version for all conflicts 
(same)") },
+  { "" },
+  { "mf", "mine-full",        N_("accept my version of entire file (even "
+                                 "non-conflicts)") },
+  { "tf", "theirs-full",      N_("accept their version of entire file (same)") 
},
+  { "" },
+  { "p",  "postpone",         N_("mark the conflict to be resolved later") },
+  { "m",  "merge",            N_("use internal merge tool to resolve 
conflict") },
+  { "l",  "launch",           N_("launch external tool to resolve conflict") },
+  { "s",  "show all options", N_("show this list") },
+  { NULL }
+};
+
+/* Resolver options for a property conflict */
+static const resolver_option_t prop_conflict_options[] =
+{
+  { "p",  "postpone",         N_("mark the conflict to be resolved later") },
+  { "mf", "mine-full",        N_("accept my version of entire file (even "
+                                "non-conflicts)") },
+  { "tf", "theirs-full",      N_("accept their version of entire file (same)") 
},
+  { NULL }
+};
+
+/* Resolver options for an obstructued addition */
+static const resolver_option_t obstructed_add_options[] =
+{
+  { "p",  "postpone",         N_("resolve the conflict later") },
+  { "mf", "mine-full",        N_("accept pre-existing item (ignore upstream 
addition)") },
+  { "tf", "theirs-full",      N_("accept incoming item (overwrite pre-existing 
item)") },
+  { "h",  "help",             N_("show this help") },
+};
+
+/* Resolver options for a tree conflict */
+static const resolver_option_t tree_conflict_options[] =
+{
+  { "p",  "postpone",         N_("resolve the conflict later") },
+  { "r",  "resolved",         N_("accept current working copy state") },
+  { "mc", "mine-conflict",    N_("prefer local change") },
+  { "tc", "theirs-conflict",  N_("prefer incoming change") },
+  { "h",  "show help",        N_("show this help") },
+  { NULL }
+};
+
+/* Return a pointer to the option description in OPTIONS matching the
+ * one- or two-character OPTION_CODE.  Return NULL if not found. */
+static const resolver_option_t *
+find_option(const resolver_option_t *options,
+            const char *option_code)
+{
+  const resolver_option_t *opt;
+
+  for (opt = options; opt->code; opt++)
+    {
+      if (strcmp(opt->code, option_code) == 0)
+        return opt;
+    }
+  return NULL;
+}
+
+/* Return a prompt string listing the options OPTIONS. If OPTION_CODES is
+ * non-null, select only the options whose codes are mentioned in it. */
+static const char *
+prompt_string(const resolver_option_t *options,
+              const char *const *option_codes,
+              apr_pool_t *pool)
+{
+  const char *result = "Select:";
+  int this_line_len = strlen(result);
+  svn_boolean_t first = TRUE;
+
+  while (1)
+    {
+      const resolver_option_t *opt;
+      const char *s;
+
+      if (option_codes)
+        {
+          if (! *option_codes)
+            break;
+          opt = find_option(options, *option_codes++);
+        }
+      else
+        {
+          opt = options++;
+          if (! opt->code)
+            break;
+        }
+
+      if (! first)
+        result = apr_pstrcat(pool, result, ",", (char *)NULL);
+      /* Break the line if adding the next option would make it too long */
+      if ((this_line_len + strlen(opt->short_desc) + 6) > MAX_PROMPT_WIDTH)
+        {
+          result = apr_pstrcat(pool, result, "\n       ", (char *)NULL);
+          this_line_len = 7;
+        }
+      s = apr_psprintf(pool, " (%s) %s",
+                       opt->code, opt->short_desc);
+      result = apr_pstrcat(pool, result, s, (char *)NULL);
+      this_line_len += strlen(s);
+      first = FALSE;
+    }
+  return apr_pstrcat(pool, result, ": ", (char *)NULL);
+}
+
+/* Return a help string listing the OPTIONS. */
+static const char *
+help_string(const resolver_option_t *options,
+            apr_pool_t *pool)
+{
+  const char *result = "";
+  const resolver_option_t *opt;
+
+  for (opt = options; opt->code; opt++)
+    {
+      /* Append a line describing OPT, or a blank line if its code is "". */
+      if (opt->code[0])
+        {
+          const char *s = apr_psprintf(pool, "  (%s)", opt->code);
+
+          result = apr_psprintf(pool, "%s%-6s %-16s - %s\n",
+                                result, s, opt->short_desc, opt->long_desc);
+        }
+      else
+        {
+          result = apr_pstrcat(pool, result, "\n", (char *)NULL);
+        }
+    }
+  return result;
+}
+
+
 /* Ask the user what to do about the text conflict described by DESC.
  * Return the answer in RESULT. B is the conflict baton for this
  * conflict resolution session.
@@ -292,8 +445,6 @@ handle_text_conflict(svn_wc_conflict_res
                      apr_pool_t *scratch_pool)
 {
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-  const char *answer;
-  char *prompt;
   svn_boolean_t diff_allowed = FALSE;
   /* Have they done something that might have affected the merged
      file (so that we need to save a .edited copy)? */
@@ -320,73 +471,45 @@ handle_text_conflict(svn_wc_conflict_res
 
   while (TRUE)
     {
-      svn_pool_clear(iterpool);
+      const char *options[12];  /* size of array must be big enough */
+      const char **next_option = options;
+      const char *prompt;
+      const char *answer;
 
-      prompt = apr_pstrdup(iterpool, _("Select: (p) postpone"));
+      svn_pool_clear(iterpool);
 
+      *next_option++ = "p";
       if (diff_allowed)
         {
-          prompt = apr_pstrcat(iterpool, prompt,
-                               _(", (df) diff-full, (e) edit, (m) merge"),
-                               (char *)NULL);
+          *next_option++ = "df";
+          *next_option++ = "e";
+          *next_option++ = "m";
 
           if (knows_something)
-            prompt = apr_pstrcat(iterpool, prompt, _(", (r) resolved"),
-                                 (char *)NULL);
+            *next_option++ = "r";
 
           if (! desc->is_binary)
-            prompt = apr_pstrcat(iterpool, prompt,
-                                 _(",\n        (mc) mine-conflict, "
-                                   "(tc) theirs-conflict"),
-                                 (char *)NULL);
+            *next_option++ = "mc";
+            *next_option++ = "tc";
         }
       else
         {
           if (knows_something)
-            prompt = apr_pstrcat(iterpool, prompt, _(", (r) resolved"),
-                                 (char *)NULL);
-          prompt = apr_pstrcat(iterpool, prompt,
-                               _(",\n        "
-                                 "(mf) mine-full, (tf) theirs-full"),
-                               (char *)NULL);
-        }
-
-      prompt = apr_pstrcat(iterpool, prompt, ",\n        ", (char *)NULL);
-      prompt = apr_pstrcat(iterpool, prompt,
-                           _("(s) show all options: "),
-                           (char *)NULL);
+            *next_option++ = "r";
+          *next_option++ = "mf";
+          *next_option++ = "tf";
+        }
+      *next_option++ = "s";
+      *next_option++ = NULL;
+      prompt = prompt_string(text_conflict_options, options, iterpool);
 
       SVN_ERR(svn_cmdline_prompt_user2(&answer, prompt, b->pb, iterpool));
 
       if (strcmp(answer, "s") == 0)
         {
-          /* These are used in svn_cl__accept_from_word(). */
-          SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
-          _("\n"
-            "  (e)  edit             - change merged file in an editor\n"
-            "  (df) diff-full        - show all changes made to merged "
-                                      "file\n"
-            "  (r)  resolved         - accept merged version of file\n"
-            "\n"
-            "  (dc) display-conflict - show all conflicts "
-                                      "(ignoring merged version)\n"
-            "  (mc) mine-conflict    - accept my version for all "
-                                      "conflicts (same)\n"
-            "  (tc) theirs-conflict  - accept their version for all "
-                                      "conflicts (same)\n"
-            "\n"
-            "  (mf) mine-full        - accept my version of entire file "
-                                      "(even non-conflicts)\n"
-            "  (tf) theirs-full      - accept their version of entire "
-                                      "file (same)\n"
-            "\n"
-            "  (p)  postpone         - mark the conflict to be "
-                                      "resolved later\n"
-            "  (m)  merge            - use internal merge tool to "
-                                      "resolve conflict\n"
-            "  (l)  launch           - launch external tool to "
-                                      "resolve conflict\n"
-            "  (s)  show all         - show this list\n\n")));
+          SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
+                                      help_string(text_conflict_options,
+                                                  iterpool)));
         }
       else if (strcmp(answer, "p") == 0 || strcmp(answer, ":-P") == 0)
         {
@@ -555,9 +678,8 @@ handle_prop_conflict(svn_wc_conflict_res
                      svn_cl__interactive_conflict_baton_t *b,
                      apr_pool_t *scratch_pool)
 {
-  const char *answer;
-  const char *prompt;
-  svn_stringbuf_t *prop_reject;
+  const char *prompt
+    = prompt_string(prop_conflict_options, NULL, scratch_pool);
   apr_pool_t *iterpool;
 
   SVN_ERR_ASSERT(desc->kind == svn_wc_conflict_kind_property);
@@ -577,6 +699,8 @@ handle_prop_conflict(svn_wc_conflict_res
    * ### This needs to be fixed so we can present better options here. */
   if (desc->their_abspath)
     {
+      svn_stringbuf_t *prop_reject;
+
       /* ### The library dumps an svn_string_t into a temp file, and
        * ### we read it back from the file into an svn_stringbuf_t here.
        * ### That's rather silly. We should be passed svn_string_t's
@@ -598,9 +722,9 @@ handle_prop_conflict(svn_wc_conflict_res
   iterpool = svn_pool_create(scratch_pool);
   while (TRUE)
     {
-      svn_pool_clear(iterpool);
+      const char *answer;
 
-      prompt = _("Select: (p) postpone, (mf) mine-full, (tf) theirs-full: ");
+      svn_pool_clear(iterpool);
 
       SVN_ERR(svn_cmdline_prompt_user2(&answer, prompt, b->pb, iterpool));
 
@@ -796,7 +920,8 @@ svn_cl__conflict_func_interactive(svn_wc
            && (desc->reason == svn_wc_conflict_reason_obstructed))
     {
       const char *answer;
-      const char *prompt;
+      const char *prompt
+        = prompt_string(obstructed_add_options, NULL, scratch_pool);
 
       SVN_ERR(svn_cmdline_fprintf(
                    stderr, subpool,
@@ -805,8 +930,6 @@ svn_cl__conflict_func_interactive(svn_wc
                    svn_cl__local_style_skip_ancestor(b->path_prefix,
                                                      desc->local_abspath,
                                                      subpool)));
-      prompt = _("Select: (p) postpone, (mf) mine-full, "
-                 "(tf) theirs-full, (h) help: ");
 
       while (1)
         {
@@ -816,13 +939,9 @@ svn_cl__conflict_func_interactive(svn_wc
 
           if (strcmp(answer, "h") == 0 || strcmp(answer, "?") == 0)
             {
-              SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
-              _("  (p)  postpone    - resolve the conflict later\n"
-                "  (mf) mine-full   - accept pre-existing item "
-                "(ignore upstream addition)\n"
-                "  (tf) theirs-full - accept incoming item "
-                "(overwrite pre-existing item)\n"
-                "  (h)  help        - show this help\n\n")));
+              SVN_ERR(svn_cmdline_fprintf(stderr, subpool, "%s\n",
+                                          help_string(obstructed_add_options,
+                                                      subpool)));
             }
           if (strcmp(answer, "p") == 0 || strcmp(answer, ":-P") == 0)
             {
@@ -845,7 +964,8 @@ svn_cl__conflict_func_interactive(svn_wc
   else if (desc->kind == svn_wc_conflict_kind_tree)
     {
       const char *answer;
-      const char *prompt;
+      const char *prompt
+        = prompt_string(tree_conflict_options, NULL, scratch_pool);
       const char *readable_desc;
 
       SVN_ERR(svn_cl__get_human_readable_tree_conflict_description(
@@ -858,10 +978,6 @@ svn_cl__conflict_func_interactive(svn_wc
                                                      scratch_pool),
                    readable_desc));
 
-      prompt = _("Select: (p) postpone, (r) mark-resolved, "
-                 "(mc) mine-conflict,\n"
-                 "        (tc) theirs-conflict, (h) help: ");
-
       while (1)
         {
           svn_pool_clear(subpool);
@@ -870,11 +986,9 @@ svn_cl__conflict_func_interactive(svn_wc
 
           if (strcmp(answer, "h") == 0 || strcmp(answer, "?") == 0)
             {
-              SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
-              _("  (p) postpone         - resolve the conflict later\n"
-                "  (r) resolved         - accept current working copy state\n"
-                "  (mc) mine-conflict   - prefer local change\n"
-                "  (tc) theirs-conflict - prefer incoming change\n")));
+              SVN_ERR(svn_cmdline_fprintf(stderr, subpool, "%s",
+                                          help_string(tree_conflict_options,
+                                                      subpool)));
             }
           if (strcmp(answer, "p") == 0 || strcmp(answer, ":-p") == 0)
             {


Reply via email to