プログラミング言語のメジャーモードを書いていますが、古いEmacsバージョンをサポートしたいと考えています。prog-mode
比較的新しいです。prog-mode
定義されている場合は継承したいのですが、それ以外の場合は賢明なことを行います。
最善のアプローチは何ですか?私がすべきdefalias
prog-mode
古いのEmacsen上、または、彼らは同じことを行う場合には、他のモードと干渉するのだろうか?
プログラミング言語のメジャーモードを書いていますが、古いEmacsバージョンをサポートしたいと考えています。prog-mode
比較的新しいです。prog-mode
定義されている場合は継承したいのですが、それ以外の場合は賢明なことを行います。
最善のアプローチは何ですか?私がすべきdefalias
prog-mode
古いのEmacsen上、または、彼らは同じことを行う場合には、他のモードと干渉するのだろうか?
回答:
追加のトップレベルシンボルバインディングを犠牲にして、define-derived-mode
フォームの繰り返しを回避する非常にきちんとしたソリューションがあります。
(defalias 'my-fancy-parent-mode
(if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode))
(define-derived-mode my-fancy-mode my-fancy-parent-mode
...)
23以上のすべてのEmacsでhaml-mode
正常に動作します。数年前にIIRC でこれを思いつきましたが、そこから他のいくつかの主要なモードに広がったようです。define-derived-mode
マクロが親モードシンボルで行う主なことは、その関数を呼び出すコードを生成することです。この意味でdefalias
、新しい変数はエイリアス関数とまったく同じになります。
1つの注意点は、これが混乱derived-mode-p
するprog-mode
可能性があることです。そのため、モードの派生元かどうかを確認するコードは正しく機能しない可能性があります。実際には問題は発生していません。そのようなコードがにフックするのがより一般的ですがprog-mode-hook
、それでも実行されます。
(ヨルゲンはコメントで指摘するように、define-derived-mode
また、使用するmode-class
親モードシンボルからプロパティを、そしてdefalias
それをコピーしません。書いている時点では、このプロパティはのみのために使われているようですspecial-mode
。)
更新:最近のバージョンでは、Emacs 24以上を要求することをお勧めします。古いバージョンは古くなっているためです。
prog-mode
ただし、これはで機能しますが、すべてのモードで機能するとは限りません。シンボルプロパティを子モードにdefine-derived-mode
コピーしmode-class
ます。defalias
うではない、このプロパティを転送します。mode-class
ユースケースに関連する場合は、手動でコピー/設定する必要があります。
mode-class
。
tl; dr:if
独自のinit関数を使用します。
(if (fboundp 'prog-mode)
(define-derived-mode your-cool-mode prog-mode "Cool"
"Docstring"
(your-cool--init))
(define-derived-mode your-cool-mode nil "Cool"
"Docstring"
(your-cool--init)))
次に、すべてのモードの初期化をで行いyour-cool-init
ます。
より長い説明:
問題は、派生したメジャーモードを書く公式な方法はdefine-derived-mode
マクロを使用することです。
(define-derived-mode your-cool-mode prog-mode ...)
古いEmacsen(24より前)では、これはで壊れprog-mode
ます。そして(if (fboundp 'prog-mode) ...)
、マクロはリテラルシンボルを期待し、展開でそれを引用するため、そこでは使用できません。
define-derived-mode
親をさまざまな方法で使用します。あなたはそれらを利用するためにあなた自身のモード定義でそれらすべてをコピーする必要があるでしょう、そしてそれは面倒でエラーを起こしやすいものです。
したがって、唯一の方法はdefine-derived-mode
、prog-mode
存在するかどうかに応じて、2つの異なるステートメントを使用することです。これにより、初期化コードを2回記述する問題が残ります。もちろんこれは悪いことなので、上記のようにそれを独自の関数に抽出します。
(もちろん、23.xのサポートを削除して字句スコープを使用するのが最善の解決策です。しかし、そのオプションをすでに検討して削除していると思います。:-))
prog-mode
古いEmacsenで最も近いものは何ですか?ですが、それが由来すると意味をなさないtext-mode
かfundamental-mode
あればprog-mode
利用できないのですか?
fboundp
、define-derived-mode
ステートメントだけで、first を使用して中間モードを導出できますか?次に、完全な定義を持つ実際のモードは、その中間モードから導出できますか?これにより、モード全体を2回定義する必要がなくなります。
fundamental-mode
由来のと等価であるnil
(実際、define-derived-mode
置き換えfundamental-mode
を有するnil
)、一方では、text-mode
プログラムコードがテキストではないように、適切ではありません。のデフォルト設定のほとんどは、text-mode
コメント以外のプログラミングモードでは意味がありません。これはなぜあるprog-mode
のEmacs 24で導入されました
define-derived-mode
に、if
フォームに2つの定義が必要になります。defun
init関数のdefine-derived-mode
forをfinalモードのaに置き換えます。これは特に好ましいとは思いません。prog-mode
元の質問が示唆しているように、自分自身を定義することもできますがfboundp
、そのモードの存在の確認に依存している他のモードを簡単に混乱させる可能性があります。
define-derived-mode
ステートメントが必要だとは思いません。数年前に別の回答として投稿したソリューションを思いつきましたが、Emacs 23とEmacs 24の両方で正常に動作するようです。このようなコードは、いくつかの一般的な主要モードで使用されています。
を使用しfboundp
てテストする方が理にかなっていると思います。
(if (fboundp 'prog-mode)
...
...)
define-derived-mode
引数を評価するためのラッパーマクロを定義できます。
(defmacro define-derived-mode* (child parent name &optional docstring &rest body)
(macroexpand `(define-derived-mode ,(eval child) ,(eval parent) ,(eval name)
,(eval docstring) . ,body)))
(define-derived-mode* 'toy-mode
(if (fboundp 'prog-mode) 'prog-mode 'fundamental-mode)
"Toy"
"Major mode for my favorite toy language"
(toy-mode-setup))
(警告:最小限のテストのみ。)
prog-mode
。特に、字句バインディングの欠如に悩まされます。