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

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

このコミットは、Go言語の初期のコード整形ツールにおける「エラスティックタブストップ(elastic tabstops)」機能に関するさらなる作業を反映しています。具体的には、エラスティックタブストップの新しい実装コードが有効化されましたが、その効果を視覚的に確認するためのコメント出力はまだ実装されていません。これは、Go言語の公式フォーマッタであるgo fmtの基礎となる、コードの自動整形機能の開発初期段階における重要な一歩です。

コミット

commit 2a58e7d7a02064f8c5a95c98e7c3e30a26e1fa55
Author: Robert Griesemer <gri@golang.org>
Date:   Mon Nov 10 17:56:46 2008 -0800

    more work on elastic tabs:
    - new code enabled, but no comments printed yet (so the effect
      of the elastic tabs is not seen yet)
    
    TBR=r
    DELTA=200  (93 added, 69 deleted, 38 changed)
    OCL=18951
    CL=18951

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

https://github.com/golang/go/commit/2a58e7d7a02064f8c5a95c98e7c3e30a26e1fa55

元コミット内容

more work on elastic tabs:
- new code enabled, but no comments printed yet (so the effect
  of the elastic tabs is not seen yet)

変更の背景

このコミットは、Go言語のコードフォーマッタ(後のgo fmt)の初期開発の一環として行われました。当時のGo言語はまだ公開されておらず、開発チームはコードの可読性と一貫性を高めるための自動整形メカニズムを模索していました。その中で、「エラスティックタブストップ」という概念が、異なる環境や個人の設定に依存せずにコードの列をきれいに揃えるための有望なアプローチとして検討されていました。

この変更の目的は、エラスティックタブストップの新しい実装を有効にし、その動作を内部的にテストすることでした。コミットメッセージにある「no comments printed yet」という記述は、この機能がまだ開発途上であり、ユーザー(または開発者自身)が整形結果を視覚的に確認するためのデバッグ出力やコメント挿入機能が未実装であることを示唆しています。これは、機能のコアロジックの実装に焦点を当て、その後の段階でユーザーインターフェースやデバッグ機能を改善していくという、典型的なソフトウェア開発のアプローチです。

前提知識の解説

エラスティックタブストップ (Elastic Tabstops)

エラスティックタブストップは、コードのインデントとアライメントを扱うための革新的なアプローチです。従来のタブ(固定幅またはユーザー設定幅)やスペースによるインデントとは異なり、エラスティックタブストップでは、タブ文字がその後のテキストの列を揃えるように動的に幅を調整します。

  • 従来のタブ/スペースの問題点:
    • 固定幅タブ: エディタのタブ幅設定が異なると、コードの見た目が崩れる可能性があります。
    • スペースインデント: 異なるインデントレベルで列を揃える場合、手動で多数のスペースを挿入する必要があり、編集が煩雑になります。また、列の途中に文字が挿入されると、それ以降の列全体を再調整する必要があります。
  • エラスティックタブストップの利点:
    • 動的なアライメント: タブ文字が、同じ列にある他の行のテキストと揃うように自身の幅を調整します。これにより、コードの見た目が常にきれいに保たれます。
    • 編集の容易さ: 列の途中に文字を挿入しても、エラスティックタブストップが自動的に幅を調整するため、手動での再調整が不要になります。
    • 個人の好みに対応: 各開発者が自分のエディタで好きなタブ幅を設定しても、コードの列は常に正しく揃います。

この概念は、Nick Gravgaardによって提唱され、特にコードの整形において高い可読性とメンテナンス性を提供すると期待されました。Go言語のgo fmtは、最終的にはエラスティックタブストップとは異なるアプローチ(固定のタブ幅と、必要に応じたスペースによるアライメント)を採用しましたが、初期の検討段階ではこのアイデアが重要な役割を果たしました。

Go言語のコード整形 (go fmt)

go fmtは、Go言語のソースコードを自動的に整形するためのツールです。Go言語の設計哲学の一つに「一貫性」があり、go fmtはその哲学を具現化する中心的なツールです。go fmtを使用することで、Goコミュニティ全体で統一されたコードスタイルが強制され、コードの可読性が向上し、レビュープロセスが簡素化されます。

go fmtは、インデント、スペース、改行、括弧の配置など、多くのスタイルガイドラインを自動的に適用します。このコミットが行われた2008年当時、go fmtはまだ開発の初期段階にあり、どのような整形ルールを採用するか、特にインデントとアライメントのメカニズムについて様々な試行錯誤が行われていました。エラスティックタブストップの検討もその一環でした。

技術的詳細

このコミットは、usr/gri/pretty/printer.goファイルに対する大幅な変更を含んでいます。このファイルは、Go言語のコードを整形するための「プリティプリンタ」のロジックを実装していると考えられます。

