Elisp 入門

最終更新: [2006/06/15]
English version is where??
Emacs Lisp でスクリプトを組んで Emacs を自分好みに!!


Hello World

ESC ESC (insert "Hello, World")
ESC ESC (message "Hello, World")

Elisp とは?

Elisp とは emacs-lisp の(私だけの?)略記で,Emacs/Mule の動作記述を行 なうことのできる言語です.ほぼすべての Emacs 用のパッケージはこの言語を 利用して書かれています.これを会得すると「貴方好み」の Emacs 環境を構築 することができるようになります.Emacs の初期設定ファイル ~/.emacs もこの 言語で記述します.(ちなみに,かな漢字変換のかんなの初期設定ファイル ~/.canna も LISP 形式で記述しますが,Emacs には読み込まれません.こちら は Canlisp といってかんな側に読み込まれます).

表記については Elisp という場合と Emacs Lisp という場合と,さらには部分 的な大文字・小文字の違いによっても実際には区別するようですが,ここでは単 純に Emacs 用の言語という意味合いで使います.Lisp というのはリスト処理を 得意とする言語です.人工知能分野の研究で多く使われていたりします.Elisp はそれをベースにした言語です.

手始めに...

まずは,Emacs の *scratch* バッファで遊びましょう.これは通常 Lisp-Interaction (「Lisp でお話モード」) モードになっています.このモー ドで直に elisp コードを実行できます.Elisp の動作を確認するには最適のモー ドです.もし,このモードになっていない場合は,"M-x lisp-interaction-mode" を実行して下さい.

値を設定してみる

*scratch* モードで次のように打ち込んで最後に C-j を入力して下さい.
  (setq foo "This is foo")
C-j は emacs に式の評価を指令します.その結果 "This is foo" が表示された と思います.setq は「指示した変数に値を設定する」という関数で,先の例を 実行した結果 foo 変数に "This is foo" という値が設定されます.バッファに 表示されたものは,この設定の結果(式の評価結果)です.

次にこの設定を確認してみましょう.

  foo
とだけ打ち込んでから C-j を入力して下さい."This ..." と表示されましたね? これは foo 変数に設定された値を示しています.

Elisp の概要

コメント

行中でセミコロン ; 以下の部分はコメントとみなされ無視されます.

検索パス

load-path

フック

add-hook, hook, hooks, run-hooks

リスト処理

car, cdr, cons, append, list, quote

リストへの繰り返し処理

mapcar

制御構造

if
while
progn
cond
catch
throw
let
provide

評価

eval-current-buffer, eval-defun, eval-region

文字列の取扱い

format, string-width, string-match, match-beginning, match-end, substring, substring-buffer, truncate, string=, downcase, upcase

変数の取扱い

defvar
defconst
set
setq
let
let*
fset

ユーザインターフェース

read-char, read-string, y-or-n-p

キー割り当て

make-sparse-keymap

値の確認

insert, message, boundp, fboundp

演算子

eq, equal, not, and, or, +, -, *, /, >, <, =

ポイント

point, point-min, point-max, goto-char, save-excursive, narrow-to-region, widen, save-restriction, current-column, move-to-column, left-margin, indent-to-left-margin, newline, beginning-of-line, forward-line, end-of-line, bobp, eobp

バッファ

get-buffer, get-buffer-create, get-buffer-window, set-buffer, with-output-to-temp-buffer, buffer-flush-undo, set-buffer-modified-p, rename-buffer, current-buffer, copy-region-as-kill, kill-region, delete-region

プロセス

start-process, call-process, get-process, process-buffer, process-list, list-processes, process-kill-without-query, process-name, process-status, processp, delete-process, kill-process, quit-process, set-process-filter

ウィンドウ

select-window, selected-window, window-live-p

ファイル

read-file

検索/正規表現

re-search-forward/backward, string-match

値の補完

completion-read

候補を並べる

