Gaucheのinfo関数の表示を日本語にするには

gauche.interactiveモジュールのinfo関数を使うとマニュアルを読むことができる。ただし英語で。

gosh> (info 'info)
9.8 `gauche.interactive' - Utilities for interactive session
============================================================

 -- Module: gauche.interactive
     Provides useful utilities for the interactive session.

     This module is automatically loaded when you run `gosh'
     interactively.

     This module also sets autoloads for functions defined in
     `gauche.reload' module (see *note Reloading modules::), so that
     those functions can be used by default in interactive development.

 -- Macro: apropos pattern &optional module
     Show a list of defined variables whose name matches PATTERN.  If
     you give a module or a module name MODULE, only the variables
     defined in that module are listed.  Without MODULE, the variables
     "visible" from the current module are listed.

でもちょっとライブラリのプログラムをいじれば、日本語のマニュアルを表示するようにできる。

gosh> (info 'info)                                                              
9.8 `gauche.interactive' - インタラクティブセッション
=====================================================

 -- Module: gauche.interactive
     インタラクティブセッションで便利なユーティリティ手続きを提供します。

     `gosh'をインタラクティブモードで起動した場合、
     このモジュールは自動的にロードされます。

     また、このモジュールは`gauche.reload' (*note
     モジュールの再ロード::参照)で定義される
     手続きに対してautoloadを設定し、それらの手続きが
     インタラクティブな開発時にデフォルトで使えるようにします。

 -- Macro: apropos pattern &optional module
     名前がPATTERNにマッチするような定義された変数のリストを表示します。
     MODULEにモジュールオブジェクトまたはモジュール名を与えた場合は、
     そのモジュール内で定義されている変数のみが表示されます。MODULEが
     省略された場合は、カレントモジュールから「見える」変数が全て表示されます。

     PATTERNはシンボルか正規表現オブジェクトでなければなりません。
     シンボルの場合、そのシンボル名を部分文字列として名前に含むような変数が
     リストされます。正規表現オブジェクトの場合は、その正規表現にマッチする
     名前を持つ変数がリストされます。

     いくつか例を示します。
変更場所

いじる必要があるファイルは

  • lib/text/info.scm
  • lib/gauche/interactive/info.scm

の二つ。text.infoモジュールはinfo形式のファイルを読み込むためもので、gauche.interactive.infoモジュールがinfo関数を定義している。
まずlib/text/info.scmで、infoファイルを読み込む部分でエンコーディングを指定する。

           (with-input-from-file file thunk))
           (with-input-from-process #`",gunzip -c ,file" thunk))

           (with-input-from-file file thunk :encoding "*jp"))
           (with-input-from-process #`",gunzip -c ,file" thunk :encoding "*jp"))

にする。マニュアルのエンコーディングGaucheの内部エンコーディングが同じなら変更しなくても動作するけど一応。
あとは、lib/gauche/interactive/info.scmを変更する。ひとつは、

(define *info-file* "gauche-refe.info")

(define *info-file* "gauche-refj.info")

に変える。
もうひとつは、info関数の定義に出てくる文字列リテラル「"Function and Syntax Index"」を「"Index - 手続きと構文索引"」に変えて、さらに適切にエンコードを指定する。あるいは「"Function and Syntax Index"」を

(list->string (map ucs->char
  '(73 110 100 101 120 32 45 32 25163 32154 12365 12392 27083 25991 32034 24341)))

に置き換えてもよい。

おまけの改変

指定した関数が見つからなかった場合にaproposっぽい挙動をするように変更してみる。

gosh> (info 'inf)
open-inflating-port            (zlib圧縮ライブラリ)
inflate-sync                   (zlib圧縮ライブラリ)
inflate-string                 (zlib圧縮ライブラリ)
sys-getaddrinfo                (Netdbインタフェース)
sys-set-console-cursor-info    (WindowsコンソールAPI)
info                           (インタラクティブセッション)
debug-source-info              (デバッグ補助)
sys-get-console-cursor-info    (WindowsコンソールAPI)
sys-get-console-screen-buffer-info (WindowsコンソールAPI)
sys-set-console-window-info    (WindowsコンソールAPI)
gosh> (apropos 'inf)
debug-source-info              (gauche)
infinite?                      (gauche)
info                           (gauche.interactive)
procedure-info                 (gauche)

プログラムの変更場所: info関数の定義の

  (let1 nodename (hash-table-get *info-index* (x->string fn) #f)
    (unless nodename (errorf "no info document for ~a" fn))
    (viewer (ref (info-get-node *info* nodename) 'content)))

の部分を

  (let1 nodename (hash-table-get *info-index* (x->string fn) #f)
    (if nodename
        (viewer (ref (info-get-node *info* nodename) 'content))
        (let1 entries (remove not
                        (hash-table-map *info-index*
                          (lambda (name node-name)
                            (and (string-scan name (x->string fn))
                                 (format #f "~30a (~a)\n" name node-name)))))
          (if (pair? entries)
              (for-each display entries)
              (errorf "no info document for ~a" fn)))))

に変える(追記: aproposの挙動に合わせるなら、entriesをソートしたほうがよかった)。ちょうど一致するものがあっても一覧を見たい、って場合には対応していないので使い勝手はあまりよくないかも。

diff -ruN orig/lib/gauche/interactive/info.scm lib/gauche/interactive/info.scm
--- orig/lib/gauche/interactive/info.scm	2009-04-02 21:14:08.000000000 +0900
+++ lib/gauche/interactive/info.scm	2010-03-26 22:02:37.000000000 +0900
@@ -44,7 +44,7 @@
   )
 (select-module gauche.interactive.info)
 
-(define *info-file* "gauche-refe.info")
+(define *info-file* "gauche-refj.info")
 (define *info* #f)
 (define *info-index* (make-hash-table 'string=?))
 
@@ -95,10 +95,19 @@
     (for-each (lambda (p)
                 (hash-table-put! *info-index* (car p) (cdr p)))
               (info-parse-menu (info-get-node *info*
-                                              "Function and Syntax Index"))))
+                                 (list->string (map ucs->char
+                                   '(73 110 100 101 120 32 45 32 25163 32154 12365 12392 27083 25991 32034 24341)))))))
   (let1 nodename (hash-table-get *info-index* (x->string fn) #f)
-    (unless nodename (errorf "no info document for ~a" fn))
-    (viewer (ref (info-get-node *info* nodename) 'content)))
+    (if nodename
+        (viewer (ref (info-get-node *info* nodename) 'content))
+        (let1 entries (remove not
+                        (hash-table-map *info-index*
+                          (lambda (name node-name)
+                            (and (string-scan name (x->string fn))
+                                 (format #f "~30a (~a)\n" name node-name)))))
+          (if (pair? entries)
+              (for-each display entries)
+              (errorf "no info document for ~a" fn)))))
   (values))
 
 (provide "gauche/interactive/info")
diff -ruN orig/lib/text/info.scm lib/text/info.scm
--- orig/lib/text/info.scm	2009-04-02 21:14:08.000000000 +0900
+++ lib/text/info.scm	2010-03-26 21:58:28.000000000 +0900
@@ -70,9 +70,9 @@
 (define (read-info-file-split file opts)
   (define (with-input-from-info thunk)
     (cond ((file-exists? file)
-           (with-input-from-file file thunk))
+           (with-input-from-file file thunk :encoding "*jp"))
           ((file-exists? #`",|file|.gz")
-           (with-input-from-process #`",gunzip -c ,file" thunk))
+           (with-input-from-process #`",gunzip -c ,file" thunk :encoding "*jp"))
           (else
            (error "can't find info file" file))))
   (with-input-from-info