]> git.neil.brown.name Git - edlib.git/commitdiff
mergeview/wiggle: improvements for space-only conflicts.
authorNeilBrown <neil@brown.name>
Mon, 12 Jun 2023 09:40:56 +0000 (19:40 +1000)
committerNeilBrown <neil@brown.name>
Wed, 28 Jun 2023 07:51:41 +0000 (17:51 +1000)
1/ change how the "wiggle" is reported.  It is no longer reported
   by "set-wiggle".  Instead a new "get-result" function returns
   various results including the "wiggle".
2/ count how many conflicts only involve white space.  If that is
   all the conflicts, then still calculate the wiggle.
3/ If a merge only has space conflicts, use blue for the merge
   markers rather the red (or green).
4/ mark the green merge markers bold so they stand out more
5/ When adding attrs, annotate conflicts which are space only,
   and respond to that with underlines instead of inverse red.

Signed-off-by: NeilBrown <neil@brown.name>
DOC/TODO.md
lib-wiggle.c
python/lib-mergeview.py

index 73eca513c5d793322865ccde1db8bbdbe3659e0b..69c02af71e6aa63148d1a5ac8ad1b1db979c67b4 100644 (file)
@@ -195,6 +195,11 @@ Module features
 
 ### popup
 
+### lib-wiggle
+
+- [ ] maybe find matches considering only alphanum first, then
+      refine the gaps considering punctuation.
+
 ### lib-diff
 
 - [ ] When viewing diff or merge can get into infinite loop.  Possibly due
@@ -221,7 +226,7 @@ Module features
          if there is a selection, paste in patch with selection as
          "orig"
          if no selection - just paste it in with empty orig
-- [ ] merge-mode to highlight markers with "space-only" or "no-diff" state
+- [X] merge-mode to highlight markers with "space-only" or "no-diff" state
       Also have green for "no conflicts", but it doesn't stand out.
       It would be nice if space-only differences didn't stand out so much.
       That would require different mark-up, or moving a mark around while
index fd3feb990482be2717003379787304c60e3d086e..0133dce7cd769c3c69277f1bd05a89327ad66f39 100644 (file)
@@ -195,6 +195,9 @@ struct wiggle_data {
                                 */
        } texts[3];
        struct command c;
+       /* After set-wiggle is called, these are set */
+       int space_conflicts, conflicts, wiggles;
+       char *wiggle;
 };
 
 DEF_CMD(notify_close)
@@ -227,6 +230,7 @@ DEF_CMD(wiggle_close)
                wd->texts[i].end = NULL;
                wd->texts[i].text = NULL;
        }
+       free(wd->wiggle);
        return 1;
 }
 
@@ -378,6 +382,38 @@ static const char *typenames[] = {
        [AlreadyApplied] = "AlreadyApplied",
 };
 