リストを使用します.
(setq foo-alist '(("^foo" . "Foo") ("^bar" . "Bar") (".+". "any")))
   (while foo-alist
     (car (car foo-alist))
     (setq foo-alist (cdr foo-alist)))

メジャーモード関数を作成してみる

major-mode

対話的な関数の指定

interactive, interactive-p, call-interactively

キーの割り当て

mode-map, local-set-key, global-set-key

インデント処理

left-margin, fill-column, fill-prefix, fill-paragraph
indent-line-function, indent-new-comment-line

もろもろ

特殊変数

this-command 直前に実行したコマンド名がわかる

デバッグ

debug-on-entry 関数でデバッグしたい関数にブレークポイントを設定する方法 もありますが,C 言語での printf デバッグと同様に,いちいち自分で確認して いくほうが結局は確実でやりやすい感じを持っています.次のような行を適宜ソー スへ組み込んでいます.
例:foo 変数(数値), bar 変数(文字列)の値を確認する
(message (format "foo:%d bar:%s" foo bar))

わからんことは Emacs に聞け!

Emacs はヘルプ機能が結構充実しています(何の情報も得られないことも多々あ りますが...).不明なことはとりあえず Emacs に聞いてみましょう.

help-for-help

"M-x help-for-help" としてヘルプを呼び出します.そして, の1文字を押して何を検索するか,次に検索項目を指定します.

Info


メモ

---
[2001/12/25]
elisp: 浮動少数点の整数への変換は (truncate, floor, ceiling, round) の
4つ.逆に整数からの変換には float を使う.
  -->floor とかで整数になるってのも不思議な実装よのう...
---
[2001/07/24]
Mew のサマリーフォーマットを変更する.
  -->昔 (mew が MH ベースだった頃) は mew-scan-form という elisp 変数
     で変更できたようだが,現在は IM というものベースになり,
     ~/.im/Config ファイル中の Form= の行で変更するのらしい. 
    -->~/.im ディレクトリがない場合は imsetup コマンドで初期設定を行な
       うこと.
  -->例:Form=%3n %m %d %i[%3K: %-14P] %s
  -->フォーマットの冒頭は %n のメッセージナンバーから始めないと mew で
     メッセージ本体を表示できない.
  -->%C で月名が表示できるらしいのだが,これを指定するとサマリー名が取
     得できず失敗する.
    -->%C を冒頭に指定していて数字でないためだったらしい.%n を指定後
       に %C を入れると動作した.
    -->"Jul 24" のように月名+日で表示したいのに,%d では 月/日となり
       日だけを指定する指定子がない.
  -->フォーマット変更の反映は mew を起動後に O (mew-summary-pack) を押
     して行なう.
---
[2001/05/14]
elisp: ie を起動させるのに start-process を使うとうまくいかない。
w32-shell-execute に "open" url の2つの引数を渡すとブラウザは選択でき
ないが現在開いているブラウザ内に URL を開くことはできる(新たにウィン
ドウを開くにはどうするんだろう?)。
---
[2000/12/01]
elisp: 改行 RET のキー割り当ては \C-m で行う.
---
[2000/11/23]
elisp: prin1-to-string と format の違い
  -->(format "%s" foo) では foo の内容に " が含まれるとこれを除外する.
     (prin1-to-string foo) では " も含めて文字列にする.
  -->(print1-to-string foo t) とすると " は含めず無加工となる.
---
[2000/05/29]
elisp で Mule/Emacs が X 上で動いているかの識別
  -->window-system 変数を見る.X 上であれば x そうでなければ nil が返
     る.
---
[1999/07/07]
elisp で現在リージョンに入っている単語に対して incremental-search を掛
けるには?
---
[1999/07/05]
elisp:save-excursion の中で一時的にこれを抜け出して実際の移動を行うよ
うにはできないか?
---
[1999/06/02]
elisp で文字列 split はどうやるか?
  -->こんなのを組んでみた.
     (defun string-split (split-char str)
       (let (list (m 0) (n 0))
         (catch 'done
           (while 1
             (if (not (setq m (string-match split-char str))) (throw 'done 1)
               (setq list (cons (substring str 0 m) list))
               (setq str (substring str (+ m 1)))
               )))
         (setq list (cons (substring str n) list))
         (reverse list)
         ))
  [2002/01/23]
  -->split-string というのがあったよ...
---
[1999/06/01]
elisp の連想リストで指定キーのリストが何番目にあるかはどうやって取得す
るのか?
  -->こんな関数を書いてみた.
     (defun nthassoc (key list)
       (if (not (assoc key list)) nil
         (let ((n 0))
           (catch 'done
             (while list
               (setq id (car (car list)))
               (if (numberp key) (setq key (int-to-string key)))
               (if (numberp id)  (setq id  (int-to-string id )))
               (if (string= id key) (throw 'done 1))
               (setq n (+ n 1))
               (setq list (cdr list))))
           n)))
---
[1997/12/27]
elisp:指定位置のリストを置き換える(あってる?)
  -->(setq aaa '(a b c d e f g h i j k))
     (append
       (reverse (nthcdr (- (length aaa) 8) (reverse aaa)))
       '(0)
       (nthcdr 9 aaa))
  [1999/06/01]
  -->こんな関数にしてみたがどうか...
     (defun nthdelete (n list)
       (nthreplace n list nil))
     (defun nthinsert (n list ins)
       (nthreplace n list (reverse (cons (nth n list) ins))))
     (defun nthreplace (n list rep)
       (append
        (reverse (nthcdr (- (length list) n) (reverse list)))
        rep
        (nthcdr (+ n 1) list)))
  [2002/05/04]
  -->次のほうが元のリストを破壊するけどシンプル
     (setcar (nthcdr n list) 'ins)
---
[1999/06/01]
elisp でのリスト要素の置き換えってどうやるんだっけ?
  -->一つずつリストを見ていって新しいリストを作成するのだったか?
---
[1999/03/19]
Emacs で空いているキーバインドを探しやすくするにはどうすればいいか?
  -->なにか elisp パッケージはないの?
---
[1999/01/26]
elisp での外部コマンド起動は shell-command ではなく start-process を使
うべきか?
  -->see.
     file:$YATEX/yahtml.el
  -->(start-process NAME BUF PROGRAM &rest ARGS)
  -->ex.
     (start-process "mecl" nil "mecl" "~/.cshrc")
---
[1999/01/13]
elisp で telnet 接続
  -->from. telnet.el
    -->(make-comint "telnet" "telnet")
       (accept-process-output (get-process "*telnet*"))
       (send-string "*telnet*" "oshiro\n")
       (send-string "*telnet*" "passwd\n")
       (send-string "*telnet*" "exit\n")
  -->Web 接続は??
     (defun get-url (path host &optional method)
       (if (null method) (setq method "GET"))
       (setq bufname "jugo-www")
       (switch-to-buffer
         (make-comint bufname "telnet" nil host "http"))
       (erase-buffer)
       (setq bufname (concat "*" bufname "*"))
       (send-string bufname (concat method " " path " HTTP/1.0\n\n"))
       (switch-to-buffer (get-buffer"*scratch*")))
     (defun format-html-buffer ()
       (switch-to-buffer "*jugo-www*")
       (goto-char (point-min))
       (replace-string "
" "")
       (goto-char (point-min))
       (if (re-search-forward "^$")
           (kill-region (point-min) (point)))
       (goto-char (point-max))
       (forward-line -3)
       (kill-region (point) (point-max))
       (goto-char (point-min))
       (mapcar (lambda (f) (goto-char (point-min)) (replace-regexp f ""))
    	    '(
    	     "</H[0-9]> *\n?"
    	     "</?A\\( [^>]+\\)*> *\n? *"
    	     "<!--.+--> *\n?"
    	     "<!--.+\n?"
    	     ".*--> *\n?"
    	     "<BODY\\(.+\\)*> *\n? *"
    	     "<HEAD\\(.+\\)*> *\n? *"
    	     "<HTML\\(.+\\)*> *\n? *"
    	     "</?TITLE\\(.+\\)*> *\n? *"
    	     "</?[IB]> *\n? *"
    	     "</BODY\\(.+\\)*> *\n? *"
    	     "</HEAD\\(.+\\)*> *\n? *"
    	     "</HTML\\(.+\\)*> *\n? *"
    	     ))
       (mapcar
         (lambda (f) (goto-char (point-min)) (replace-regexp f "\n"))
                     '("</?DL> *\n? *" "</?UL> *\n? *"
		       "<H[0-9]> *\n? *"
		       "</?DIV\\(.+\\)*> *\n? *"))
       (mapcar
         (lambda (f) (goto-char (point-min)) (replace-regexp f "\n  * "))
    	             '("<LI> *\n? *" "<DT> *\n? *" "<DD> *\n? *"))
       (goto-char (point-min))
       (replace-regexp " *\n?<HR> *\n? *" "<HR>")
       (goto-char (point-min))
       (replace-string "<HR>" "\n------")
       )
     (get-url "/" "jugo")
     (format-html-buffer)
    -->w3/url.el の次の関数が参考になるかも.
       url-open-stream
       url-http
       url-retrieve-internal
       url-retrieve
      -->url-open-strem でも telnet で接続してるみたい
	 (start-process name buffer "itelnet" host service)
	-->itelnet って何?
---
elisp: gnus のサマリーで b を押すと gnus-group-check-bogus-groups が呼
び出され,(たぶん)その時点でサーバ側に存在していないニュースグループ
を削除する.このとき一つずつ削除するかどうかを聞いてくるので削除件数が
多いとこの確認がうっとおしい.これを一気に行なうには ESC ESC で 
eval-expression に入り (gnus-check-bogus-newsgroups nil) とする.
---
Emacs/Mule で関数や変数を探すときに help-for-help してからの補完を利用
することが多い.特に動作変更を行うための設定変数を探すのに有効な感じ.
---
Emacs でのエイリアス
  -->defmacro では interactive に呼び出せない??
    -->関数形式にして,該当関数を call-interactively で呼び出すように
       した.
       (defun ct () (interactive) (call-interactively 'canna-touroku-region))
      -->defalias というものがあった.これを使えば大丈夫.
	 (defalias 'ct 'canna-touroku-region)
---
Emacs/Mule でのメールエイリアス更新.
  -->(build-mail-aliases &optional FILENAME)
     non-interactive 関数
---
~/.canna ファイルは Emacs が読む elisp ではなく canlisp という別の処
理系のものだったらしい.canlisp のドキュメントはなんとか見つけたけど,
mule から呼び出せるかんなの関数ってどこにドキュメントが存在しているん
だろうか?
---
Emacs のファイルの冒頭でのモード指定('# -*- memo -*-' など)で,サブモード(?,auto-fillとか)を追加して指定するのはどうやるの?
  -->冒頭指定ではないけど (setq memo-mode-hook 'turn-on-auto-fill) で
     指定可能
  -->c.f. Info:emacs-jp:Major Modes:
---
Emacs でのモード指定について
  -->vid.Info:emacs-jp:Choosing Modes
---
elisp での let と let* の違い
  -->表面的にはどちらの動作も局所変数の作成で同じ.ただし,let* では局
     所変数への初期値設定で既に出現している局所変数の値を使用すること
     ができる.
  -->eljman.info:ローカル変数をバインドする より抜粋
    -->(setq Y 2)
       (let (X (Y 1) (Z Y)
         (list X Y Z))
      -->nil 1 2
	 スコープに入る前の Y の値が Z に設定された
    -->(setq Y 2)
       (let* (X (Y 1) (Z Y))
	    (list X Y Z))
      -->(nil 1 1)
	 スコープに入ってからの Y の値が Z に設定された.
---
emacs lisp の eq と equal は違う
  -->eq は対象(アトム?)そのものが同じかどうか
  -->equal は字面や値が同じかどうか
---
elisp のモード指定をファイルオープン時に自動的に行うときには,そのモー
ド移行が行われているときにはウィンドウはまだポップアップされていないら
しい.だから,set-window-start などはそのままでは有効とならない
(goto-char, re-search-forward などは有効となる).
  -->set-window-start を使うためには switch-to-buffer で明示処理する必
     要がある?
     (switch-to-buffer (buffer-name (current-buffer)))
---
elisp で外部起動コマンドを取り込むには
  -->(shell-command ...)
     (call-process ...)
---
elisp でポイント設定
  -->p に値を設定 ((setq p (point)) など).
     (set-window-point (get-buffer-window (current-buffer)) p)
---
elisp でカーソル位置に記述されている情報を取得するのは ...-at-point と
いう関数を使うのか?
  -->edic.el などのデフォルト引数の取得に使えるはず.
  -->vid. ~mule/lisp/help.el:function-called-at-point
  -->(defun show-word-at-point ()
       (interactive)
       (let (b e w)
         (save-excursion
           (backward-word 1)
	   (setq b (point))
	   (forward-word 1)
	   (setq e (point))
	   (setq w (buffer-substring b e))
	   (message "%s" w))))
---
elisp でカーソル位置のテキストから変数名を取得するには?
  -->variable-at-point?
---
elisp では  save-excursion と indent-to-left-margin は相性が悪い?
  -->(save-excursion ...(indent-to-left-margin)...) という場合にカレン
     トポイントが行頭スペースだと,ゼロカラムに位置が戻ってしまう??
  -->save-restriction という関数もあるらしい.これはなに?
    -->(narrow-to-region b e) の影響除去に使う.
---
elisp でバッファ・ウィンドウを操作
  -->buffer
    -->buffer-list
    -->bufferp: (bufferp (get-buffer "*scratch*"))
  -->window
    -->windowp
    -->one-window-p
    -->delete-window
    -->window-live-p
    -->selected-window
    -->select-window
    -->next-window
    -->set-window-buffer
---
リスト要素の削除と保存
  -->(progn
     (setq a '("b" ("c" "d")("e" "f")))
     (if t (setq g a) (setq g (copy-alist a)))
     (delq (car (cdr g)) g)
     (eq a g)
     )
---
elisp:delq でリスト要素の削除
  -->(progn
     (setq a-lis '(a b c))
     (setq a-cdr (cdr a-lis))
     (setq a     (car a-cdr))
     (setq a-cdr (delq a (copy-alist a-cdr)))
     )
    -->(c)     # b が削除できた
       (a b c) # a-lis はそのまま
  -->(progn
     (setq a-lis '(a (b c) (d e)))
     (setq a-cdr (cdr a-lis))
     (setq a     (car a-cdr))
     (setq a-cdr (delq a (copy-alist a-cdr)))
     )
    -->((b c) (d e)) # (b c) が削除できない
  -->(progn
     (setq a-lis '(a (b c) (d e))) # '(a b c) でも動作は同様
     (setq a-cdr (cdr a-lis))
     (setq a     (car a-cdr))
     (setq a-cdr (cdr (delq a a-lis)))
     )
    -->((d e))   # これなら削除できる
       (a (d e)) # a-lis にも反映した
---
elisp:指定位置のリストを置き換える(あってる?)
  -->(setq aaa '(a b c d e f g h i j k))
     (append
       (reverse (nthcdr (- (length aaa) 8) (reverse aaa)))
       '(0)
       (nthcdr 9 aaa))
---
elisp で指定位置のリストを抜きだす
  -->(setq foo '(a b c))
     (elt foo 2)
    -->c
---
elisp で連想配列を使う
  -->(progn
       (setq foo '(("bar" (1 2 3)) ("baz" (4 5 6))))
       (assoc "baz" foo))
    -->RET VALUE:("baz" (4 5 6))
---
elisp でリスト操作
  -->(setq foo-alist '(("^foo" . "Foo") ("^bar" . "Bar") (".+". "any")))
     (while foo-alist
       (car (car foo-alist))
       (setq foo-alist (cdr foo-alist)))
---
elispメモ
  -->先行引数のチェック current-prefix-arg
     一定時間の処理停止 sit-for
     インタラクティブ呼出チェック interactive-p
---
elisp で,C-u で与えた値を得る関数
  -->prefix-numeric-value
---
elisp で生成した変数を消去するにはどうやるの?
---
Elisp での候補の選択と学習の例
  -->yatex.el:YaTeX-cplread-with-learning
    -->completing-read-with-history
       (completing-read prompt table predicate require-match init)
  -->glog-require-bbs-type
---
elisp の mapcar は何の処理をするの?
  -->ex. (mapcar 'make-local-variable (... indent-line-function ...)
  -->(mapcar 'foo (A B C..)) の形式で 
     (foo A) (foo B)... の繰り返し呼出しを実現できる.
---
Mule でドラッグ&ドロップってできるのかしら?
  -->vid.
     http://www.asahi-net.or.jp/~pi9s-nnb/myelisp.html
       難波 elisp ページ
       http://www.asahi-net.or.jp/~pi9s-nnb/dired-dd_toc.html
         Dired Drag and Drop
---
Elisp の引数を対話的に入力する際に (default: foo) と聞いてきてくれる場
合がある.あれはどうやって実現してるのかなぁ.
  -->elisp で (default:XXX) とデフォルト候補を処理するにはどうするか?
    -->vid. glog-require-file-name
  -->TYPE-I:空引数でデフォルトを指定する
     (setq foo (read-string (format "Foo: %s"
                 (if defval (format "(default: %s)" defval) ""))))
     (if (string= foo "") (setq foo defval))
  -->TYPE-II:文字列取得の初期文字列を指定する(編集させたいときなど)
     (setq foo (read-string "Foo: " defval))
---
elisp でプロセスの処理をしているときに sleep-for を使うと勝手にそのプ
ロセスが終ってしまうことがあるらしい.
---
cons によるリストの結合
  (cons 'd '(a b c) )
    -->(d a b c)
  (cons '(a b c) 'd )
    -->((a b c) . d)
  (cons '(d) '(a b c))
    -->((d) a b c)
  (cons '(a b c) '(d))
    -->((a b c) d)
---
Elisp での文字数カウント
  -->(string-width "xxx")
interactive がうまくいかないのは関数定義への引数リストを付け忘れている
からかも
---
memo-mode.el で string-width という関数を使用していたが,標準ではない
らしい.
  -->vm に付属の関数か?
    -->tiny-mime に付属の関数のようである.
  -->Mule なら標準装備の関数なのかな?
---
Mule 中での全角文字列の取扱いは?
  -->(setq str "あいうえおかきくけこさしすせそ")
    -->(string-width str)
       30
    -->(substring str 0 30)
       "あいうえおかきくけこ"
    -->(substring str 0 45)
       "あいうえおかきくけこさしすせそ"
  -->(setq str "abcdefghijklmnopqrstuvwxyz")
    -->(string-width str)
       26
    -->(substring str 0 26)
       "abcdefghijklmnopqrstuvwxyz"
  -->全角の場合は substring と string-width とで字数の数えかたに食い違
     いがある??
  -->truncate-string をならば大丈夫?
---
elisp での,y/n の取得
  -->(y-or-n-p "Ok?")
---
elisp で改行挿入を表すにはどうするのか?
  -->物理的に改行するしかないのか.論理的な表現はないのか('\n' のよう
     な).
    -->'\n' でいけました (^^).
---

参考文献


|| Doc || Programs || BBS || Oshiro Home ||

Mail-to: oshiro@mibai.tec.u-ryukyu.ac.jp