Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 1214] ファイルの概要

このコミットは、Go言語の標準ライブラリの一部である tabwriter パッケージの機能拡張と改善に関するものです。tabwriter は、テキストデータを整形し、タブ区切りのカラムを自動的に整列させるためのユーティリティを提供します。これにより、出力が読みやすくなり、表形式のデータをきれいに表示できます。

このコミットで変更された主なファイルは以下の通りです。

  • src/lib/tabwriter/tabwriter.go: tabwriter パッケージのコアロジックが実装されているファイルです。パディング文字の任意指定、右寄せアライメントの導入、エラーハンドリングの改善など、主要な機能変更が行われました。
  • src/lib/tabwriter/tabwriter_test.go: tabwriter パッケージのテストファイルです。新しい機能の動作を検証するための多数のテストケースが追加されました。
  • usr/gri/pretty/printer.go: tabwriter を利用している内部ツール pretty/printer のファイルです。tabwriter のAPI変更に合わせて更新されました。
  • usr/gri/pretty/untab.go: tabwriter を利用している内部ツール pretty/untab のファイルです。tabwriter のAPI変更に合わせて更新されました。

コミット

このコミットは、tabwriter パッケージに以下の重要な機能を追加し、既存の機能を改善しました。

  • 任意のパディング文字の実装: これまで空白またはタブに限定されていたパディング文字を、任意のASCII文字に指定できるようになりました。
  • 右寄せアライメントの実装: 特に数値結果の表示において有用な、右寄せ(right-to-left)アライメントの機能が追加されました。
  • コメントとエラーハンドリングの改善: コードの可読性を高め、堅牢性を向上させるために、コメントが追加・修正され、エラーハンドリングが強化されました。
  • テストの追加: 新しい機能と既存の機能の正確性を保証するために、より多くのテストケースが追加されました。
  • 依存ファイルの更新: tabwriter のAPI変更に伴い、それを使用している内部ツールが更新されました。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/130e6f42f1b993c1764dc1c346c9af222e59e1d2

元コミット内容

- 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

変更の背景

このコミットが行われた背景には、既存の tabwriter の機能的な制約がありました。以前の tabwriter は、カラムの整列において空白またはタブのみをパディング文字として使用でき、アライメントも基本的に左寄せに限定されていました。

しかし、表形式のデータを整形する際には、以下のようなより柔軟な機能が求められます。

  1. パディング文字の多様性: 特定の用途(例: ログ出力、特殊なレポート)では、空白やタブ以外の文字(例: .-_)でパディングしたい場合があります。これにより、視覚的な区切りを明確にしたり、特定のフォーマット要件を満たしたりできます。
  2. 右寄せアライメント: 数値データ(例: 金額、統計値)を表示する場合、桁を揃えるために右寄せアライメントが不可欠です。左寄せでは数値の比較が困難になることがあります。
  3. 堅牢性と使いやすさ: より良いエラーハンドリングと明確なコメントは、ライブラリの堅牢性を高め、開発者がより安全かつ効率的に利用できるようにするために重要です。

これらの要求に応えるため、tabwriter の設計が見直され、任意のパディング文字と右寄せアライメントの機能が導入されました。また、http://nickgravgaard.com/elastictabstops/index.html で言及されている「Elastic Tabstops」の概念も、この tabwriter の設計思想に影響を与えています。Elastic Tabstopsは、タブ区切りのテキストにおいて、各カラムの幅が自動的に調整され、常に最適なアライメントが保たれるようにするアイデアです。

前提知識の解説

tabwriter の役割

tabwriter は、Go言語でテキストを整形するためのパッケージです。特に、タブ (\t) で区切られたテキストの各カラムを自動的に整列させる機能を提供します。これにより、以下のような整形された出力を簡単に生成できます。

Name    Age  City
Alice   30   New York
Bob     25   London
Charlie 35   Paris

tabwriterio.Writer インターフェースを実装しており、任意の io.Writer にデータを書き込むことで整形された出力を得られます。

io.Writer インターフェース

Go言語の io パッケージで定義されている Writer インターフェースは、データを書き込むための基本的な抽象化を提供します。

type Writer interface {
    Write(p []byte) (n int, err error)
}

