>> 古い記事: 入力したXPathにマッチした要素をハイライトし要素数を表示するブックマークレット
<< 新しい記事: 電子書籍メモ

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Emacs Lisp メモ: バッファローカル変数

メモです。 正確なところはマニュアルなどで確認してください。

以下、変数 x を使って説明します。

デフォルト変数

  • バッファローカルでない、ふつうの変数。
  • どこ(どのバッファ)から参照・変更しても同じ値。
  • 一般的なプログラミング言語の「グローバル変数」と似てる。

どのバッファから見ても x:D が参照される。

バッファローカル変数

  • バッファごとに別。
  • グローバルな(ふつうの)変数をマスクする。
  • 「バッファ固有のマスク変数」みたいな感じ。
  • 一般的なプログラミング言語の「ローカル変数」は 「スコープに対してローカル(特定のスコープ内だけから参照できる)」だが、 バッファローカル変数は「バッファに対してローカル (特定のバッファ内だけから参照できる)」

Emacs Lisp ではこの場合 x:D も x:L も同じ x として扱う(書く)が、 x:L はバッファ1 からしか参照できない。 バッファ1 では x:L によってマスクされるため x:D は参照できない。

バッファ2 で x(=x:D)の値を変更すると バッファ3 では変更された値が参照できる(x:Dが参照されている)

※シンボルとしては同じ x だが、 説明のために x:D、x:L と書いて区別する。 D, L はそれぞれ Default, Local の頭文字。

関数

バッファローカル変数を作ってデフォルト変数を隠す・マスクする

  カレントバッファだけ:
    make-local-variable 
  すべてのバッファで:
    make-variable-buffer-local

「set した時に自動的にバッファローカルにする印」 (長ったらしいので以下では「(変数の)if-set値」と呼ぶ。 変数にこの印がついているかどうかは local-variable-if-set-p で確認できる) というものがあり、 make-local-variable と make-variable-buffer-local とでこの印の扱いが異なる。

手元で試したところ、(make-local-variable 'x) すると if-set値が non-nil になるが、 その後 (kill-local-variable 'x) すれば if-set値は nil に戻る。
一方、一旦 (make-variable-buffer-local 'x) を実行して if-set値が non-nil になった場合、 kill-local-variable しても nil には戻らない。makunbound でも戻らない。 if-set値を nil に戻す方法があるかどうかは不明。

なので、単に「カレントバッファだけ」「全体」という、効果の範囲だけを意識して使うのではなく、 バッファ間で違う変数であることを保証したい、 違う変数であることを前提としている場合 (バッファ1 で値を変更したらバッファ2 での値が勝手に変わるようでは困る、という場合) に前もって make-variable-buffer-local を実行しておく、という使い方になるらしい。

バッファローカル変数を消してデフォルト変数を参照できるようにする

  1つの変数について、カレントバッファでだけ削除:
    kill-local-variable

kill-all-local-variables という関数もあるが、こっちは 「カレントバッファの permanent-local でないバッファローカル変数をすべて」削除する。 メジャーモードの初期化で使うらしい (参考: uenox HomePage Meadow Major Mode)。
というか、マニュアルにも 「この関数が最初に行うことは、ノーマルフックchange-major-mode-hookを実行することである」 とあるし、 影響が大きすぎるので、メジャーモードの初期化以外の場面で使うことはほぼないのでは。

1つの変数についてすべてのバッファでバッファローカル変数を削除したい場合、 組み込みの関数はないようなので次のようにすれば良いかと。


(dolist (buf (buffer-list))
    (with-current-buffer buf
      (kill-local-variable VAR-SYMBOL)))

バッファローカル変数があってもデフォルト変数へアクセスする

バッファローカル変数 x:L をパスして x:D を参照:
  default-value
バッファローカル変数 x:L をパスして x:D を変更:
  set-default
  setq-default

その他の関数

buffer-local-value
buffer-local-variables
  バッファローカル変数の alist を返す
local-variable-p
describe-variable

動作確認用にデフォルト・ローカルの値を一覧表示

実際に自分で手を動かして動作を確認するときに使うと便利。


(defun fixed-width-format (width str)
  (let ((template))
    (setq template (concat "% "
                           (number-to-string width)
                           "s"))
    (substring (format template str)
               0 width)))


;; 1つのバッファに対して
(defun format-local-and-default-variable (buf-name var-symbol)
  (with-current-buffer (get-buffer buf-name)
    (format "%s %s %s %s"
            (fixed-width-format 16 buf-name)
            (fixed-width-format 16 (if (local-variable-p var-symbol)
                                       (symbol-value var-symbol)
                                     "")) ; バッファローカル変数でない場合は何も表示しない
            (fixed-width-format  6 (local-variable-if-set-p var-symbol))
            (fixed-width-format 16 (default-value var-symbol))
            )))


(defun list-local-and-default-variables (var-symbol)
  (interactive)

  (let ((orig-win (selected-window))
        (temp-buffer (get-buffer-create " *local/default variables*"))
        (buffer-names)
        (var-bound (boundp var-symbol)))
    (setq buffer-names
          (remove-if
           (lambda (buf-name) (string-match-p "^ " buf-name))
           (sort (mapcar (lambda (buf) (buffer-name buf))
                         (buffer-list))
                 'string<)))

    (with-current-buffer temp-buffer
      (erase-buffer)
      (insert (format "%s: " (symbol-name var-symbol)))

      (insert
       (if var-bound
           (concat "\n"
                   "buffer           local            if-set default         \n"
                   "---------------- ---------------- ------ ----------------\n"
                   (mapconcat
                    (lambda (buf-name)
                      (format-local-and-default-variable buf-name var-symbol))
                    buffer-names
                    "\n"))
         "not bound"))
      
      (goto-char (point-min)))

    (pop-to-buffer temp-buffer)
    (select-window orig-win)))

list-local-and-default-variables の引数に変数名(シンボル)を渡して実行すると、 バッファ名、バッファローカル変数の値、local-variable-if-set-p の値、デフォルト変数の値が 別ウィンドウで次のように一覧表示される。

x: 
buffer           local            if-set default         
---------------- ---------------- ------ ----------------
      *Messages*                     nil              123
         buffer1                1      t              123
         buffer2                2      t              123

次を実行するとタイマーで自動的に表示・更新させられます。


(defvar timer nil)

;; M-x list-local-and-default-variables:start RET
;; 監視したい変数名を入力して RET
(defun list-local-and-default-variables:start ()
  (interactive)
  (if (memq timer timer-idle-list)
      (message "already started")
    (progn
      (setq var-symbol (intern (read-string "input variable name: ")))
      (setq timer (run-with-idle-timer
                   0.5
                   t
                   (lambda () (list-local-and-default-variables var-symbol) nil))))))

;; 監視をやめる
(defun list-local-and-default-variables:stop ()
  (interactive)
  (cancel-timer timer))

参考(外部リンク)

>> 古い記事: 入力したXPathにマッチした要素をハイライトし要素数を表示するブックマークレット
<< 新しい記事: 電子書籍メモ
** ホームに戻る

コメント

コメントの投稿

管理者にだけ表示を許可する

|
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。