プログラミング言語のメジャーモードを書いていますが、古い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つの定義が必要になります。defuninit関数のdefine-derived-modeforを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。特に、字句バインディングの欠如に悩まされます。