Hisashi Morita ([info]hisashim) wrote,
@ 2006-10-03 22:26:00
Previous Entry  Add to memories!  Tell a Friend!  Next Entry
[xyzzy][Lisp][LaTeX] latex-insert-index
latex-insert-index
------------------

LaTeXでの索引設定がつらくなったので、xyzzy用にでっちあげた。動く。

Usage
-----
      M-x latex-index-insert ENTER          リージョンを索引項目にする
  C-u M-x latex-index-insert ENTER          最近killした文字列を索引項目にする
      M-x kata-to-hira-region ENTER         リージョン内のカタカナをひらがなに(おまけ)


(defun latex-insert-index (begin end pre &optional sortkey)
  "Insert \\index using region text (or killed text if with prefix arg)
as index term. If term is non-ASCII and sortkey is ommited, term is
copied as sortkey."
  (interactive "r\nP\nsSort Key:")
  (let ((term (if pre (caar *kill-ring*) (buffer-substring begin end))))
    (insert (concat
             "\\index{"
             (if (or (equal sortkey nil) (equal sortkey ""))
                 (if (string-match "\\sj" term) (concat (kata-to-hira-string term) "@") "")
               (concat sortkey "@"))
             term
             "}" )
            )))
(defun kata-to-hira (chr)
  "Convert katakana character to hiragana character if possible."
  (let* ((kata (mapcar 'character
                       (split-string (concat
                                      "ァ ア ィ イ ゥ ウ ェ エ ォ オ "
                                      "カ ガ キ ギ ク グ ケ ゲ コ ゴ "
                                      "サ ザ シ ジ ス ズ セ ゼ ソ ゾ "
                                      "タ ダ チ ヂ ッ ツ ヅ テ デ ト ド "
                                      "ナ ニ ヌ ネ ノ "
                                      "ハ バ パ ヒ ビ ピ フ ブ プ ヘ ベ ペ ホ ボ ポ "
                                      "マ ミ ム メ モ "
                                      "ャ ヤ ュ ユ ョ ヨ "
                                      "ラ リ ル レ ロ ゎ ワ ゐ ゑ ヲ ン") " ")))
         (hira (mapcar 'character
                       (split-string (concat
                                      "ぁ あ ぃ い ぅ う ぇ え ぉ お "
                                      "か が き ぎ く ぐ け げ こ ご "
                                      "さ ざ し じ す ず せ ぜ そ ぞ "
                                      "た だ ち ぢ っ つ づ て で と ど "
                                      "な に ぬ ね の "
                                      "は ば ぱ ひ び ぴ ふ ぶ ぷ へ べ ぺ ほ ぼ ぽ "
                                      "ま み む め も "
                                      "ゃ や ゅ ゆ ょ よ "
                                      "ら り る れ ろ ゎ わ ゐ ゑ を ん") " ")))
         (dic (mapcar (lambda (x y) (list* x y)) kata hira))
         (counterpart (cdr (assoc chr dic :test 'equal))))
    (if counterpart counterpart chr)))
(defun kata-to-hira-string (str)
  "Return a string converting katakana to hiragana."
  (map 'string 'kata-to-hira str))
(defun kata-to-hira-region (begin end)
  "Convert katakana in region to hiragana."
  (interactive "r")
  (let ((src (buffer-substring begin end)))
    (progn
      (delete-region begin end)
      (insert (kata-to-hira-string src)))))


Todo
----

* プロンプトで必要に応じてsortkeyの候補が提示されるとうれしい(ASCIIな語句では不要なことが多いので、[↑]や[TAB]で提示されるとか)。

* 日本語の場合は、読みをsortkeyとして自動的に提示したい(現状ではカタカナをひらがなにするだけで、漢字はそのまま)。

* 読みはカタカナとひらがなから選べるように。

* Emacsでも動くように。とりあえず\sjを\cjにする必要あり。

* メニューの編集-変換-*にあるコマンドの実体はどこに?

雑感
----

* テキストは、新規に書くよりも編集することのほうが多い。だからマークアップ言語編集用のモードは、既存のテキストを後からマークアップしやすいように設計してあるとうれしい(良い例:psgml)。


疑問
----

xyzzyでバッファを使わずにプロセスの出力を受ける方法が分からない。

プロセスを起動する方法は、

* make-processで作ったプロセスにprocess-send-stringで入力を書き込む

* make-temp-file-nameで作ったファイル名を引数にcall-processで外部プロセスを呼ぶ

あたりが目的のものらしいということが分かった。xyzzy付属のfilter.lが参考になる。

でも、バッファを使わずにプロセスの出力を受ける方法が分からない。filter-regionとかは結果をinsert-fileでバッファに挿入してる。

ちょっとしたテキストを外部プロセスで処理した結果を、無駄なバッファを作ったり消したりせずに、stringとして受け取りたいだけなんだけど。

エディタ組み込みのシステムだから、バッファを介してやり取りするのが原則ということなんだろうか。

make-process-input-streamとかmake-process-output-streamとかいう関数があって、それでプロセスをストリーム化できたらいいのになあ。見落としてる?

----------------

2006-10-10 追記

佐野さんのコメントを受けて、とりあえずプロセスのほうだけ改善。MeCabを使えるようになって、かなり快適。文字変換のほうの改善はこれから。

----

直交性の話は、ちょっと文脈と無関係に持ち出してしまったけど、EmacsやxyzzyのようなLispを使ったエディタに対して、ずっと次のような疑問を持っていたため。

例えばこういうふうなインタフェースになってると、テキスト編集の処理を書くのが楽そうな気がする。

* 文字列を受け取って文字列を返すような編集関数を定義
* 高階関数を使って、編集関数をバッファやリージョンに適用(擬似コードだと(replace region (apply 'editing-function region))のような感じ)

文字列の操作と対象への適用が直交しているので、ユーザは適用対象がバッファかどうかなど考えずに編集関数だけ書けばいい。そして編集関数はどんな文字列データに適用してもいいので、独立していて使い回しがきく。
(プロセスの処理の場合も同様で、最終的に出力をバッファに挿入するかどうかなんて考えずに、入力する文字列と出力される文字列のことだけ考えたい)

でも、そうはなっていない。Emacsでもxyzzyでも、正規表現でバッファをなめる関数を使って、マッチ情報を後で取り出して何かしたりとかいうのが定石みたい。

たぶん実行効率のせいなんだろうなと思うけど、普段はあれこれ気持ちいいのに、バッファの操作になると途端に自由度が下がって気持ち良さが減るので、残念。

(余談だけど、UnixのCLIシェル環境で、標準入出力を使ってくれるコマンドはパイプの途中に自由に挟み込めるので気持ちがいい。反対に、入出力にファイルしか受け付けないコマンドは使っていてつらい。データをどこに流すかはシェルで制御するから、自由にさせてよという感じ。それと少し似ているかも)

(Haskellで書かれたEmacsライクなエディタがあったけど、あれはどうなってるんだろう)


;; 2006-10 utility for LaTeX work
(defun latex-insert-index-ja (begin end pre &optional key)
  "Insert \\index{key@term} using region text (or last killed text if
   with prefix arg) as index term. If the term is Japanese and the key is
   ommited, term's reading is copied as key."
  (interactive "r\nP\nsKey:")
  (let ((term (if pre (caar *kill-ring*) (buffer-substring begin end))))
    (insert (concat
             "\\index{"
             (if (or (equal key nil) (equal key ""))
                 (if (string-match "\\sj" term)
                     (concat (mecab-reading-hiragana term) "@")
                   "")
       (concat key "@"))
             term
             "}" )
            )))

(defun kata-to-hira (chr)
  "Convert katakana character to hiragana character if possible."
  (let* ((kata (mapcar 'character
               (split-string (concat
                              "ァ ア ィ イ ゥ ウ ェ エ ォ オ "
                                      "カ ガ キ ギ ク グ ケ ゲ コ ゴ "
                                      "サ ザ シ ジ ス ズ セ ゼ ソ ゾ "
                                      "タ ダ チ ヂ ッ ツ ヅ テ デ ト ド "
                                      "ナ ニ ヌ ネ ノ "
                                      "ハ バ パ ヒ ビ ピ フ ブ プ ヘ ベ ペ ホ ボ ポ "
                                      "マ ミ ム メ モ "
                                      "ャ ヤ ュ ユ ョ ヨ "
                                      "ラ リ ル レ ロ ゎ ワ ゐ ゑ ヲ ン") " ")))
         (hira (mapcar 'character
               (split-string (concat
                              "ぁ あ ぃ い ぅ う ぇ え ぉ お "
                                      "か が き ぎ く ぐ け げ こ ご "
                                      "さ ざ し じ す ず せ ぜ そ ぞ "
                                      "た だ ち ぢ っ つ づ て で と ど "
                                      "な に ぬ ね の "
                                      "は ば ぱ ひ び ぴ ふ ぶ ぷ へ べ ぺ ほ ぼ ぽ "
                                      "ま み む め も "
                                      "ゃ や ゅ ゆ ょ よ "
                                      "ら り る れ ろ ゎ わ ゐ ゑ を ん") " ")))
         (dic (mapcar (lambda (x y) (list* x y)) kata hira))
         (counterpart (cdr (assoc chr dic :test 'equal))))
    (if counterpart counterpart chr)))

(defun kata-to-hira-string (str)
  "Return a string converting katakana to hiragana."
  (map 'string 'kata-to-hira str))

(defun kata-to-hira-region (begin end)
  "Convert katakana in region to hiragana."
  (interactive "r")
  (let ((src (buffer-substring begin end)))
    (progn
      (delete-region begin end)
      (insert (kata-to-hira-string src)))))

;; thanks to snmsts
(defun run-command (cmd &optional input)
  "Run CMD as sub-process with INPUT for stdin. Return stdout as string."
  (let ((buf (create-new-buffer "tmp"))
  proc)
    (save-window-excursion
      (with-set-buffer
        (setup-temp-buffer buf)
        (set-buffer buf)
        (setq proc (make-process cmd))
        (if input (process-send-string proc (concat input (string #\C-z)))
          nil)
        (while (eql :run (process-status proc))
          (do-events))
        (prog1
            (string-right-trim "\r\n" (buffer-substring (point-min) (point-max)))
          (delete-buffer buf))))))

(defun mecab-reading-katakana (str)
  "Acquire reading of Japanese text in katakana using MeCab morphological analyzer."
  (run-command "C:/Program Files/MeCab/bin/mecab.exe -Oyomi" str))

(defun mecab-reading-hiragana (str)
  "Acquire reading of Japanese text in hiragana using MeCab morphological analyzer."
  (kata-to-hira-string (mecab-reading-katakana str)))




(Post a new comment)


(Anonymous)
2006-10-07 05:03 am UTC (link)
佐野です

make-processが基本だと思います。
バッファをいちいち作らないといけないのは私も嫌いなのですが
知覚できなければまぁいいのかなと思っています。

(defun runcmd (cmd)
(let ((buf (create-new-buffer "hoge"))
proc)
(save-window-excursion
(with-set-buffer
(set-buffer buf)
(setq proc (make-process cmd))
(while (eql :run (process-status proc))
(do-events))
(prog1
(buffer-substring (point-min) (point-max))
(delete-buffer buf))))))

(runcmd "cmd /c dir c:")

ついでに
(let ((hiragana (format nil "~C" (code-char 164)))
(katakana (format nil "~C" (code-char 165))))
(defun hira-to-kata (str)
(coerce
(mapcar
(lambda (c)
(let ((str (format nil "~C" c)))
(if (string-match "[ぁ-ん]" str)
(svref (map-euc-to-internal
(concat katakana
(substring (map-internal-to-euc str) 1))) 0)
c)))
(coerce str 'list)) 'string)))

emacs風の文字コード依存変換を。
メニューの変換の中身はCまで落ちないと拾えないと思います。

(Reply to this) (Thread)


[info]hisashim
2006-10-07 02:02 pm UTC (link)
ありがとうございます。やっぱりバッファ経由が普通ですか。
;; 直交性とか言ってるうちは、清濁併せ呑む立派なCommon Lispユーザになれない?

とにかく、教えていただいたやり方を基に試してみます。
「読む側の程度に合った」「飾りのない」「動く」コード例はありがたいです。

(Reply to this) (Parent)


Create an Account
Forgot your login or password?
Login w/ OpenID
English • Español • Deutsch • Русский…