tabwriter.Writer はこのインターフェースを満たしているため、os.Stdoutbytes.Buffer など、様々な出力先に整形されたテキストを書き出すことができます。

タブストップとアライメントの概念

  • タブストップ: テキストエディタやプリンタにおいて、タブ文字 (\t) が到達したときにカーソルが移動する固定位置のことです。伝統的なタブストップは一定間隔(例: 8文字ごと)で設定されます。
  • アライメント: テキストを特定の基準に沿って整列させることです。
    • 左寄せ (Left Alignment): テキストが左端に揃えられ、右側に空白が追加されます。
    • 右寄せ (Right Alignment): テキストが右端に揃えられ、左側に空白が追加されます。数値の桁揃えによく使われます。
    • 中央寄せ (Center Alignment): テキストが中央に揃えられ、左右に空白が追加されます。

tabwriter は、これらの概念を動的に適用し、入力されたテキストの各カラムの幅を計算して、指定されたアライメントとパディングで出力します。

Go言語の基本的な型

  • byte: 8ビットの符号なし整数型で、ASCII文字を表すのに使われます。
  • bool: 真偽値 (true または false) を表す型です。
  • int: 整数型です。システムに依存しますが、通常は32ビットまたは64ビットです。

技術的詳細

このコミットにおける tabwriter の主要な技術的変更点は以下の通りです。

  1. Writer 構造体の変更:

    • tabwidth int フィールドが cellwidth int に名称変更されました。これは、タブの幅だけでなく、セルの最小幅をより一般的に表現するためです。
    • usetabs bool フィールドが削除され、代わりに padbytes [8]bytealign_left bool が追加されました。
      • padbytes: パディングに使用する文字を格納するバイト配列です。これにより、任意の文字をパディングに使えるようになりました。配列サイズが8なのは、効率的な書き込みのためと考えられます。
      • align_left: 左寄せアライメントを行うかどうかを示すブール値です。true の場合は左寄せ、false の場合は右寄せを行います。
  2. Init 関数の引数と初期化ロジックの変更:

    • 旧: 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
    • tabwidthcellwidth に、usetabspadcharalign_left に置き換わりました。
    • cellwidthpadding が負の値でないかどうかのチェックが追加され、負の値の場合は panic を発生させるようになりました。これにより、不正な引数に対する堅牢性が向上しました。
    • padbytes フィールドは、padchar で指定された文字で初期化されます。
    • align_left は、引数で渡された値、または padchar がタブ (\t) の場合は強制的に true に設定されます。これは、タブ文字によるパディングは常に左寄せとして扱われるべきであるという設計上の考慮に基づいています。
  3. パディング文字の任意指定 (padchar) とその実装 (padbytes):

    • Writer 構造体内の padbytes [8]byte フィールドが、パディング文字を保持するために導入されました。
    • Init 関数内で、指定された padcharpadbytes 配列全体が埋められます。
    • WritePadding 関数では、以前は固定の Tabs または Blanks スライスを使用していた箇所が、b.padbytes を使用するように変更されました。これにより、任意の文字でパディングが可能になりました。
  4. 右寄せアライメント (align_left) の導入と WriteLines での処理:

    • Writer 構造体に align_left bool フィールドが追加されました。
    • WriteLines 関数内で、align_left の値に基づいてテキストの書き込み順序が変更されました。
      • align_lefttrue (左寄せ) の場合: まずテキストを書き込み、次にパディングを書き込みます。
      • align_leftfalse (右寄せ) の場合: まずパディングを書き込み、次にテキストを書き込みます。
    • これにより、同じカラム幅内でテキストが左寄せまたは右寄せに適切に配置されるようになりました。
  5. WritePadding の変更点:

    • パディング文字がタブ (\t) の場合、cellwcellwidth の倍数に調整するロジックが追加されました。これは、タブが固定幅で表示されることを前提とした動作です。
    • パディングの書き込みループが、len(b.padbytes) を基準に行われるようになりました。
  6. New 関数の変更:

    • tabwriter.New 関数のシグネチャも Init 関数に合わせて変更され、padcharalign_left を引数として受け取るようになりました。

これらの変更により、tabwriter はより柔軟なテキスト整形機能を提供し、特に数値データの表示においてその有用性を高めました。

