]> Cypherpunks repositories - gostls13.git/commitdiff
- implemented arbitrary padding char for tabwriter
authorRobert Griesemer <gri@golang.org>
Fri, 21 Nov 2008 17:35:49 +0000 (09:35 -0800)
committerRobert Griesemer <gri@golang.org>
Fri, 21 Nov 2008 17:35:49 +0000 (09:35 -0800)
- implemented right-to-left alignment (numerical results)
- better comments and error handling
- added more tests
- updated dependent files

R=r
DELTA=232  (175 added, 11 deleted, 46 changed)
OCL=19761
CL=19780

src/lib/tabwriter/tabwriter.go
src/lib/tabwriter/tabwriter_test.go
usr/gri/pretty/printer.go
usr/gri/pretty/untab.go

index 3eb0ba195b6a6ac1a2701f681e34effc232be99f..2615186e7a0d8cf64890f9a69810ed0cf3415bf9 100644 (file)
@@ -63,34 +63,38 @@ func (b *ByteArray) Append(s *[]byte) {
 // ----------------------------------------------------------------------------
 // Writer is a filter implementing the io.Write interface. It assumes
 // that the incoming bytes represent ASCII encoded text consisting of
-// lines of tab-separated "cells". Cells in adjacent lines constitute
+// lines of tab-terminated "cells". Cells in adjacent lines constitute
 // a column. Writer rewrites the incoming text such that all cells in
 // a column have the same width; thus it effectively aligns cells. It
 // does this by adding padding where necessary.
 //
-// Formatting can be controlled via parameters:
+// Note that any text at the end of a line that is not tab-terminated
+// is not a cell and does not enforce alignment of cells in adjacent
+// rows. To make it a cell it needs to be tab-terminated. (For more
+// information see http://nickgravgaard.com/elastictabstops/index.html)
 //
-// tabwidth  the minimal with of a cell
-// padding   additional padding
-// usetabs   use tabs instead of blanks for padding
-//           (for correct-looking results, tabwidth must correspond
-//           to the tabwidth in the editor used to look at the result)
+// Formatting can be controlled via parameters:
 //
-// (See alse http://nickgravgaard.com/elastictabstops/index.html)
+// cellwidth  minimal cell width
+// padding    additional cell padding
+// padchar    ASCII char used for padding
+//            if padchar == '\t', the Writer will assume that the
+//            width of a '\t' in the formatted output is tabwith,
+//            and cells are left-aligned independent of align_left
+//            (for correct-looking results, cellwidth must correspond
+//            to the tabwidth in the editor used to look at the result)
 
-// TODO Should support UTF-8
-// TODO Should probably implement a couple of trivial customization options
-//      such as arbitrary padding character, left/right alignment, and inde-
-//      pendant cell and tab width.
+// TODO Should support UTF-8 (requires more complicated width bookkeeping)
 
 
 export type Writer struct {
        // TODO should not export any of the fields
        // configuration
        writer io.Write;
-       tabwidth int;
+       cellwidth int;
        padding int;
-       usetabs bool;
+       padbytes [8]byte;
+       align_left bool;
 
        // current state
        buf ByteArray;  // the collected text w/o tabs and newlines
@@ -105,11 +109,20 @@ func (b *Writer) AddLine() {
 }
 
 
-func (b *Writer) Init(writer io.Write, tabwidth, padding int, usetabs bool) *Writer {
+func (b *Writer) Init(writer io.Write, cellwidth, padding int, padchar byte, align_left bool) *Writer {
+       if cellwidth < 0 {
+               panic("negative cellwidth");
+       }
+       if padding < 0 {
+               panic("negative padding");
+       }
        b.writer = writer;
-       b.tabwidth = tabwidth;
+       b.cellwidth = cellwidth;
        b.padding = padding;
-       b.usetabs = usetabs;
+       for i := len(b.padbytes) - 1; i >= 0; i-- {
+               b.padbytes[i] = padchar;
+       }
+       b.align_left = align_left || padchar == '\t';  // tab enforces left-alignment
        
        b.buf.Init(1024);
        b.lines.Init(0);
@@ -156,15 +169,12 @@ func (b *Writer) Write0(buf *[]byte) *os.Error {
 }
 
 
-var Tabs = &[]byte{'\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t'}
-var Blanks = &[]byte{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}
 var Newline = &[]byte{'\n'}
 
-
 func (b *Writer) WritePadding(textw, cellw int) (err *os.Error) {
-       if b.usetabs {
-               // make cell width a multiple of tabwidth
-               cellw = ((cellw + b.tabwidth - 1) / b.tabwidth) * b.tabwidth;
+       if b.padbytes[0] == '\t' {
+               // make cell width a multiple of cellwidth
+               cellw = ((cellw + b.cellwidth - 1) / b.cellwidth) * b.cellwidth;
        }
 
        n := cellw - textw;
@@ -172,20 +182,18 @@ func (b *Writer) WritePadding(textw, cellw int) (err *os.Error) {
                panic("internal error");
        }
 
-       padding := Blanks;
-       if b.usetabs {
-               n = (n + b.tabwidth - 1) / b.tabwidth;
-               padding = Tabs;
+       if b.padbytes[0] == '\t' {
+               n = (n + b.cellwidth - 1) / b.cellwidth;
        }
        
-       for n > len(padding) {
-               err = b.Write0(padding);
+       for n > len(b.padbytes) {
+               err = b.Write0(&b.padbytes);
                if err != nil {
                        goto exit;
                }
-               n -= len(padding);
+               n -= len(b.padbytes);
        }
-       err = b.Write0(padding[0 : n]);
+       err = b.Write0((&b.padbytes)[0 : n]);  // BUG 6g should not require ()'s
 
 exit:
        return err;
@@ -198,16 +206,33 @@ func (b *Writer) WriteLines(pos0 int, line0, line1 int) (pos int, err *os.Error)
                line := b.Line(i);
                for j := 0; j < line.Len(); j++ {
                        w := line.At(j);
-                       err = b.Write0(b.buf.a[pos : pos + w]);
-                       if err != nil {
-                               goto exit;
-                       }
-                       pos += w;
-                       if j < b.widths.Len() {
-                               err = b.WritePadding(w, b.widths.At(j));
+
+                       if b.align_left {
+                               err = b.Write0(b.buf.a[pos : pos + w]);
+                               if err != nil {
+                                       goto exit;
+                               }
+                               pos += w;
+                               if j < b.widths.Len() {
+                                       err = b.WritePadding(w, b.widths.At(j));
+                                       if err != nil {
+                                               goto exit;
+                                       }
+                               }
+
+                       } else {  // align right
+
+                               if j < b.widths.Len() {
+                                       err = b.WritePadding(w, b.widths.At(j));
+                                       if err != nil {
+                                               goto exit;
+                                       }
+                               }
+                               err = b.Write0(b.buf.a[pos : pos + w]);
                                if err != nil {
                                        goto exit;
                                }
+                               pos += w;
                        }
                }
                err = b.Write0(Newline);
@@ -252,7 +277,7 @@ func (b *Writer) Format(pos0 int, line0, line1 int) (pos int, err *os.Error) {
                        last = this;
                        
                        // column block begin
-                       width := b.tabwidth;  // minimal width
+                       width := b.cellwidth;  // minimal width
                        for ; this < line1; this++ {
                                line = b.Line(this);
                                if column < line.Len() - 1 {
@@ -338,6 +363,6 @@ func (b *Writer) Append(buf *[]byte) {
 }
 
 
-export func New(writer io.Write, tabwidth, padding int, usetabs bool) *Writer {
-       return new(Writer).Init(writer, tabwidth, padding, usetabs)
+export func New(writer io.Write, cellwidth, padding int, padchar byte, align_left bool) *Writer {
+       return new(Writer).Init(writer, cellwidth, padding, padchar, align_left)
 }
index 42c443f78a4fc8b425fc2a29dbf7ba784883e741..980cc7336948be65022f4f2c61a721c875da52e5 100644 (file)
@@ -42,40 +42,171 @@ func (b *Buffer) String() string {
 }
 
 
-func Check(t *testing.T, tabwidth, padding int, usetabs bool, src, expected string) {
+func Check(t *testing.T, tabwidth, padding int, padchar byte, align_left bool, src, expected string) {
        var b Buffer;
        b.Init(1000);
 
        var w tabwriter.Writer;
-       w.Init(&b, tabwidth, padding, usetabs);
+       w.Init(&b, tabwidth, padding, padchar, align_left);
 
        io.WriteString(&w, src);
 
        res := b.String();
        if res != expected {
-               t.Errorf("src:\n%s\nfound:\n%s\nexpected:\n%s\n", src, res, expected)
+               t.Errorf("--- src:\n%s\n--- found:\n%s\n--- expected:\n%s\n", src, res, expected)
        }
 }
 
 
 export func Test1(t *testing.T) {
        Check(
-               t, 8, 1, false,
+               t, 8, 1, ' ', true,
                "\n",
                "\n"
        );
 
        Check(
-               t, 8, 1, false,
+               t, 8, 1, '*', true,
                "Hello, world!\n",
                "Hello, world!\n"
        );
 
        Check(
-               t, 8, 1, false,
-               "a\tb\tc\naa\tbbb\tcccc\naaa\tbbbb\n\n",
+               t, 0, 0, '.', true,
+               "1\t2\t3\t4\n"
+               "11\t222\t3333\t44444\n\n",
+
+               "1.2..3...4\n"
+               "11222333344444\n\n"
+       );
+
+       Check(
+               t, 5, 0, '.', true,
+               "1\t2\t3\t4\n\n",
+               "1....2....3....4\n\n"
+       );
+
+       Check(
+               t, 5, 0, '.', true,
+               "1\t2\t3\t4\t\n\n",
+               "1....2....3....4....\n\n"
+       );
+
+       Check(
+               t, 8, 1, ' ', true,
+               "a\tb\tc\n"
+               "aa\tbbb\tcccc\tddddd\n"
+               "aaa\tbbbb\n\n",
+
                "a       b       c\n"
-               "aa      bbb     cccc\n"
+               "aa      bbb     cccc    ddddd\n"
                "aaa     bbbb\n\n"
        );
+
+       Check(
+               t, 8, 1, ' ', false,
+               "a\tb\tc\t\n"
+               "aa\tbbb\tcccc\tddddd\t\n"
+               "aaa\tbbbb\t\n\n",
+
+               "       a       b       c\n"
+               "      aa     bbb    cccc   ddddd\n"
+               "     aaa    bbbb\n\n"
+       );
+
+       Check(
+               t, 2, 0, ' ', true,
+               "a\tb\tc\n"
+               "aa\tbbb\tcccc\n"
+               "aaa\tbbbb\n\n",
+
+               "a  b  c\n"
+               "aa bbbcccc\n"
+               "aaabbbb\n\n"
+       );
+
+       Check(
+               t, 8, 1, '_', true,
+               "a\tb\tc\n"
+               "aa\tbbb\tcccc\n"
+               "aaa\tbbbb\n\n",
+
+               "a_______b_______c\n"
+               "aa______bbb_____cccc\n"
+               "aaa_____bbbb\n\n"
+       );
+
+       Check(
+               t, 4, 1, '-', true,
+               "4444\t333\t22\t1\t333\n"
+               "999999999\t22\n"
+               "7\t22\n"
+               "\t\t\t88888888\n"
+               "\n"
+               "666666\t666666\t666666\t4444\n"
+               "1\t1\t999999999\t0000000000\n\n",
+
+               "4444------333-22--1---333\n"
+               "999999999-22\n"
+               "7---------22\n"
+               "------------------88888888\n"
+               "\n"
+               "666666-666666-666666----4444\n"
+               "1------1------999999999-0000000000\n\n"
+       );
+
+       Check(
+               t, 4, 3, '.', true,
+               "4444\t333\t22\t1\t333\n"
+               "999999999\t22\n"
+               "7\t22\n"
+               "\t\t\t88888888\n"
+               "\n"
+               "666666\t666666\t666666\t4444\n"
+               "1\t1\t999999999\t0000000000\n\n",
+
+               "4444........333...22...1...333\n"
+               "999999999...22\n"
+               "7...........22\n"
+               "....................88888888\n"
+               "\n"
+               "666666...666666...666666......4444\n"
+               "1........1........999999999...0000000000\n\n"
+       );
+
+       Check(
+               t, 8, 1, '\t', true,
+               "4444\t333\t22\t1\t333\n"
+               "999999999\t22\n"
+               "7\t22\n"
+               "\t\t\t88888888\n"
+               "\n"
+               "666666\t666666\t666666\t4444\n"
+               "1\t1\t999999999\t0000000000\n\n",
+
+               "4444\t\t333\t22\t1\t333\n"
+               "999999999\t22\n"
+               "7\t\t22\n"
+               "\t\t\t\t88888888\n"
+               "\n"
+               "666666\t666666\t666666\t\t4444\n"
+               "1\t1\t999999999\t0000000000\n\n"
+       );
+
+       Check(
+               t, 4, 2, ' ', false,
+               ".0\t.3\t2.4\t-5.1\t\n"
+               "23.0\t12345678.9\t2.4\t-989.4\t\n"
+               "5.1\t12.0\t2.4\t-7.0\t\n"
+               ".0\t0.0\t332.0\t8908.0\t\n"
+               ".0\t-.3\t456.4\t22.1\t\n"
+               ".0\t1.2\t44.4\t-13.3\t\n\n",
+
+               "    .0          .3    2.4    -5.1\n"
+               "  23.0  12345678.9    2.4  -989.4\n"
+               "   5.1        12.0    2.4    -7.0\n"
+               "    .0         0.0  332.0  8908.0\n"
+               "    .0         -.3  456.4    22.1\n"
+               "    .0         1.2   44.4   -13.3\n\n"
+       );
 }
index cefabb66f977e9a979f508671dd962d7aad93933..6bb755b4b70a5b4afb5a5f5d86ad49b5dde44a31 100644 (file)
@@ -604,7 +604,11 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) {
 
 func (P *Printer) Program(p *AST.Program) {
        // TODO should initialize all fields?
-       P.writer = TabWriter.New(OS.Stdout, int(tabwidth.IVal()), 1, usetabs.BVal());
+       padchar := byte(' ');
+       if usetabs.BVal() {
+               padchar = '\t';
+       }
+       P.writer = TabWriter.New(OS.Stdout, int(tabwidth.IVal()), 1, padchar, true);
        
        P.clist = p.comments;
        P.cindex = 0;
index cd5981b539a64ca97d295311a14b53c2ca84508e..af4814c1de9ac3d97f2847c5503dea672f26c566 100644 (file)
@@ -36,7 +36,11 @@ func Untab(name string, src *os.FD, dst *tabwriter.Writer) {
 
 func main() {
        flag.Parse();
-       dst := tabwriter.New(os.Stdout, int(tabwidth.IVal()), 1, usetabs.BVal());
+       padchar := byte(' ');
+       if usetabs.BVal() {
+               padchar = '\t';
+       }
+       dst := tabwriter.New(os.Stdout, int(tabwidth.IVal()), 1, padchar, true);
        if flag.NArg() > 0 {
                for i := 0; i < flag.NArg(); i++ {
                        name := flag.Arg(i);