xyzzyで対応する括弧や引用符を自動的に挿入したり消したりする

Eclipseの挙動をイメージ。いずれもsiteinit.lに設定。

括弧や文字列を勝手に閉じる設定

特定のキーを入力したときに、指定した文字列をカーソルの後ろに挿入するようにした。
例えば"("を入力すると"()"と入力され、カーソルは括弧の中に入る。

;; 括弧や文字列は勝手に閉じる
(defun define-auto-suffix (keymap key suffix)
  "add suffixes automatically"
  (define-key keymap key #'(lambda () (interactive "*") (insert key) (save-excursion (insert suffix)))))
(define-auto-suffix *global-keymap* #\{ "}")
(define-auto-suffix *global-keymap* #\[ "]")
(define-auto-suffix *global-keymap* #\( ")")
(define-auto-suffix *global-keymap* #\' "'")
(define-auto-suffix *global-keymap* #\" "\"")

開始括弧や引用符を消したときに、閉じ括弧や対応する引用符を削除する設定

自動挿入する設定だけだと、間違って入力したときに前後を削除するのが面倒。
そこで、開き括弧などを消そうとしたときに、
カーソルが開き括弧と閉じ括弧に挟まれていれば、閉じ括弧側も消去する関数を用意した。
例えばカーソルが"()"の中にあるときに"("を消すと、自動的に")"も削除する。

;; 対応する括弧や引用符は勝手に消す
(defun delete-backward-char-or-suffix-or-selection-factory (prefix-suffix-pair-list)
  #'(lambda (&optional (n 1))
      (interactive "*p")
      ; selectionがあればそれを消す
      (if (pre-selection-p)
          (delete-selection)
        ; カーソル位置が接頭・接尾ペアに挟まれているかどうか各組み合わせについて確認し、一致するものがあればまとめて消す
        (if (not (dolist (element prefix-suffix-pair-list)
                   (let ((prefix (car element))
                         (suffix (cdr element)))
                     (if (and (string= prefix (buffer-substring (point) (save-excursion (backward-char (length prefix)) (point))))
                              (string= suffix (buffer-substring (point) (save-excursion (forward-char (length suffix)) (point)))))
                         (progn (delete-backward-char (length prefix))
                           (delete-char (length suffix))
                           (return t))))))
            ; マッチするものが無ければ普通のバックスペース
            (delete-backward-char n)))))

使用する際は、モードごとに前方消去のコマンドを上書きする。

;;; javascript-mode
(load-library "javascript-mode")
...
(define-key *javascript-mode-map* #\C-h
            (delete-backward-char-or-suffix-or-selection-factory
             '(("{"."}") ("[" . "]") ("(" . ")") ("'" . "'") ("\"" . "\""))))

設定ファイル全体は以下
https://github.com/phithon/dotfiles/blob/master/siteinit.l

xyzzyのjavascript-modeにおけるインデントの挙動をカスタマイズする

配布されているjavascript-modeのインデントの挙動がいくつか気になったので調整した。
前提として、インデント設定としてはタブ不使用の空白4桁を使用している。
javascript-modeは以下のリンク先のものを利用。
http://xyzzy.s53.xrea.com/wiki/index.php?%B3%C8%C4%A5lisp%2Fjavascript-mode

オブジェクトのプロパティーがswitch文のラベルと誤認されることへの対処

以下のような状態で、矢印位置で改行した場合の挙動について。

var foo = { /* ↓ */ bar: 'foobar'
};

デフォルトでは、以下のようにインデント量が2桁になってしまう。
javascript-modeでは内部的にC言語の組み込みインデント判定を利用しているようで、
どうも2行目がswitch文などのラベルと誤認されているらしい。

var foo = {
  bar: 'foobar'
};

これについては以下のように、ラベルのインデント量をデフォルトの-2から0へ変更した。
switch文のラベルも4桁になってしまうが、ほとんど使わないので良しとする。

(setf c-label-offset 0)

オブジェクトの閉じ括弧が前行のプロパティーの継続行と誤認されることへの対処

以下のように、オブジェクトのプロパティーを表す行の次で、閉じ括弧が2桁インデントされてしまう。

var foo = {
    bar: 'foobar'
  }

こちらの場合は、どうもプロパティーの行がセミコロンで終わっていないために、
前の行が継続していると判定されているらしい。
対策として元のインデント用関数を上書きし、
現在行が閉じ括弧で始まる場合だけ継続行のインデントを一時的に0にするようにした。
siteinit.lに記載した内容は以下の通り。javascript-modeのロードより後ろに書くことに注意。

; 元のインデント関数の保持
(setf (symbol-function 'original-javascript-indent-line) #'javascript-indent-line)
; 現在行を文字列として取り出す関数
(defun current-line-string ()
  "get current line as string"
  (save-excursion (buffer-substring (progn (goto-bol) (point))
                                    (progn (goto-eol) (point)))))
(defun javascript-indent-line ()
  "revoke indent for right brases"
  (interactive "*")
  (if (not (string-match "^[ \t]*}" (current-line-string)))
      (original-javascript-indent-line)
    ; 現在行が閉じ括弧で始まる場合、継続行のインデントを一時的に0にする
    (let ((original-continued-offset c-continued-statement-offset))
      (setf c-continued-statement-offset 0)
      (original-javascript-indent-line)
      (setf c-continued-statement-offset original-continued-offset))))

空の中括弧内で改行したら展開してブロック内の1行目にカーソルを移動する

以下のような中括弧内で改行した場合について。

var foo = {}

デフォルトでは以下のような状態になる。

var foo = {
} // 行頭にカーソル

これを、以下のような状態になるよう変更した。

var foo = {
    // ここにカーソル
}

siteinit.lに記載した内容は以下の通り。
改行した瞬間にカーソルが"{}"や"[]"の中に入っていたら、余分に改行やインデントを行うようにしている。
こちらもjavascript-modeのロードより後ろに書くことに注意。

;; 中括弧内で改行したら展開してカーソルを中に入れる
(defun javascript-newline-and-expand-brases (&optional (arg 1))
  "open brases and move the cursor inside of the brases"
  (interactive "*")
  (javascript-newline-and-indent arg)
  (flet ((current-line-string () (buffer-substring (progn (goto-bol) (point)) (progn (goto-eol) (point)))))
    (if (or (save-excursion (and (string-match "^[ \t]*}" (current-line-string)) ; {}内で改行した場合
                                 (string-match "{[ \t]*$" (progn (previous-line) (current-line-string)))))
            (save-excursion (and (string-match "^[ \t]*\\]" (current-line-string)) ; []内で改行した場合
                                 (string-match "\\[[ \t]*$" (progn (previous-line) (current-line-string))))))
        (progn (save-excursion (javascript-newline-and-indent arg))
          (javascript-indent-line)))))
(define-key *javascript-mode-map* #\RET 'javascript-newline-and-expand-brases)
(define-key *javascript-mode-map* #\LFD 'javascript-newline-and-expand-brases)

設定ファイル全体は以下
https://github.com/phithon/dotfiles/blob/master/siteinit.l

xyzzyで指定したキーバインドを削除する

define-keyを解除する方法。
ライブラリーを直接いじる方が手軽だけど、設定ファイル内で完結させたかったので。
/lisp/keymap.lにundefine-keyという関数が定義されていた。
指定されたキーマップから指定したキーバインドを外せる。
以下のように使用。

(undefine-key *javascript-mode-map* #\.)

設定ファイル全体
https://github.com/phithon/dotfiles/blob/master/siteinit.l

xyzzyのdiffの有効化と開いているファイルの自動選択

xyzzyのdiffとしてMinGWのdiff.exeを使用

まずdiff自体に関しては、Git Bashに付いてきたMinGWのdiff.exeを指定したらそのまま使えた。
設定は以下。shell設定のときと異なり、2重エスケープでも上手く行かなかったので、
「Program Files (x86)」の代わりにその短縮名である「Progra~2」を使うことにした。

;; MinGWのdiffを使う
(setf ed::*diff-command-name* "C:\\Progra~2\\Git\\bin\\diff.exe")

xyzzyで開いているファイルを自動的にdiffの対象にする

毎回新旧ファイルを探して指定するのは手間なので、
現在開いているバッファを自動的にdiffの対象とする関数を作成し、C-x dに割り当てた。
記事最下部にあるサイトの関数を参考に、ウィンドウを分割していない場合や、
ファイルに紐付いていないバッファを開いている場合も、足りないファイルだけ指定すれば良いようにした。

;; カレントバッファでdiff
; new file: 現在のバッファがファイルに紐づいていればこのバッファ、そうでなければファイルを指定する
; old file: 別ウィンドウに開いているバッファがファイルに紐づいていればそのバッファ、そうでなければファイルを指定する
(defun diff-current-buffers ()
  (interactive)
  (let ((current-file-name (get-buffer-file-name (selected-buffer)))
        (another-file-name (and (not (eq (count-windows) 1))
                                (get-buffer-file-name (window-buffer (next-window (selected-window)))))))
    (diff (or current-file-name (read-file-name "diff(new file): "))
          (or (and (not (eq current-file-name another-file-name)) another-file-name)
              (read-file-name "another file(older): ")))))
;; C-x dでカレントバッファのdiff
(define-key ctl-x-map #\d 'diff-current-buffers)

設定ファイル全体は以下
https://github.com/phithon/dotfiles/blob/master/siteinit.l

参考

xyzzy で diff を使う - メモ用紙
http://d.hatena.ne.jp/scientre/20120518/xyzzy_diff

xyzzyから起動するシェルとしてGit Bashを設定する

意外とハマったのでメモ。
siteinit.lに以下のように記述すればOK。

;; C-x cで開くシェルをGit Bashにする
(setf *eshell* "\"C:\\\\Program Files (x86)\\\\Git\\\\bin\\\\sh.exe\" --login -i")

ポイントは2つあって、1つは*eshell*変数に使いたいシェルプログラムを指定するという点。
もう1つはファイルパスに空白を含むような場合、
二重引用符で括ったあと、内部を2段階エスケープする必要があるという点。
こうしないとM-x shellを使ってxyzzy内でshell-modeを起動する際に怒られる。
とはいえ、色付けのことなどを考えるとshell-modeからGit Bashを起動するメリットは薄そう。

xyzzyをWindows7に導入

PCごとに微妙に設定が異なって来たので、ログを残しつつ設定内容を統一してみる。

インストール

ダウンロード先
http://xyzzy-022.github.io/
「C:\Users\<ユーザー名>\Tools\xyzzy」に解凍。

環境変数の設定(カテゴリー表示の場合)

「コントロールパネル > システムとセキュリティ > (右側)システム > (左側)システムの詳細設定」を選択。
システム環境変数に変数名として「XYZZYHOME」、変数値は解凍先(xyzzy.exeの存在するディレクトリー)を指定。
これを設定しないとabbrevとかが上手く動かなかったような気がする(うろ覚え)。
「xyzzycli.exe」のショートカットを作成して起動。
ちなみに「xyzzy.exe」との違いは、新たなファイルを開こうとしたときに
別ウィンドウを起動するかどうか(cliは既に開いているウィンドウがあればそれを使う)。

表示設定

表示 > ツールバー > バッファ を選択。

共通設定

ツール > 共通設定 から各項目を設定していく。
設定結果は「/usr/<ユーザー名>/wxp/」ディレクトリーの
xyzzy.ini」ファイルと「.xyzzy.history」ファイルに保存される。

フォント

文字セット「ASCII」、「日本語」をそれぞれ「MS ゴシック」、サイズ「10」に設定。

表示

「全角スペース」を表示、「EOF」を非表示にする。
中段は下表のように設定。

テキストの折り返し ウィンドウの幅
挿入時のキャレット 細いやつ
上書き時のキャレット ブロック
マウスカーソル 細いの(ブラウザのテキストボックス風のやつ)
タブの幅 4

「行番号は表示行」を無効に、「マーク行の行番号を反転」を有効にする。

バックアップファイル

「バックアップファイルをつくる」を無効化する。

さまざま

クリップボードへのコピーで範囲をクリアしない」を有効化。
「起動時にウィンドウの位置を復元」〜「終了時にウィンドウサイズを保存」を全て無効化。

設定結果

https://github.com/phithon/dotfiles/blob/master/xyzzy.ini
改行コードがLFに変換されているので、
そのまま使う場合はC-x C-k nなどでCRLFに変換しないと上手く読み込めない可能性あり。

siteinit.lの設定

/site-lisp/siteinit.l」を作成し、編集する。
編集後に一旦xyzzyを終了し、Ctrl+Shiftを押しながらxyzzyを起動すると読み込まれる。
このとき、改行コードがCRLFになっていないと上手く読み込めないことに注意。

設定結果

https://github.com/phithon/dotfiles/blob/master/siteinit.l
改行コードがLFに変換されているので、そのまま使う場合はCRLFに変換する必要あり。
以下、主な拡張Lisp一覧。

javascript-mode

以下のURLから圧縮ファイルを取得して、同ページ内にある、みずぴーさんの設定をする。
拡張lisp/javascript-mode - XyzzyWiki
http://xyzzy.s53.xrea.com/wiki/index.php?%B3%C8%C4%A5lisp%2Fjavascript-mode

python-mode

py-modeという名前の拡張が提供されている。
*Megahurtz
http://www.geocities.co.jp/Milano-Cat/2067/xyzzy.html#py-mode

scala-mode

こちらで発見。
k-tsj/scala-mode.l · GitHub
https://github.com/k-tsj/scala-mode.l

結婚式のスピーチのために覚えておくと良いこと

結婚式でちょっと友人代表スピーチをする機会があったので、メモ程度に残しておきます。

事前準備

まず何より、以下の3点については、頼まれたらすぐに確認した方が良いです。

  • スピーチの長さ
  • 他の参加者層や式の規模
  • 求められているキャラ

この辺は早めに確認しておかないと、準備してなかったのがバレバレです。
私は直前に聞きましたが、聞かないよりはマシということで。
長さに関しては普通は3分程度だと思います。最悪長すぎなければ良いのでは。
参加者層については職場の人が多いのか、友人が多いのかとか。
職場関係が少ない場合でも、親族に冗談の通じない人がいる可能性を考えて、
かなり安全側に倒した方が良いとは思います。盛り上げ役は余興がいるでしょうし。

スピーチ以外に関して。
包む額と熨斗(のし)の豪華さに関しては書籍やサイトによってまちまちなので、
最初からパッケージに○万円用とか書いてあるのを使うと良いと思います。

当日

スピーチ前の食事で少しはお酒を入れておいた方が良いです。
ネットでお酒は全く入れない方が良いという主張があったのでそうしてみましたが、
想像以上に緊張しますし、コップ半分ぐらいは入れておくべきだったと後になって思いました。
あと、もしものためにカンペを用意する場合は、予め手に持っておくこと。
ポケットなどに入れていると、本当に緊張しているときは存在を忘れます。
あと練習は声に出してやっておかないと効果無いですね。

参考

私の場合は以下の書籍を参考にしました。

スピーチ例はあまりしっくり来ませんでしたが、準備や礼儀的な部分は結構役に立ちました。
スピーチ例はググれば代筆業者などがサンプルを載せているので、
そちらを参考にすると雰囲気が掴みやすいと思います。