コアとなるコードの変更箇所

src/lib/tabwriter/tabwriter.go

Writer 構造体の定義変更

--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -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.
 //
-// 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

Init 関数のシグネチャと初期化ロジックの変更

--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -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);

WritePadding 関数の変更

--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -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;

WriteLines 関数のアライメントロジック変更

--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -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);

New 関数のシグネチャ変更

--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -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)
 }

src/lib/tabwriter/tabwriter_test.go

Check 関数のシグネチャ変更とテストケースの追加

--- a/src/lib/tabwriter/tabwriter_test.go
+++ b/src/lib/tabwriter/tabwriter_test.go
@@ -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(\n-\t\tt, 8, 1, false,\n+\t\tt, 8, 1, ' ', true,\n \t\t"\\n",\n \t\t"\\n"\n \t);\n  
  	Check(\n-\t\tt, 8, 1, false,\n+\t\tt, 8, 1, '*', true,\n \t\t"Hello, world!\\n",\n \t\t"Hello, world!\\n"\n \t);\n  
  	Check(\n-\t\tt, 8, 1, false,\n-\t\t"a\\tb\\tc\\naa\\tbbb\\tcccc\\naaa\\tbbbb\\n\\n",\n+\t\tt, 0, 0, '.', true,\n+\t\t"1\\t2\\t3\\t4\\n"\n+\t\t"11\\t222\\t3333\\t44444\\n\\n",\n+\n+\t\t"1.2..3...4\\n"\n+\t\t"11222333344444\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 5, 0, '.', true,\n+\t\t"1\\t2\\t3\\t4\\n\\n",\n+\t\t"1....2....3....4\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 5, 0, '.', true,\n+\t\t"1\\t2\\t3\\t4\\t\\n\\n",\n+\t\t"1....2....3....4....\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 8, 1, ' ', true,\n+\t\t"a\\tb\\tc\\n"\n+\t\t"aa\\tbbb\\tcccc\\tddddd\\n"\n+\t\t"aaa\\tbbbb\\n\\n",\n+\n \t\t"a       b       c\\n"\n-\t\t"aa      bbb     cccc\\n"\n+\t\t"aa      bbb     cccc    ddddd\\n"\n \t\t"aaa     bbbb\\n\\n"\n \t);\n+\n+\tCheck(\n+\t\tt, 8, 1, ' ', false,\n+\t\t"a\\tb\\tc\\t\\n"\n+\t\t"aa\\tbbb\\tcccc\\tddddd\\t\\n"\n+\t\t"aaa\\tbbbb\\t\\n\\n",\n+\n+\t\t"       a       b       c\\n"\n+\t\t"      aa     bbb    cccc   ddddd\\n"\n+\t\t"     aaa    bbbb\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 2, 0, ' ', true,\n+\t\t"a\\tb\\tc\\n"\n+\t\t"aa\\tbbb\\tcccc\\n"\n+\t\t"aaa\\tbbbb\\n\\n",\n+\n+\t\t"a  b  c\\n"\n+\t\t"aa bbbcccc\\n"\n+\t\t"aaabbbb\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 8, 1, '_', true,\n+\t\t"a\\tb\\tc\\n"\n+\t\t"aa\\tbbb\\tcccc\\n"\n+\t\t"aaa\\tbbbb\\n\\n",\n+\n+\t\t"a_______b_______c\\n"\n+\t\t"aa______bbb_____cccc\\n"\n+\t\t"aaa_____bbbb\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 4, 1, '-', true,\n+\t\t"4444\\t333\\t22\\t1\\t333\\n"\n+\t\t"999999999\\t22\\n"\n+\t\t"7\\t22\\n"\n+\t\t"\\t\\t\\t88888888\\n"\n+\t\t"\\n"\n+\t\t"666666\\t666666\\t666666\\t4444\\n"\n+\t\t"1\\t1\\t999999999\\t0000000000\\n\\n",\n+\n+\t\t"4444------333-22--1---333\\n"\n+\t\t"999999999-22\\n"\n+\t\t"7---------22\\n"\n+\t\t"------------------88888888\\n"\n+\t\t"\\n"\n+\t\t"666666-666666-666666----4444\\n"\n+\t\t"1------1------999999999-0000000000\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 4, 3, '.', true,\n+\t\t"4444\\t333\\t22\\t1\\t333\\n"\n+\t\t"999999999\\t22\\n"\n+\t\t"7\\t22\\n"\n+\t\t"\\t\\t\\t88888888\\n"\n+\t\t"\\n"\n+\t\t"666666\\t666666\\t666666\\t4444\\n"\n+\t\t"1\\t1\\t999999999\\t0000000000\\n\\n",\n+\n+\t\t"4444........333...22...1...333\\n"\n+\t\t"999999999...22\\n"\n+\t\t"7...........22\\n"\n+\t\t"....................88888888\\n"\n+\t\t"\\n"\n+\t\t"666666...666666...666666......4444\\n"\n+\t\t"1........1........999999999...0000000000\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 8, 1, '\\t', true,\n+\t\t"4444\\t333\\t22\\t1\\t333\\n"\n+\t\t"999999999\\t22\\n"\n+\t\t"7\\t22\\n"\n+\t\t"\\t\\t\\t88888888\\n"\n+\t\t"\\n"\n+\t\t"666666\\t666666\\t666666\\t4444\\n"\n+\t\t"1\\t1\\t999999999\\t0000000000\\n\\n",\n+\n+\t\t"4444\\t\\t333\\t22\\t1\\t333\\n"\n+\t\t"999999999\\t22\\n"\n+\t\t"7\\t\\t22\\n"\n+\t\t"\\t\\t\\t\\t88888888\\n"\n+\t\t"\\n"\n+\t\t"666666\\t666666\\t666666\\t\\t4444\\n"\n+\t\t"1\\t1\\t999999999\\t0000000000\\n\\n"\n+\t);\n+\n+\tCheck(\n+\t\tt, 4, 2, ' ', false,\n+\t\t".0\\t.3\\t2.4\\t-5.1\\t\\n"\n+\t\t"23.0\\t12345678.9\\t2.4\\t-989.4\\t\\n"\n+\t\t"5.1\\t12.0\\t2.4\\t-7.0\\t\\n"\n+\t\t".0\\t0.0\\t332.0\\t8908.0\\t\\n"\n+\t\t".0\\t-.3\\t456.4\\t22.1\\t\\n"\n+\t\t".0\\t1.2\\t44.4\\t-13.3\\t\\n\\n",\n+\n+\t\t"    .0          .3    2.4    -5.1\\n"\n+\t\t"  23.0  12345678.9    2.4  -989.4\\n"\n+\t\t"   5.1        12.0    2.4    -7.0\\n"\n+\t\t"    .0         0.0  332.0  8908.0\\n"\n+\t\t"    .0         -.3  456.4    22.1\\n"\n+\t\t"    .0         1.2   44.4   -13.3\\n\\n"\n+\t);\n }\n```

## コアとなるコードの解説

### `Writer` 構造体の定義変更

*   `tabwidth` から `cellwidth` への変更は、このフィールドが単にタブの幅を示すだけでなく、各セルの最小幅を制御する汎用的なパラメータであることを明確にしています。
*   `usetabs bool` が削除され、`padbytes [8]byte` と `align_left bool` が追加されたのは、パディングの柔軟性とアライメント制御を向上させるための根本的な変更です。
    *   `padbytes`: 以前は空白かタブのどちらかしか選べませんでしたが、この配列に任意のASCII文字を格納することで、その文字をパディングとして使用できるようになりました。配列サイズが8なのは、`WritePadding` 関数内で効率的にバイト列を書き込むためです。
    *   `align_left`: これまで暗黙的に左寄せだったアライメントを、明示的に制御できるようにしました。`true` なら左寄せ、`false` なら右寄せとなります。

### `Init` 関数のシグネチャと初期化ロジックの変更

*   新しい引数 `padchar byte` と `align_left bool` は、`Writer` の初期化時にパディング文字とアライメント方向を直接指定できるようにします。
*   `cellwidth < 0` または `padding < 0` のチェックが追加され、負の値が渡された場合に `panic` を発生させるようになりました。これは、不正な入力に対する早期のエラー検出と、ライブラリの堅牢性向上に貢献します。
*   `for i := len(b.padbytes) - 1; i >= 0; i-- { b.padbytes[i] = padchar; }` のループは、`padbytes` 配列全体を `padchar` で埋めることで、任意のパディング文字を使用するための準備をします。
*   `b.align_left = align_left || padchar == '\t';` の行は重要です。これは、ユーザーが `align_left` を `false` (右寄せ) に指定したとしても、もしパディング文字がタブ (`\t`) であれば、強制的に左寄せ (`true`) に設定することを意味します。これは、タブ文字が通常、固定幅のタブストップで表示され、その性質上、左寄せとして機能するためです。

### `WritePadding` 関数の変更

*   `if b.usetabs { ... }` のブロックが `if b.padbytes[0] == '\t' { ... }` に変更されました。これは、パディング文字がタブである場合にのみ、`cellw` を `cellwidth` の倍数に調整するロジックを適用するためです。これにより、タブによるパディングが正しく機能します。
*   `padding := Blanks;` や `padding = Tabs;` といった固定のバイトスライスを使用していた箇所が削除され、代わりに `b.padbytes` を使用するようになりました。これにより、`Init` 関数で設定された任意の `padchar` がパディングに利用されます。
*   `for n > len(b.padbytes) { err = b.Write0(&b.padbytes); ... }` のループは、必要なパディングの長さ `n` が `padbytes` の長さ(8バイト)よりも大きい場合に、`padbytes` を繰り返し書き込むことで効率的にパディングを生成します。
*   `err = b.Write0((&b.padbytes)[0 : n]);` は、残りのパディングを書き込む部分です。コメントにある `BUG 6g should not require ()'s` は、当時のGoコンパイラ (6g) のバグを示唆しており、スライス操作のために括弧が必要だったことを示しています。

