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

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

このコミットは、Go言語の公式リポジトリからdoc/godocs.jsdoc/play/playground.jsの2つのJavaScriptファイルを削除するものです。これらのファイルは、Goのドキュメントサイトにおける検索機能、目次生成、折りたたみ可能なセクションの表示、Go Playgroundの機能(コードの実行、フォーマット、共有)などを提供していました。

コミット

commit 461f82526a0535e6a63a5c87aec50ab6bc558fe5
Author: Andrew Gerrand <adg@golang.org>
Date:   Thu Aug 1 15:09:18 2013 +1000

    doc: remove godocs.js and playground.js from core repo
    
    These are moved to code.google.com/p/go.tools/cmd/godoc.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/12220043

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

https://github.com/golang/go/commit/461f82526a0535e6a63a5c87aec50ab6bc558fe5

元コミット内容

doc: remove godocs.js and playground.js from core repo

These are moved to code.google.com/p/go.tools/cmd/godoc.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/12220043

変更の背景

このコミットの背景には、Goプロジェクトにおけるツール群の整理とモジュール化の推進があります。以前はGoのコアリポジトリ内に、ドキュメント表示やコード実行環境(Go Playground)に関連するJavaScriptファイルが含まれていました。しかし、これらの機能はGoの言語本体というよりも、Goのツールエコシステムの一部と見なされるようになりました。

具体的には、godocs.jsはGoのドキュメントサイト(godocコマンドによって生成されるもの)のクライアントサイドのインタラクティブ機能を提供していました。一方、playground.jsは、Go Playgroundのウェブインターフェースにおけるコードの実行、フォーマット、共有といった主要なロジックを担っていました。

これらのファイルをコアリポジトリから分離し、code.google.com/p/go.tools/cmd/godoc(現在のgolang.org/x/tools/cmd/godocに相当)という独立したツールリポジトリに移動することで、以下の目的が達成されます。

  1. 関心の分離: Go言語のコア開発と、その周辺ツール開発の責務を明確に分離します。これにより、コアリポジトリは言語本体と標準ライブラリに集中でき、ツールの開発はより独立して進められるようになります。
  2. 依存関係の整理: コアリポジトリの肥大化を防ぎ、依存関係をシンプルに保ちます。
  3. ツールの再利用性向上: godocコマンドがこれらのJavaScriptファイルを直接管理することで、ドキュメント生成とPlayground機能がより密接に連携し、他のプロジェクトでもgodocツールを介してこれらの機能を利用しやすくなります。
  4. 開発体制の効率化: ツール関連の変更がコアリポジトリのリリースサイクルに縛られず、より迅速にデプロイできるようになります。

この変更は、Goプロジェクトが成熟し、そのエコシステムが拡大していく過程で、より持続可能で効率的な開発体制を構築するための一環と言えます。

前提知識の解説

このコミットを理解するためには、以下の概念について知っておく必要があります。

  1. Go言語のドキュメンテーションシステム (godoc):

    • Go言語は、ソースコード内のコメントから自動的にドキュメントを生成するgodocというツールを提供しています。
    • 開発者はGoのコードに特定の形式でコメントを記述することで、APIドキュメントやパッケージの概要を生成できます。
    • godocコマンドは、これらのドキュメントをウェブサーバーとして提供する機能も持っており、Goの公式ドキュメントサイト(pkg.go.devなど)の基盤となっています。
    • godocs.jsは、このgodocによって生成されたHTMLページ上で、検索ボックスの挙動、目次の動的な生成、コード例の折りたたみ表示など、ユーザーインタラクションを強化するためのJavaScriptコードでした。
  2. Go Playground:

    • Go Playgroundは、ウェブブラウザ上でGoのコードを記述し、コンパイル・実行できるオンライン環境です。
    • ユーザーはGoのコードを試したり、他のユーザーとコードを共有したりすることができます。
    • Go Playgroundのバックエンドは、安全なサンドボックス環境でGoコードを実行し、その結果をフロントエンドに返します。
    • playground.jsは、このGo Playgroundのフロントエンド部分、特にユーザーが入力したコードをバックエンドに送信し、実行結果を受け取って表示するロジック、コードのフォーマット機能、共有機能などを実装していました。
  3. Goのツールリポジトリ (golang.org/x/tools):

    • Goプロジェクトでは、言語のコア部分(コンパイラ、ランタイム、標準ライブラリ)とは別に、様々な開発ツール(リンター、デバッガー、コード分析ツールなど)をgolang.org/x/toolsのような独立したリポジトリで管理しています。
    • これにより、コア言語の安定性を保ちつつ、ツールの開発をより柔軟に進めることができます。
    • cmd/godocは、このgolang.org/x/toolsリポジトリに含まれるgodocコマンドの実装です。
  4. JavaScriptとDOM操作:

    • godocs.jsplayground.jsはどちらもJavaScriptで書かれており、ウェブページのHTML要素(DOM)を操作して動的な機能を提供していました。
    • jQueryライブラリが使用されており、要素の選択、イベントハンドリング、AJAXリクエストなどが簡潔に記述されています。

