入門 xyzzy 読書ノート 3/3

著者:

山本泰三・日枝政弘・稲川知久・佐野匡俊

出版社:

株式会社オーム社

発行年:

2005 年

ISBN:

978-4-274-06600-9

Aa xyzzy Lisp 導入編

.xyzzy やスクラッチバッファに記述するだけで使える Lisp コードを挙げていくコーナー。

Aa.1 定型の文字列の挿入

  • insert 関数でキャレット位置に指定文字列を挿入する。

  • 関数定義の最初に (interactive "*") がある。

  • global-set-key 関数で関数とキーバインドを関連付ける。

    • 文字列とキーバインドを関連付けることもできる。

Aa.2 日付の挿入

  • format-date-string 関数で現在時刻の取得と書式整形を同時に行う。

Aa.3 タグの挿入

「キャレット位置を一時的に移動してから insert する」例を挙げている。

(save-excursion
  (何かキャレット位置を変更する関数)
  ...)
; ここでキャレット位置が復帰する

Aa.4 一括置換

異なるパターンのテキスト置換を一度に行う例を挙げている。

(save-excursion
  (beginning-of-buffer)
  (replace-string "《" "【" t)
  (beginning-of-buffer)
  (replace-string "》" "】" t))

Ab xyzzy Lisp 言語編

Ab.1 実行例について

  • <実行例は場面に応じて *scratch* バッファとミニバッファで評価します> (p. 305)

  • <ミニバッファで式を評価するには、Esc キーを 2 度押すとミニバッファにカーソルが移動するので、式を入力してください> (p. 306)

  • 特殊な理由により、キャレットやバッファを移動するような式は、ミニバッファで評価する。

Ab.2 xyzzy の構成

  • <xyzzy が機能性や拡張性に優れているのは、標準 Lisp ファイルが使用しているさらに低レベルに位置する関数を、ユーザも同じように使うことができるからです> (p. 306)

Ab.4 xyzzy Lisp の言語仕様

すべてを習得する必要はないので(いざとなったら外部アプリ呼び出しができる)、テキトーにノートをとる。

コメント

  • <慣習的に、行頭ならば ;;; 、行頭でない場合には ;; 、コメントに先立って何か入力があれば ; 、と使い分けられているようです> (p. 309)

データ型

  • シンボルがちょっとわかりにくい。 <他のプログラミング言語でいうところの関数名、変数名に相当します> (p. 311) とある。C/C++ でいうところの識別子という解釈でよろしいか。

  • <シンボルを式中に記述する場合には、'シンボル という形式で記述します> (p. 311)

キーワード引数の指定

;;; 文字列の比較 (p. 316)
(string= "other" "other") => t
(string= "other" "mother") => nil
(string= "other" "mother" :start2 1) => t

文字列のマッチ・置換

foo とか bar とかいう変な名前を多用するのは勘弁して。

  • string-match と match-string をペアで利用する。

制御構造

;;; 複数の式を評価する。if で頻繁に使うらしい。
(progn 式1 ... 式N)

;;; let のスコープで有効な変数を扱える
(let (変数名1 ... 変数名N) 式1 ... 式M)

;;; if
(if 条件式 式1 [式2])

;;; when - if と似ている
;;; unless - if not の意
(when 条件式 式1 ... 式N)
(unless 条件式 式1 ... 式N)

;;; cond - C 言語の switch に相当する
(cond (条件式1 式11 ... 式1N1)
      (条件式2 式21 ... 式2N2)
      ...
      (条件式M 式M1 ... 式MNM))

;;; dolist - リストの走査
(dolist (変数 リスト) 式1 ... 式N)

;;; dotimes - カウンターを使ったループ
(dotimes (変数 回数) 式1 ... 式N)

;;; while
(while 条件式 式1 ... 式N)

;;; loop - endless loop
(loop 式1 ... 式N)

;;; return - ループを break する
(return [式])

関数の定義

(defun 関数名 (引数1 ... 引数N)
   式1 ... 式M)
  • デフォルト引数は &optional

  • キーワード引数は &key

    • 値の指定方法は :仮引数名 と書く。

  • 可変個引数は &rest

マクロの定義

Lisp はマクロが強力だと聞いたことがある。

  • <xyzzy Lisp では実行時の情報を参照しつつマクロ展開を動的に行うことができる> (p. 333)

Ac xyzzy Lisp 基本編

大雑把にノートをとるので、後で確かめるときには既存の Lisp ファイルに当たること。

Ac.1 テキスト

  • M-x から入力して実行できる関数は (interactive) なモノに限る。

  • 各行について処理を行うコードは次のようになる。

    ;;; 各行について処理を行うコード
    (loop 処理 (unless (next-line) (return)))
    
  • スキップ系の関数を使い分ける。 skip-chars-forward, skip-white-forward, skip-syntax-spec-forward 等がある。

  • テキストの挿入は insert が基本。キャレット位置に文字列をねじ込む。

  • テキストの取得は buffer-substring を使う。

    ;;; カーソルのある行を取得 (p. 342)
    (buffer-substring (progn (goto-bol) (point))
                      (progn (goto-eol) (point)))
    
  • テキストの削除は delete-region を使う。

  • テキストの検索は <scan-buffer を使うことをおすすめします> (p. 342)

    • しばしば match-string, match-beginning, match-end を併用する。

    • scan-buffer のキーワード引数を使いこなせるように。

  • テキストの置換は replace-buffer を使う。こちらもキーワード引数がある。

  • save-excursionsave-restriction を同時に利用するならば、この順序で入れ子にするべし。さもないと、キャレット位置が元に戻らない場合がある。

  • 正規表現のコンパイルは compile-regexp を用いる。

    • 正規表現はバックスラッシュが面倒。

