patman: Support erasing a previously unfinished text line
authorSimon Glass <sjg@chromium.org>
Thu, 9 Apr 2020 21:08:40 +0000 (15:08 -0600)
committerSimon Glass <sjg@chromium.org>
Tue, 21 Apr 2020 12:33:47 +0000 (06:33 -0600)
When printing progress it is useful to print a message and leave the
cursor at the end of the line until the operation is finished. When it is
finished, the line needs to be erased so a new line can start in its place.

Add a function to handle clearing a line previously written by
terminal.Print()

Signed-off-by: Simon Glass <sjg@chromium.org>
tools/patman/patman.py
tools/patman/terminal.py

index cf53e532ddf8e89fd72dcbe9a7b627c92297942b..7f4ac9aef482b44cd29c59cbf04108beeaaf4917 100755 (executable)
@@ -93,7 +93,7 @@ elif options.test:
         suite = unittest.TestLoader().loadTestsFromTestCase(module)
         suite.run(result)
 
-    for module in ['gitutil', 'settings']:
+    for module in ['gitutil', 'settings', 'terminal']:
         suite = doctest.DocTestSuite(module)
         suite.run(result)
 
index 6541fa8f410412f829e477b6a942fd8185560a34..c7693eb57ad750f38c54f38e9378d71fec8d4ed5 100644 (file)
@@ -10,6 +10,7 @@ This module handles terminal interaction including ANSI color codes.
 from __future__ import print_function
 
 import os
+import re
 import sys
 
 # Selection of when we want our output to be colored
@@ -19,6 +20,13 @@ COLOR_IF_TERMINAL, COLOR_ALWAYS, COLOR_NEVER = range(3)
 print_test_mode = False
 print_test_list = []
 
+# The length of the last line printed without a newline
+last_print_len = None
+
+# credit:
+# stackoverflow.com/questions/14693701/how-can-i-remove-the-ansi-escape-sequences-from-a-string-in-python
+ansi_escape = re.compile(r'\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
+
 class PrintLine:
     """A line of text output
 
@@ -36,6 +44,33 @@ class PrintLine:
         return 'newline=%s, colour=%s, text=%s' % (self.newline, self.colour,
                 self.text)
 
+def CalcAsciiLen(text):
+    """Calculate the length of a string, ignoring any ANSI sequences
+
+    Args:
+        text: Text to check
+
+    Returns:
+        Length of text, after skipping ANSI sequences
+
+    >>> col = Color(COLOR_ALWAYS)
+    >>> text = col.Color(Color.RED, 'abc')
+    >>> len(text)
+    14
+    >>> CalcAsciiLen(text)
+    3
+    >>>
+    >>> text += 'def'
+    >>> CalcAsciiLen(text)
+    6
+    >>> text += col.Color(Color.RED, 'abc')
+    >>> CalcAsciiLen(text)
+    9
+    """
+    result = ansi_escape.sub('', text)
+    return len(result)
+
+
 def Print(text='', newline=True, colour=None):
     """Handle a line of output to the terminal.
 
@@ -47,6 +82,8 @@ def Print(text='', newline=True, colour=None):
         newline: True to add a new line at the end of the text
         colour: Colour to use for the text
     """
+    global last_print_len
+
     if print_test_mode:
         print_test_list.append(PrintLine(text, newline, colour))
     else:
@@ -55,8 +92,18 @@ def Print(text='', newline=True, colour=None):
             text = col.Color(colour, text)
         if newline:
             print(text)
+            last_print_len = None
         else:
             print(text, end='', flush=True)
+            last_print_len = CalcAsciiLen(text)
+
+def PrintClear():
+    """Clear a previously line that was printed with no newline"""
+    global last_print_len
+
+    if last_print_len:
+        print('\r%s\r' % (' '* last_print_len), end='', flush=True)
+        last_print_len = None
 
 def SetPrintTestMode():
     """Go into test mode, where all printing is recorded"""