主な変更点は以下の通りです。

  1. Buffer構造体の変更:
    • segmentフィールドが追加されました。これは、現在の行セグメント(タブで区切られる前の文字列)を保持するために使用されます。
    • widthsフィールドが削除されました。これは、エラスティックタブストップの新しいアプローチにおいて、列幅の計算方法が変更されたことを示唆しています。
  2. Bufferメソッドの追加と変更:
    • Line(i int) *AST.List: 指定されたインデックスの行を取得するヘルパーメソッド。
    • Tab(): 現在のsegmentを現在の行に追加し、segmentをリセットします。これは、タブ文字が検出されたときに新しい列セグメントを開始する役割を担います。
    • Print(s string): 文字列sを現在のsegmentに追加します。これは、整形対象のコードをバッファに書き込む主要なメソッドとなります。
    • Newline(): Tab()を呼び出して現在のセグメントを確定した後、新しい行を開始します。
    • Init(): Bufferの初期化ロジックが変更され、widthsの初期化が削除されました。
    • ComputeWidths()メソッドが完全に削除されました。これは、以前の列幅計算ロジックが新しいエラスティックタブストップの実装に置き換えられたことを意味します。
    • Flush()メソッドが大幅に書き換えられました。以前はComputeWidths()を呼び出して列幅を計算し、その後行を直接出力していましたが、新しい実装ではTab()を呼び出して最後のセグメントを確定し、Format()メソッドを呼び出して整形処理を行います。
    • PrintLines(line0, line1 int, widths *AST.List): 指定された行範囲を、与えられたwidths(列幅)に基づいて出力する新しいヘルパーメソッド。
    • Format(line0, line1 int, widths *AST.List): エラスティックタブストップの核心となる整形ロジックを実装する新しいメソッド。このメソッドは再帰的に呼び出され、コードの列を分析し、適切なタブ幅を計算して適用します。
    • Dump(): デバッグ目的でバッファの内容をダンプする新しいメソッド。
  3. Printer構造体の変更:
    • NEW_CODE定数が削除されました。以前はfalseに設定されており、新しいエラスティックタブストップのコードパスが有効になっていなかったことを示唆しています。この定数の削除は、新しいコードがデフォルトで有効になったことを意味します。
    • String(pos int, s string)メソッドが変更され、直接print関数を呼び出す代わりに、P.buf.Print(s)P.buf.Newline()といったBufferのメソッドを使用するようになりました。これにより、出力ロジックがBuffer内にカプセル化され、エラスティックタブストップのロジックが適用されるようになりました。
    • コメントの処理ロジックも変更され、CountNewlinesAndTabsという新しいヘルパー関数が導入されました。
    • Program(p *AST.Program)メソッドの最後にあったP.String(0, "")P.buf.Flush()に変更されました。これは、整形処理の最終段階でBufferFlushメソッドを呼び出すことで、エラスティックタブストップの整形ロジックを適用することを意味します。

これらの変更は、Go言語のコード整形において、従来の固定幅タブや単純なスペースインデントから、より高度なエラスティックタブストップベースのアライメントシステムへの移行を試みたことを明確に示しています。

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

