Emacs/Emacs Lisp:我可以在交互式表单之前插入建议吗?或者如何智能地预先设置compile命令?

Emacs/Emacs Lisp:我可以在交互式表单之前插入建议吗?或者如何智能地预先设置compile命令?,emacs,elisp,advising-functions,defadvice,Emacs,Elisp,Advising Functions,Defadvice,我想做的是智能地为compile函数的字符串参数预先设置一个缓冲区本地默认值 现在compile.el默认使用“make”作为命令。我可以通过设置编译命令来设置。我甚至可以将变量缓冲区设置为本地。如果我总是想要相同的静态值,那就行了 但是我想根据缓冲区的内容、缓冲区的名称、文件包含目录的内容(如果有)以及月亮的相位,智能地选择compile命令。基本上,我希望控制默认值,然后允许交互用户覆盖该预设值 我希望在得到建议之前就这样做。但这并不像我预期的那样有效 阅读advice.el文件,我明白了

我想做的是智能地为
compile
函数的字符串参数预先设置一个缓冲区本地默认值

现在compile.el默认使用“make”作为命令。我可以通过设置
编译命令
来设置。我甚至可以将变量缓冲区设置为本地。如果我总是想要相同的静态值,那就行了

但是我想根据缓冲区的内容、缓冲区的名称、文件包含目录的内容(如果有)以及月亮的相位,智能地选择
compile命令。基本上,我希望控制默认值,然后允许交互用户覆盖该预设值

我希望在得到建议之前就这样做。但这并不像我预期的那样有效

阅读advice.el文件,我明白了

假设一个函数/macro/subr/special表单有N条before通知、M条around通知和K条after通知。假设所有通知均未受到保护,其建议定义如下(主体形式索引对应于相应通知在该建议类别中的位置):