Ac.3 ファイル

  • 意味のあるフォルダ各種を取得する関数がいくつか存在する。

    (default-directory) ; バッファに関係する作業フォルダを返す
    
    (si:system-root) ; $XYZZY を返す
    (etc-path) ; $XYZZY/etc を返す
    (user-homedir-pathname) ; ホームディレクトリ ~ を返す
    
    (get-special-folder-location :start-menu) ; スタートメニューのパスを返す
    
  • パス名を表す文字列を操作するための関数がある。

    • パスを連結するには merge-pathnames を用いる。Python での sys.path.join に相当する?

    • フォルダセパレーターは / でも \ でも構わない。

  • パス名を表す文字列を与えて、それが実在するか、属性はどうなっているのか、といったことをテストする関数が存在する。

  • ファイルのコピーや削除といった、シェル的な操作をする関数も提供する。

  • 関数 directory を使うと、UNIX の ls みたいなことができる。本関数はリストを返すので、これを dolist で走査するのが基本的な利用法。

Ac.4 バッファ

バッファは、ファイルに関連付けられた「オブジェクト」だと解釈すればよさそうだ。

  • xyzzy では、常にバッファが一つだけ選択されている(カレントバッファ)。 selected-buffer で得られる。

  • ファイル名、パス名からバッファを得ることが可能 (find-buffer, get-file-buffer)

  • buffer-list 関数で、その名前が示すものが得られる。

  • すべてのバッファを対象とする処理は、次のように書くことになる。

    (save-excursion
      (dolist (buf (buffer-list))
         (set-buffer buf)
         (バッファを対象とする処理)))
    
  • エンコーディング

    • buffer-fileio-encoding, set-buffer-fileio-encoding

    • <文字エンコーディングに使用する定数は $XYZZY/lisp/encoding.l で定義されています> (p. 357)

  • 改行コードを buffer-eol-code, set-buffer-eol-code でアクセスできる。

  • メジャーモードを変数 buffer-mode で参照できる。

  • need-not-save 変数で、バッファの内容を保存する必要性を操作できる。変数名に not が入っているので、意味を逆に取り違えそうで怖い。

Ac.6 キーマップ

キーバインドと、それに関連付けられたコマンドの対応をまとめた表をキーマップと呼ぶ。

キーマップは 3 つに分類して理解するとよさそうだ。

  1. グローバルキーマップ。優先度が最も低い。変数 *global-keymap* で管理されている。

  2. ローカルキーマップ。メジャーモードのキーマップ。例えばテキストモード (text-mode) のキーマップは text-mode-map で管理されている。

  3. マイナーモードマップ。マイナーモードのキーマップであり、優先度が最も高い。

  • 既存のキーマップに、キーバインドを追加することと、キーマップに指定するキーバインドがあるか否かをテストする方法だけ押さえておけばよさそうだ。

    • (define-key キーマップ キー コマンド) で追加。

    • (lookup-keymap キーマップ キー) でコマンドまたは nil が返る。

Ad xyzzy Lisp 応用編

Ad.1 メニューを作成する

  • メニューは Lisp で定義されている。ユーザーがカスタマイズすることが可能。

  • メニューには、メニューバー (*app-menu*) とポップアップ (*app-popup-menu*) の二種類がある。

  • メニューは、xyzzy の起動時に作成されている。

メニューの静的な作成

(define-menu 項目1 ... 項目N)
(define-popup-menu 項目1 ... 項目N)
  • 項目は次のどれかの式になる。

    :sep
    :separator
    (:sep タグ)
    (:separator タグ)
    (:item タグ 項目名)
    (:item タグ 項目名 コマンド)
    (:item タグ 項目名 コマンド 表示形式)
    (:popup タグ 項目名 項目1 ... 項目N)
    
    • タグがよくわからない。 nil でよいようだ。

    • 項目名は文字列。

    • コマンドは選択時の処理を記述するわけだが、 <対話的な関数を指定しないと、メニュー項目を選択した時点でエラーが発生する> (p. 373)

    • 表示形式はメニュー項目のグレーアウトの条件を決める。 :modified, :undo, :clipboard 等がある。

    • $XYZZY/lisp/app-menu.l が参考になる。

メニューの動的な作成

;;; メニューをゼロから作成する
(create-pop-menu) => メニューを作る
(create-popup-menu) => ポップアップメニューを作る

;;; メニューにセパレーターを入れるには次のいずれかを用いる
(add-menu-separator メニュー)
(add-menu-separator メニュー タグ)

