]> Cypherpunks repositories - gostls13.git/commitdiff
- correct error handling throughout
authorRobert Griesemer <gri@golang.org>
Fri, 21 Nov 2008 00:26:43 +0000 (16:26 -0800)
committerRobert Griesemer <gri@golang.org>
Fri, 21 Nov 2008 00:26:43 +0000 (16:26 -0800)
- documentation, cleanups
- more options

R=r
OCL=19736
CL=19736

usr/gri/pretty/printer.go
usr/gri/pretty/tabwriter.go
usr/gri/pretty/untab.go

index 3e364618a7609b1a0304150df42556a9a1178c5b..cefabb66f977e9a979f508671dd962d7aad93933 100644 (file)
@@ -15,8 +15,8 @@ import OS "os"
 import TabWriter "tabwriter"
 
 var (
-       usetabs = Flag.Bool("usetabs", false, nil, "align with tabs instead of blanks");
        tabwidth = Flag.Int("tabwidth", 4, nil, "tab width");
+       usetabs = Flag.Bool("usetabs", false, nil, "align with tabs instead of blanks");
        comments = Flag.Bool("comments", false, nil, "enable printing of comments");
 )
 
@@ -604,7 +604,7 @@ func (P *Printer) Declaration(d *AST.Decl, parenthesized bool) {
 
 func (P *Printer) Program(p *AST.Program) {
        // TODO should initialize all fields?
-       P.writer = TabWriter.MakeTabWriter(OS.Stdout, usetabs.BVal(), int(tabwidth.IVal()));
+       P.writer = TabWriter.New(OS.Stdout, int(tabwidth.IVal()), 1, usetabs.BVal());
        
        P.clist = p.comments;
        P.cindex = 0;
index 53a275514c222832f0fea35827427f6bf6958a99..58ae5ff1e6c108cce7edbe596394c8cbea2f259a 100644 (file)
@@ -13,7 +13,7 @@ import (
 
 // ----------------------------------------------------------------------------
 // ByteArray
-// TODO move this into std lib eventually
+// TODO should use a ByteArray library eventually
 
 type ByteArray struct {
        a *[]byte;
@@ -30,21 +30,6 @@ func (b *ByteArray) Clear() {
 }
 
 
-func (b *ByteArray) Len() int {
-       return len(b.a);
-}
-
-
-func (b *ByteArray) At(i int) byte {
-       return b.a[i];
-}
-
-
-func (b *ByteArray) Set(i int, x byte) {
-       b.a[i] = x;
-}
-
-
 func (b *ByteArray) Slice(i, j int) *[]byte {
        return b.a[i : j];  // BUG should really be &b.a[i : j]
 }
@@ -76,27 +61,42 @@ func (b *ByteArray) Append(s *[]byte) {
 
 
 // ----------------------------------------------------------------------------
-// Implementation of flexible tab stops.
-
-// TabWriter is a representation for a list of lines consisting of
-// cells. A new cell is added for each Tab() call, and a new line
-// is added for each Newline() call.
+// Tabwriter 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
+// a column. Tabwriter 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:
+//
+// 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)
 //
-// The lines are formatted and printed such that all cells in a column
-// of adjacent cells have the same width (by adding padding). For more
-// details see: http://nickgravgaard.com/elastictabstops/index.html .
+// (See alse http://nickgravgaard.com/elastictabstops/index.html)
+
+// 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.
+
 
 export type TabWriter struct {
+       // TODO should not export any of the fields
        // configuration
        writer io.Write;
-       usetabs bool;
        tabwidth int;
+       padding int;
+       usetabs bool;
 
        // current state
        buf ByteArray;  // the collected text w/o tabs and newlines
        width int;  // width of last incomplete cell
        lines array.Array;  // list of lines; each line is a list of cell widths
-       widths array.IntArray;  // list of column widths - (re-)used during formatting
+       widths array.IntArray;  // list of column widths - re-used during formatting
 }
 
 
@@ -105,15 +105,18 @@ func (b *TabWriter) AddLine() {
 }
 
 
-func (b *TabWriter) Init(writer io.Write, usetabs bool, tabwidth int) {
+func (b *TabWriter) Init(writer io.Write, tabwidth, padding int, usetabs bool) *TabWriter {
        b.writer = writer;
-       b.usetabs = usetabs;
        b.tabwidth = tabwidth;
+       b.padding = padding;
+       b.usetabs = usetabs;
        
        b.buf.Init(1024);
        b.lines.Init(0);
        b.widths.Init(0);
        b.AddLine();  // the very first line
+       
+       return b;
 }
 
 
@@ -135,7 +138,7 @@ func (b *TabWriter) Dump() {
                print("(", i, ") ");
                for j := 0; j < line.Len(); j++ {
                        w := line.At(j);
-                       print("[", string(b.buf.a[pos : pos + w]), "]");
+                       print("[", string(b.buf.Slice(pos, pos + w)), "]");
                        pos += w;
                }
                print("\n");
@@ -144,59 +147,95 @@ func (b *TabWriter) Dump() {
 }
 
 
+func (b *TabWriter) Write0(buf *[]byte) *os.Error {
+       n, err := b.writer.Write(buf);
+       if n != len(buf) && err == nil {
+               err = os.EIO;
+       }
+       return err;
+}
+
+
 var Tabs = &[]byte{'\t', '\t', '\t', '\t', '\t', '\t', '\t', '\t'}
 var Blanks = &[]byte{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}
 var Newline = &[]byte{'\n'}
 
 
-func (b *TabWriter) Padding(textwidth, cellwidth int) {
-       n := cellwidth - textwidth;
+func (b *TabWriter) 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;
+       }
+
+       n := cellw - textw;
        if n < 0 {
                panic("internal error");
        }
+
+       padding := Blanks;
        if b.usetabs {
-               if cellwidth % b.tabwidth != 0 {
-                       panic("internal error");  // cellwidth should be a multiple of tabwidth
-               }
                n = (n + b.tabwidth - 1) / b.tabwidth;
-               for n > len(Tabs) {
-                       m, err := b.writer.Write(Tabs);
-                       n -= len(Tabs);
-               }
-               m, err := b.writer.Write(Tabs[0 : n]);
-       } else {
-               for n > len(Blanks) {
-                       m, err := b.writer.Write(Blanks);
-                       n -= len(Blanks);
+               padding = Tabs;
+       }
+       
+       for n > len(padding) {
+               err = b.Write0(padding);
+               if err != nil {
+                       goto exit;
                }
-               m, err := b.writer.Write(Blanks[0 : n]);
+               n -= len(padding);
        }
+       err = b.Write0(padding[0 : n]);
+
+exit:
+       return err;
 }
 
 
-func (b *TabWriter) PrintLines(pos int, line0, line1 int) int {
+func (b *TabWriter) WriteLines(pos0 int, line0, line1 int) (pos int, err *os.Error) {
+       pos = pos0;
        for i := line0; i < line1; i++ {
                line := b.Line(i);
                for j := 0; j < line.Len(); j++ {
                        w := line.At(j);
-                       m, err := b.writer.Write(b.buf.a[pos : pos + w]);
-                       if m != w {
-                               panic();
+                       err = b.Write0(b.buf.a[pos : pos + w]);
+                       if err != nil {
+                               goto exit;
                        }
                        pos += w;
                        if j < b.widths.Len() {
-                               b.Padding(w, b.widths.At(j));
+                               err = b.WritePadding(w, b.widths.At(j));
+                               if err != nil {
+                                       goto exit;
+                               }
                        }
                }
-               m, err := b.writer.Write(Newline);
+               err = b.Write0(Newline);
+               if err != nil {
+                       goto exit;
+               }
        }
-       return pos;
+
+exit:
+       return pos, err;
 }
 
 
-func (b *TabWriter) Format(pos int, line0, line1 int) int {
-       column := b.widths.Len();
-       
+// TODO use utflen for correct formatting
+func utflen(buf *[]byte) int {
+       n := 0;
+       for i := 0; i < len(buf); i++ {
+               if buf[i]&0xC0 != 0x80 {
+                       n++
+               }
+       }
+       return n
+}
+
+
+func (b *TabWriter) Format(pos0 int, line0, line1 int) (pos int, err *os.Error) {
+       pos = pos0;
+       column := b.widths.Len();       
        last := line0;
        for this := line0; this < line1; this++ {
                line := b.Line(this);
@@ -206,7 +245,10 @@ func (b *TabWriter) Format(pos int, line0, line1 int) int {
                        // (note that the last cell per line is ignored)
                        
                        // print unprinted lines until beginning of block
-                       pos = b.PrintLines(pos, last, this);
+                       pos, err = b.WriteLines(pos, last, this);
+                       if err != nil {
+                               goto exit;
+                       }
                        last = this;
                        
                        // column block begin
@@ -214,9 +256,8 @@ func (b *TabWriter) Format(pos int, line0, line1 int) int {
                        for ; this < line1; this++ {
                                line = b.Line(this);
                                if column < line.Len() - 1 {
-                                       // cell exists in this column
-                                       // update width
-                                       w := line.At(column) + 1; // 1 = minimum space between cells
+                                       // cell exists in this column => update width
+                                       w := line.At(column) + b.padding;
                                        if w > width {
                                                width = w;
                                        }
@@ -226,85 +267,77 @@ func (b *TabWriter) Format(pos int, line0, line1 int) int {
                        }
                        // column block end
 
-                       if b.usetabs {
-                               // make width a multiple of the tab width
-                               width = ((width + b.tabwidth - 1) / b.tabwidth) * b.tabwidth;
-                       }
-                       
                        // format and print all columns to the right of this column
                        // (we know the widths of this column and all columns to the left)
                        b.widths.Push(width);
-                       pos = b.Format(pos, last, this);
+                       pos, err = b.Format(pos, last, this);
                        b.widths.Pop();
                        last = this;
                }
        }
 
        // print unprinted lines until end
-       return b.PrintLines(pos, last, line1);
+       pos, err = b.WriteLines(pos, last, line1);
+       
+exit:
+       return pos, err;
 }
 
 
-func (b *TabWriter) EmptyLine() bool {
-       return b.LastLine().Len() == 0 && b.width == 0;
+func (b *TabWriter) Append(buf *[]byte) {
+       b.buf.Append(buf);
+       b.width += len(buf);
 }
 
 
-func (b *TabWriter) Tab() {
-       b.LastLine().Push(b.width);
+/* export */ func (b *TabWriter) Flush() *os.Error {
+       dummy, err := b.Format(0, 0, b.lines.Len());
+       // reset (even in the presence of errors)
+       b.buf.Clear();
        b.width = 0;
-}
-
-
-func (b *TabWriter) Newline() {
-       b.Tab();  // add last cell to current line
-
-       if b.LastLine().Len() == 1 {
-               // The current line has only one cell which does not have an impact
-               // on the formatting of the following lines (the last cell per line
-               // is ignored by Format), thus we can print the TabWriter contents.
-               if b.widths.Len() != 0 {
-                       panic("internal error");
-               }
-               b.Format(0, 0, b.lines.Len());
-               if b.widths.Len() != 0 {
-                       panic("internal error");
-               }
-
-               // reset TabWriter
-               b.width = 0;
-               b.buf.Clear();
-               b.lines.Init(0);
-       }
-
+       b.lines.Init(0);
        b.AddLine();
+       return err;
 }
 
 
-func (b *TabWriter) Write(buf *[]byte) (i int, err *os.Error) {
+/* export */ func (b *TabWriter) Write(buf *[]byte) (written int, err *os.Error) {
        i0, n := 0, len(buf);
-       for i = 0; i < n; i++ {
-               switch buf[i] {
-               case '\t':
-                       b.width += i - i0;
-                       b.buf.Append(buf[i0 : i]);
-                       i0 = i + 1;  // don't append '\t'
-                       b.Tab();
-               case '\n':
-                       b.width += i - i0;
-                       b.buf.Append(buf[i0 : i]);
-                       i0 = i + 1;  // don't append '\n'
-                       b.Newline();
+       
+       // split text into cells
+       for i := 0; i < n; i++ {
+               if ch := buf[i]; ch == '\t' || ch == '\n' {
+                       b.Append(buf[i0 : i]);
+                       i0 = i + 1;  // exclude ch from (next) cell
+
+                       // terminate cell
+                       b.LastLine().Push(b.width);
+                       b.width = 0;
+
+                       if ch == '\n' {
+                               if b.LastLine().Len() == 1 {
+                                       // The last line has only one cell which does not have an
+                                       // impact on the formatting of the following lines (the
+                                       // last cell per line is ignored by Format), thus we can
+                                       // flush the TabWriter contents.
+                                       err = b.Flush();
+                                       if err != nil {
+                                               return i0, err;
+                                       }
+                               } else {
+                                       // We can't flush yet - just add a new line.
+                                       b.AddLine();
+                               }
+                       }
                }
        }
-       b.width += n - i0;
-       b.buf.Append(buf[i0 : n]);
-       return i, nil;
+       
+       // append leftover text
+       b.Append(buf[i0 : n]);
+       return n, nil;
 }
 
 
-export func MakeTabWriter(writer io.Write, usetabs bool, tabwidth int) *TabWriter {
-       b := new(TabWriter);
-       b.Init(writer, usetabs, tabwidth);
-       return b;
+export func New(writer io.Write, tabwidth, padding int, usetabs bool) *TabWriter {
+       return new(TabWriter).Init(writer, tabwidth, padding, usetabs)
 }
index a2232b35172d0e65773f72789b588e5fdcfb64fa..d2a26f4389b9f81bf44d71a290369ca5a6fb0ac3 100644 (file)
@@ -14,8 +14,8 @@ import (
 
 
 var (
-       usetabs = flag.Bool("usetabs", false, nil, "align with tabs instead of blanks");
        tabwidth = flag.Int("tabwidth", 4, nil, "tab width");
+       usetabs = flag.Bool("usetabs", false, nil, "align with tabs instead of blanks");
 )
 
 
@@ -36,7 +36,7 @@ func Untab(name string, src *os.FD, dst *tabwriter.TabWriter) {
 
 func main() {
        flag.Parse();
-       dst := tabwriter.MakeTabWriter(os.Stdout, usetabs.BVal(), int(tabwidth.IVal()));
+       dst := tabwriter.New(os.Stdout, int(tabwidth.IVal()), 1, usetabs.BVal());
        if flag.NArg() > 0 {
                for i := 0; i < flag.NArg(); i++ {
                        name := flag.Arg(i);