[ovs-dev] [PATCH 1/2] nroff: Improve formatting of ASCII diagrams.

Ben Pfaff blp at ovn.org
Wed Dec 28 21:10:27 UTC 2016


This makes diagrams in ASCII output look about as good as one might
reasonably expect, so that in ovn-architecture(7), for example, this:

. 9 bits: reserved (0)
. 15 bits: ingress port
. 16 bits: egress port
. 24 bits: datapath

now gets formatted as:

    9          15          16         24
+--------+------------+-----------+--------+
|reserved|ingress port|egress port|datapath|
+--------+------------+-----------+--------+
    0

which isn't perfect but certainly more evocative than a bulleted list.

This will be more useful in upcoming commits that start using diagrams more
frequently.

Signed-off-by: Ben Pfaff <blp at ovn.org>
---
 python/build/nroff.py | 80 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 61 insertions(+), 19 deletions(-)

diff --git a/python/build/nroff.py b/python/build/nroff.py
index aed60eb..dd5f223 100644
--- a/python/build/nroff.py
+++ b/python/build/nroff.py
@@ -121,7 +121,19 @@ def fatal(msg):
     sys.exit(1)
 
 
-def diagram_header_to_nroff(header_node):
+def put_text(text, x, y, s):
+    extend = x + len(s) - len(text[y])
+    if extend > 0:
+        text[y] += ' ' * extend
+    text[y] = text[y][:x] + s + text[y][x + len(s):]
+
+
+def put_centered(text, x, width, y, s):
+    put_text(text, x + (width - len(s)) / 2, y, s)
+
+
+def diagram_header_to_nroff(header_node, text, x):
+    # Parse header.
     header_fields = []
     i = 0
     for node in header_node.childNodes:
@@ -145,10 +157,11 @@ def diagram_header_to_nroff(header_node):
         else:
             fatal("unknown node %s in diagram <header> element" % node)
 
+    # Format pic version.
     pic_s = ""
     for f in header_fields:
-        pic_s += "  %s: box \"%s\" width %s" % (f['tag'], f['name'],
-                                                f['width'])
+        name = f['name'].replace('...', '. . .')
+        pic_s += "  %s: box \"%s\" width %s" % (f['tag'], name, f['width'])
         if f['fill'] == 'yes':
             pic_s += " fill"
         pic_s += '\n'
@@ -164,43 +177,72 @@ def diagram_header_to_nroff(header_node):
     pic_s += "from %s.nw + (0,textht) " % header_fields[0]['tag']
     pic_s += "to %s.ne + (0,textht)\n" % header_fields[-1]['tag']
 
-    text_s = ""
+    # Format text version.
+    header_width = 1
+    for f in header_fields:
+        field_width = max(len(f['above']), len(f['below']), len(f['name']))
+        f['width'] = field_width
+        header_width += field_width + 1
+    min_header_width = 2 + len(name)
+    while min_header_width > header_width:
+        for f in header_fields:
+            f['width'] += 1
+            header_width += 1
+            if header_width >= min_header_width:
+                break
+
+    if name != "":
+        put_centered(text, x, header_width, 0, name)
+        if header_width >= 4:
+            arrow = '<' + '-' * (header_width - 4) + '>'
+            put_text(text, x + 1, 1, arrow)
     for f in header_fields:
-        text_s += """.IP \\(bu
-%s bits""" % (f['above'])
-        if f['name']:
-            text_s += ": %s" % f['name']
-        if f['below']:
-            text_s += " (%s)" % f['below']
-        text_s += "\n"
-    return pic_s, text_s
+        box1 = '+' + '-' * f['width'] + '+'
+        box2 = '|' + ' ' * f['width'] + '|'
+        put_text(text, x, 3, box1)
+        put_text(text, x, 4, box2)
+        put_text(text, x, 5, box1)
+
+        put_centered(text, x + 1, f['width'], 2, f['above'])
+        put_centered(text, x + 1, f['width'], 4, f['name'])
+        put_centered(text, x + 1, f['width'], 6, f['below'])
+
+        x += f['width'] + 1
+
+    return pic_s, x + 1
 
 
 def diagram_to_nroff(nodes, para):
     pic_s = ''
-    text_s = ''
+    text = [''] * 7
+    x = 0
     move = False
     for node in nodes:
         if node.nodeType == node.ELEMENT_NODE and node.tagName == 'header':
             if move:
                 pic_s += "move .1\n"
-                text_s += ".sp\n"
-            pic_header, text_header = diagram_header_to_nroff(node)
+                x += 1
+            elif x > 0:
+                x -= 1
+            pic_header, x = diagram_header_to_nroff(node, text, x)
             pic_s += "[\n" + pic_header + "]\n"
-            text_s += text_header
             move = True
         elif node.nodeType == node.ELEMENT_NODE and node.tagName == 'nospace':
             move = False
         elif node.nodeType == node.ELEMENT_NODE and node.tagName == 'dots':
             pic_s += "move .1\n"
             pic_s += '". . ." ljust\n'
-            text_s += ".sp\n"
+
+            put_text(text, x, 4, " ... ")
+            x += 5
         elif node.nodeType == node.COMMENT_NODE:
             pass
         elif node.nodeType == node.TEXT_NODE and node.data.isspace():
             pass
         else:
             fatal("unknown node %s in diagram <header> element" % node)
+
+    text_s = '.br\n'.join(["\\fL%s\n" % s for s in text if s != ""])
     return para + """
 .\\" check if in troff mode (TTY)
 .if t \{
@@ -213,9 +255,9 @@ fillval = .2
 \\}
 .\\" check if in nroff mode:
 .if n \{
-.RS
+.nf
 """ + text_s + """\
-.RE
+.fi
 \\}"""
 
 
-- 
2.10.2



More information about the dev mailing list