複合語サーバが返した候補には色を付ける

先日の作業で skkserv2 と mecab-skkserv の検索結果にはアノテーションを付けて区別できるようにしてみたのだが、それだけでは自分には不足のようだ。

何度か SPC を押して候補一覧の状態になってしまえばアノテーションは候補の後ろに表示されるのですぐ気付くのだが、それ以前の段階だとアノテーションエコーエリアに表示されるだけ*1なのでうっかり見逃してしまうことがあるようなのだ*2

刺激が足りない。

そこで複合語アノテーションならば更に候補に色を付けてみることにした。`skk-treat-candidate-appearance-function' の雛形をちょっと変えただけだけど、この機能はこういう時に自由が効くのでとても重宝している。

(copy-face 'skk-henkan-face-default 'my-skk-compound-word-face)
(set-face-background 'my-skk-compound-word-face "misty rose")
(setq skk-treat-candidate-appearance-function
      (lambda (candidate listing-p)
	(let* ((value (skk-treat-strip-note-from-word candidate))
	       (cand (car value))
	       (note (cdr value))
	       (sep (if note
			(propertize (if (skk-annotation-display-p 'list)
					" ≒ "
				      " !")
				    'face 'skk-tut-do-it-face)
		      nil)))
	  (cond (note
		 (put-text-property
		  0 (length cand) 'face
		  (if (string-match "<\\(?:skkserv2\\|MeCab\\)>\\'" note)
		      'my-skk-compound-word-face
		    'skk-henkan-face-default)
		  cand)
		 (put-text-property 0 (length note)
				    'face 'skk-tut-hint-face note)
		 (cons cand (cons sep note)))
		(t
		 (put-text-property 0 (length cand)
				    'face 'skk-henkan-face-default cand)
		 cand)))))


さすがにここまでやっときゃ気付くだろう。

*1:そもそも、一覧になる前でもアノテーションをインラインに表示する選択肢があってもいいんじゃなかろうか

*2:念の為、個人辞書から "" と "" というアノテーションgrep して表示するスクリプトを cron に仕込んでたので気付いた

Viper patch 機能追加

久々に Viper の visual-mode patch に機能を追加してみた。

  • `` block と tag block object を追加
  • blockwise visual mode 対応
  • text object に対して A, d, D, I, J, r, R, s, S, u, U, x, X, y, Y, O, ~ operater が使えるように
  • visual mode から ex mode に移行した場合の prompt の default を "'<,'>" に変更
  • text object の細かい bug を修正
    • 行末での Word object の扱いに bug があったのを修正
    • ("f-!-oo") のような所に cursor があった場合の ( ) block object の扱いがマズかったのを修正
    • その他もろもろ

やっつけで作ったものも多いけど、これでだいたいの機能は揃ったのではなかろうか。gv とか作ってないけど使うのかなあ。自分が有効活用できてないので利点がなかなかピンとこない。

visual mode の後の repeat と block の yank は微妙なのでいずれなんとかしたいけど、スマートな解決方法を思い付かないので当面は放置かなあ。

[Debian][Gnus] マルチパートのデフォルトの MIME type を変更する

Debian etch + Emacs snapshot + No Gnus でのお話。

最近 (といってもかなり昔に書いたメモだけど) Gnus でメールに hogehoge.diff なファイルをアタッチしても、そのパートが色付いて表示されないのが気になっていた。以前は diff-mode でハイライトされてカラフルで見易かったのに。特に設定を変えた覚えはないんだけど。Gnus の仕様が変わった? んなこたないよなあ…。

などと放置していたのだが、フと昔のメールを表示した際にちゃんと diff のパートがハイライトされていることに気付いた。あれ?

昔のメールと最近のものをよくよく見比べると、なるほど、前者は MIME type が text/x-patch となっているのに対して後者は text/x-diff になっている。ふーむ、どちらも特に自分で指定した覚えはなく単にデフォルトで提示されるタイプを使っているだけなのでそこいらが変わってるクサい。

ちょっと調べてみますか。ごそごそ。

Gnus はアタッチするファイルの拡張子を見て MIME type を決めるが、それは `mailcap-mime-extensions' で定義されている。mailcap.el を見るとちゃんと

