read-only と sticky
たとえば、
From: Mail: ----------
という入力欄を作りたいとして "From: " とかは書きかえられたくないので、 text-property の read-only 属性を使って
;; (get-buffer-create "hoge") しておいてくださいね。
(with-current-buffer "hoge" (let ((inhibit-read-only t)) (erase-buffer)) (insert (propertize "From: " 'read-only t) "\n" (propertize "Mail: " 'read-only t) "\n" (propertize "---------" 'read-only t) "\n"))
とします。 read-only 属性が t であるような文字列は変更することができません。
ところが、実際にはこうすると "From: " までいれたところで "Text is read-only" と怒られてしまいます。これは「read-only である文字列の隣に、 read-only を継承するような文字列をいれることはできない」ためです。
挿入された文字列は基本的に、前方の文字列の text-property を「継承」します。ここでは "From: " の後ろに追加される文字列("\n")が "From: " の text-property を継承し read-only 属性がつくために、"\n" を追加しようとしたところでエラーが発生します。
じゃあ、どうするのか。
property には sticky というものがあり、これが新しく追加された text に property を付けるかどうか制御しています。 先の例では `rear-nonsticky' が nil (default) なので、 "\n" が read-only を継承してしまいました。 これを t にしましょう。
(with-current-buffer "hoge" (let ((inhibit-read-only t)) (erase-buffer)) (insert (propertize "From: " 'read-only t 'rear-nonsticky t) "\n" (propertize "Mail: " 'read-only t 'rear-nonsticky t) "\n" (propertize "---------" 'read-only t 'rear-nonsticky t) "\n"))
すると、今度は元の文字列を消去することはできないものも、"F" と "r" の間にいろいろ入力できてしまいますね。 `front-sticky' 属性を t にして前方に挿入される文字列に対して read-only が継承されるようにしてやりましょう。
(with-current-buffer "hoge" (let ((inhibit-read-only t)) (erase-buffer)) (insert (propertize "From: " 'read-only t 'rear-nonsticky t 'front-sticky t) "\n" (propertize "Mail: " 'read-only t 'rear-nonsticky t 'front-sticky t) "\n" (propertize "---------" 'read-only t 'rear-nonsticky t 'front-sticky t) "\n"))
これで望みのものができた!と思いきや "From: " の後ろで C-k すると "From: Mail: " となってしまい、しかも "RET" やら "C-j" やら押しても "Text is read-only" となってしまいます。
"\n" も read-only にしましょう。ただし、 "\n" の前には入力することができなければいけないので、今度は front-sticky はいりません。
(with-current-buffer "hoge" (let ((inhibit-read-only t)) (erase-buffer)) (insert (propertize "From: " 'read-only t 'rear-nonsticky t 'front-sticky t) (propertize "\n" 'read-only t 'rear-nonsticky t) (propertize "Mail: " 'read-only t 'rear-nonsticky t 'front-sticky t) (propertize "\n" 'read-only t 'rear-nonsticky t) (propertize "---------\n" 'read-only t 'rear-nonsticky t 'front-sticky t)))
今度こそ完成です。
;; ちなみに inhibit-read-only を t にしておくと、 read-only 属性が無視されるのですよ。
参考