From 49954fa9216a25872f4458f9ac9780ba3f8314d4 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 20 Nov 2008 16:26:43 -0800 Subject: - correct error handling throughout - documentation, cleanups - more options R=r OCL=19736 CL=19736 --- usr/gri/pretty/printer.go | 4 +- usr/gri/pretty/tabwriter.go | 259 +++++++++++++++++++++++++------------------- usr/gri/pretty/untab.go | 4 +- 3 files changed, 150 insertions(+), 117 deletions(-) (limited to 'usr') diff --git a/usr/gri/pretty/printer.go b/usr/gri/pretty/printer.go index 3e364618a..cefabb66f 100644 --- a/usr/gri/pretty/printer.go +++ b/usr/gri/pretty/printer.go @@ -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; diff --git a/usr/gri/pretty/tabwriter.go b/usr/gri/pretty/tabwriter.go index 53a275514..58ae5ff1e 100644 --- a/usr/gri/pretty/tabwriter.go +++ b/usr/gri/pretty/tabwriter.go @@ -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) } diff --git a/usr/gri/pretty/untab.go b/usr/gri/pretty/untab.go index a2232b351..d2a26f438 100644 --- a/usr/gri/pretty/untab.go +++ b/usr/gri/pretty/untab.go @@ -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); -- cgit v1.2.3