変更はすべて /usr/gri/pretty/printer.go ファイル内で行われています。

  • Buffer 構造体の定義変更:
    --- a/usr/gri/pretty/printer.go
    +++ b/usr/gri/pretty/printer.go
    @@ -36,96 +36,117 @@ func PrintBlanks(n int) {
     // (http://nickgravgaard.com/elastictabstops/index.html)
     
     type Buffer struct {
    +	segment string;  // current line segment
     	lines AST.List;  // a list of lines; and each line is a list of strings
    -	widths AST.List;
     }
    
  • Buffer メソッドの追加と変更:
    • Line, Tab, Print メソッドの追加。
    • Newline, Init メソッドの変更。
    • ComputeWidths メソッドの削除。
    • PrintLines, Format, Dump メソッドの追加。
    • Flush メソッドの大幅な変更。
  • Printer 構造体の String メソッドの変更:
    --- a/usr/gri/pretty/printer.go
    +++ b/usr/gri/pretty/printer.go
    @@ -142,28 +163,42 @@ export type Printer struct {
     }
     
     
    -const NEW_CODE = false;
    +func CountNewlinesAndTabs(s string) (int, int, string) {
    +	nls, tabs := 0, 0;
    +	for i := 0; i < len(s); i++ {
    +		switch ch := s[i]; ch {
    +		case '\n': nls++;
    +		case '\t': tabs++;
    +		case ' ':
    +		default:
    +			// non-whitespace char
    +			assert(ch == '/');
    +			return nls, tabs, s[i : len(s)];
    +		}
    +	}
    +	return nls, tabs, "";
    +}
    +
      func (P *Printer) String(pos int, s string) {
      if P.semi && P.level > 0 {  // no semicolons at level 0
    -		if NEW_CODE {
    -			P.buf.Print(";");
    -		} else {
    -			print(";");
    -		}
    +		P.buf.Print(";");
      }
      
      /*
       for pos > P.cpos {
       	// we have a comment
    -		c := P.clist.at(P.cindex).(*AST.Comment);
    -		if len(c.text) > 1 && c.text[1] == '/' {
    -			print("  " + c.text);
    +		comment := P.clist.at(P.cindex).(*AST.Comment);
    +		nls, tabs, text := CountNewlinesAndTabs(comment.text);
    +		
    +		if nls == 0 && len(text) > 1 && text[1] == '/' {
    +			P.buf.Tab();
    +			P.buf.Print(text);
      		if P.newl <= 0 {
    -				P.newl = 1;  // line comments must have a newline
    +				//P.newl = 1;  // line comments must have a newline
      			}
      		} else {
    -			print(c.text);
    +			P.buf.Print(text);
      		}
      		P.cindex++;
      		if P.cindex < P.clist.len() {
    @@ -175,30 +210,19 @@ func (P *Printer) String(pos int, s string) {
      */
      
      if P.newl > 0 {
    -		if NEW_CODE {
    -			P.buf.Flush();
    -		}
    -		for i := P.newl; i > 0; i-- {
    -			if NEW_CODE {
    +		P.buf.Newline();
    +		if P.newl > 1 {
    +			for i := P.newl; i > 1; i-- {
    +				//P.buf.Flush();
    +				P.buf.Newline();
    +			}
    +		}
    +		for i := P.indent; i > 0; i-- {
    +			P.buf.Tab();
    +		}
    +	}
    +
    +	P.buf.Print(s);
    
      	P.semi, P.newl = false, 0;
      }
    @@ -668,5 +692,5 @@ func (P *Printer) Program(p *AST.Program) {
      	}
      	P.newl = 1;
      
    -	P.String(0, "");  // flush
    +	P.buf.Flush();  // TODO should not access P.buf directly here
      }
    

コアとなるコードの解説

このコミットの核心は、Buffer構造体とそれに関連するメソッド群の再設計にあります。

  • Buffer構造体:
    • 以前はlineswidthsという2つのAST.Listを持っていましたが、新しい実装ではsegmentという文字列フィールドが追加され、widthsが削除されました。これは、整形処理の単位が「行全体」から「行セグメント」(タブで区切られた部分)に変わり、列幅の計算が動的に行われるようになったことを示しています。
  • Tab()Print()メソッド:
    • Print(s string)は、入力された文字列を一時的にsegmentに蓄積します。
    • Tab()は、segmentに蓄積された文字列を現在の行のAST.Listに追加し、segmentをクリアします。これにより、タブ文字が検出されるたびに新しい列が開始されるというエラスティックタブストップの動作が実現されます。
  • Format()メソッド:
    • このメソッドは、エラスティックタブストップの整形ロジックの主要な部分を担っています。コードの行を走査し、各列の最大幅を計算します。そして、その計算された幅に基づいて、PrintLinesを呼び出して空白を挿入し、列を揃えます。このメソッドは再帰的に呼び出されることで、ネストされた構造や複雑なアライメント要件にも対応できるように設計されています。
  • Printer.String()の変更:
    • 以前は直接print関数を呼び出していましたが、変更後はP.buf.Print()P.buf.Newline()といったBufferのメソッドを介して文字列を出力するようになりました。これにより、すべての出力がBufferの整形ロジック(エラスティックタブストップ)の制御下に置かれることになります。
  • NEW_CODE定数の削除:
    • この定数が削除されたことで、以前は条件付きで有効になっていた新しいエラスティックタブストップのコードパスが、常に有効になるようになりました。これは、この機能が開発の次の段階に進んだことを示しています。

これらの変更により、Go言語のプリティプリンタは、より洗練されたアライメント機能を持つように進化しました。最終的にgo fmtはエラスティックタブストップとは異なるアプローチを採用しましたが、このコミットはGo言語のコード整形における初期の重要な探求と試行錯誤の過程を示しています。

関連リンク

参考にした情報源リンク

  • コミットメッセージと差分 (./commit_data/1100.txtの内容)
  • エラスティックタブストップに関する一般的な情報源 (Nick Gravgaardのウェブサイトなど)
  • Go言語の歴史とgo fmtの進化に関する一般的な知識
  • Go言語のAST (Abstract Syntax Tree) およびコード生成/整形に関する一般的なプログラミング知識
  • GitHubのコミットページ: https://github.com/golang/go/commit/2a58e7d7a02064f8c5a95c98e7c3e30a26e1fa55