たまーに欲しくなる set-keymap-parent を作ってみた。 src/keymap.cc を見た感じだと後ろにベクタをくっつけても参照してくれなさそう。なので疎なキーマップしか親になれない。
(defmacro set-keymap-parent (keymap parent)
`(when (consp ,parent)
(if (consp ,keymap)
(setq ,keymap (append ,keymap ,parent))
)))
副作用のある引数を与えることはまったく想定してないマクロ。
paren-highlight がテキスト属性を消してしまうことがあるのがごくたまに気になっていたので、 paren.l を弄ってハイライトを消したときにもとのテキスト属性を復元するようにしてみた。
(in-package "editor")
(defvar-local *stored-text-attributes* nil)
(defun save-text-attributes (&optional start end)
(setq *stored-text-attributes*
(list-text-attributes start end)))
(defun restore-text-attributes ()
(mapc (lambda (attr) (apply #'set-text-attribute attr))
*stored-text-attributes*)
(setq *stored-text-attributes* nil))
(defun paren-highlight ()
(delete-text-attributes *paren-tag*)
(restore-text-attributes)
(when *paren-status*
(cond ((and (syntax-close-p (preceding-char))
(string/= "#\\" (buffer-substring (- (point) 3) (- (point) 1))))
(save-text-attributes)
(do-paren-highlight 'close))
((and (syntax-open-p (following-char))
(not (looking-back "#\\")))
(save-text-attributes)
(do-paren-highlight 'open)))))
(in-package "user")
テキスト属性が消えるのが気になる理由はもしかしたら *paren-highlight-only-paren* が nil (括弧の中身にも下線をつけている) だったり { } とか [ ] までハイライトさせていたりするからかもしれない。
不具合がひとつあって、変なところに消えるべきテキスト属性が残ってしまうことがある。これはコマンドを実行したときにテキスト属性が消されるべき状態になっても対処できないため。例えば CLtL のインデックスで [next] [up] とか書いてあるところを行き来するとリンクの色が変わったままになる。たぶんこれを直すには *paren-tag* 以外のタグがついたところに上書きしないようにすればいい。けど、放置してもあんまり問題ないような気はする。
encap.l を入れてみた。コードを修正するたびに unencapsulate するのが面倒なので encapsulate を encapsulate してみた。
(require "encap")
(setf (get 'encapsulate 'lisp-indent-hook) 2)
(encapsulate 'encapsulate 'overwrite
'((let ((symbol (car argument-list))
(type (cadr argument-list)))
(while (encapsulated-p symbol type)
(unencapsulate symbol type)))
(apply basic-definition argument-list)))
最近になってようやくフレームの使い方を覚えた。 C-x 6 o とかはちょっと使いにくかったのでキーバインドを追加した。
(global-set-key #\C-Right 'next-pseudo-frame) (global-set-key #\C-Left 'previous-pseudo-frame)
"~/../test" と書くべきところが "~/..test" となっていたので修正。
他の PC に xyzzy を入れたら *MyDocuments* を defconstant しておいたのがちょっと役に立った。でもその直後に (get-special-folder-location :personal) としておけば全然問題ないことに気付いた。
コンパイルとロード用の関数で mc- が付くのと付かないのとあるけどどう違うんだろうと思ったら、どうやら文字コードを指定できるかどうかの差らしい。Shift-JIS のファイルを扱う限りは mc- を使う必要はないようだ。
ついでにロード系の関数についてメモ。
実験してみた。
| ファイル名 | 中身 |
|---|---|
| ~/test.l | (setq a 1) |
| ~/test.lc | (setq a 2) をバイトコンパイルしたもの |
| ~/test | (setq a 3) |
結果 ;;; *load-path* に "~/" は入っていないが "~/site-lisp/" は入っている (and (si:*load-library "../test" :no-suffix nil) a) => 2 ; test.lc をロード (and (si:*load-library "../test" :no-suffix t) a) => nil ; ~/../test は見つからない (and (si:*load-library "test" :no-suffix t) a) => 3 ; test をロード (and (load "../test") a) => ../test 指定されたファイルが見つかりません。 ; ~/../test は見つからない (and (load "test") a) => 3 ; test をロード (rename-file "test.lc" "test.lc.bak") => t ; test.lc をリネームしてみた (and (load-library "../test" :no-suffix nil) a) => 1 ; test.l をロード (and (load-library "test" :no-suffix t) a) => 3 ; test をロード (and (load "test") a) => 3 ; test をロード
便利なのかどうなのかよく分からないマクロを書いてしまった。
;; どうなのか
(setf (get 'define-S-sensitive-cmd 'lisp-indent-hook) 1)
(defmacro define-S-sensitive-cmd (name cmd S-cmd)
`(defun ,name (&optional (arg 1))
(interactive "p")
(if (minusp (GetKeyState #x10)) (setq arg (- arg)))
(if (plusp arg) (dotimes (_ arg) (,cmd))
(dotimes (_ (- arg)) (,S-cmd)))))
こんな感じで使うことを想定。
(define-key *www-view-mode-map* #\TAB
(define-S-sensitive-cmd www-view-link
www-view-next-link www-view-previous-link))
マイドキュメントの rename によりセッションファイルの一部が機能しなくなることに気付く。バックアップを探し出して直接書き換えたら解決したけど、こういうフォルダを不用意に rename なんかするもんじゃないな。
諸事情により以前から マイドキュメント を rename していたのを元に戻したところ、 .xyzzy を数箇所書き換えなければならなくなった。そこで次にこんなことがあっても簡単に書き換えられるように、定数 *MyDocuments* を定義して全部 merge-pathnames で書き直してみた。
(defconstant *MyDocuments*
(map-backslash-to-slash
(read-registry
"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
"Personal" :current-user)))
けど、考えてみたら今後この恩恵を蒙ることは多分ないだろうと思う。
ところで恩恵を蒙るってこういう時に使っていいのだろうか。ちょっと不安になって調べてみたら違うような感じがする。
ローカル変数の作り方の違いがよく分かってなかったのでまとめてみた。よく分かってないので間違ってるかも。
(defmacro defvar-local (name initial-value &optional doc)
`(progn
,(if doc
`(si:*putprop ',name ,doc 'lisp::variable-documentation))
(or (boundp ',name)
(set-default ',name ,initial-value))
(make-variable-buffer-local ',name)
',name))関係ないがいつの間にこんなマクロを読めるようになったのか。
なんか最近 do-paren-highlight が goal-column を保存しなくなったなあと思っていたら paren-highlight をいじったのが原因だったらしい。 do-paren-highlight と同じ方法で保存されるように修正。
wwc.l を p-complete.l と改名して wiki に書いておいた(partial-completion 風シンボル名補完)。需要があるかどうか想像つかないが少なくとも個人的には lisp を書くときは必ず使ってるのでまあ書く価値なくはないだろう。検索しても見つからないから、既出ってこともないだろうし。
wwc.l を修正。コロンで始まるキーワードも補完できるようにした。最初 keyword package の存在を知らず、 :foo は keyword:foo のことだと気付かなかったのでどうやって対応したらいいか悩んでしまった。
Backspace を F13 にしたら IME の「確定取消」の挙動が「反復」になって困った。 IME 側で F13 に何かを割り当てているのは関係ないようだし、 C-Backspace は xyzzy では何も割り当てていない(*1)のでこれも関係ないはず。というかそもそも IME で何かが割り当てられていれば xyzzy のキーバインドには関係なくそちらが機能する(と思う)。
それで一体何なのだろうかと思いつつ色々試しながら「最近入力したキー」を見ていると、変換直後に C-BS を打ったところで直前に確定した文字列の長さと同じだけ F13 が入力されていることが分かった。今 F13 は Backspace を入力したと解釈できるから、「確定取消」の挙動が 「Backspace を入力して確定した分を消して『反復』」したのと同じになっているということだと予想できる。ということは、確定取消を使うには Backspace は C-h でなければならないということになる(正確には、直前の一文字を削除するコマンドがバインドされたキーであればいいが)。
IME では何も割り当てずに rewind-ime-composition を使うということも可能だが、それだと xyzzy 上でしか使えない。というわけでやむをえず BS, S-BS にしていたキーを S-BS, S-C-BS に変えて一応解決。
それにしても、確定取消ってそうやって実現してたのか。前からどうなってるのかなーと思ってたけど。
;; *1 こういうこと (svref *extended-key-translate-table* exkey-C-backspace) => nil
wwc.l にバグ発見、修正。 * が正規表現で特別な意味を持つことが考慮されていなかったので regexp-quote するようにした。それから補完対象が delimiter で終わる場合に最後の delimiter が無視されていたのも修正。
paren.l が #\ に続く括弧に対して「対応する括弧がないで」と言うのが最近気になったので対策を施してみた。 →さらに修正版
(in-package "editor")
(defun paren-highlight ()
(delete-text-attributes *paren-tag*)
(when *paren-status*
(let ((goal-column (goal-column))) ; 9/22 追加
(cond ((syntax-close-p (preceding-char))
(or (save-excursion
(backward-char 1)
(looking-back "#\\"))
(do-paren-highlight 'close)))
((syntax-open-p (following-char))
(or (looking-back "#\\")
(do-paren-highlight 'open))))
(set-goal-column goal-column)))) ; 9/22 追加
(in-package "user")
最上段の数字キーってあんまり使わないなあと思って他のコマンドを割り当てる試みを開始。 012 はさすがに使う頻度高めなので残して、他をつぶしてしまおうかと考える。 4,7 あたりで () が打てたらいいかなとか。
wwc.l を修正。 * のつく変数の補完が色々おかしかったのを直した、つもり。それから delimiter の指定のしかたがまずかったのも修正。
ちょっとあるコードを書き換えたら変な挙動をするようになった。色々コメントアウトして実験してみた結果 long-operation が原因のように見える。どうも long-operation に複数のフォームを与えると一つ目の値が返ってくるようで、 (long-operation 1 2) で 2 ではなく 1 が返る。
ソース (~/lisp/misc.l) を見たら確かにそういう定義になっているが、なぜそうなっているのかは分からない。検索してみたら 2ch で一年半前に同じ話が出たようだ。反映されていないということはもともと現在の定義のつもりで書かれていたということか。まあ body を progn で囲めばいいからそんなに問題ではないのだが。
今までファイラを使ってはいたが、あまり便利だとは感じていなかった。でもよく考えたら lisp でカスタマイズできるんだしもっと便利に使えるはずと思い、ちょっと調べてみる。
まず *filer-directories* をいじることで filer-jump-directory が便利になる(というかそうしないと使えないのだが)ことに気付く。それから *filer-send-to-list* もカスタマイズすると便利そう。しかし filer-send-to (#\]) なんてソースを読んで初めて存在を知った。
それから mayu-mode で indent-relative-maybe が便利かもしれないと思って emacs から移植してみたり、思いつきで html+-mode でダブルクォートを打つと適当に実体参照するコマンドを書いたりした。が、まだほとんど使っていないので使えるかどうかは不明。
ポップアップメニューから選択してメモをポップアップするコマンドを書いて使っているのだが、数百行のメモを .xyzzy に書いていると編集の際にその部分が邪魔だったので別ファイルにしてみた。なんか余計なコードが増えて合計のファイルサイズは増えた気がするが .xyzzy はすっきりしたのでいいことにする。
ついでに popup-string を等幅フォントにする方法はないものかと調べてみた。コントロールパネルから設定をいじればよかったらしい。でも再起動しないとポップアップには反映されなかったからしばらくそれでいいことに気付かなかった。
補完をちょっと変更。末尾にワードがまだ続くようなシンボル名を補完候補にしたりしなかったりをカスタマイズできるようにした。それに伴って正規表現を生成する関数を変更。……こういうことはファイルに更新履歴として書くべきではないかとここに書いた後で思った。けど面倒なのでやらない。
バックアップ。case 文の仕様を理解せずに (nil form) とか書いてしまい、うまくいかず悩む。 macroexpand したりソースを見たりしてこの節は展開の際に消えることを知り、なんでそんな仕様なんだろうかと本気で疑問に思う。
しかしその理由はわかってみれば簡単なことで、 key がリストなら eql ではなく member でテストされるから。keyform が nil の場合を書きたければ (nil) と書けばよいのだった。ついでに t の場合も (t) と書けばいい。
使うこともあるかなあと思って例の補完の Emacs lisp 版 も書いてみた。というか手抜き移植。
補完を暫定公開してみた。それなりにパッケージに対応してるつもり。開き括弧の前では fboundp なものだけを候補とする(lisp-complete-symbol はそうなっている)案もあったが面倒なので却下。
リンクチェックをちょっと修正して Wiki に書いてみた。それから二月ごろにやっていたシンボルの補完も修正中。パッケージ修飾子のコロンが一つの場合と二つの場合の違いがよくわからなくて CLTL を読んだりする。候補がありすぎる場合にちょっと問題あり。そもそも候補がありすぎる場合は補完として機能しないので適当に return してしまったほうがいいか。
S-TAB が効かなくなってなんでだろうといろいろ調べてみる。(read-char) してみるとそもそも S-TAB を打っても読んでくれないことが分かり、以下のコードを .xyzzy に書いて解決。
(set-extended-key-translate-table exkey-S-tab #\TAB)
*extended-key-translate-table* を見ると確かに exkey-S-tab のところは nil になっていたのだが・・・これまで書かなくても効いていたのがおかしいのか、書かないといけなくなったのがおかしいのかは分からない。何か設定変えたっけ?特に思い当たることはないのだが。
info-modoki-mode でシンボルへのジャンプができるようにしてみた。 (point) から前方(負の引数を与えると後方)へ、キーワードファイルに入っているシンボル名を探してそこへ移動する。キーワードリストを作っといてシンボル構成文字を探しては入ってるかチェック、という方法をとった。遅すぎるのではという気がしないでもなかったが、実際試してみるとそんなことはなかった。もうちょっと使ってみてバグがなさそうならどっかに晒すつもり。
なお、シフトを入れたときに後方へ飛ぶために GetKeyState 使用。
人狼審問は IE よりも www-mode の方がアンカー辿るのが楽(というか速い、一度読み込んでおけば)なことに気付く。それで HTML のソースはどこに保存してるんだろうと思ったら、バッファに書き出されているだけ?
ログ自体は特に読みやすくはないのだが、特に不満があるわけでもない。2ch-mode みたいにポップアップできたらもっと嬉しいのだが、これは 2ch-mode のソース見ながらやればできるか?
TAB に前置引数与えられるようにしてみた。ついでに - を negative-argument に、数字キーを digit-argument にバインド。ちょっと快適になった。
parse-point-syntax の挙動がいまいちよく分からない。use-syntax-table するだけでは HTML タグが認識されず、加えて html-load-keyword-file するとうまくいった。キーワードのことはよくわかってないんだけど、とりあえずそういう仕様なのだろうか。
思いつきでこんなものを作ってみた。本当は昔やった「グリコ」(みんなそう呼んでたっけ?)みたいに、どの手で勝ったかによって報酬の量が違うのをやりたかったんだけど、ややこしくなりそうなのでやめた。でもそれができたら人狼BBSにも少しは応用可能だろうなあなどと夢想してみる。いや、人工知能の専門家ならすぐにでもできるだろうけど。
あー、後で気付いたけど勝敗の判定ではなくて与えられる報酬を返すようにすればよかった。今度気が向いたらやってみよう。
パックアップのつもりで作っていたが、ファイルの自動コピーだけにして圧縮は別にやったほうがいいような気がしてきた。既に書いてあるプログラム自体を変える必要はたぶんほとんどない。
バックアップ、一通り完成?もうちょっと書き直さないとダメかも。とりあえずテストをそのうちしてみることにする。
そういえばダイアログのなんか16進数っぽいところの意味がよくわからない。適当に書いたら動いているのでいいことにしている。
ダイアログの作り方を調べてみたらちょっとだけわかった。バックアップに使う予定。でもちゃんとわかってるかどうかは自信がない。
バックアップをいじる。パスが存在しなかったりアクセスが拒否された場合の処理を書いていたら、もともとは数行しかなかった関数が40行ぐらいになってしまった。でも実際にその部分が役立つことはあんまりないと。まあ message-box の書き方を思い出したし、エラー処理の仕方もちょっとわかったからいいか。