1
0
mirror of https://github.com/golang/go synced 2024-09-25 15:10:11 -06:00

support for "hard" and "soft" tabs:

- soft-tab separated columns can be discarded if empty and DiscardEmptyColumns is set
- hard-tab separated columns are never discarded

R=rsc
DELTA=63  (42 added, 7 deleted, 14 changed)
OCL=35421
CL=35435
This commit is contained in:
Robert Griesemer 2009-10-07 10:55:45 -07:00
parent c2874976e4
commit f77b255c38
2 changed files with 53 additions and 18 deletions

View File

@ -23,11 +23,13 @@ import (
// A cell represents a segment of text delineated by tabs, form-feed, // A cell represents a segment of text delineated by tabs, form-feed,
// or newline chars. The text itself is stored in a separate buffer; // or newline chars. The text itself is stored in a separate buffer;
// cell only describes the segment's size in bytes and width in runes. // cell only describes the segment's size in bytes, its width in runes,
// and whether it's an htab ('\t') or vtab ('\v') terminated call.
// //
type cell struct { type cell struct {
size int; // cell size in bytes size int; // cell size in bytes
width int; // cell width in runes width int; // cell width in runes
htab bool; // true if the cell is terminated by an htab ('\t')
} }
@ -43,6 +45,12 @@ type cell struct {
// not tab-separated: trailing non-tab text at the end of a line // not tab-separated: trailing non-tab text at the end of a line
// is not part of any cell. // is not part of any cell.
// //
// Horizontal and vertical tabs may be used to terminate a cell.
// If DiscardEmptyColumns is set, empty columns that are terminated
// entirely by vertical (or "soft") tabs are discarded. Columns
// terminated by horizontal (or "hard") tabs are not affected by
// this flag.
//
// The Writer assumes that all characters have the same width; // The Writer assumes that all characters have the same width;
// this may not be true in some fonts, especially with certain // this may not be true in some fonts, especially with certain
// UTF-8 characters. // UTF-8 characters.
@ -314,16 +322,19 @@ func (b *Writer) format(pos0 int, line0, line1 int) (pos int, err os.Error) {
// column block begin // column block begin
width := b.cellwidth; // minimal column width width := b.cellwidth; // minimal column width
wsum := 0; // the sum of all unpadded cell widths in this column discardable := true; // true if all cells in this column are empty and "soft"
for ; this < line1; this++ { for ; this < line1; this++ {
line = b.line(this); line = b.line(this);
if column < line.Len() - 1 { if column < line.Len() - 1 {
// cell exists in this column // cell exists in this column
w := line.At(column).(cell).width; c := line.At(column).(cell);
wsum += w;
// update width // update width
if t := w + b.padding; t > width { if w := c.width + b.padding; w > width {
width = t; width = w;
}
// update discardable
if c.width > 0 || c.htab {
discardable = false;
} }
} else { } else {
break break
@ -332,7 +343,7 @@ func (b *Writer) format(pos0 int, line0, line1 int) (pos int, err os.Error) {
// column block end // column block end
// discard empty columns if necessary // discard empty columns if necessary
if wsum == 0 && b.flags & DiscardEmptyColumns != 0 { if discardable && b.flags & DiscardEmptyColumns != 0 {
width = 0; width = 0;
} }
@ -391,7 +402,8 @@ func (b *Writer) terminateHTML() {
// Terminate the current cell by adding it to the list of cells of the // Terminate the current cell by adding it to the list of cells of the
// current line. Returns the number of cells in that line. // current line. Returns the number of cells in that line.
// //
func (b *Writer) terminateCell() int { func (b *Writer) terminateCell(htab bool) int {
b.cell.htab = htab;
line := b.line(b.lines.Len() - 1); line := b.line(b.lines.Len() - 1);
line.Push(b.cell); line.Push(b.cell);
b.cell = cell{}; b.cell = cell{};
@ -411,7 +423,7 @@ func (b *Writer) Flush() os.Error {
// inside html tag/entity - terminate it even if incomplete // inside html tag/entity - terminate it even if incomplete
b.terminateHTML(); b.terminateHTML();
} }
b.terminateCell(); b.terminateCell(false);
} }
// format contents of buffer // format contents of buffer
@ -435,12 +447,12 @@ func (b *Writer) Write(buf []byte) (written int, err os.Error) {
if b.html_char == 0 { if b.html_char == 0 {
// outside html tag/entity // outside html tag/entity
switch ch { switch ch {
case '\t', '\n', '\f': case '\t', '\v', '\n', '\f':
// end of cell // end of cell
b.append(buf[i0 : i], true); b.append(buf[i0 : i], true);
i0 = i+1; // exclude ch from (next) cell i0 = i+1; // exclude ch from (next) cell
ncells := b.terminateCell(); ncells := b.terminateCell(ch == '\t');
if ch != '\t' { if ch == '\n' || ch == '\f' {
// terminate line // terminate line
b.addLine(); b.addLine();
if ch == '\f' || ncells == 1 { if ch == '\f' || ncells == 1 {

View File

@ -418,14 +418,21 @@ var tests = []entry {
entry{ entry{
"15b", "15b",
4, 0, '.', DiscardEmptyColumns, 4, 0, '.', DiscardEmptyColumns,
"a\t\tb", "a\t\tb", // htabs - do not discard column
"a...b" "a.......b"
}, },
entry{ entry{
"15c", "15c",
4, 0, '.', DiscardEmptyColumns,
"a\v\vb",
"a...b"
},
entry{
"15d",
4, 0, '.', AlignRight | DiscardEmptyColumns, 4, 0, '.', AlignRight | DiscardEmptyColumns,
"a\t\tb", "a\v\vb",
"...ab" "...ab"
}, },
@ -448,14 +455,30 @@ var tests = []entry {
entry{ entry{
"16b", "16b",
100, 0, '\t', DiscardEmptyColumns, 100, 0, '\t', DiscardEmptyColumns,
"a\tb\t\td\n" "a\vb\v\vd\n"
"a\vb\v\vd\ve\n"
"a\n"
"a\vb\vc\vd\n"
"a\vb\vc\vd\ve\n",
"a\tb\td\n"
"a\tb\td\te\n"
"a\n"
"a\tb\tc\td\n"
"a\tb\tc\td\te\n"
},
entry{
"16c",
100, 0, '\t', DiscardEmptyColumns,
"a\tb\t\td\n" // hard tabs - do not discard column
"a\tb\t\td\te\n" "a\tb\t\td\te\n"
"a\n" "a\n"
"a\tb\tc\td\n" "a\tb\tc\td\n"
"a\tb\tc\td\te\n", "a\tb\tc\td\te\n",
"a\tb\td\n" "a\tb\t\td\n"
"a\tb\td\te\n" "a\tb\t\td\te\n"
"a\n" "a\n"
"a\tb\tc\td\n" "a\tb\tc\td\n"
"a\tb\tc\td\te\n" "a\tb\tc\td\te\n"