### `WriteLines` 関数のアライメントロジック変更

*   この関数は、バッファに蓄積された行とセルのデータを実際の出力ライターに書き出す役割を担っています。
*   `if b.align_left { ... } else { ... }` の条件分岐が追加されたことで、アライメントの制御が実現されました。
    *   **左寄せ (`b.align_left` が `true`)**: 従来の動作と同様に、まずセルのテキストを書き込み (`b.Write0(b.buf.a[pos : pos + w])`)、その後に必要なパディングを書き込みます (`b.WritePadding(w, b.widths.At(j))`)。
    *   **右寄せ (`b.align_left` が `false`)**: まず必要なパディングを書き込み (`b.WritePadding(w, b.widths.At(j))`)、その後にセルのテキストを書き込みます (`b.Write0(b.buf.a[pos : pos + w])`)。これにより、テキストがセルの右端に揃えられます。

### `New` 関数のシグネチャ変更

*   `tabwriter.New` は `tabwriter.Writer` のコンストラクタ関数であり、そのシグネチャが `Init` 関数に合わせて変更されました。これにより、外部から `tabwriter` を利用する際に、新しいパディングとアライメントのオプションを指定できるようになりました。

これらの変更は、`tabwriter` の柔軟性と表現力を大幅に向上させ、より多様なテキスト整形ニーズに対応できるようになりました。特に、数値データの右寄せアライメントは、表形式のレポートやCLIツールでの出力において非常に重要な機能です。

## 関連リンク

*   Elastic Tabstops: [http://nickgravgaard.com/elastictabstops/index.html](http://nickgravgaard.com/elastictabstops/index.html)

## 参考にした情報源リンク

*   Go言語の公式ドキュメント (tabwriterパッケージ): [https://pkg.go.dev/text/tabwriter](https://pkg.go.dev/text/tabwriter) (コミット当時のバージョンとは異なる可能性がありますが、概念理解に役立ちます)
*   Go言語の `io` パッケージ: [https://pkg.go.dev/io](https://pkg.go.dev/io)
*   Gitのコミット履歴と差分表示 (GitHub): [https://github.com/golang/go/commit/130e6f42f1b993c1764dc1c346c9af222e59e1d2](https://github.com/golang/go/commit/130e6f42f1b993c1764dc1c346c9af222e59e1d2)
*   Go言語の `panic` について: [https://go.dev/blog/defer-panic-and-recover](https://go.dev/blog/defer-panic-and-recover) (概念理解に役立ちます)