go/usr/gri/pretty/tabwriter.go

310 lines
6.4 KiB
Go

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tabwriter
import (
OS "os";
IO "io";
Vector "vector";
)
// ----------------------------------------------------------------------------
// ByteArray
type ByteArray struct {
a *[]byte;
}
func (b *ByteArray) Init(initial_size int) {
b.a = new([]byte, initial_size)[0 : 0];
}
func (b *ByteArray) Clear() {
b.a = b.a[0 : 0];
}
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]
}
func (b *ByteArray) Append(s *[]byte) {
a := b.a;
n := len(a);
m := n + len(s);
if m > cap(a) {
n2 := 2*n;
if m > n2 {
n2 = m;
}
b := new([]byte, n2);
for i := 0; i < n; i++ {
b[i] = a[i];
}
a = b;
}
a = a[0 : m];
for i := len(s) - 1; i >= 0; i-- {
a[n + i] = s[i];
}
b.a = a;
}
// ----------------------------------------------------------------------------
// Implemententation 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.
//
// 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 .
export type TabWriter struct {
// configuration
writer IO.Write;
usetabs bool;
tabwidth int;
// current state
buf ByteArray; // the collected text w/o tabs and newlines
width int; // width of last incomplete cell
lines Vector.Vector; // list of lines; each line is a list of cell widths
widths Vector.Vector; // list of column widths - (re-)used during formatting
}
func (b *TabWriter) AddLine() {
b.lines.Append(Vector.New());
}
func (b *TabWriter) Init(writer IO.Write, usetabs bool, tabwidth int) {
b.writer = writer;
b.usetabs = usetabs;
b.tabwidth = tabwidth;
b.buf.Init(1024);
b.lines.Init();
b.widths.Init();
b.AddLine(); // the very first line
}
func (b *TabWriter) Line(i int) *Vector.Vector {
return b.lines.At(i).(*Vector.Vector);
}
func (b *TabWriter) LastLine() *Vector.Vector {
return b.lines.At(b.lines.Len() - 1).(*Vector.Vector);
}
// debugging support
func (b *TabWriter) Dump() {
pos := 0;
for i := 0; i < b.lines.Len(); i++ {
line := b.Line(i);
print("(", i, ") ");
for j := 0; j < line.Len(); j++ {
w := line.At(j).(int);
print("[", string(b.buf.a[pos : pos + w]), "]");
pos += w;
}
print("\n");
}
print("\n");
}
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;
if n < 0 {
panic("internal error");
}
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);
}
m, err := b.writer.Write(Blanks[0 : n]);
}
}
func (b *TabWriter) PrintLines(pos int, line0, line1 int) int {
for i := line0; i < line1; i++ {
line := b.Line(i);
for j := 0; j < line.Len(); j++ {
w := line.At(j).(int);
m, err := b.writer.Write(b.buf.a[pos : pos + w]);
if m != w {
panic();
}
pos += w;
if j < b.widths.Len() {
b.Padding(w, b.widths.At(j).(int));
}
}
m, err := b.writer.Write(Newline);
}
return pos;
}
func (b *TabWriter) Format(pos int, line0, line1 int) int {
column := b.widths.Len();
last := line0;
for this := line0; this < line1; this++ {
line := b.Line(this);
if column < line.Len() - 1 {
// cell exists in this column
// (note that the last cell per line is ignored)
// print unprinted lines until beginning of block
pos = b.PrintLines(pos, last, this);
last = this;
// column block begin
width := b.tabwidth; // minimal width
for ; this < line1; this++ {
line = b.Line(this);
if column < line.Len() - 1 {
// cell exists in this column
// update width
w := line.At(column).(int) + 1; // 1 = minimum space between cells
if w > width {
width = w;
}
} else {
break
}
}
// 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.Append(width);
pos = b.Format(pos, last, this);
b.widths.Remove(b.widths.Len() - 1);
last = this;
}
}
// print unprinted lines until end
return b.PrintLines(pos, last, line1);
}
func (b *TabWriter) EmptyLine() bool {
return b.LastLine().Len() == 0 && b.width == 0;
}
func (b *TabWriter) Tab() {
b.LastLine().Append(b.width);
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.Reset();
}
b.AddLine();
}
func (b *TabWriter) Write(buf *[]byte) (i 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();
}
}
b.width += n - i0;
b.buf.Append(buf[i0 : n]);
return i, nil;
}
export func MakeTabWriter(writer IO.Write, usetabs bool, tabwidth int) *TabWriter {
b := new(TabWriter);
b.Init(writer, usetabs, tabwidth);
return b;
}