Subject: [PATCH] Restart and reset ANSI escapes on each line for compatibility with less -R

From: "Dennis Lambe Jr." <[email-masked]>
Date: Sun, 08 Apr 2018 19:59:31 -0400

Hello!

GNU less can display ANSI-colored text with the -R flag, but this  
support has some limitations. One of them is that if an escape  
sequence starts on one line and ends on a different line, only the  
first line will be colored in less.

As a result, when diff creates colored output with multi-line deletes  
or adds, less will only color the first line.

I've attached a patch to reset ANSI color to the default at the end of  
every line and restart it at the beginning of the next. It patches  
normal and context mode. Side-by-side already worked in my testing.

I hope it's useful to you. Please let me know if there are changes you  
would like made before you can accept it.

--Dennis Lambe Jr.
--- a/src/normal.c
+++ b/src/normal.c
@@ -60,12 +60,11 @@
   if (changes & OLD)
     {
       if (first0 <= last0)
-        set_color_context (DELETE_CONTEXT);
       for (i = first0; i <= last0; i++)
         {
+          set_color_context (DELETE_CONTEXT);
           print_1_line_nl ("<", &files[0].linbuf[i], true);
-          if (i == last0)
-            set_color_context (RESET_CONTEXT);
+          set_color_context (RESET_CONTEXT);
           if (files[0].linbuf[i + 1][-1] == '\n')
             putc ('\n', outfile);
         }
@@ -77,13 +76,11 @@
   /* Print the lines that the second file has.  */
   if (changes & NEW)
     {
-      if (first1 <= last1)
-        set_color_context (ADD_CONTEXT);
       for (i = first1; i <= last1; i++)
         {
+          set_color_context (ADD_CONTEXT);
           print_1_line_nl (">", &files[1].linbuf[i], true);
-          if (i == last1)
-            set_color_context (RESET_CONTEXT);
+          set_color_context (RESET_CONTEXT);
           if (files[1].linbuf[i + 1][-1] == '\n')
             putc ('\n', outfile);
         }
--- a/src/context.c
+++ b/src/context.c
@@ -43,8 +43,11 @@
 		     char const *name,
 		     char const *label)
 {
+  set_color_context (HEADER_CONTEXT);
   if (label)
-    fprintf (outfile, "%s %s\n", mark, label);
+    {
+      fprintf (outfile, "%s %s", mark, label);
+    }
   else
     {
       char buf[MAX (INT_STRLEN_BOUND (int) + 32,
@@ -71,8 +74,10 @@
 	      sprintf (buf, "%"PRIuMAX".%.9d", sec, nsec);
 	    }
 	}
-      fprintf (outfile, "%s %s\t%s\n", mark, name, buf);
+      fprintf (outfile, "%s %s\t%s", mark, name, buf);
     }
+  set_color_context (RESET_CONTEXT);
+  fprintf (outfile, "\n");
 }
 
 /* Print a header for a context diff, with the file names and dates.  */
@@ -80,7 +85,6 @@
 void
 print_context_header (struct file_data inf[], char const *const *names, bool unidiff)
 {
-  set_color_context (HEADER_CONTEXT);
   if (unidiff)
     {
       print_context_label ("---", &inf[0], names[0], file_label[0]);
@@ -91,7 +95,6 @@
       print_context_label ("***", &inf[0], names[0], file_label[0]);
       print_context_label ("---", &inf[1], names[1], file_label[1]);
     }
-  set_color_context (RESET_CONTEXT);
 }
 
 /* Print an edit script in context format.  */
@@ -219,11 +222,10 @@
     {
       struct change *next = hunk;
 
-      if (first0 <= last0)
-        set_color_context (DELETE_CONTEXT);
-
       for (i = first0; i <= last0; i++)
 	{
+          set_color_context (DELETE_CONTEXT);
+
 	  /* Skip past changes that apply (in file 0)
 	     only to lines before line I.  */
 
@@ -241,8 +243,7 @@
               prefix = (next->inserted > 0 ? "!" : "-");
             }
 	  print_1_line_nl (prefix, &files[0].linbuf[i], true);
-          if (i == last0)
-            set_color_context (RESET_CONTEXT);
+          set_color_context (RESET_CONTEXT);
           if (files[0].linbuf[i + 1][-1] == '\n')
             putc ('\n', out);
 	}
@@ -259,11 +260,10 @@
     {
       struct change *next = hunk;
 
-      if (first1 <= last1)
-        set_color_context (ADD_CONTEXT);
-
       for (i = first1; i <= last1; i++)
 	{
+          set_color_context (ADD_CONTEXT);
+
 	  /* Skip past changes that apply (in file 1)
 	     only to lines before line I.  */
 
@@ -281,8 +281,7 @@
               prefix = (next->deleted > 0 ? "!" : "+");
             }
 	  print_1_line_nl (prefix, &files[1].linbuf[i], true);
-          if (i == last1)
-            set_color_context (RESET_CONTEXT);
+	  set_color_context (RESET_CONTEXT);
           if (files[1].linbuf[i + 1][-1] == '\n')
             putc ('\n', out);
 	}
@@ -390,19 +389,17 @@
 	  /* For each difference, first output the deleted part. */
 
 	  k = next->deleted;
-          if (k)
-            set_color_context (DELETE_CONTEXT);
 
 	  while (k--)
 	    {
 	      char const * const *line = &files[0].linbuf[i++];
+              set_color_context (DELETE_CONTEXT);
 	      putc ('-', out);
 	      if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
 		putc ('\t', out);
 	      print_1_line_nl (NULL, line, true);
 
-              if (!k)
-                set_color_context (RESET_CONTEXT);
+	      set_color_context (RESET_CONTEXT);
 
               if (line[1][-1] == '\n')
                 putc ('\n', out);
@@ -411,19 +408,17 @@
 	  /* Then output the inserted part. */
 
 	  k = next->inserted;
-          if (k)
-            set_color_context (ADD_CONTEXT);
 
           while (k--)
 	    {
 	      char const * const *line = &files[1].linbuf[j++];
+              set_color_context (ADD_CONTEXT);
 	      putc ('+', out);
 	      if (initial_tab && ! (suppress_blank_empty && **line == '\n'))
 		putc ('\t', out);
 	      print_1_line_nl (NULL, line, true);
 
-              if (!k)
-                set_color_context (RESET_CONTEXT);
+	      set_color_context (RESET_CONTEXT);
 
               if (line[1][-1] == '\n')
                 putc ('\n', out);
--- a/tests/colors
+++ b/tests/colors
@@ -25,9 +25,9 @@
     local de=$(printf "$e[${de}m")
     local ln=$(printf "$e[${ln}m")
     printf '%s' \
-"$hd--- a$tab$epoch_plus
-+++ b$tab$epoch_plus
-$rs${ln}@@ -1 +1 @@$rs
+"$hd--- a$tab$epoch_plus$rs
+$hd+++ b$tab$epoch_plus$rs
+${ln}@@ -1 +1 @@$rs
 $de-a$rs
 $ad+b$rs
 "
@@ -42,9 +42,9 @@
     local de=$(printf "$e[${de}m")
     local ln=$(printf "$e[${ln}m")
     printf '%s' \
-"$hd*** a$tab$epoch_posix_1003_1_2001
---- b$tab$epoch_posix_1003_1_2001
-$rs***************
+"$hd*** a$tab$epoch_posix_1003_1_2001$rs
+$hd--- b$tab$epoch_posix_1003_1_2001$rs
+***************
 $ln*** 1 ****$rs
 $de! a$rs
 $ln--- 1 ----$rs