Hatena::ブログ(Diary)

sawatの日記 RSSフィード

2006-11-16 (木)

[][]Rubyリファレンスマニュアルにメソッドの概要を追加する 00:23 Rubyリファレンスマニュアルにメソッドの概要を追加するを含むブックマーク

no titleRubyリファレンスマニュアル]を見ていて思ったのですが、Rubyってひとつクラス/モジュールあたりのメソッド数がすごく多いですよね。そう。すごくヒューメイン

それはいいのだけど、逆にリファレンスマニュアルから自分が使いたいメソッドを見つけるが大変。Arrayなんか全画面表示20ページ分ぐらいあるから、その中から目的にあったメソッドを探し出すのはかなりきつい。

Javadocなら、頭のほうに「メソッドの概要」として「メソッドのシグネチャ+ワンセンテンス説明」がまとまっているのでそこを見れば必要なメソッドがすぐ見つけられるんだけど…。

と言うわけで今回もGreasemonkeyRubyリファレンスマニュアルにjavadoc風なメソッドの概要を追加します。

以下のスクリプトをインストール

http://sawat.jf.land.to/gm/AddSummary2RubyMan.user.js

スクリーンショットとか

こんな感じ。

f:id:sawat:20061117001626p:image

PStoreの場合

脚注の下で、ページフッターの上という中途半端な場所にメソッドの概要が追加されます。ホントはJavadoc風に普通のメソッドの説明の上に追加したかったのですが、そうすると他のページからメソッドの説明へのリンクに飛んできた場合、追加される概要テーブル分表示位置がずれてしまうので仕方なしに下にいれることにしました。概要が見たいときはEndキーを押してください。

見ての通り、テーブルの左側がメソッド名。もちろん、クリックすれば詳細説明へジャンプします。そして、右側がワンセンテンス説明。もともとあるメソッド説明文のうち、最初の「。」までを抽出します。うまく抽出できていないこともあり。

対象ページ

初期状態では、http://www.ruby-lang.org/ja/man/index.cgi*が対象になっています。ローカルにダウンロードしたリファレンスを使用している場合は、そのURLを@includeに追加してください。

あと、リファレンスページのURLの都合上、クラスやモジュール以外のページでも実行されてしまいます。Ruby用語集とか。

動作環境とか

Firefox2.0+Greasemonkey0.6.6 on WindowsXP で確認。WinIE6.0+Trixieでも、少し挙動が違うけどおおむね大丈夫です。

ソース

非常に雑なつくりですがこんな感じです。

// ==UserScript==
// @name          AddSummary2RubyMan
// @namespace     http://d.hatena.ne.jp/sawat/
// @description   Add summary tables of methods to the ruby reference manual. 
// @include       http://www.ruby-lang.org/ja/man/index.cgi*
// ==/UserScript==
(function () {
if(window.RUBY_MAN_METHOD_LIST) {
  return;
}
window.RUBY_MAN_METHOD_LIST = true;
 
function summaryMethods(dl) {
  var children = dl.childNodes;
  
  if(children === undefined) {
    return;
  }
  var title = getTitle(dl);
 
  if(title === null) {
     return;
  }
  
  var methods = new Array();
  var currentMethod = {name:'', text:''};
  for(var i=0,n=children.length;i<n;i++) {
    var el = children[i];
    if(el.tagName == 'DT') {
      var href = '';
      var methodName = '';
      for(var ch=el.childNodes,j=0,m=ch.length;j<m;j++) {
        if(ch[j].tagName=="A") {
          href = '#' + ch[j].id;
          methodName = ch[j].innerHTML;
        }
        else {
          if(ch[j].innerHTML) {
            methodName = ch[j].innerHTML;
            break;
          }
        }
      }
      if(methodName == '' || href == '') continue;
      methodName = methodName.replace(/\(\(.*\)\)/,'');
      currentMethod.name += '<a href="' + href + '">' + methodName + '</a><br/>';
    } else if(el.tagName == 'DD') {
      for(var ch=el.childNodes,j=0,m=ch.length;j<m;j++) {
        if(el.childNodes[j].tagName == 'P') {
          currentMethod.text = el.childNodes[j].innerHTML;
          if(currentMethod.text.match(/<a.*>ruby .* feature<\/a>/i)) {
            continue;
          }
          
          var idx = currentMethod.text.indexOf(String.fromCharCode(0x3002));
          if(idx > 0) {
            currentMethod.text = currentMethod.text.substring(0, idx+1);
          }
          if(currentMethod.name !== '') {
            methods.push(currentMethod);
          }
          currentMethod = {name:'', text:''};
          break;
        }
      }
    }
  }
  if(methods.length != 0) {
    var table = createSummaryTable(methods);
    var parent = dl.parentNode;
    parent.appendChild(document.createElement("h2")).innerHTML = title;
    parent.appendChild(table);
  }
}
function createSummaryTable(methods) {
  var table = document.createElement("table");
  table.border = 1;
  table.style.fontSize = "small";
  var tbody = table.appendChild(document.createElement("tbody"));
  
  for(var i=0,n=methods.length;i<n;i++) {
    var tr = tbody.appendChild(document.createElement("tr"));
    tr.appendChild(document.createElement("td")).innerHTML = methods[i].name;
    tr.appendChild(document.createElement("td")).innerHTML = methods[i].text;
  }
  return table;
}
function getTitle(dl) {
  var prev = dl.previousSibling;
  if(prev === undefined) {
    return null;
  }
  if(prev.tagName != 'H2') {
    prev = prev.previousSibling;
  }
  
  if(!prev || prev.tagName != 'H2') {
    return null;
  }
  return prev.innerHTML.replace(":","")+String.fromCharCode(0x306e,0x6982,0x8981);
}
var dls = document.getElementsByTagName("dl");
for(var i=0,n=dls.length;i<n;i++) {
  summaryMethods(dls[i]);
}
})();

その他

Greasemonkeyのuser.jsでスクリプトのcharsetを指定することってできないの?「。」とか「の概要」とかをそのままソースに埋め込むと文字化けしちゃう。String.fromCharCodeを使って回避したけど、もっといい方法は無いのかな?

トラックバック - http://d.hatena.ne.jp/sawat/20061116/1163690584