[インデックス 1793] ファイルの概要
このコミットは、Go言語の標準ライブラリの一部である tabwriter
パッケージのドキュメンテーションを大幅に改善するものです。特に、パッケージの目的、Writer
型の動作、およびその初期化メソッド Init
のパラメータに関する説明が詳細化されています。また、内部で使用される io.Write
インターフェースを実装するフィールド名が writer
から output
へと変更され、より意図が明確になっています。
コミット
commit e7980732ee01350c2797e16cb9d2088d577f8b18
Author: Robert Griesemer <gri@golang.org>
Date: Tue Mar 10 14:55:04 2009 -0700
tabwriter documentation
R=rsc
DELTA=62 (31 added, 5 deleted, 26 changed)
OCL=26022
CL=26040
---
src/lib/tabwriter/tabwriter.go | 82 +++++++++++++++++++++++++++---------------
1 file changed, 54 insertions(+), 28 deletions(-)
diff --git a/src/lib/tabwriter/tabwriter.go b/src/lib/tabwriter/tabwriter.go
index de37204d15..cc20294e0f 100644
--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// The tabwriter package implements a write filter (tabwriter.Writer)
+// that translates tabbed columns in input into properly aligned text,
+// using the Elastic Tabstops algorithm described at
+// http://nickgravgaard.com/elastictabstops/index.html.
+//
package tabwriter
import (
@@ -66,36 +71,31 @@ func (b *byteArray) append(s []byte) {\
// ----------------------------------------------------------------------------
-// Writer is a filter implementing the io.Write interface. It assumes
-// that the incoming bytes represent UTF-8 encoded text consisting of
-// 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. All characters (ASCII
-// or not) are assumed to be of the same width - this may not be true
-// for arbitrary UTF-8 characters visualized on the screen.\n
+// Filter implementation
+\n
+// A Writer is a filter that inserts padding around
+// tab-delimited columns in its input to align them
+// in the output.\n
//
-// 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)\n
+// The Writer treats incoming bytes as UTF-8 encoded text\n
+// consisting of tab-terminated cells. Cells in adjacent lines\n
+// constitute a column. The Writer inserts padding as needed\n
+// to make all cells in a column have the same width, effectively\n
+// aligning the columns. Note that cells are tab-terminated,\n
+// not tab-separated: trailing non-tab text at the end of a line\n
+// is not part of any cell.\n
//
-// Formatting can be controlled via parameters:\n
+// The Writer assumes that all characters have the same width;\n
+// this may not be true in some fonts, especially with certain\n
+// UTF-8 characters.\n
+//\n
+// The Writer must buffer input internally, because proper spacing\n
+// of one line may depend on the cells in future lines. Clients must\n
+// call Flush when done calling Write.\n
//
-// cellwidth\tminimal cell width\n
-// padding additional cell padding\n
-// padchar ASCII char used for padding\n
-// if padchar == '\\t', the Writer will assume that the\n
-// width of a '\\t' in the formatted output is cellwidth,\n
-// and cells are left-aligned independent of align_left\n
-// (for correct-looking results, cellwidth must correspond\n
-// to the tabwidth in the viewer displaying the result)\n
-// filter_html ignores html tags and handles entities (starting with '&'\n
-// and ending in ';') as single characters (width = 1)\n
-\n
type Writer struct {\n
\t// configuration\n
-\twriter io.Write;\n
+\toutput io.Write;\n
\tcellwidth int;\n
\tpadding int;\n
\tpadbytes [8]byte;\n
@@ -113,6 +113,7 @@ type Writer struct {\
\twidths vector.IntVector; // list of column widths in runes - re-used during formatting\n
}\n \n+\n // Internal representation (current state):\n //\n // - all text written is appended to buf; tabs and newlines are stripped away\n@@ -143,14 +144,29 @@ func (b *Writer) addLine() {\
}\n \n \n-func (b *Writer) Init(writer io.Write, cellwidth, padding int, padchar byte, align_left, filter_html bool) *Writer {\n+// A Writer must be initialized with a call to Init. The first parameter (output)\n+// specifies the filter output. The remaining parameters control the formatting:\n+//\n+//\tcellwidth\tminimal cell width\n+//\tpadding\t\tadditional cell padding\n+//\tpadchar\t\tASCII char used for padding\n+//\t\t\t\tif padchar == '\\t', the Writer will assume that the\n+//\t\t\t\twidth of a '\\t' in the formatted output is cellwidth,\n+//\t\t\t\tand cells are left-aligned independent of align_left\n+//\t\t\t\t(for correct-looking results, cellwidth must correspond\n+//\t\t\t\tto the tab width in the viewer displaying the result)\n+//\talign_left\talignment of cell content\n+//\tfilter_html\tignores html tags and treats entities (starting with '&'\n+//\t\t\t\tand ending in ';') as single characters (width = 1)\n+//\n+func (b *Writer) Init(output io.Write, cellwidth, padding int, padchar byte, align_left, filter_html bool) *Writer {\n \tif cellwidth < 0 {\n \t\tpanic(\"negative cellwidth\");\n \t}\n \tif padding < 0 {\n \t\tpanic(\"negative padding\");\n \t}\n-\tb.writer = writer;\n+\tb.output = output;\n \tb.cellwidth = cellwidth;\n \tb.padding = padding;\n \tfor i := len(b.padbytes) - 1; i >= 0; i-- {\
@@ -194,7 +210,7 @@ func (b *Writer) dump() {\
\n \n func (b *Writer) write0(buf []byte) *os.Error {\n-\tn, err := b.writer.Write(buf);\n+\tn, err := b.output.Write(buf);\n \tif n != len(buf) && err == nil {\n \t\terr = os.EIO;\n \t}\
@@ -339,6 +355,9 @@ exit:\
}\n \n \n+// Flush should be called after the last call to Write to ensure\n+// that any data buffered in the Writer is written to output.\n+//\n func (b *Writer) Flush() *os.Error {\n \tdummy, err := b.format(0, 0, b.lines_size.Len());\n \t// reset (even in the presence of errors)\n@@ -373,6 +392,10 @@ func (b *Writer) append(buf []byte) {\
}\n \n \n+// Write writes buf to the writer b.\n+// The only errors returned are ones encountered\n+// while writing to the underlying output stream.\n+//\n func (b *Writer) Write(buf []byte) (written int, err *os.Error) {\n \ti0, n := 0, len(buf);\n \n@@ -444,6 +467,9 @@ func (b *Writer) Write(buf []byte) (written int, err *os.Error) {\
}\n \n \n+// New allocates and initializes a new tabwriter.Writer.\n+// The parameters are the same as for the the Init function.\n+//\n func New(writer io.Write, cellwidth, padding int, padchar byte, align_left, filter_html bool) *Writer {\n \treturn new(Writer).Init(writer, cellwidth, padding, padchar, align_left, filter_html)\n }\n```
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/e7980732ee01350c2797e16cb9d2088d577f8b18](https://github.com/golang/go/commit/e7980732ee01350c2797e16cb9d2088d577f8b18)
## 元コミット内容
このコミットの元の内容は、`tabwriter` パッケージのドキュメンテーションの更新です。具体的には、パッケージレベルのコメント、`Writer` 型の構造体コメント、`Init` メソッドのパラメータ説明、および `Flush`、`Write`、`New` メソッドのコメントが追加・修正されています。また、`Writer` 構造体内の `writer` フィールドが `output` にリネームされています。
## 変更の背景
このコミットの主な背景は、`tabwriter` パッケージの利用者がその機能と使い方をより正確に理解できるように、ドキュメンテーションを改善することにあります。特に、`tabwriter` が「Elastic Tabstops」アルゴリズムに基づいていること、タブ区切りではなくタブ終端のセルを扱うこと、そして内部バッファリングのために `Flush` の呼び出しが必要であることなど、重要な概念が明確に説明されていませんでした。
また、`Writer` 構造体の `writer` フィールドは、その役割が「出力先」であることをより明確にするために `output` にリネームされました。これはコードの可読性と意図の明確化を目的とした、小さなリファクタリングです。
## 前提知識の解説
### `io.Writer` インターフェース
Go言語における `io.Writer` インターフェースは、データを書き込むための基本的な抽象化を提供します。これは `Write([]byte) (n int, err error)` という単一のメソッドを持つインターフェースで、ファイル、ネットワーク接続、標準出力など、様々な出力先に統一された方法でデータを書き込むことを可能にします。`tabwriter.Writer` はこの `io.Writer` インターフェースを実装しており、自身が書き込み可能なオブジェクトとして振る舞います。
### Elastic Tabstops アルゴリズム
`tabwriter` パッケージの核となるのが「Elastic Tabstops」アルゴリズムです。これは、テキストエディタや表示ツールにおいて、タブ区切りのデータを自動的に整形し、列をきれいに揃えるためのアルゴリズムです。
従来のタブ(`\t`)は、固定幅のスペースに展開されることが多く、これにより列がずれて表示される問題がありました。特に、異なる長さのテキストが同じ列に存在する場合、手動でスペースを調整する必要がありました。
Elastic Tabstops はこの問題を解決します。このアルゴリズムでは、タブは「次のタブストップ」までスペースを埋めるのではなく、「同じ列の最も長い要素の幅」に合わせてスペースを埋めます。これにより、以下のような特徴があります。
1. **動的な列幅**: 各列の幅は、その列に含まれる最も長いテキスト要素の長さに合わせて動的に調整されます。
2. **自動的な再配置**: ユーザーがテキストを編集して列の幅が変わっても、他の列が自動的に再配置され、常にきれいに揃った状態が保たれます。
3. **タブ終端**: `tabwriter` の文脈では、セルはタブで「区切られる」のではなく「終端される」という点が重要です。つまり、行の最後にタブがないテキストは、それ自体がセルとは見なされず、列の揃えには影響しません。
このアルゴリズムにより、プログラマーは手動でスペースを調整する手間を省き、コードやデータを整形する際に高い可読性を得ることができます。
### バッファリングと `Flush` の必要性
`tabwriter.Writer` は、Elastic Tabstops アルゴリズムの性質上、入力されたデータを内部的にバッファリングする必要があります。これは、ある行の列の幅を決定するためには、その列の将来の行のデータ(つまり、その列の最も長い要素)を知る必要があるためです。
そのため、`Write` メソッドが呼び出されても、データはすぐに出力先の `io.Writer` に書き込まれるわけではありません。データは内部バッファに蓄積され、`Flush` メソッドが明示的に呼び出されたときに初めて、整形されたデータが実際の出力先に書き出されます。このため、`tabwriter.Writer` を使用する際には、すべてのデータが書き込まれた後に必ず `Flush()` を呼び出すことが重要です。これを怠ると、バッファリングされたデータが出力されないままになる可能性があります。
## 技術的詳細
このコミットは、`src/lib/tabwriter/tabwriter.go` ファイルに対して行われました。主な変更点は以下の通りです。
1. **パッケージレベルのコメントの追加**:
* ファイルの冒頭に、`tabwriter` パッケージが `tabwriter.Writer` という書き込みフィルターを実装していること、そしてそれが「Elastic Tabstops」アルゴリズムを使用していることを明確に記述しました。
* Elastic Tabstops アルゴリズムの公式ページへのリンク (`http://nickgravgaard.com/elastictabstops/index.html`) が追加され、詳細な情報源が提供されました。
2. **`Writer` 構造体コメントの改善**:
* `Writer` が「タブ区切りの列」ではなく「タブ終端のセル」を扱うことを強調しました。これにより、行の末尾にタブがないテキストが列の揃えに影響しないという重要な挙動が明確になりました。
* 文字幅に関する仮定(すべての文字が同じ幅であると仮定されるが、UTF-8文字ではそうでない場合がある)が再確認されました。
* `Writer` が内部的に入力をバッファリングする必要があること、そしてクライアントが `Write` の呼び出しを終えたら `Flush` を呼び出す必要があることが明記されました。これは、`tabwriter` の正しい使用方法にとって非常に重要な情報です。
3. **`Writer` 構造体フィールドのリネーム**:
* `Writer` 構造体内の `writer io.Write` フィールドが `output io.Write` にリネームされました。これは、このフィールドが `tabwriter.Writer` の最終的な出力先であることをより直感的に示すための変更です。
4. **`Init` メソッドのパラメータ説明の拡充**:
* `Init` メソッドの各パラメータ (`output`, `cellwidth`, `padding`, `padchar`, `align_left`, `filter_html`) について、より詳細で分かりやすい説明が追加されました。特に、`padchar` がタブ文字 (`\t`) の場合の挙動や、`filter_html` の機能について具体例を交えて説明されています。
5. **`Flush`, `Write`, `New` メソッドのコメント追加**:
* `Flush` メソッドには、バッファリングされたデータが出力に書き込まれることを保証するために、`Write` の最後の呼び出し後に呼び出す必要があるという説明が追加されました。
* `Write` メソッドには、返されるエラーが基になる出力ストリームへの書き込み中に発生したエラーのみであるという説明が追加されました。
* `New` メソッドには、`Init` 関数と同じパラメータを受け取る新しい `tabwriter.Writer` を割り当てて初期化するという説明が追加されました。
これらの変更は、`tabwriter` パッケージのドキュメンテーションの質を大幅に向上させ、開発者がこのパッケージをより効果的に使用できるようにすることを目的としています。
## コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主に `src/lib/tabwriter/tabwriter.go` ファイル内のコメントと、`Writer` 構造体のフィールド名変更です。
**1. パッケージレベルのコメント追加 (L2-L6)**
```diff
--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -2,6 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+// The tabwriter package implements a write filter (tabwriter.Writer)
+// that translates tabbed columns in input into properly aligned text,
+// using the Elastic Tabstops algorithm described at
+// http://nickgravgaard.com/elastictabstops/index.html.
+//
package tabwriter
2. Writer
構造体コメントの変更とフィールド名変更 (L66-L85, L94)
--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -66,36 +71,31 @@ func (b *byteArray) append(s []byte) {
// ----------------------------------------------------------------------------
-// Writer is a filter implementing the io.Write interface. It assumes
-// that the incoming bytes represent UTF-8 encoded text consisting of
-// 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. All characters (ASCII
-// or not) are assumed to be of the same width - this may not be true
-// for arbitrary UTF-8 characters visualized on the screen.
+// Filter implementation
+
+// A Writer is a filter that inserts padding around
+// tab-delimited columns in its input to align them
+// in the output.
//
-// 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)
+// The Writer treats incoming bytes as UTF-8 encoded text
+// consisting of tab-terminated cells. Cells in adjacent lines
+// constitute a column. The Writer inserts padding as needed
+// to make all cells in a column have the same width, effectively
+// aligning the columns. Note that cells are tab-terminated,
+// not tab-separated: trailing non-tab text at the end of a line
+// is not part of any cell.
//
-// Formatting can be controlled via parameters:
+// The Writer assumes that all characters have the same width;
+// this may not be true in some fonts, especially with certain
+// UTF-8 characters.
+//
+// The Writer must buffer input internally, because proper spacing
+// of one line may depend on the cells in future lines. Clients must
+// call Flush when done calling Write.
//
-// 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 cellwidth,
-// and cells are left-aligned independent of align_left
-// (for correct-looking results, cellwidth must correspond
-// to the tabwidth in the viewer displaying the result)
-// filter_html ignores html tags and handles entities (starting with '&'
-// and ending in ';') as single characters (width = 1)
-
type Writer struct {
// configuration
-\twriter io.Write;
+\toutput io.Write;
cellwidth int;
padding int;
padbytes [8]byte;
3. Init
メソッドのパラメータ説明の拡充とフィールド参照の変更 (L143-L159)
--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -143,14 +144,29 @@ func (b *Writer) addLine() {
}
-func (b *Writer) Init(writer io.Write, cellwidth, padding int, padchar byte, align_left, filter_html bool) *Writer {
+// A Writer must be initialized with a call to Init. The first parameter (output)
+// specifies the filter output. The remaining parameters control the formatting:
+//
+// 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 cellwidth,
+// and cells are left-aligned independent of align_left
+// (for correct-looking results, cellwidth must correspond
+// to the tab width in the viewer displaying the result)
+// align_left alignment of cell content
+// filter_html ignores html tags and treats entities (starting with '&'
+// and ending in ';') as single characters (width = 1)
+//
+func (b *Writer) Init(output io.Write, cellwidth, padding int, padchar byte, align_left, filter_html bool) *Writer {
if cellwidth < 0 {
panic("negative cellwidth");
}
if padding < 0 {
panic("negative padding");
}
-\tb.writer = writer;
+\tb.output = output;
b.cellwidth = cellwidth;
b.padding = padding;
for i := len(b.padbytes) - 1; i >= 0; i-- {
4. write0
メソッド内のフィールド参照の変更 (L194)
--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -194,7 +210,7 @@ func (b *Writer) dump() {
func (b *Writer) write0(buf []byte) *os.Error {
-\tn, err := b.writer.Write(buf);
+\tn, err := b.output.Write(buf);
if n != len(buf) && err == nil {
err = os.EIO;
}
5. Flush
, Write
, New
メソッドのコメント追加 (L355-L357, L373-L376, L444-L447)
--- a/src/lib/tabwriter/tabwriter.go
+++ b/src/lib/tabwriter/tabwriter.go
@@ -339,6 +355,9 @@ exit:
}
+// Flush should be called after the last call to Write to ensure
+// that any data buffered in the Writer is written to output.
+//
func (b *Writer) Flush() *os.Error {
dummy, err := b.format(0, 0, b.lines_size.Len());
// reset (even in the presence of errors)
@@ -373,6 +392,10 @@ func (b *Writer) append(buf []byte) {
}
+// Write writes buf to the writer b.
+// The only errors returned are ones encountered
+// while writing to the underlying output stream.
+//
func (b *Writer) Write(buf []byte) (written int, err *os.Error) {
i0, n := 0, len(buf);
@@ -444,6 +467,9 @@ func (b *Writer) Write(buf []byte) (written int, err *os.Error) {
}
+// New allocates and initializes a new tabwriter.Writer.
+// The parameters are the same as for the the Init function.
+//
func New(writer io.Write, cellwidth, padding int, padchar byte, align_left, filter_html bool) *Writer {
return new(Writer).Init(writer, cellwidth, padding, padchar, align_left, filter_html)
}
コアとなるコードの解説
パッケージレベルのコメント
Goのパッケージは、その目的を明確にするためにパッケージ宣言の直前にコメントを持つことが推奨されます。このコミットでは、tabwriter
パッケージが「Elastic Tabstops」アルゴリズムを使用してタブ区切りの入力を適切に整形する tabwriter.Writer
を実装していることを明記しました。これにより、パッケージの主要な機能と基盤となるアルゴリズムが、コードを読み始める前に理解できるようになります。また、アルゴリズムの詳細を知りたい開発者向けに、公式のWebサイトへのリンクが提供されています。
Writer
構造体コメントとフィールド名変更
Writer
構造体は tabwriter
パッケージの中心となる型です。以前のコメントは、Writer
が io.Write
インターフェースを実装するフィルターであること、およびタブ終端のセルを扱うことを説明していましたが、このコミットではその説明がより詳細かつ正確になりました。
特に重要なのは、以下の点です。
- タブ終端の明確化: 「セルはタブで終端されるのであって、タブで区切られるのではない」という点が強調されました。これは、行の末尾にタブがないテキストが列の揃えに影響しないという
tabwriter
の重要な挙動を理解するために不可欠です。 - バッファリングの必要性:
Writer
が内部的に入力をバッファリングする必要があること、そしてすべての書き込みが完了した後にFlush()
を呼び出す必要があることが明記されました。これは、tabwriter
を正しく使用し、すべての出力が確実に書き込まれるようにするために非常に重要です。
また、Writer
構造体内の writer io.Write
フィールドが output io.Write
にリネームされました。これは、このフィールドが tabwriter.Writer
の最終的な出力先であることをより直感的に示すための変更です。この変更に伴い、Init
メソッドや write0
メソッドなど、このフィールドを参照している箇所も b.writer
から b.output
に変更されています。これは機能的な変更ではなく、コードの可読性と意図の明確化を目的としたリファクタリングです。
Init
メソッドのパラメータ説明
Init
メソッドは tabwriter.Writer
を初期化するために使用されます。このコミットでは、Init
メソッドの各パラメータ(output
, cellwidth
, padding
, padchar
, align_left
, filter_html
)について、より詳細で分かりやすい説明が追加されました。特に、padchar
がタブ文字 (\t
) の場合の特殊な挙動(cellwidth
がタブ幅として扱われ、セルが左揃えになる)や、filter_html
がHTMLタグを無視し、エンティティを単一文字として扱う機能について具体的に説明されています。これにより、開発者は各パラメータの役割と、それが整形された出力にどのように影響するかを正確に理解できます。
Flush
, Write
, New
メソッドのコメント
これらのメソッドは tabwriter.Writer
の主要な公開APIです。
Flush()
メソッドのコメントは、Write
の最後の呼び出し後に呼び出すことで、バッファリングされたデータが確実に出力に書き込まれることを保証するという、その重要な役割を強調しています。Write()
メソッドのコメントは、返されるエラーが基になる出力ストリームへの書き込み中に発生したエラーのみであるということを明確にしています。これにより、開発者はエラーハンドリングの際にどの種類のエラーを期待すべきかを理解できます。New()
メソッドのコメントは、それがInit
関数と同じパラメータを受け取り、新しいtabwriter.Writer
を割り当てて初期化するコンビニエンス関数であることを説明しています。
これらのコメントの追加と改善により、tabwriter
パッケージのAPIがより使いやすく、理解しやすくなりました。
関連リンク
- Go言語
tabwriter
パッケージのドキュメンテーション: https://pkg.go.dev/text/tabwriter (現在のドキュメンテーション) - Elastic Tabstops アルゴリズム: http://nickgravgaard.com/elastictabstops/index.html
参考にした情報源リンク
- Go言語のソースコード (特に
src/text/tabwriter/tabwriter.go
): https://github.com/golang/go/blob/master/src/text/tabwriter/tabwriter.go - Elastic Tabstops の概念に関する一般的な情報源 (Web検索結果に基づく)
- Go言語の
io
パッケージのドキュメンテーション: https://pkg.go.dev/io