([macro]lambda
[[(交互式…)]
(let(ad返回值)
{}*
....
{}*
{}*
{}*
....
{}*
(setq ad返回值
)
{}*
....
{}*
{}*
{}*
....
{}*
广告返回值)
这对我来说意味着,当adviced函数是交互式的时,'callinteractively'在调用before建议或任何建议之前调用交互式表单

而且,当我向
编译
添加建议时,我观察到的行为证实了这一点。处理交互表单后,将调用通知。交互式表单建议在我的建议有机会猜测它应该是什么并预先设置它之前,使用该字符串进行编译

所以

  • 如何让代码在交互窗体之前运行?建议能做到这一点吗?如果不是建议,还有别的吗?或
  • 如何为任何缓冲区动态预设
    编译命令

想法很受欢迎。

啊,你知道我做了什么吗?我用了一种迂回的策略

我已将C-xC-e全局设置为
compile
。我没有使用advice,而是定义了一个包装
compile
的函数,然后将C-xC-e绑定到该函数。在包装器中,我猜测compile命令

(defun cheeso-invoke-compile-interactively ()
  "fn to wrap the `compile' function.  This simply
checks to see if `compile-command' has been previously guessed, and
if not, invokes `cheeso-guess-compile-command' to set the value.
Then it invokes the `compile' function, interactively."
  (interactive)
  (cond
   ((not (boundp 'cheeso-local-compile-command-has-been-set))
(cheeso-guess-compile-command)
(set (make-local-variable 'cheeso-local-compile-command-has-been-set) t)))
  ;; local compile command has now been set
  (call-interactively 'compile))
猜测函数是这样的:

(defun cheeso-guess-compile-command ()
  "set `compile-command' intelligently depending on the
current buffer, or the contents of the current directory."
  (interactive)
  (set (make-local-variable 'compile-command)
   (cond
    ((or (file-expand-wildcards "*.csproj" t)
     (file-expand-wildcards "*.vcproj" t)
     (file-expand-wildcards "*.vbproj" t)
     (file-expand-wildcards "*.shfbproj" t)
     (file-expand-wildcards "*.sln" t))
     "msbuild ")

    ;; sometimes, not sure why, the buffer-file-name is
    ;; not set.  Can use it only if set.
    (buffer-file-name
     (let ((filename (file-name-nondirectory buffer-file-name)))
       (cond

    ;; editing a .wxs (WIX Soluition) file
    ((string-equal (substring buffer-file-name -4) ".wxs")
     (concat "nmake "
         ;; (substring buffer-file-name 0 -4) ;; includes full path
         (file-name-sans-extension filename)
         ".msi" ))

    ;; a javascript file - run jslint
    ((string-equal (substring buffer-file-name -3) ".js")
     (concat (getenv "windir")
         "\\system32\\cscript.exe c:\\cheeso\\bin\\jslint-for-wsh.js "
         filename))

    ;; something else - do a typical .exe build
    (t
     (concat "nmake "
         (file-name-sans-extension filename)
         ".exe")))))

    (t
     "nmake "))))

一个选项是在模式挂钩中设置变量,类似

(add-hook 'c++-mode-hook 'my-c++-set-compile-command)
(defun my-c++-set-compile-command ()
   (setq (make-local-variable 'compile-command) (format "gmake %s" (buffer-file-name))))
我有时会添加专门的命令来调整当前编译行(打开/关闭调试标志、优化标志等),然后将这些命令绑定到迷你缓冲区中方便的按键

关于在
交互式
表单之前添加建议,您需要制作具有您想要的交互式表单的建议(在之前或周围)。从advice.el库中,有一个示例:

;;(defadvice switch-to-buffer (around confirm-non-existing-buffers activate)
;;  "Switch to non-existing buffers only upon confirmation."
;;  (interactive "BSwitch to buffer: ")
;;  (if (or (get-buffer (ad-get-arg 0))
;;          (y-or-n-p (format "`%s' does not exist, create? " (ad-get-arg 0))))
;;      ad-do-it))

compile命令不必是字符串。compile函数对其求值,因此它可以是一个函数,返回特定于缓冲区的字符串或取决于一天中的时间等:

(setq compile-command (lambda () (if (eq phase-of-moon 'waning) 
                                     "make -DWANING=1" 
                                    "make -DWANING=0")))
此外,尽管对于您的特定需求可能没有用处,但您始终可以在文件变量部分定义compile命令:

/* -*- compile-command: "make -DFOO"; -*- */


compile命令实际上用作中文件变量的示例。

根据手册,您可以通过在建议中包含交互式表单,简单地用自己的函数重写交互式表单。我认为您不能修改或包装现有的交互表单,只能完全覆盖它


以下是我正在使用的一些代码,它们可以通过。我目前已将其设置为
C、C++
Fortran
。您可以添加更多内容以满足您的需要。如果我打开一个具有
C++
扩展名的程序,并执行M-xcompile,我的编译命令将弹出
g++-Wall-currentfilename.cpp-o currentfilename-std=C++14
。然后我只需按回车键


当您认为所有建议都可以访问函数调用的参数值时,行为是有意义的,为了在交互上下文中确定这些值,必须事先对交互表单进行评估。(任何感兴趣的人都应该参考Trey的答案,它演示了如何建议交互式表单。)我刚刚在一个我想更改的函数上测试了这个功能,它工作得非常好。
/* -*- compile-command: "make -DFOO"; -*- */
// Local Variables:
// compile-command: "make -DSOMETHING_SPECIAL"
// End:
;; M-x compile smarter in order to guess language
(require 'compile)
(defvar compile-guess-command-table
  '((c-mode       . "gcc -Wall -g %s -o %s -lm")
    (c++-mode     . "g++ -Wall %s -o %s -std=c++14")
    (fortran-mode . "gfortran -C %s -o %s")
    ))

(defun compile-guess-command ()
  (let ((command-for-mode (cdr (assq major-mode
                                     compile-guess-command-table))))
    (if (and command-for-mode
             (stringp buffer-file-name))
        (let* ((file-name (file-name-nondirectory buffer-file-name))
               (file-name-sans-suffix (if (and (string-match "\\.[^.]*\\'"
                                                             file-name)
                                               (> (match-beginning 0) 0))
                                          (substring file-name
                                                     0 (match-beginning 0))
                                        nil)))
          (if file-name-sans-suffix
              (progn
                (make-local-variable 'compile-command)
                (setq compile-command
                      (if (stringp command-for-mode)
                          ;; Optimize the common case.
                          (format command-for-mode
                                  file-name file-name-sans-suffix)
                        (funcall command-for-mode
                                 file-name file-name-sans-suffix)))
                compile-command)
            nil))
      nil)))


;; Add the appropriate mode hooks.
(add-hook 'c-mode-hook       (function compile-guess-command))
(add-hook 'c++-mode-hook     (function compile-guess-command))
(add-hook 'fortran-mode-hook (function compile-guess-command))