(defvar mailcap-mime-extensions
...
    (".diff"  . "text/x-patch")
...

となっていて Gnus では text/x-patch がデフォルトになっている。だが、C-h v で実際に値を見ると

...
 (".diff" . "text/x-diff")
...
 (".diff" . "text/x-patch")
...

とダブって登録されている。他の拡張子についてもいろいろ追加されているようだ。誰がやってんの?

mailcap.el 自身がやってました。というか `mailcap-mime-extensions' の docstring にちゃんと書いてあった。

It is merged with values from mailcap files by `mailcap-parse-mimetypes'.

mailcap-parse-mimetypes() は ~/.mime.types や /etc/mime.types などなどのファイルを調べて MIME type を追加してくれるらしい。Debian だと mime-support パッケージが入っていれば /etc/mime.types が存在する。覗いてみると

text/x-diff                                     diff patch

確かにこういうエントリがあった。犯人はこいつか。

詳しく調べると mailcap.el と /etc/mime.types で86個の拡張子が重複しているようだ。そのうち MIME type が異なるものが35個。

単に重複してるものについては無駄だから消しちゃって、type が違うものについては mailcap.el のほうを採用しよう。えい。

(defadvice mailcap-parse-mimetypes (around delete-dup-entry activate)
  (let* ((parsed mailcap-mimetypes-parsed-p)
	 (orig (or parsed mailcap-mime-extensions)))
    ad-do-it
    (unless parsed
      (setq mailcap-mime-extensions
	    (let ((list mailcap-mime-extensions)
		  entry ret add)
	      (while (setq entry (car list))
		(unless (assoc (car entry) (cdr list))
		  (add-to-list (if (member entry orig) 'ret 'add) entry))
		(setq list (cdr list)))
	      (nreverse (nconc add ret)))))))

こんなのを ~/.gnus.el に追加して無事 *.diff なファイルについてはデフォルトで text/x-patch を提示してくれるようになった。


これで自分が出すメールについては問題なくなったが、他からは text/x-diff で送られてくる可能性がある。そちらもちゃんと対処したいけど、それはまたいずれ。

hatedara.el アップデート

昨日久々にここを書いたときにシンタックスハイライトが出来るようになってたのを思い出したのでハイライトだとか補完入力だとかできるように hatedara.el を弄ってみる。ついでに以前から放置してたリージョンを囲むタグ挿入も作ってみた。


で、そのときに気付いたんですが、似たようなのがいくつか作られてますね。simple-hatena-mode さんの

同様の機能を提供するelispについては、すでにいくつかのものがありますが、
simple-hatena-modeには、それらと比べて以下の利点があります。

・複数のアカウントに対応。
・それぞれのアカウントのグループ日記に対応。

http://coderepos.org/share/wiki/SimpleHatenaMode

はなるほど、そういったニーズもありますか。自分はろくに更新もしないような人間なんでグループも使ったことないし、ましてや複数アカウントなんて持っていないんで作ったところでテスト出来ないという。でも、まぁマメに書く方には必要でしょうしあって然る可き機能ですね。

それぞれのいいとこどりする形でうまくマージできるといいような気もするんだけどなあ。キーバインドとか好みもあるんだろうけど。

そういう意味では simple-hatena-mode さんが公開リポジトリでやられてるようなのでそちらに突撃するのがよさげな気もするけど、そういう時に限って他にやるべきことが沢山あるという…。

SKK の環境をいろいろと整備してみる

最近、SKK 周りの設定をいろいろと弄ってみたのでそのあたりのメモ。

作業前のサーバ環境は

rskkserv ┬ SKK-JISYO.L
         ├ skkserv2
         └ mecab-skkserv

yaskkserv (サーバコンプリーション用)
bskk (ベイジアン学習サーバ)

となっていて、rskkserv で SKK-JISYO.L の他に skkserv2 と mecab-skkserv の複合語辞書サーバを串刺しにして使っていた。

`skk-search-prog-list' の設定はこんな感じ。

((skk-search-jisyo-file skk-jisyo 0 t)	; 個人辞書
 (skk-search-server skk-aux-large-jisyo 10000) ; rskkserv
 (skk-abbrev-search)
 (skk-look)
 ...
 (skk-server-completion-search))	; サーバコンプリーション

これでずっと使っていたのだが、それなりに不満な点もあった。

複合語が候補として出てくるのが早い
複合語はあくまでも補完的な位置付けにしたいので後のほうに出てきてほしい。
その変換候補を誰が出してきたのか一見して判らない
候補が SKK-JISYO.L 由来のものならもちろん問題ない。複合語辞書サーバの返答ならば眉に唾つけて見ないといけないのだが、そのあたりの区別がつかないので簡単に信用できない。
個人辞書に変なのが混ざるかも
そういった信用ならない候補を間違って確定してしまった場合も個人辞書に登録される。その場で取り消せばいいのだが、見逃すと後の祭りとなる。

要は複合語辞書サーバ絡み。使うのを止めればいいのではあるが、激しく便利なときもあるのでそうもいかず。

そこでこれらを改善すべく対処していく。

rskkserv での串刺しをやめる

とにかく rskkserv で抱え込んでしまってるとどうにもならないので各サーバは別建てにして個別にアクセスすることにする。

複製のサーバを検索できるように

現在の SKK には複数の辞書サーバにアクセスする仕組は用意されていないので自力でやる必要がある。

以前やったのと似たようなことをやればいい訳だが、面倒なのでマクロをでっちあげる。

(defmacro my-define-skk-search-server (name host port &optional id)
  "HOST, PORT を変更した `skk-search-server' を NAME の名前で定義する。"
  `(defun ,name (file limit &optional nomsg id)
     (let* ((server (or ,host ,skk-server-host))
	    (port (or ,port ,skk-server-portnum))
	    (name (symbol-name ',name))
	    (skkserv-working-buffer (get-buffer-create
				     (concat " *" name "*")))
	    (proc (get-buffer-process skkserv-working-buffer))
	    (skkserv-process (or (and (skk-server-live-p proc) proc)
				 (open-network-stream name
						      skkserv-working-buffer
						      server port))))
       ;; emacs21 hasn't `set-process-query-on-exit-flag'
       (when (fboundp 'set-process-query-on-exit-flag)
	 (set-process-query-on-exit-flag skkserv-process nil))
       ;; don't work if default coding system is utf-8
       (set-process-coding-system skkserv-process 'euc-japan 'euc-japan)
       (my-skk-add-dictid-to-annotation
	(skk-search-server file limit nomsg) ,id))))

