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

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

このコミットは、Go言語の公式ウェブサイトに組み込まれているGo PlaygroundのフロントエンドJavaScriptファイルである doc/play/playground.js の同期(更新)に関するものです。Go Playgroundは、ユーザーがブラウザ上でGoコードを記述、コンパイル、実行できるインタラクティブな環境を提供します。このファイルは、そのGo Playgroundの機能、特にコードの実行、出力の表示、エラーハンドリング、そして新しい機能としてコードのフォーマット(go fmt)とサンプルコード("toys")の読み込みを制御するロジックを含んでいます。

コミット

このコミットは、playground.js ファイルを更新し、Go Playgroundのユーザーエクスペリエンスと機能性を向上させています。主な変更点としては、出力表示の改善、エラーメッセージのハイライト精度の向上、そしてコードの自動フォーマット機能(go fmt)と、あらかじめ用意されたサンプルコード("toys")を読み込む機能の追加が挙げられます。これにより、ユーザーはより快適にGoコードを試すことができるようになります。

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

https://github.com/golang/go/commit/f1a39ff003db83dad301a045f4796384a1037e62

元コミット内容

doc: sync playground.js

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5956043

変更の背景

この変更の背景には、Go Playgroundのユーザーエクスペリエンスを向上させ、よりリッチな機能を提供したいという意図があります。具体的には、以下の点が挙げられます。

  1. ユーザーへのフィードバックの改善: コードの実行中やエラー発生時に、より明確な視覚的フィードバックを提供することで、ユーザーが現在の状態を把握しやすくする。
  2. コード品質の向上支援: go fmt コマンドの機能をGo Playgroundに統合することで、ユーザーが記述したGoコードを簡単にGoの標準的なスタイルに整形できるようにし、コードの可読性と一貫性を高める。これは、Goコミュニティ全体で推奨されるプラクティスであり、Go言語の設計思想の一部でもあります。
  3. 学習と探索の促進: "toys"(サンプルコード)機能を追加することで、Go言語の様々な機能やイディオムをユーザーが手軽に試せるようにし、学習プロセスを支援する。これにより、Go言語に不慣れなユーザーでも、既存のコードをベースに試行錯誤しやすくなります。
  4. コードの保守性と拡張性: playground.js 内のコードをリファクタリングし、共通の処理を関数としてまとめることで、将来的な機能追加やメンテナンスを容易にする。

これらの改善は、Go Playgroundを単なるコード実行環境としてだけでなく、Go言語の学習ツールとしても強化することを目的としています。

前提知識の解説

このコミットの変更内容を理解するためには、以下の前提知識があると役立ちます。

1. Go Playground

Go Playgroundは、Go言語の公式ウェブサイト (golang.org) 上で提供されているオンラインツールです。ユーザーはブラウザ上でGoコードを記述し、サーバーサイドでコンパイル・実行された結果をブラウザで確認できます。これは、Go言語の学習、簡単なコードのテスト、Goコードのスニペットの共有などに広く利用されています。Go Playgroundのバックエンドは、ユーザーのコードをサンドボックス化された環境で安全に実行し、その結果を返します。

2. go fmt

go fmt は、Go言語の標準的なコードフォーマッタです。Goのソースコードを自動的に整形し、Goコミュニティで推奨される統一されたスタイルに準拠させます。これにより、コードの可読性が向上し、異なる開発者間でのコードスタイルの不一致による議論を減らすことができます。go fmt はGoツールチェインの一部として提供されており、多くのGo開発者が日常的に使用しています。

3. JavaScriptとjQuery

playground.js はJavaScriptで書かれており、DOM操作やAjaxリクエストのためにjQueryライブラリを使用しています。

  • JavaScript: ウェブブラウザ上で動作するスクリプト言語で、ウェブページに動的な機能を追加するために使用されます。
  • jQuery: JavaScriptライブラリの一つで、HTMLドキュメントのトラバーサル、操作、イベントハンドリング、アニメーション、Ajaxなどの機能を簡潔な構文で提供します。$ シンボルは通常jQueryオブジェクトを指します。

4. Ajax (Asynchronous JavaScript and XML)