+static bool merge_has_nonspace(struct file f, int pos, int len)
+{
+       char *cp;
+       char *endcp;
+
+       if (len == 0)
+               return False;
+       if (!f.list)
+               return True;
+       endcp = f.list[pos+len-1].start + f.list[pos+len-1].len;
+       cp = f.list[pos].start;
+       return has_nonspace(cp, endcp-cp);
+}
+
+static int count_space_conflicts(struct merge *merge safe,
+                                struct file a, struct file b, struct file c)
+{
+       int cnt = 0;
+       struct merge *m;
+
+       for (m = merge; m->type != End; m++) {
+
+               if (m->type != Conflict)
+                       continue;
+               if (!merge_has_nonspace(a, m->a, m->al) &&
+                   !merge_has_nonspace(b, m->b, m->bl) &&
+                   !merge_has_nonspace(c, m->c, m->cl))
+                       cnt += 1;
+       }
+       return cnt;
+}
+
 static void add_merge_markup(struct pane *p safe,
                             struct mark *st,
                             int skip, int choose,
@@ -395,7 +431,10 @@ static void add_merge_markup(struct pane *p safe,
        for (m = merge; m->type != End; m++) {
                int len;
                const char *cp, *endcp;
+               wint_t wch;
+               bool non_space;
                int chars;
+               char *suffix = "";
                char buf[30];
 
                switch (which) {
@@ -425,10 +464,17 @@ static void add_merge_markup(struct pane *p safe,
                endcp = f.list[pos+len-1].start + f.list[pos+len-1].len;
                pos += len;
                chars = 0;
-               while (get_utf8(&cp, endcp) < WERR)
+               non_space = False;
+               while ((wch = get_utf8(&cp, endcp)) < WERR) {
                        chars += 1;
+                       if (!iswspace(wch))
+                               non_space = True;
+               }
 
-               snprintf(buf, sizeof(buf), "%d %s", chars, typenames[m->type]);
+               if (m->type == Conflict && !non_space)
+                       suffix = " spaces";
+               snprintf(buf, sizeof(buf), "%d %s%s",
+                        chars, typenames[m->type], suffix);
                call("doc:set-attr", p, 0, st, attr, 0, NULL, buf);
                while (chars > 0) {
                        wint_t ch = doc_next(p, st);
@@ -439,8 +485,8 @@ static void add_merge_markup(struct pane *p safe,
                                doskip(p, st, NULL, skip, choose);
                        chars -= 1;
                        if (is_eol(ch) && chars > 0) {
-                               snprintf(buf, sizeof(buf), "%d %s", chars,
-                                        typenames[m->type]);
+                               snprintf(buf, sizeof(buf), "%d %s%s", chars,
+                                        typenames[m->type], suffix);
                                call("doc:set-attr", p, 0, st, attr,
                                     0, NULL, buf);
                        }
@@ -481,6 +527,7 @@ static char *collect_merge(struct merge *merge safe,
        for (m = merge; m->type != End; m++) {
                if (m->type == Unmatched ||
                    m->type == AlreadyApplied ||
+                   m->type == Conflict ||
                    m->type == Unchanged)
                        l += copy_words(NULL, &of, m->a, m->al);
                else if (m->type == Changed)
@@ -492,6 +539,7 @@ static char *collect_merge(struct merge *merge safe,
        for (m = merge; m->type != End; m++) {
                if (m->type == Unmatched ||
                    m->type == AlreadyApplied ||
+                   m->type == Conflict ||
                    m->type == Unchanged)
                        l += copy_words(str+l, &of, m->a, m->al);
                else if (m->type == Changed)
@@ -532,12 +580,14 @@ DEF_CMD(wiggle_set_wiggle)
        csl2 = wiggle_diff(bf, af, 1);
        info = wiggle_make_merger(of, bf, af, csl1, csl2, 1, 1, 0);
        if (info.merger) {
-               if (ci->comm2 && info.conflicts == 0) {
-                       char *str = collect_merge(info.merger, of, bf, af);
-                       if (str)
-                               comm_call(ci->comm2, "cb", ci->focus, 0, NULL, str);
-                       free(str);
-               }
+               free(wd->wiggle);
+               wd->wiggle = NULL;
+               wd->conflicts = info.conflicts;
+               wd->wiggles = info.wiggles;
+               wd->space_conflicts = count_space_conflicts(info.merger,
+                                                           of, bf, af);
+               if (info.conflicts == wd->space_conflicts)
+                       wd->wiggle = collect_merge(info.merger, of, bf, af);
                if (*attr) {
                        add_merge_markup(ci->focus,
                                         wd->texts[0].start,
@@ -668,6 +718,30 @@ DEF_CMD(wiggle_find)
        return ret > 0 ? fuzz + 1 : Efail;
 }
 
+DEF_CMD(wiggle_get)
+{
+       struct wiggle_data *wd = ci->home->data;
+
+       if (wd->conflicts < 0)
+               return Einval;
+       if (!ci->str)
+               return Enoarg;
+       if (strcmp(ci->str, "wiggle") == 0) {
+               if (wd->wiggle)
+                       return comm_call(ci->comm2, "cb", ci->focus, 0, NULL,
+                                        wd->wiggle);
+               else
+                       return Efalse;
+       }
+       if (strcmp(ci->str, "space-conflicts") == 0)
+               return wd->space_conflicts + 1;
+       if (strcmp(ci->str, "conflicts") == 0)
+               return wd->conflicts + 1;
+       if (strcmp(ci->str, "wiggles") == 0)
+               return wd->wiggles + 1;
+       return Einval;
+}
+
 DEF_CMD(wiggle_find_best) { return 0; }
 
 static struct map *wiggle_map;
@@ -690,11 +764,13 @@ DEF_CMD(make_wiggle)
                key_add(wiggle_map, "set-wiggle", &wiggle_set_wiggle);
                key_add(wiggle_map, "find", &wiggle_find);
                key_add(wiggle_map, "find-best", &wiggle_find_best);
+               key_add(wiggle_map, "get-result", &wiggle_get);
        }
 
        alloc(wd, pane);
        wd->c = do_wiggle;
        wd->c.free = wiggle_free;
+       wd->conflicts = -1;
        p = pane_register(pane_root(ci->focus), 0,
                          &wiggle_pane.c, wd);
        if (!p) {
index 229bfa63bd4190d358ac17e7fbf4f729422f3593..507c3c465fae8351b639135d90c750bc98aacca8 100644 (file)
@@ -20,6 +20,7 @@ class MergePane(edlib.Pane):
         self.marks = None
         self.wig = None
         self.conflicts = 0
+        self.space_conflicts = 0
         self.call("doc:request:doc:replaced")
 
     def fore(self, m, end, ptn):
@@ -63,12 +64,12 @@ class MergePane(edlib.Pane):
         self.call("doc:EOL", 1, t, 1)
         cmd("after", self, t, m3)
 
-        ret = cmd("set-wiggle", self, "render:merge-same", ret='str')
-        if type(ret) == str:
-            self.wig = ret
-            self.conflicts = 0
-        else:
-            self.conflicts = 1
+        ret = cmd("set-wiggle", self, "render:merge-same")
+        self.conflicts = ret-1
+        self.space_conflicts = cmd("get-result", self, "space-conflicts") - 1
+        if self.conflicts == self.space_conflicts:
+            self.wig = cmd("get-result", self, "wiggle", ret='str')
+
         del cmd
 
         self.marks = [start, m1, m2, m3]
@@ -96,7 +97,7 @@ class MergePane(edlib.Pane):
                 return 1
             if num == 0:
                 # if no conflicts remain, wiggle the merge
-                if self.conflicts or self.wig is None:
+                if self.wig is None:
                     focus.call("Message", "Cannot complete merge while conflicts remain")
                     return 1
                 focus.call("doc:set-attr", "render:merge-same",
@@ -241,34 +242,40 @@ class MergePane(edlib.Pane):
 
         if str == "start-of-line":
             if mark == o or mark == b or mark == a or mark == e:
-                if self.conflicts:
+                if self.conflicts > self.space_conflicts:
                     comm2("attr:cb", focus, mark, "fg:red-40",
                           0, 102)
+                elif self.conflicts:
+                    comm2("attr:cb", focus, mark, "fg:blue-80",
+                          0, 102)
                 else:
-                    comm2("attr:cb", focus, mark, "fg:green-40",
+                    comm2("attr:cb", focus, mark, "fg:green-60,bold",
                           0, 102)
             return edlib.Efallthrough
 
         if str == "render:merge-same":
             w = str2.split()
-            len = int(w[0])
+            alen = int(w[0])
             if w[1] == "Unmatched":
-                comm2("attr:cb", focus, mark, "fg:blue-80,bg:cyan+20", len, 103)
+                comm2("attr:cb", focus, mark, "fg:blue-80,bg:cyan+20", alen, 103)
             if w[1] == "Extraneous":
-                comm2("attr:cb", focus, mark, "fg:cyan-60,bg:yellow", len, 103)
+                comm2("attr:cb", focus, mark, "fg:cyan-60,bg:yellow", alen, 103)
             if w[1] == "Changed":
                 if mark < a:
-                    comm2("attr:cb", focus, mark, "fg:red-60", len, 103)
+                    comm2("attr:cb", focus, mark, "fg:red-60", alen, 103)
                 else:
-                    comm2("attr:cb", focus, mark, "fg:green-60", len, 103)
+                    comm2("attr:cb", focus, mark, "fg:green-60", alen, 103)
             if w[1] == "Conflict":
-                comm2("attr:cb", focus, mark, "fg:red-60,inverse", len, 103)
+                if len(w) >= 3 and w[2] == "spaces":
+                    comm2("attr:cb", focus, mark, "fg:red-60,underline", alen, 103)
+                else:
+                    comm2("attr:cb", focus, mark, "fg:red-60,inverse", alen, 103)
             if w[1] == "AlreadyApplied":
                 if mark > b and mark < a:
                     # This part is 'before' - mosly irrelevant
-                    comm2("attr:cb", focus, mark, "fg:cyan-60", len, 103)
+                    comm2("attr:cb", focus, mark, "fg:cyan-60", alen, 103)
                 else:
-                    comm2("attr:cb", focus, mark, "fg:cyan-60,inverse", len, 103)
+                    comm2("attr:cb", focus, mark, "fg:cyan-60,inverse", alen, 103)
 
             return edlib.Efallthrough