;;; メニューアイテムの追加は次のいずれか
(add-menu-item メニュー タグ 項目名)
(add-menu-item メニュー タグ 項目名 コマンド)
(add-menu-item メニュー タグ 項目名 コマンド 表示形式)

;;; ポップアップメニューの追加はこれ
(add-popup-menu メニュー ポップアップメニュー 項目名)

メニューの挿入

  • pipe-commandfilter-buffer を呼び出す例は、いいヒントになりそう。

(insert-popup-menu *app-menu* 6 自作メニュー "外部コマンド(&O)")
  • <このように位置を直接指定してもよいのですが、タグを指定したほうが他の拡張 Lisp との兼ね合い上安全です> (p. 377)

;;; 修正後
(insert-popup-menu *app-menu*
  (get-menu-position *app-menu* 'ed::help)
  自作メニュー "外部コマンド(&O)")
  • <use-local-menu を使うとバッファだけに有効なメニューバーを設定できます> (p. 378)

  • 初期設定ファイルで *app-menu* を直接変更してはならない。 <初期設定ファイルでメニューのカスタマイズを行う際には必ず次のように記述してください。(略)メニューバーが画面から消えてしまう場合があります> (p. 379)

    (add-hook '*init-app-menus-hook* メニューのカスタマイズを記述)
    

Ad.2 ツールバーを作成する

普通の意味でのツールバーと、タブバーをまとめて説明している。

Ad.3 メジャーモードを作成する

  • <基本的にファイル名から拡張子をとったものをモジュール名とします。モジュールを宣言すると、ロード済みモジュールを管理しているリスト *modules* にモジュール名が追加されます> (p. 391)

  • <*scratch* などのバッファで評価する場合には user パッケージが選択されています> (p. 391)

  • <キーワードファイルは *keyword-load-path* から検索されます> (p. 393)

  • シンタックステーブルとは、<文字の構文上の扱いの違いを表現する> (p. 394) もの。構文クラスという考え方があり、空白文字、シンボル構成文字、コメント開始・終了文字などがある。

    • <正規表現で検索する場合には、単語の開始位置、シンボルの開始位置はシンタックステーブルを参照して判断されます> (p. 395)

  • mode-specific-なんとか 変数群は面白そうだ。

B その他の情報

B.1 FAQ

  • C-q TAB (M-x quote-char) で、どんなときでもタブ文字をキャレット位置に挿入できる。

  • フック関数にて、仕込んであるキーバインドを undefine-key 関数で削除できる。

  • USB メモリ上で使用するやり方。今ならもうちょっといい方法がありそう。

Emacs 風設定

個人的に必須だと判断したものを抜書きしておく。

;;; pp. 410-412

;;; インクリメンタルサーチ
;;; C-s でダイアログが出なくなる
(require "isearch")

;;; M-% を確認アリ置換にする
(define-key esc-map #\% 'query-replace)
(define-key esc-map #\C-% 'query-replace-regexp)

;;; C 言語系編集時にはこれがないと話にならない
(define-key esc-map #\C-\\ 'indent-region)

;;; 矩形処理各種のうち、次の三つはキーバインド必須
(define-key ctl-x-map '(#\r #\k) 'kill-rectangle)
(define-key ctl-x-map '(#\r #\t) 'string-rectangle)
(define-key ctl-x-map '(#\r #\y) 'yank-rectangle)

;;; M-/ で動的補完
(require "dabbrev")
(define-key esc-map #\/ 'dabbrev-expand)

B.4 共通設定ダイアログ

確認したい項目だけノート。

  • フォントタブ

    • フォント (Terminal)

    • サイズ (10)

    • をバックスラッシュに変換 (ON)

  • 表示タブ

    • 表示する?

      • 改行 (ON)

      • TAB (ON)

      • 全角スペース (ON)

      • 行番号 (ON)

      • EOF (ON)

      • ルーラ (ON)

    • テキストの折り返し (OFF)

    • タブの幅 (4)

    • キャレットを点滅 (OFF)

    • タブバーを横書きにする (ON)

  • バックアップファイルタブ

    • バックアップファイルをつくる (OFF)

  • 自動セーブタブ

    • 自動セーブする (OFF)

  • さまざま

    • 音を鳴らす系のオプションを無効にする。

    • ウィンドウの位置・サイズに関するオプションを有効にする。

B.5 ローカル設定ダイアログ

  • <ローカル設定は共通設定よりも優先され、現在のバッファのみに適用されます> (p. 436)

B.6 正規表現

  • 日本語が扱える

  • ()|{} は通常の文字として扱われる。エスケープが必要。

  • 文字クラスや等価クラスといったものには対応していない。

  • メタキャラクター

    • \s, \S は構文クラス文字マッチ。

B.8 標準キーバインド一覧

  • 「標準のまま利用するもの」「別のキーバインドに置き換えるもの」「無効化するもの」に分類して整理したい。

    • Emacs と同じ意味を持つキーバインドは「標準のまま利用」する。

    • Emacs と違う意味を持つものは、Emacs 風にキーバインドを定義しなおす。

    • 自分が絶対に利用しないコマンドのキーバインドは、すべて undefine-key する。