Ajaxは、ウェブページ全体をリロードすることなく、非同期にサーバーとデータをやり取りするための技術です。Go Playgroundでは、ユーザーが入力したコードをサーバーに送信してコンパイル・実行したり、フォーマットしたり、サンプルコードを読み込んだりするためにAjaxリクエストが使用されます。

5. 正規表現 (Regular Expressions)

正規表現は、文字列の中から特定のパターンを検索、置換、抽出するための強力なツールです。このコミットでは、コンパイルエラーメッセージから行番号を抽出するために正規表現が使用されています。

技術的詳細

このコミットにおける playground.js の変更は、主に以下の技術的な側面に焦点を当てています。

1. エラーメッセージの正規表現の改善

元のコードでは、コンパイルエラーの正規表現 /[a-z]+\.go:([0-9]+): /g が使用されていました。これはファイル名、行番号、そしてその後にスペースが続くパターンにマッチします。しかし、新しい正規表現は /[a-z]+\.go:([0-9]+):/g となり、行番号の後のスペースが削除されています。これにより、より厳密なマッチングが可能になり、エラーメッセージの解析精度が向上します。

2. 出力表示の共通化と改善

loading() 関数と setOutput() 関数が新しく導入され、出力エリアの表示管理が共通化されました。

  • loading() 関数: コードの実行やフォーマット処理中に「Waiting for remote server...」というメッセージを表示し、ユーザーに処理が進行中であることを伝えます。これにより、ユーザーはアプリケーションがフリーズしていると誤解することなく、待機状態を認識できます。
  • setOutput(text, error) 関数: Go Playgroundの出力エリアにテキストを表示するための共通関数です。error 引数が true の場合、出力エリアに error クラスを追加し、エラーメッセージを視覚的に区別できるようにします。これにより、エラー表示の一貫性が保たれ、コードの重複が削減されます。

これらの関数は、以前は run() 関数内に直接記述されていた出力関連のロジックを抽象化し、コードの可読性と保守性を向上させています。

3. go fmt 機能の追加

新しい fmtEl (format button element) にクリックイベントリスナーが追加され、ユーザーがボタンをクリックするとコードのフォーマット処理が実行されるようになりました。

  • ユーザーが fmtEl をクリックすると、loading() 関数が呼び出され、処理中であることを示します。
  • 現在のコードの内容が /fmt エンドポイントにPOSTリクエストとして送信されます。
  • サーバーからの応答がJSON形式で返され、Error フィールドにエラーがあれば setOutput で表示し、Body フィールドに整形されたコードがあれば setBody でエディタに反映します。

この機能は、Go Playgroundが単なる実行環境ではなく、Goコードの記述と整形を支援するツールとしての役割を強化します。

4. "Toys" (サンプルコード) 機能の追加

toysEl (select element with a list of toys) に change イベントリスナーが追加され、ユーザーがドロップダウンリストからサンプルコードを選択すると、そのコードがエディタに読み込まれるようになりました。

  • ユーザーが toysEl の選択を変更すると、loading() 関数が呼び出されます。
  • 選択されたサンプルコードのパス(例: /doc/play/hello.go)に対してGETリクエストが送信されます。
  • サーバーからサンプルコードのテキストが返されると、setBody でエディタにそのコードが設定され、setOutput で出力エリアがクリアされます。

この機能は、Go言語の様々な機能やイディオムをユーザーが手軽に試せるようにすることで、学習体験を向上させます。

5. 不要なコードの削除とリファクタリング

以前の toysEl に関連するコードブロックが削除され、新しい実装に置き換えられています。また、console.log(xhr.status); のようなデバッグ用のコードも削除され、プロダクション環境に適したクリーンなコードベースになっています。

これらの変更は、Go Playgroundのフロントエンドロジックをより堅牢で、ユーザーフレンドリーで、拡張性のあるものにすることを目的としています。

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

1. playground 関数のオプションに fmtEl を追加

@@ -6,13 +6,14 @@
 // 	codeEl - code editor element 
 // 	outputEl - program output element
 // 	runEl - run button element
