Emacs 定义符号后的求值形式

Emacs 定义符号后的求值形式,emacs,elisp,lazy-loading,key-bindings,Emacs,Elisp,Lazy Loading,Key Bindings,我有一个函数永久设置密钥,它可以自动将本地和全局的密钥绑定定义添加到init文件中。当用户请求添加本地密钥绑定时,该函数会根据我前面提出的问题的答案,使用非常健壮的方法确定当前的本地密钥映射 因此,现在我知道了(当前本地地图)的符号名称,并具有相应的sexp来定义所需的键,例如: (define-key python-shell-map [3 6] 'python-describe-symbol) 但是,在初始化时,这些映射通常是未定义的,因此急切地评估上面的表单将导致错误 什么是一种健壮的编

我有一个函数
永久设置密钥
,它可以自动将本地和全局的密钥绑定定义添加到init文件中。当用户请求添加本地密钥绑定时,该函数会根据我前面提出的问题的答案,使用非常健壮的方法确定当前的本地密钥映射

因此,现在我知道了
(当前本地地图)
的符号名称,并具有相应的sexp来定义所需的键,例如:

(define-key python-shell-map [3 6] 'python-describe-symbol)
但是,在初始化时,这些映射通常是未定义的,因此急切地评估上面的表单将导致错误

什么是一种健壮的编程方法来安排这些表单在适当的时间进行评估

到目前为止,我一直在做的是假设当前本地映射存在一个次要模式,然后猜测次要模式文件的名称,以便根据惯例
foo mode map
将上述sexp包装在
eval after load
表单中。例如,这是自动生成的:

(eval-after-load 'dired '(define-key dired-mode-map [8388711] 'run_gdmap_dired))
并且恰好工作(因为确实存在
dired
模式和文件)

对于第一个示例,这种方法不起作用:不存在
pythonshell
minor或major模式。主模式
comint模式
处理多个“子模式”,因此仅为“python”版本添加所需的自定义似乎并不合适

如何确定将定义符号(如
pythonshellmap
)的文件名


我想我可以使用并检查所有可能已定义的新符号,但可能有更直接的解决方案。

我刚刚找到了自己的答案,多亏了一个我之前没有注意到的
问题:

(symbol-file SYMBOL &optional TYPE)



For more information check the manuals.

Return the name of the file that defined SYMBOL.
The value is normally an absolute file name.  It can also be nil,
if the definition is not associated with any file.  If SYMBOL
specifies an autoloaded function, the value can be a relative
file name without extension.

If TYPE is nil, then any kind of definition is acceptable.  If
TYPE is `defun', `defvar', or `defface', that specifies function
definition, variable definition, or face definition only.

[back]
所以这是可行的:

(symbol-file 'python-shell-map)--> "/usr/share/emacs/23.3/lisp/progmodes/python.elc"
编辑:

为了更明确地说明这一点:

(format "(eval-after-load \"%s\" '%s)" (symbol-file keymap-symbol) define-key-sexp)

评论后更正:这对我来说适用于24.3版的python.el:

(eval-after-load 'python-mode (lambda () 
  (funcall (define-key inferior-python-mode-map MY-KEY COMMAND))))

我最终使用的另一个解决方案是使用
加载后函数
钩子来定义函数
eval after sym loaded
,该函数在调用
加载文件
后,一旦给定符号绑定,就会对0元函数进行求值——可能立即:

(defun eval-after-sym-loaded (sym fun)
  "funcall the 0-argument function `fun' only after `sym' has been bound after a `load-file'
if `sym' is already bound, call `fun' immediately"
  (if (boundp sym)
      (funcall fun)
    (push (cons sym fun) eval-after-sym-loaded-alist)))

(defvar eval-after-sym-loaded-alist nil
  "alist used internally by eval-after-sym-loaded")

(defun eval-after-sym-loaded-hook (file-loaded)
  (setf eval-after-sym-loaded-alist
    (loop for (sym . fun) in eval-after-sym-loaded-alist
          if (boundp sym) do
              (funcall fun)
          else collect (cons sym fun))))

(add-hook 'after-load-functions 'eval-after-sym-loaded-hook)

;; example usage:

(eval-after-sym-loaded 'html-mode-map
                       (lambda () (message "the symbol 'html-mode-map has been loaded!")))

(eval-after-sym-loaded 'js-mode-map
                       (lambda () (message "the symbol 'js-mode-map has been loaded!")))

获取您要查找的行为的另一种途径可能是生成以下表单的代码:

(add-hook '<MAJOR-MODE>-hook
          (lambda () (local-set-key <KEY> <BINDING>)))
(添加hook'-hook
(lambda()(本地设置键)))

这样,您就不必关心keymap的名称,也不必关心初始化keymap变量的文件名。

如果我理解正确,那么您引用的以及您建议您需要一个值为当前本地映射的符号的建议都是错误的/误导的。你没有
define key
将一个键映射作为其参数,因此您只需传递它
(当前本地映射)
,而不知道或关心哪些符号可能绑定到该键映射值。Drew,当然,当当前本地映射是我要修改的映射时,这将起作用,但正如我在另一篇文章中所指出的,我在初始化时修改各种本地映射,即使本地映射定制持久化。初始化时
(当前本地映射)
的值与我自定义的各种本地映射无关。您可能会觉得需要一个符号,因此可以执行此处尝试执行的操作。在不知道您要做什么的情况下,我可能只会将
define键
添加到相应的模式挂钩中(当您生成
define键
代码时,您可能知道当前的
主模式
)。看起来(同样,在不知道您正在做什么的情况下)您正在使用实际上并不需要的间接方式(符号、文件加载)。我想您只需要知道模式,然后将
(define key(当前本地映射))…
添加到模式挂钩(是的,在启动时)。我只是在自动将持续定义键的过程添加到本地映射中。当我在本地映射中并决定要覆盖keybinding时,我不想手动执行此操作。如上例所示,依赖主模式并不总是有效,例如,在
comint模式下
。此外,我更喜欢不依赖命名约定,让我的代码大部分时间都能正常工作。对于如何通过编程找到合适的模式挂钩,我很欢迎任何想法,但是这个解决方案似乎是最健壮的,即使它没有使用明确的定制机制。变量
主模式
告诉您当前模式,即当您将代码添加到init文件时(以交互方式),正确的?当您在新会话中进入模式时,
当前本地映射
(或任何)使用的任何本地映射都将再次变为当前映射---作为当前本地映射
返回的值。因此,如果您将
(define key(current local map)key CMD)
放在模式挂钩上(通过您的init文件),那么无论何时输入该模式,该键都将绑定在正确的映射(当时的本地映射)中,无论该模式是什么,也不管可能绑定到该键映射的符号是什么
eval-after-load
需要一个文件或
provide
d符号,而
substander-python-mode-map
两者都不是,因此尽管上面的sexp没有错误,但它从来不会实际计算它提供的表单。对。除非您是相关库的维护者,在这种情况下,您可以添加任意数量的
提供
表达式,并将任何您喜欢的内容称为“功能”,否则这将无济于事。它还受到我提到的wrt的另一个问题的影响,这取决于映射状态的文件加载:它是相当静态的。地图可以动态创建和更新——这就是为什么我们有
当前本地地图
等。mode命令(不一定是顶级文件)通常负责map.draw。是的