ついでにこんなのも作ってみる。

(defun my-skk-add-dic (list)
  "`skk-search-prog-list' の末尾に list を追加する。"
  (add-to-list 'skk-search-prog-list list t))

こうやっておいて、

;; 複合語サーバ (skkserv2, mecab-skkserv) を加える
(my-define-skk-search-server my-skk-server-skkserv2 nil 1179 "<skkserv2>")
(my-skk-add-dic '(my-skk-server-skkserv2 nil nil))
(my-define-skk-search-server my-skk-server-mecab nil 1180 "<MeCab>")
(my-skk-add-dic '(my-skk-server-mecab nil nil))

とやると、`skk-server-host' のポート1179, 1180 に上げてあるサーバにアクセス出来るようになる。ID 引数については次項。

複合語サーバの検索結果にはアノテーションを付ける

skkserv2 と mecab-skkserv の検索結果には注意喚起としてアノテーションを追加することにする。

(defun my-skk-add-dictid-to-annotation (list id)
  "LIST の各要素に ID をアノテーションとして付加する。"
  (if (and id list)
      (let (cand)
	(mapcar (lambda (x)
		  (setq cand (skk-treat-strip-note-from-word x))
		  (concat (car cand)
			  (if (cdr cand)
			      (concat ";" (cdr cand) " " id)
			    (concat ";" id))))
		list))
    list))

こんなんを追記。これで `my-skk-server-skkserv2' の第4引数に指定した文字列がそのサーバの返した候補全てにアノテーションとして付加される。元々アノテーションがついている場合にはその後に。


といったことで、現在のところサーバ側は

rskkserv ┬ SKK-JISYO.L+
         ├ SKK-JISYO.jinmei
         └ SKK-JISYO.fullname

skkserv2
mecab-skkserv
yaskkserv (サーバコンプリーション用)
bskk (ベイジアン学習サーバ)

こうなった。各サーバは単独に。ついでに rskkserv には SKK-JISYO.L の変わりに SKK-JISYO.L+ を喰わせてみるとともに、jinmei と fullname も加えてみた。

`skk-search-prog-list' はこう。

((skk-search-jisyo-file skk-jisyo 0 t)	; 個人辞書
 (skk-search-server skk-aux-large-jisyo 10000) ; rskkserv
 (skk-abbrev-search)
 (skk-look)
 ...
 (my-skk-server-skkserv2 nil nil)	; skkserv2
 (my-skk-server-mecab nil nil)		; mecab-skkserv
 ...
 (skk-server-completion-search))	; サーバコンプリーション

複合語サーバは後のほうにもってきたので候補は後半に出てくるし、アノテーションも付くので一見して候補の出自が判る。うっかり個人辞書に登録してもアノテーションに指定した ID を grep すればいいだけなので後から探すのも容易になった。

utf-8 環境での server-completion

Debian etch にしたところ yaskkserv を入れて server-completion してみる - (((memo))) が効かなくなってたのに気付く。効かないというか、TAB を押しても No more completions for "まちだ" と言われて補完候補が一切出てこなくなった。

yaskkserv 君と直接話をしてみる。きみきみ、どうなってるわけ?

% echo "4まちだ "|netcat -q 2 localhost 1182
4まちだ

うーん、ホントに補完してくれてない。

あ、もしかして etch にしたのを機に utf-8 環境に移行したから?

% echo "4まちだ "|nkf -e|netcat -q 2 localhost 1182|nkf -w
1/まちだ/まちだえき/まちだおだきゅう/まちだかいどう/まちだかん/まちだがくえん/まちだし/まちだしみん/まちだてん/まちだほうめん/

やっぱり。

という訳で、以前のもの

 						   skkserv-working-buffer
 						   server port))))
     (set-process-query-on-exit-flag skkserv-process nil)
+    (set-process-coding-system skkserv-process 'euc-japan 'euc-japan)
     ad-do-it))

こんな感じで明示的にプロセスの入出力文字コードを指定してあげると元通り補完してくれるようになりました。

[追記:2007-05-21]
proc じゃなくて skkserv-process を使わないとマズいじゃん、ということでパッチを修正。

なんか、現在 migemo が動いてない気がするんだけどこのあたりが原因のような。

viper で検索語をハイライトする

普通の isearch だとバッファに存在する全ての検索語がハイライトしてくれるが、viper のそれだとポイント直下のものしかハイライトされずにちょっと淋しい。

以前からなんとかしたかったのだが、今日はちょっと気が向いたので viper-search でもハイライトするようにしてみた。

(defvar viper-search-highlight-overlay-priority 300)
(defvar viper-search-highlight-overlays nil)
(make-variable-buffer-local 'viper-search-highlight-overlays)
(defvar viper-search-highlight-pattern nil)
(make-variable-buffer-local 'viper-search-highlight-pattern)
(defvar viper-search-highlight-limit 500)

(defface viper-search-highlight-overlay
  '((((class color)) (:background "lavender"))
    (t (:weight bold)))
  "*Face used to highlight the search pattern."
  :group 'viper-highlighting)
(defvar viper-search-highlight-overlay-face 'viper-search-highlight-overlay)

(defun viper-highlight-cleanup ()
  (while viper-search-highlight-overlays
    (delete-overlay (car viper-search-highlight-overlays))
    (setq viper-search-highlight-overlays
	  (cdr viper-search-highlight-overlays))))

(defun viper-highlight-search-pattern ()
  (viper-highlight-cleanup)
  (let (ranges beg end ov)
    (setq ranges
	  (cons (cons (window-start) (window-end))
		(if viper-s-forward
		    (list (cons (window-end) (point-max))
			  (cons (point-min) (window-start)))
		  (list (cons (point-min) (window-start))
			(cons (window-end) (point-max))))))
    (dolist (range ranges)
      (setq beg (car range)
	    end (cdr range))
      (save-excursion
	(goto-char beg)
	(save-match-data
	  (while (and (re-search-forward viper-s-string end t)
		      (< (length viper-search-highlight-overlays)
			 viper-search-highlight-limit))
	    (setq ov (viper-make-overlay (match-beginning 0) (match-end 0)))
	    (push ov viper-search-highlight-overlays)
	    (viper-overlay-put ov 'priority
			       viper-search-highlight-overlay-priority)
	    (viper-overlay-put ov 'face viper-search-highlight-overlay-face)
	    (viper-overlay-put ov 'window (selected-window))))))))

(defadvice viper-flash-search-pattern
  (before highlight-search-pattern activate compile)
  (when (and (viper-has-face-support-p)
	     (or (null viper-search-highlight-overlays)
		 (not (string= viper-s-string viper-search-highlight-pattern))))
    (viper-highlight-search-pattern)
    (setq viper-search-highlight-pattern viper-s-string)))

こんなかな。

[追記:2006-12-02] なんか全然イケてなかったので全面的に書き替えてみた。