技術的詳細

このコミットは、単にファイルを移動するだけでなく、Goプロジェクトのアーキテクチャにおける重要な変更を示唆しています。

godocs.jsの機能と移動の意義: godocs.jsは、Goのドキュメントサイトのユーザビリティを向上させるためのクライアントサイドスクリプトでした。主な機能は以下の通りです。

  • 検索ボックスのイベントバインディング: 検索入力フィールドのプレースホルダー表示/非表示を制御し、ユーザーが入力しやすいようにしていました。
  • 目次 (Table of Contents) の生成: ページのh2およびh3タグから動的に目次を生成し、ナビゲーションを容易にしていました。特に、長いドキュメントではこの機能が非常に有用です。
  • 折りたたみ可能なセクション (Toggles): ドキュメント内の特定のセクション(例: コード例)を折りたたんだり展開したりする機能を提供していました。これにより、ページの視認性を高め、必要な情報に素早くアクセスできるようにしていました。
  • リンクとトグルの連携: 目次やその他のリンクから、対応する折りたたみ可能なセクションを自動的に展開する機能を持っていました。
  • Google +1 ボタンの追加: ソーシャルメディア連携のためのGoogle +1ボタンを動的に追加していました。

これらの機能は、godocコマンドが生成するHTMLに組み込まれることで、Goの公式ドキュメントサイトのインタラクティブな体験を支えていました。このファイルをgo.tools/cmd/godocに移動することで、godocコマンド自体がこれらのクライアントサイド機能を直接管理し、提供する形になります。これは、godocが単なる静的HTMLジェネレーターではなく、リッチなウェブアプリケーションを提供するツールとしての役割を強化するものです。

playground.jsの機能と移動の意義: playground.jsは、Go Playgroundのウェブインターフェースの心臓部でした。その主要な機能は以下の通りです。

  • トランスポート層の抽象化: コードのコンパイルと実行のための通信メカニズム(HTTPまたはWebSocket)を抽象化していました。
    • HTTPTransport: AJAXリクエストを通じてコードをサーバーに送信し、実行結果をJSON形式で受け取る方式。実行結果はイベントのシーケンスとして(遅延を伴って)再生されることで、実際のプログラムの出力挙動を模倣していました。
    • SocketTransport: WebSocketを通じてリアルタイムにサーバーと通信し、コードの実行と結果のストリーミングを行う方式。
  • PlaygroundOutput: サーバーからの実行結果(標準出力、標準エラー出力、プログラム終了ステータスなど)をウェブページ上の指定された要素に表示するロジック。特に、^L(フォームフィード)文字による画面クリアや、画像出力(IMAGE:プレフィックス付きのBase64エンコードデータ)のハンドリングも含まれていました。
  • コードエディタのインタラクション:
    • 自動インデント: TabキーやEnterキーが押された際に、Goの標準的なインデントルールに従って自動的にインデントを調整する機能。
    • エラーハイライト: コンパイルエラーやランタイムエラーが発生した場合、エラーメッセージに基づいてコード内の該当行をハイライト表示する機能。
  • 実行 (Run) ボタン: ユーザーが入力したGoコードをサーバーに送信し、実行結果を表示するトリガー。
  • フォーマット (Fmt) ボタン: go fmtコマンドと同様に、Goコードを標準的なスタイルに自動整形する機能。
  • 共有 (Share) ボタン: 現在のコードをGo Playgroundのサーバーに保存し、一意のURLを生成して共有する機能。HTML5 History APIを利用して、URLを動的に更新する機能も含まれていました。
  • サンプルコード (Toys) の読み込み: 事前定義されたサンプルコードをドロップダウンから選択して読み込む機能。

