nadvice.elの引数リストを操作するには?


12

新しいアドバイスシステムに関する別の質問への回答に続きます

古いスタイルadvice.elでは、アドバイスされた関数の引数リストの個々のメンバーを操作することは可能でしたが、それらのメンバーに関する操作は行われませんでした。たとえば、次のアドバイス:

(defadvice ansi-term (around prompt-for-name last)
  (let ((name (read-from-minibuffer "Tag: ")))
    (and (not (string= name ""))
         (ad-set-arg 1 (concat "Term: " name)))
    ad-do-it))

ansi-term呼び出しに対するbuffer-name引数の(オプションの)提供を許可しますが、ansi-term独自の対話形式に従ってプロンプトを表示することにより、最初の引数を取得します。

(後で参照するため、ansi-termの署名は(PROGRAM &optional BUFFER-NAME)であり、その対話形式はいくつかの可能なデフォルトでPROGRAMを要求しますが、BUFFER-NAMEに関しては何もしません。)

これが可能かどうかはわかりませんnadvice.el。もしそうなら、私はそれがどのように行われるか確信がありません。アドバイスされた関数の引数リストを置き換えるいくつかの方法を見つけました。

たとえば、* info *(elisp)アドバイスコンビネータから

`:filter-args'
 Call FUNCTION first and use the result (which should be a list) as
 the new arguments to pass to the old function.  More specifically,
 the composition of the two functions behaves like:
      (lambda (&rest r) (apply OLDFUN (funcall FUNCTION r)))

他のコンビネータは、同様の機能を提供し、それらの間の共通のスレッドは、関数の引数リストを交換することができる一方で、切り捨て、拡張、らは、リスト内の指定された位置で引数を変更するための機能のアドバイスのための明らかな方法はありません、それをあるなしそれの残りについて何かを主張します。

議論中のケースでは、アドバイス作成者がansi-termバッファ名のみを渡すことは不可能に見えます。なぜなら、位置1に値を持ち、nil位置0には何もない、さらにはリストを作成することができないからです。アドバイスの作成者が位置0を超えて引数を任意に変更することは不可能と思われます。

これは、同様の効果を生み出すために、コードをコピーansi-termアンドペーストする必要があるという点で不幸に思えます:具体的には、のインタラクティブなフォームをコピーして自分の好みに合わせて拡張するか、ansi-termすべてコピーして同様に拡張することができます。どちらの場合でも、今では、initファイルでEmacs Lispディストリビューションの一部を再定義する必要があります。これは、耐久性と美観の両方の点で望ましくないと感じています。

私の質問は、次のとおりです。この種の引数リストのマングリングは次のようにできますnadvice.elか?もしそうなら、どのように?


3
ansi-termの上に独自の対話型コマンドを定義してみませんか?ここが望ましい解決策だと思います。
lunaryorn

1
もちろん、それを妨げるものは何もありませんが、10年分の筋肉記憶の大部分を置き換える必要があります。
アーロンミラー

回答:


5

これは、同様の効果を生み出すためにコードをコピーアンドペーストする必要があるという点で不幸に思えます:[...] ansi-termのインタラクティブなフォームをコピーできます

それどころか、ここで実際にそうする必要はありませんが、アドバイスされた機能のインタラクティブなフォームをコピーして貼り付けることは良い考えだと思います。

質問を上から下に読みます。コードブロックに到達したとき、あなたのアドバイスはおそらくバッファ名を変更していると推測しました。しかし、後で署名をコメントとして提供するまで知りませんでした。

議論中のケースでは、アドバイス作成者がansi-termバッファー名のみを渡すことは不可能に見えます。なぜなら、位置1に値を持ち、nil位置0には何もない、リストさえも構築できないからです。

確かに、何よりも何も劣ることはありません。:-)しかし、それはここではほとんど関係ありません。

引用したドキュメントを見るとわかるように、アドバイスによって返された値は、アドバイスされた関数の引数として使用されます。戻り値は、変更された引数だけでなく、すべての引数のリストである必要があります。

古いアドバイスにできるだけ近づけて、これはあなたがしなければならないことですnadvice

(defun ansi-term--tag-buffer (args)
  ;; As npostavs pointed out we also have to make sure the list is
  ;; two elements long.  Which makes this approach even more undesirable.
  (when (= (length args) 1)
    (setq args (nconc args (list nil))))
  (let ((name (read-from-minibuffer "Tag: ")))
    (and (not (string= name ""))
         (setf (nth 1 args) (concat "Term: " name))))
  args)

(advice-add 'ansi-term :filter-args 'ansi-term--tag-buffer)

ただし、代わりに次のようなアドバイスを定義することをお勧めします。

(defun ansi-term--tag-buffer (program &optional buffer-name)
  (list program
        (let ((tag (read-from-minibuffer "Tag: ")))
          (if (string= tag "")
              buffer-name
            (concat "Term: " tag)))))

この変種は実際には自明です。


最初のバリアントargsでは、などの呼び出しの場合にリストを拡張する必要があります。(ansi-term "foo")そうし(setf (nth 1 args)...ないと、エラーが発生します。
npostavs

はい、あなたが正しい。2番目のバリアントを使用する別の理由-最初のバリアントにはバグがあります;-)デモンストレーションの目的で、それbuffer-nameが必須であると仮定します。
タリシウス

「それどころか、アドバイスされた機能のインタラクティブなフォームをコピー&ペーストするのは良い考えだと思う」-なぜそうなのか?コードをコピーして貼り付けることは、ほとんどすべての場合において悪い考えです。なぜここにいないのですか?
アーロンミラー

実際、この場合、「コピーと貼り付け」は正しい用語だとは思いませんが、使用したので使用しました。ただし、ここでその用語を使用するのが適切であったとしても、「コピー&ペーストしない」は単なる絶対的なルールではなく、ヒューリスティックなルールです。私が思うに、他のヒューリスティック、DOここで適用されますが、「あなたが何かを複雑または冗長であることの間の選択を持っている場合、冗長で行く」「変数と引数に意味のある名前を与える」としています。
タジウス

1
ええと、実際には、これはまだ壊れています。:filter-argsアドバイスは、アドバイスされた関数への引数のリストである単一の引数&restを取得します。
-npostavs

3

私がやる方法は次のとおりです。

(defun my-ansi-term-prompt-for-name (orig-fun program
                                     &optional buffer-name &rest args)
  (apply orig-fun program
         (or buffer-name
             (let ((name (read-string "Tag: ")))
               (and (> (length name) 0)
                    (concat "Term: " name))))
         args))
(advice-add 'ansi-term :around #'my-ansi-term-prompt-for-name)

紹介したのは私ですが、:filter-args個人的にはめったに便利ではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.