2011-02-11

Emacs Lisp モードで M-x find-* を使う

Emacs Lisp モードでシンボルが定義されているソースファイルを直接開くコマンドは以下の4つがあります。

M-x find-library
M-x find-variable
M-x find-face-definition
M-x find-function


M-x find-* とタイプするのは面倒だし、キーバインドを4つも定義したくない。
そこで、カーソル位置のシンボルから自動で上の4つを選択してくれたら、というのがここで紹介する方法。

変数、関数、フェイス等、複数の値を持つシンボルに関しては変数を優先するようになってます。
シンボルが値を持たない場合、 M-x find-tag を実行するので、下の例では M-. に割り当てています。

(defun my-find-tag ()
  "In Emacs Lisp mode, one of these functions is called:
`find-library', `find-variable', `find-face-definition',
`find-function' and `find-tag'."
  (interactive)
  (call-interactively
   (let ((symbol (variable-at-point t))
         (variable (variable-at-point))
         (function (function-called-at-point)))
     (cond
      ((or (eq function 'require)
           (eq function 'featurep))
       'find-library)
      ((and (symbolp variable)
            (boundp variable))
       'find-variable)
      ((and (symbolp symbol)
            (facep symbol))
       'find-face-definition)
      ((and (fboundp function)
            (eq symbol function))
       'find-function)
      (t
       'find-tag)))))

(eval-after-load "lisp-mode"
  '(define-key lisp-mode-shared-map "\M-." 'my-find-tag)) ; find-tag

2011-02-10

Emacs でバイナリ形式の .plist を編集する

Mac OS X の .plist はテキスト形式とバイナリ形式があります。
Xcode 付属の Property List Editor で編集できますが、できれば Emacs を使いたい。

元ネタ
Edit and save binary plist files
http://hints.macworld.com/article.php?story=2005061422012079

auto-compression-mode で対処する方法が紹介されてますが、これだとバイナリ形式ではない .plist でもバイナリ形式で保存してしまったりと不具合があります。
その記事のコメントに別の方法が投稿されていますが、それでもまだ若干問題があります。

  • バイナリ形式ではない .plist を開くと xml-mode にならない
  • 書き込み権限のないバイナリ形式の .plist を開くことができない
  • 日本語等が文字化けする (Emacs22)

その問題点を改良したものがこちら。

;; Edit and save binary plist files
;; http://hints.macworld.com/article.php?story=2005061422012079
(add-to-list 'auto-mode-alist '("\\.plist\\'" . visit-bplist))
(add-to-list 'auto-mode-alist '("\\.nib\\'" . visit-bplist))

(add-to-list 'magic-mode-alist '("\\`bplist" . visit-bplist))
(add-to-list 'auto-coding-regexp-alist '("\\`bplist" . utf-8))

(defvar plist-converted-binary nil
  "Buffer local variable indicating if file came from binary-plist.")
(make-variable-buffer-local 'plist-converted-binary)

(defun visit-bplist ()
  (let (bplist)
    (when (string-match "\\`bplist" (buffer-string))
      (setq bplist t)
      (save-excursion
        (let (buffer-read-only)
          (message "Reading in binary plist")
          (erase-buffer)
          (let ((process-coding-system-alist '(("plutil" . utf-8))))
            (call-process "plutil" nil t nil
                          "-convert" "xml1" "-o" "-" (buffer-file-name))))))
    (xml-mode)
    (when bplist
      (set-buffer-modified-p nil)
      (setq buffer-undo-list nil)
      (setq plist-converted-binary t))))

(defadvice save-buffer (after convert-plist (&optional args))
  (when plist-converted-binary
    (shell-command
     (format "/usr/bin/plutil -convert binary1 %s"
             (shell-quote-argument (buffer-file-name))) nil nil)
    (message "Wrote bplist %s" (buffer-file-name))))
(ad-activate 'save-buffer)

動作確認は Mac OS X 10.6 と GNU Emacs 22 でしています。

2011-02-09

プロパティリストを調べる describe-plist

Emacs をもう少し詳しく理解するための関数。
Elispの基本的なことですが、シンボルは4つの値を持つことができます。

・名前
・変数
・関数
・プロパティリスト

詳しくは info を参照。
M-: (info "(elisp)Symbol Components")
変数は M-x describe-variable (C-h v) 、関数は M-x describe-function (C-h f) で調べることができますが、最後のプロパティリストについては便利なコマンドは用意されていません。
そこで、 apropos.el に apropos-describe-plist という関数があるので、それを呼び出すコマンドを作ります。

(defun describe-plist (symbol)
  "Display SYMBOL's property list."
  (interactive
   (let ((sym (variable-at-point t))
         (enable-recursive-minibuffers t)
         str)
     (when (or (not (symbolp sym))
               (not (symbol-plist sym)))
       (setq sym nil))
     (setq str (completing-read
                (if sym
                    (format
                     "Describe property list (default %s): " sym)
                  "Describe property list: ")
                obarray
                'symbol-plist
                t nil nil
                (when sym (symbol-name sym))))
     (list (if (equal str "")
               sym (intern str)))))
  (unless (symbol-plist symbol)
    (error "Symbol %s has no property list" symbol))
  (require 'apropos)
  (apropos-describe-plist symbol))

(define-key help-map "P" 'describe-plist)

キーバインドは一例です。上の例だと C-h P に割り当てています。
挙動は M-x describe-variable 等と同様に、シンボルがプロパティリストを持たない場合はミニバッファでの補完はききません。

試しに M-x describe-plist RET mark-inactive RET とすると、このように表示されます。

Symbol mark-inactive's plist is
 (error-conditions (mark-inactive error)
  error-message "The mark is not active now")

[back]

動作確認は GNU Emacs 22 でしています。

Dired で選択範囲にマークを付ける

リージョン指定してマークを付ける。
C-u 付きで選択範囲のマークを消す。

dired-mark-files-in-region という関数があるのでそれを利用した。
キーバインドは一例です。

(defun dired-mark-region (start end &optional arg)
  "Mark all files in region.
With prefix argument, unflag all those files."
  (interactive "r\nP")
  (let ((dired-marker-char (if arg ?\040 ?*))) ; \040 = SPC
    (dired-mark-files-in-region
     (save-excursion
       (goto-char start)
       (line-beginning-position))
     end)))

(eval-after-load "dired"
  '(define-key dired-mode-map "*r" 'dired-mark-region))