これらの機能は、Go Playgroundが単なるコードエディタではなく、インタラクティブな学習・実験環境として機能するために不可欠でした。playground.jsgo.tools/cmd/godocに移動することは、godocコマンドがGo Playgroundの機能も内包し、Goのドキュメントとコード実行環境をシームレスに統合する方向性を示しています。これにより、ドキュメント内のコード例をその場で実行できるような、よりリッチな体験が提供可能になります。

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

このコミットでは、以下の2つのファイルが削除されています。

  1. doc/godocs.js
  2. doc/play/playground.js

変更は純粋な削除であり、ファイルの移動先はコミットメッセージに記載されていますが、このコミット自体では移動先のファイルは含まれていません。

--- a/doc/godocs.js
+++ /dev/null
@@ -1,266 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-/* A little code to ease navigation of these documents.
- *
- * On window load we:
- *  + Bind search box hint placeholder show/hide events (bindSearchEvents)
- *  + Generate a table of contents (generateTOC)
- *  + Bind foldable sections (bindToggles)
- *  + Bind links to foldable sections (bindToggleLinks)
- */
-
-(function() {
-'use strict';
-
-function bindSearchEvents() {
-
-  var search = $('#search');
-  if (search.length === 0) {
-    return; // no search box
-  }
-
-  function clearInactive() {
-    if (search.is('.inactive')) {
-      search.val('');
-      search.removeClass('inactive');
-    }
-  }
-
-  function restoreInactive() {
-    if (search.val() !== '') {
-      return;
-    }
-    search.val(search.attr('placeholder'));
-    search.addClass('inactive');
-  }
-
-  search.on('focus', clearInactive);
-  search.on('blur', restoreInactive);
-
-  restoreInactive();
-}
-
-/* Generates a table of contents: looks for h2 and h3 elements and generates
- * links. "Decorates" the element with id=="nav" with this table of contents.
- */
-function generateTOC() {
-  if ($('#manual-nav').length > 0) {
-    return;
-  }
-
-  var nav = $('#nav');
-  if (nav.length === 0) {
-    return;
-  }
-
-  var toc_items = [];
-  $(nav).nextAll('h2, h3').each(function() {
-    var node = this;
-    if (node.id == '')
-      node.id = 'tmp_' + toc_items.length;
-    var link = $('<a/>').attr('href', '#' + node.id).text($(node).text());
-    var item;
-    if ($(node).is('h2')) {
-      item = $('<dt/>');
-    } else { // h3
-      item = $('<dd/>');
-    }
-    item.append(link);
-    toc_items.push(item);
-  });
-  if (toc_items.length <= 1) {
-    return;
-  }
-
-  var dl1 = $('<dl/>');
-  var dl2 = $('<dl/>');
-
-  var split_index = (toc_items.length / 2) + 1;
-  if (split_index < 8) {
-    split_index = toc_items.length;
-  }
-  for (var i = 0; i < split_index; i++) {
-    dl1.append(toc_items[i]);
-  }
-  for (/* keep using i */; i < toc_items.length; i++) {
-    dl2.append(toc_items[i]);
-  }
-
-  var tocTable = $('<table class="unruled"/>').appendTo(nav);
-  var tocBody = $('<tbody/>').appendTo(tocTable);
-  var tocRow = $('<tr/>').appendTo(tocBody);
-
-  // 1st column
-  $('<td class="first"/>').appendTo(tocRow).append(dl1);
-  // 2nd column
-  $('<td/>').appendTo(tocRow).append(dl2);
-}
-
-function bindToggle(el) {
-  $('.toggleButton', el).click(function() {
-    if ($(el).is('.toggle')) {
-      $(el).addClass('toggleVisible').removeClass('toggle');
-    } else {
-      $(el).addClass('toggle').removeClass('toggleVisible');
-    }
-  });
-}
-function bindToggles(selector) {
-  $(selector).each(function(i, el) {
-    bindToggle(el);
-  });
-}
-
-function bindToggleLink(el, prefix) {
-  $(el).click(function() {
-    var href = $(el).attr('href');
-    var i = href.indexOf('#'+prefix);
-    if (i < 0) {
-      return;
-    }
-    var id = '#' + prefix + href.slice(i+1+prefix.length);
-    if ($(id).is('.toggle')) {
-      $(id).find('.toggleButton').first().click();
-    }
-  });
-}
-function bindToggleLinks(selector, prefix) {
-  $(selector).each(function(i, el) {
-    bindToggleLink(el, prefix);
-  });
-}
-
-function setupDropdownPlayground() {
-  if (!$('#page').is('.wide')) {
-    return; // don't show on front page
-  }
-  var button = $('#playgroundButton');
-  var div = $('#playground');
-  var setup = false;
-  button.toggle(function() {
-    button.addClass('active');
-    div.show();
-    if (setup) {
-      return;
-    }
-    setup = true;
-    playground({
-      'codeEl': $('.code', div),
-      'outputEl': $('.output', div),
-      'runEl': $('.run', div),
-      'fmtEl': $('.fmt', div),
-      'shareEl': $('.share', div),
-      'shareRedirect': 'http://play.golang.org/p/'
-    });
-  },
-  function() {
-    button.removeClass('active');
-    div.hide();
-  });
-  button.show();
-  $('#menu').css('min-width', '+=60');
-}
-
-function setupInlinePlayground() {
-	'use strict';
-	// Set up playground when each element is toggled.
-	$('div.play').each(function (i, el) {
-		// Set up playground for this example.
-		var setup = function() {
-			var code = $('.code', el);
-			playground({
-				'codeEl':   code,
-				'outputEl': $('.output', el),
-				'runEl':    $('.run', el),
-				'fmtEl':    $('.fmt', el),
-				'shareEl':  $('.share', el),
-				'shareRedirect': 'http://play.golang.org/p/'
-			});
-
-			// Make the code textarea resize to fit content.
-			var resize = function() {
-				code.height(0);
-				var h = code[0].scrollHeight;
-				code.height(h+20); // minimize bouncing.
-				code.closest('.input').height(h);
-			};
-			code.on('keydown', resize);
-			code.on('keyup', resize);
-			code.keyup(); // resize now.
-		};
-		
-		// If example already visible, set up playground now.
-		if ($(el).is(':visible')) {
-			setup();
-			return;
-		}
-
-		// Otherwise, set up playground when example is expanded.
-		var built = false;
-		$(el).closest('.toggle').click(function() {
-			// Only set up once.
-			if (!built) {
-				setup();
-				built = true;
-			}
-		});
-	});
-}
-
-// fixFocus tries to put focus to div#page so that keyboard navigation works.
-function fixFocus() {
-  var page = $('div#page');
-  var topbar = $('div#topbar');
-  page.css('outline', 0); // disable outline when focused
-  page.attr('tabindex', -1); // and set tabindex so that it is focusable
-  $(window).resize(function (evt) {
-    // only focus page when the topbar is at fixed position (that is, it's in
-    // front of page, and keyboard event will go to the former by default.)
-    // by focusing page, keyboard event will go to page so that up/down arrow,
-    // space, etc. will work as expected.
-    if (topbar.css('position') == "fixed")
-      page.focus();
-  }).resize();
-}
-
-function toggleHash() {
-    var hash = $(window.location.hash);
-    if (hash.is('.toggle')) {
-      hash.find('.toggleButton').first().click();
-    }
-}
-
-function addPlusButtons() {
-  var po = document.createElement('script');
-  po.type = 'text/javascript';
-  po.async = true;
-  po.src = 'https://apis.google.com/js/plusone.js';
-  var s = document.getElementsByTagName('script')[0];
-  s.parentNode.insertBefore(po, s);
-}
-
-$(document).ready(function() {
-  bindSearchEvents();
-  generateTOC();
-  bindToggles(".toggle");
-  bindToggles(".toggleVisible");
-  bindToggleLinks(".exampleLink", "example_");
-  bindToggleLinks(".overviewLink", "");
-  bindToggleLinks(".examplesLink", "");
-  bindToggleLinks(".indexLink", "");
-  setupDropdownPlayground();
-  setupInlinePlayground();
-  fixFocus();
-  toggleHash();
-  addPlusButtons();
-
-  // godoc.html defines window.initFuncs in the <head> tag, and root.html and
-  // codewalk.js push their on-page-ready functions to the list.\n  // We execute those functions here, to avoid loading jQuery until the page
-  // content is loaded.\n  for (var i = 0; i < window.initFuncs.length; i++) window.initFuncs[i]();
-});
-
-})();
--- a/doc/play/playground.js
+++ /dev/null
@@ -1,428 +0,0 @@
-// Copyright 2012 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.\n\n// This is a copy of present/js/playground.js from the repository at
-// https://code.google.com/p/go.talks\n// Please make changes to that repository.\n\n/*
-In the absence of any formal way to specify interfaces in JavaScript,
-here's a skeleton implementation of a playground transport.
-
-        function Transport() {
-                // Set up any transport state (eg, make a websocket connnection).
-                return {
-                        Run: function(body, output, options) {
-                                // Compile and run the program 'body' with 'options'.
-				// Call the 'output' callback to display program output.
-                                return {
-                                        Kill: function() {
-                                                // Kill the running program.
-                                        }
-                                };
-                        }
-                };
-        }
-
-	// The output callback is called multiple times, and each time it is
-	// passed an object of this form.
-        var write = {
-                Kind: 'string', // 'start', 'stdout', 'stderr', 'end'
-                Body: 'string'  // content of write or end status message
-        }
-
-	// The first call must be of Kind 'start' with no body.
-	// Subsequent calls may be of Kind 'stdout' or 'stderr'
-	// and must have a non-null Body string.
-	// The final call should be of Kind 'end' with an optional
-	// Body string, signifying a failure ("killed", for example).
-
-	// The output callback must be of this form.
-	// See PlaygroundOutput (below) for an implementation.
-        function outputCallback(write) {
-        }
-*/
-
-function HTTPTransport() {
-	'use strict';
-
-	// TODO(adg): support stderr
-
-	function playback(output, events) {
-		var timeout;
-		output({Kind: 'start'});
-		function next() {
-			if (events.length === 0) {
-				output({Kind: 'end'});
-				return;
-			}
-			var e = events.shift();
-			if (e.Delay === 0) {
-				output({Kind: 'stdout', Body: e.Message});
-				next();
-				return;
-			}
-			timeout = setTimeout(function() {
-				output({Kind: 'stdout', Body: e.Message});
-				next();
-			}, e.Delay / 1000000);\n		}
-		next();
-		return {
-			Stop: function() {
-				clearTimeout(timeout);
-			}
-		}
-	}
-
-	function error(output, msg) {
-		output({Kind: 'start'});
-		output({Kind: 'stderr', Body: msg});
-		output({Kind: 'end'});
-	}
-
-	var seq = 0;
-	return {
-		Run: function(body, output, options) {
-			seq++;
-			var cur = seq;
-			var playing;
-			$.ajax('/compile', {
-				type: 'POST',
-				data: {'version': 2, 'body': body},
-				dataType: 'json',
-				success: function(data) {
-					if (seq != cur) return;
-					if (!data) return;
-					if (playing != null) playing.Stop();
-					if (data.Errors) {
-						error(output, data.Errors);
-						return;
-					}
-					playing = playback(output, data.Events);
-				},
-				error: function() {
-					error(output, 'Error communicating with remote server.');
-				}
-			});
-			return {
-				Kill: function() {
-					if (playing != null) playing.Stop();
-					output({Kind: 'end', Body: 'killed'});
-				}
-			};
-		}
-	};
-}
-
-function SocketTransport() {
-	'use strict';
-
-	var id = 0;
-	var outputs = {};
-	var started = {};
-	var websocket = new WebSocket('ws://' + window.location.host + '/socket');
-
-	websocket.onclose = function() {
-		console.log('websocket connection closed');
-	}
-
-	websocket.onmessage = function(e) {
-		var m = JSON.parse(e.data);
-		var output = outputs[m.Id];
-		if (output === null)
-			return;
-		if (!started[m.Id]) {
-			output({Kind: 'start'});
-			started[m.Id] = true;
-		}
-		output({Kind: m.Kind, Body: m.Body});
-	}
-
-	function send(m) {
-		websocket.send(JSON.stringify(m));
-	}
-
-	return {
-		Run: function(body, output, options) {
-			var thisID = id+'';
-			id++;
-			outputs[thisID] = output;
-			send({Id: thisID, Kind: 'run', Body: body, Options: options});
-			return {
-				Kill: function() {
-					send({Id: thisID, Kind: 'kill'});
-				}
-			};
-		}
-	};
-}
-
-function PlaygroundOutput(el) {
-	'use strict';
-
-	return function(write) {
-		if (write.Kind == 'start') {
-			el.innerHTML = '';
-			return;
-		}
-
-		var cl = 'system';
-		if (write.Kind == 'stdout' || write.Kind == 'stderr')
-			cl = write.Kind;
-
-		var m = write.Body;
-		if (write.Kind == 'end') 
-			m = '\nProgram exited' + (m?(': '+m):'.');
-
-		if (m.indexOf('IMAGE:') === 0) {
-			// TODO(adg): buffer all writes before creating image
-			var url = 'data:image/png;base64,' + m.substr(6);
-			var img = document.createElement('img');
-			img.src = url;
-			el.appendChild(img);
-			return;
-		}
-
-		// ^L clears the screen.
-		var s = m.split('\x0c');
-		if (s.length > 1) {
-			el.innerHTML = '';
-			m = s.pop();
-		}
-
-		m = m.replace(/&/g, '&amp;');
-		m = m.replace(/</g, '&lt;');
-		m = m.replace(/>/g, '&gt;');
-
-		var needScroll = (el.scrollTop + el.offsetHeight) == el.scrollHeight;
-
-		var span = document.createElement('span');
-		span.className = cl;
-		span.innerHTML = m;
-		el.appendChild(span);
-
-		if (needScroll)
-			el.scrollTop = el.scrollHeight - el.offsetHeight;
-	}
-}
-
-(function() {
-  function lineHighlight(error) {
-    var regex = /prog.go:([0-9]+)/g;
-    var r = regex.exec(error);
-    while (r) {
-      $(".lines div").eq(r[1]-1).addClass("lineerror");
-      r = regex.exec(error);
-    }
-  }
-  function highlightOutput(wrappedOutput) {
-    return function(write) {
-      if (write.Body) lineHighlight(write.Body);
-      wrappedOutput(write);
-    }
-  }
-  function lineClear() {
-    $(".lineerror").removeClass("lineerror");
-  }
-
-  // opts is an object with these keys
-  //  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)
-  //  toysEl - toys select element (optional)
-  //  enableHistory - enable using HTML5 history API (optional)
-  //  transport - playground transport to use (default is HTTPTransport)
-  function playground(opts) {
-    var code = $(opts.codeEl);
-    var transport = opts['transport'] || new HTTPTransport();
-    var running;
-  
-    // autoindent helpers.
-    function insertTabs(n) {
-      // find the selection start and end
-      var start = code[0].selectionStart;
-      var end   = code[0].selectionEnd;
-      // split the textarea content into two, and insert n tabs
-      var v = code[0].value;
-      var u = v.substr(0, start);
-      for (var i=0; i<n; i++) {
-        u += "\t";
-      }
-      u += v.substr(end);
-      // set revised content
-      code[0].value = u;
-      // reset caret position after inserted tabs
-      code[0].selectionStart = start+n;
-      code[0].selectionEnd = start+n;
-    }
-    function autoindent(el) {
-      var curpos = el.selectionStart;
-      var tabs = 0;
-      while (curpos > 0) {
-        curpos--;
-        if (el.value[curpos] == "\t") {
-          tabs++;
-        } else if (tabs > 0 || el.value[curpos] == "\n") {
-          break;
-        }
-      }
-      setTimeout(function() {
-        insertTabs(tabs);
-      }, 1);
-    }
-  
-    function keyHandler(e) {
-      if (e.keyCode == 9) { // tab
-        insertTabs(1);
-        e.preventDefault();
-        return false;
-      }
-      if (e.keyCode == 13) { // enter
-        if (e.shiftKey) { // +shift
-          run();
-          e.preventDefault();
-          return false;
-        } else {
-          autoindent(e.target);
-        }
-      }
-      return true;
-    }
-    code.unbind('keydown').bind('keydown', keyHandler);
-    var outdiv = $(opts.outputEl).empty();
-    var output = $('<pre/>').appendTo(outdiv);
-  
-    function body() {
-      return $(opts.codeEl).val();
-    }
-    function setBody(text) {
-      $(opts.codeEl).val(text);
-    }
-    function origin(href) {
-      return (""+href).split("/").slice(0, 3).join("/");
-    }
-  
-    var pushedEmpty = (window.location.pathname == "/");
-    function inputChanged() {
-      if (pushedEmpty) {
-        return;
-      }
-      pushedEmpty = true;
-      $(opts.shareURLEl).hide();
-      window.history.pushState(null, "", "/");
-    }
-    function popState(e) {
-      if (e === null) {
-        return;
-      }
-      if (e && e.state && e.state.code) {
-        setBody(e.state.code);
-      }
-    }
-    var rewriteHistory = false;
-    if (window.history && window.history.pushState && window.addEventListener && opts.enableHistory) {
-      rewriteHistory = true;
-      code[0].addEventListener('input', inputChanged);
-      window.addEventListener('popstate', popState);
-    }
-
-    function setError(error) {
-      if (running) running.Kill();
-      lineClear();
-      lineHighlight(error);
-      output.empty().addClass("error").text(error);
-    }
-    function loading() {
-      lineClear();
-      if (running) running.Kill();
-      output.removeClass("error").text('Waiting for remote server...');
-    }
-    function run() {
-      loading();
-      running = transport.Run(body(), highlightOutput(PlaygroundOutput(output[0])));
-    }
-    function fmt() {
-      loading();
-      $.ajax("/fmt", {
-        data: {"body": body()},
-        type: "POST",
-        dataType: "json",
-        success: function(data) {
-          if (data.Error) {
-            setError(data.Error);
-          } else {
-            setBody(data.Body);
-            setError("");
-          }
-        }
-      });
-    }
-
-    $(opts.runEl).click(run);
-    $(opts.fmtEl).click(fmt);
-  
-    if (opts.shareEl !== null && (opts.shareURLEl !== null || opts.shareRedirect !== null)) {
-      var shareURL;
-      if (opts.shareURLEl) {
-        shareURL = $(opts.shareURLEl).hide();
-      }
-      var sharing = false;
-      $(opts.shareEl).click(function() {
-        if (sharing) return;
-        sharing = true;
-        var sharingData = body();
-        $.ajax("/share", {
-          processData: false,
-          data: sharingData,
-          type: "POST",
-          complete: function(xhr) {
-            sharing = false;
-            if (xhr.status != 200) {
-              alert("Server error; try again.");
-              return;
-            }\n            if (opts.shareRedirect) {
-              window.location = opts.shareRedirect + xhr.responseText;
-            }
-            if (shareURL) {
-              var path = "/p/" + xhr.responseText;
-              var url = origin(window.location) + path;
-              shareURL.show().val(url).focus().select();
-  
-              if (rewriteHistory) {
-                var historyData = {"code": sharingData};
-                window.history.pushState(historyData, "", path);
-                pushedEmpty = false;
-              }
-            }
-          }
-        });
-      });
-    }
-  
-    if (opts.toysEl !== null) {
-      $(opts.toysEl).bind('change', function() {
-        var toy = $(this).val();
-        $.ajax("/doc/play/"+toy, {
-          processData: false,
-          type: "GET",
-          complete: function(xhr) {
-            if (xhr.status != 200) {
-              alert("Server error; try again.");
-              return;
-            }
-            setBody(xhr.responseText);
-          }
-        });
-      });
-    }
-  }
-
-  window.playground = playground;
-})();