+// 	fmtEl - fmt button element (optional)
 // 	shareEl - share button element (optional)
 // 	shareURLEl - share URL text input element (optional)
 // 	shareRedirect - base URL to redirect to on share (optional)
-// 	preCompile - callback to mutate request data before compiling
-// 	postCompile - callback to read response data after compiling
-//      simple - use plain textarea instead of CodeMirror.\n-//      toysEl - select element with a list of toys.\n+// 	preCompile - callback to mutate request data before compiling (optional)
+// 	postCompile - callback to read response data after compiling (optional)
+// 	simple - use plain textarea instead of CodeMirror. (optional)
+// 	toysEl - select element with a list of toys. (optional)
 function playground(opts) {
  	var simple = opts['simple'];
  	var code = $(opts['codeEl']);

2. エラー正規表現の修正

@@ -97,7 +98,7 @@ function playground(opts) {
  		if (!editor) {
  			return;
  		}
-\t\tvar errorRe = /[a-z]+\.go:([0-9]+): /g;
+\t\tvar errorRe = /[a-z]+\.go:([0-9]+):/g;
  		var result;
  		while ((result = errorRe.exec(text)) != null) {
  			var line = result[1]*1-1;

3. loading() および setOutput() 関数の追加

@@ -120,13 +121,23 @@ function playground(opts) {
  	function origin(href) {
  		return (""+href).split("/").slice(0, 3).join("/");
  	}
+\tfunction loading() {
+\t\toutput.removeClass("error").html(
+\t\t\t'<div class="loading">Waiting for remote server...</div>'
+\t\t);
+\t}
+\tfunction setOutput(text, error) {
+\t\toutput.empty();
+\t\tif (error) {
+\t\t\toutput.addClass("error");
+\t\t}
+\t\t$("<pre/>").text(text).appendTo(output);
+\t}

4. run() 関数内での loading()setOutput() の利用

@@ -134,8 +145,7 @@ function playground(opts) {
  	var seq = 0;
  	function run() {
  		clearErrors();
-\t\toutput.removeClass("error").html(
-\t\t\t'<div class="loading">Waiting for remote server...</div>'
-\t\t);
+\t\tloading();
  		seq++;
  		var cur = seq;
  		var data = {"body": body()};
@@ -149,8 +159,6 @@ function playground(opts) {
  			\tif (seq != cur) {
  			\t\treturn;
  			\t}
-\t\t\t\tpre = $("<pre/>");
-\t\t\t\toutput.empty().append(pre);
  			\tif (opts['postCompile']) {
  			\t\topts['postCompile'](data);
  			\t}
@@ -158,8 +166,7 @@ function playground(opts) {
  			\t\treturn;
  			\t}
  			\tif (data.compile_errors != "") {
-\t\t\t\t\tpre.text(data.compile_errors);
-\t\t\t\t\toutput.addClass("error");
+\t\t\t\t\tsetOutput(data.compile_errors, true);
  			\t\thighlightErrors(data.compile_errors);
  			\t\treturn;
  			\t}
@@ -172,11 +179,10 @@ function playground(opts) {
  			\t\toutput.empty().append(img);
  			\t\treturn;
  			\t}
-\t\t\t\tpre.text(out);
+\t\t\t\tsetOutput(out, false);
  			\t},
  			\terror: function(xhr) {
  			\t\tvar text = "Error communicating with remote server.";
-\t\t\t\t\tconsole.log(xhr.status);\n \t\t\t\tif (xhr.status == 501) {
  			\t\ttext = xhr.responseText;
  			\t}

5. fmtEltoysEl のイベントハンドラ追加

@@ -178,6 +185,41 @@ function playground(opts) {
  	}\n \t$(opts['runEl']).click(run);\n \n+\t$(opts['fmtEl']).click(function() {
+\t\tloading();
+\t\t$.ajax("/fmt", {
+\t\t\tdata: {"body": body()},
+\t\t\ttype: "POST",
+\t\t\tdataType: "json",
+\t\t\tsuccess: function(data) {
+\t\t\t\tif (data.Error) {
+\t\t\t\t\tsetOutput(data.Error, true);
+\t\t\t\t\thighlightErrors(data.Error);
+\t\t\t\t\treturn;
+\t\t\t\t}
+\t\t\t\tsetBody(data.Body);
+\t\t\t\tsetOutput("", false);
+\t\t\t}
+\t\t});
+\t});
+\n+\t$(opts['toysEl']).bind('change', function() {
+\t\tvar toy = $(this).val();
+\t\tloading();
+\t\t$.ajax("/doc/play/"+toy, {
+\t\t\tprocessData: false,
+\t\t\ttype: "GET",
+\t\t\tcomplete: function(xhr) {
+\t\t\t\tif (xhr.status != 200) {
+\t\t\t\t\tsetOutput("Server error; try again.", true);
+\t\t\t\t\treturn;
+\t\t\t\t}
+\t\t\t\tsetBody(xhr.responseText);
+\t\t\t\tsetOutput("", false);
+\t\t\t}
+\t\t});
+\t});
+\n \tif (opts['shareEl'] != null && (opts['shareURLEl'] != null || opts['shareRedirect'] != null)) {
 \t\tvar shareURL;
 \t\tif (opts['shareURLEl']) {
@@ -213,22 +255,5 @@ function playground(opts) {
  \t\t});
  \t}
  \n-\tif (opts['toysEl'] != null) {
-\t\t$(opts['toysEl']).bind('change', function() {
-\t\t\tvar toy = $(this).val();
-\t\t\t$.ajax("/doc/play/"+toy, {
-\t\t\t\tprocessData: false,
-\t\t\t\ttype: "GET",
-\t\t\t\tcomplete: function(xhr) {
-\t\t\t\t\tif (xhr.status != 200) {
-\t\t\t\t\t\talert("Server error; try again.")
-\t\t\t\t\t\treturn;
-\t\t\t\t\t}\n-\t\t\t\t\tsetBody(xhr.responseText);
-\t\t\t\t}\n-\t\t\t});
-\t\t});
-\t}\n-\n \treturn editor;\n }\n```

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

### 1. `playground` 関数のオプション変更

`playground` 関数は、Go Playgroundのインスタンスを初期化するための設定オブジェクト `opts` を引数として取ります。このコミットでは、`opts` に新しいオプション `fmtEl` が追加されました。これは、コードフォーマットボタンのDOM要素を指します。また、既存のオプションの説明もより詳細になり、`optional` の記述が追加されています。これにより、`playground` 関数の柔軟性が向上し、フォーマット機能がGo Playgroundに統合される基盤が作られました。

### 2. エラー正規表現の修正

`var errorRe = /[a-z]+\.go:([0-9]+):/g;` の変更は、コンパイルエラーメッセージから行番号を正確に抽出するためのものです。以前の正規表現 `/[a-z]+\.go:([0-9]+): /g` は行番号の後にスペースを要求していましたが、Goコンパイラが出力するエラーメッセージの形式が変更されたか、より厳密なマッチングが必要になったため、このスペースが削除されました。これにより、Go Playgroundがエラーメッセージ内の行番号を正しく解析し、エディタ内でエラー箇所をハイライトする精度が向上します。

### 3. `loading()` および `setOutput()` 関数の追加

*   **`loading()` 関数**:
    ```javascript
    function loading() {
        output.removeClass("error").html(
            '<div class="loading">Waiting for remote server...</div>'
        );
    }
    ```
    この関数は、`output` 要素(Go Playgroundの出力表示エリア)から `error` クラスを削除し、"Waiting for remote server..." というメッセージを含む `div` 要素を挿入します。これは、コードの実行やフォーマット処理が開始された際に、ユーザーに視覚的なフィードバックを提供し、処理が進行中であることを示すために使用されます。

*   **`setOutput(text, error)` 関数**:
    ```javascript
    function setOutput(text, error) {
        output.empty();
        if (error) {
            output.addClass("error");
        }
        $("<pre/>").text(text).appendTo(output);
    }
    ```
    この関数は、Go Playgroundの出力エリアにテキストを表示するための汎用的なユーティリティです。まず `output` 要素の内容をクリアし、`error` 引数が `true` の場合は `error` クラスを追加してエラー表示を強調します。その後、与えられた `text` を `<pre>` タグで囲んで `output` 要素に追加します。これにより、出力表示のロジックが一元化され、コードの重複が排除され、エラー表示の一貫性が保たれます。

### 4. `run()` 関数内での `loading()` と `setOutput()` の利用

`run()` 関数は、ユーザーが「Run」ボタンをクリックしたときにGoコードを実行する主要なロジックを含んでいます。このコミットでは、以前直接記述されていた出力表示のロジックが、新しく定義された `loading()` と `setOutput()` 関数に置き換えられました。

*   `output.removeClass("error").html(...)` の代わりに `loading();` が呼び出されるようになりました。
*   コンパイルエラーが発生した場合の `pre.text(data.compile_errors); output.addClass("error");` の代わりに `setOutput(data.compile_errors, true);` が呼び出されるようになりました。
*   正常な出力の場合の `pre.text(out);` の代わりに `setOutput(out, false);` が呼び出されるようになりました。

これにより、`run()` 関数のコードがより簡潔になり、出力表示のロジックが他の機能(フォーマット、サンプルコード読み込み)と共通化されました。

### 5. `fmtEl` と `toysEl` のイベントハンドラ追加

*   **`$(opts['fmtEl']).click(function() { ... });`**:
    このコードブロックは、`fmtEl` オプションで指定された要素(フォーマットボタン)がクリックされたときに実行される処理を定義しています。
    1.  `loading()` を呼び出し、処理中であることを表示します。
    2.  `$.ajax("/fmt", ...)` を使用して、現在のエディタの内容 (`body()`) を `/fmt` エンドポイントにPOSTリクエストとして送信します。
    3.  サーバーからの応答(JSON形式)を受け取り、`data.Error` が存在すればエラーとして表示し、`data.Body` に整形されたコードがあれば `setBody(data.Body)` でエディタの内容を更新します。
    4.  最後に `setOutput("", false)` で出力エリアをクリアします。
    これにより、Go Playgroundに `go fmt` の機能が統合され、ユーザーは簡単にコードを整形できるようになりました。

*   **`$(opts['toysEl']).bind('change', function() { ... });`**:
    このコードブロックは、`toysEl` オプションで指定された要素(サンプルコード選択ドロップダウン)の選択が変更されたときに実行される処理を定義しています。
    1.  `var toy = $(this).val();` で選択されたサンプルコードのファイル名を取得します。
    2.  `loading()` を呼び出し、処理中であることを表示します。
    3.  `$.ajax("/doc/play/"+toy, ...)` を使用して、選択されたサンプルコードのファイル(例: `/doc/play/hello.go`)をGETリクエストで取得します。
    4.  サーバーからの応答 (`xhr.responseText`) を受け取り、`setBody(xhr.responseText)` でエディタの内容をそのサンプルコードに更新します。
    5.  最後に `setOutput("", false)` で出力エリアをクリアします。
    これにより、ユーザーはGo Playgroundで様々なサンプルコードを簡単に読み込み、試すことができるようになりました。

これらの変更は、Go Playgroundの機能性を大幅に拡張し、ユーザーエクスペリエンスを向上させるための重要なステップです。

## 関連リンク

*   Go Playground: [https://go.dev/play/](https://go.dev/play/)
*   `go fmt` のドキュメント: [https://go.dev/blog/go-fmt](https://go.dev/blog/go-fmt)
*   jQuery 公式サイト: [https://jquery.com/](https://jquery.com/)

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

*   Go Playground の仕組み (The Go Playground): [https://go.dev/blog/playground](https://go.dev/blog/playground)
*   Go Playground のソースコード (GitHub): [https://github.com/golang/go/tree/master/doc/play](https://github.com/golang/go/tree/master/doc/play)
*   Ajax と jQuery.ajax(): [https://api.jquery.com/jquery.ajax/](https://api.jquery.com/jquery.ajax/)
*   正規表現 (MDN Web Docs): [https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_expressions](https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Regular_expressions)
*   Go言語の公式ブログ: [https://go.dev/blog/](https://go.dev/blog/)
*   Go言語のドキュメント: [https://go.dev/doc/](https://go.dev/doc/)