// ----------------------------------------------------------------------------
// 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
}
-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);
}
-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;
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;
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);
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 {
}
-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)
}
}
-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"
+ );
}