コアとなるコードの解説

このコミットは、既存のコードを削除するのみであるため、新しいコードの追加や既存コードの修正に関する解説はありません。しかし、削除されたコードがどのような機能を提供していたか、そしてその削除がGoプロジェクトのアーキテクチャにどのような影響を与えるかについて解説します。

doc/godocs.jsの解説: このファイルは、Goのドキュメントサイトのフロントエンドにおけるインタラクティブな挙動を制御するJavaScriptコードでした。主な機能は以下の通りです。

  • bindSearchEvents(): 検索ボックスのプレースホルダーテキストの表示/非表示を切り替えるイベントハンドラを設定します。ユーザーが検索ボックスにフォーカスするとプレースホルダーが消え、フォーカスを外して入力がない場合は再び表示されます。
  • generateTOC(): ページのh2およびh3要素から動的に目次を生成し、#nav要素に追加します。これにより、長いドキュメントでも主要なセクションに素早くジャンプできるようになります。目次が長くなる場合は、2列に分割して表示するロジックも含まれていました。
  • bindToggle() / bindToggles(): .toggleクラスを持つ要素(通常はコード例など)に対して、クリックで内容の表示/非表示を切り替える機能を追加します。これにより、ページの初期表示をすっきりと保ちつつ、必要な時に詳細を確認できます。
  • bindToggleLink() / bindToggleLinks(): 目次やその他のリンクが、対応する折りたたみ可能なセクション(#example_...のようなIDを持つ要素)を自動的に展開するように設定します。
  • setupDropdownPlayground() / setupInlinePlayground(): Go Playgroundの機能をドキュメントページに組み込むための設定を行います。ドロップダウン形式でPlaygroundを表示したり、インラインのコード例をPlaygroundとして機能させたりするロジックが含まれていました。これらはplayground.jsで定義されたplayground関数を呼び出して初期化されます。
  • fixFocus(): ページのリサイズ時に、キーボードナビゲーションが正しく機能するようにdiv#page要素にフォーカスを当てる処理を行います。
  • toggleHash(): URLのハッシュ(例: #section-id)に基づいて、対応する折りたたみ可能なセクションを自動的に展開します。
  • addPlusButtons(): Google +1ボタンをページに追加します。
  • $(document).ready(): ページが完全にロードされた後に、上記の関数を呼び出して各種イベントハンドラの設定やDOM操作を実行します。

このファイルが削除されたことで、これらの機能はGoのコアリポジトリから切り離され、go.tools/cmd/godocの責任範囲となりました。

doc/play/playground.jsの解説: このファイルは、Go Playgroundのウェブインターフェースの主要なロジックを実装していました。

  • HTTPTransport(): HTTP POSTリクエストを使用してGoコードをサーバーに送信し、コンパイルと実行を行います。サーバーからの応答はJSON形式で、実行結果のイベント(標準出力、標準エラー出力、遅延など)の配列を含みます。このトランスポートは、イベントを順番に再生することで、プログラムの実行をシミュレートします。
  • SocketTransport(): WebSocketを使用してサーバーとリアルタイムに通信し、Goコードの実行と結果のストリーミングを行います。これは、よりインタラクティブな体験を提供するために使用されます。
  • PlaygroundOutput(el): サーバーから受け取った実行結果のイベントを、指定されたHTML要素(el)に表示する関数を返します。
    • Kindプロパティ(start, stdout, stderr, end)に基づいて、出力の種類を判別します。
    • ^L(フォームフィード)文字を検出すると、出力エリアをクリアします。
    • IMAGE:で始まるBase64エンコードされた文字列を検出すると、それを画像として表示します。
    • HTMLエンティティのエスケープ処理も行います。
  • lineHighlight(error) / highlightOutput(wrappedOutput) / lineClear(): コンパイルエラーやランタイムエラーが発生した場合に、エラーメッセージから行番号を抽出し、コードエディタの該当行をハイライト表示する機能を提供します。
  • playground(opts): Go Playgroundの主要な機能を初期化する関数です。
    • コードエディタ (codeEl)、出力エリア (outputEl)、実行ボタン (runEl)、フォーマットボタン (fmtEl)、共有ボタン (shareEl) などの要素を受け取ります。
    • キーハンドラ: Tabキーでのインデント挿入、Enterキーでの自動インデント、Shift+Enterでのコード実行など、コードエディタのキーボードショートカットを処理します。
    • run(): コードを実行するロジック。transport(HTTPまたはWebSocket)を使用してコードをサーバーに送信し、結果をPlaygroundOutputで表示します。
    • fmt(): コードをフォーマットするロジック。/fmtエンドポイントにコードを送信し、整形されたコードを受け取ってエディタに反映します。
    • 共有機能: /shareエンドポイントにコードを送信し、一意のURLを受け取って共有URLを表示したり、リダイレクトしたりします。HTML5 History APIを使用して、ブラウザの履歴を操作し、共有URLをアドレスバーに表示します。
    • サンプルコードの読み込み: /doc/play/以下のパスからサンプルコードを読み込み、エディタに表示します。

このファイルが削除されたことで、Go PlaygroundのフロントエンドロジックはGoのコアリポジトリから切り離され、go.tools/cmd/godocの一部として管理されることになりました。これにより、godocコマンドがGo Playgroundの機能も提供できるようになり、Goのドキュメントとインタラクティブなコード実行環境がより密接に統合されることになります。

関連リンク

参考にした情報源リンク

  • コミットメッセージと差分情報: GitHub (上記リンク)
  • Go Playgroundの仕組みに関する一般的な知識
  • godocコマンドの機能に関する一般的な知識
  • JavaScript (jQuery) によるDOM操作とイベントハンドリングの知識
  • HTML5 History APIに関する知識
  • WebSocketとAJAX通信に関する知識
  • Go言語のプロジェクト構造とxリポジトリに関する知識