mirror of https://github.com/golang/go.git
- implemented arbitrary padding char for tabwriter
- 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
This commit is contained in:
parent
60db3d6d3f
commit
130e6f42f1
|
|
@ -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.
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// 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)
|
||||
//
|
||||
// (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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue