通过Emacs Lisp中的函数设置模式

时间:2012-01-05 21:48:30

标签: elisp

我的.emacs文件中有以下代码,它可以按照您的预期运行:

;; Ruby
(global-font-lock-mode 1)
(autoload 'ruby-mode "ruby-mode" "Ruby editing mode." t)
(setq auto-mode-alist (cons '("\\.rb$" . ruby-mode) auto-mode-alist))
(setq auto-mode-alist (cons '("\\.rsel$" . ruby-mode) auto-mode-alist))
(setq auto-mode-alist (cons '("\\.rhtml$" . html-mode) auto-mode-alist))
(setq auto-mode-alist (cons '("\\.erb$" . html-mode) auto-mode-alist))
(setq auto-mode-alist (cons '("\\.prawn$" . html-mode) auto-mode-alist))
(setq auto-mode-alist (cons '("Rakefile$" . ruby-mode) auto-mode-alist))

然而,我尝试干掉它有点失败:

(defun set-mode-for-filename-patterns (mode filename-pattern-list)
  (mapcar
    (lambda (filename-pattern)
      (setq 
        auto-mode-alist 
        (cons '(filename-pattern . mode) auto-mode-alist)))
    filename-pattern-list))

;; Ruby
(global-font-lock-mode 1)
(autoload 'ruby-mode "ruby-mode" "Ruby editing mode." t)
(set-mode-for-filename-patterns 
  ruby-mode
  '("\\.rb$"
    "\\.rsel$"
    "\\.rhtml$"
    "\\.erb$" 
    "\\.prawn$"
    "Rakefile$"
    "Gemfile$"))

...出现以下错误:

Debugger entered--Lisp error: (void-variable ruby-mode)
  (set-mode-for-filename-patterns ruby-mode (quote ("\\.rb$" "\\.rsel$" "\\.rhtml$" "\\.erb$" "\\.prawn$" "Rakefile$" "Gemfile$")))
  eval-buffer(#<buffer  *load*> nil "/home/duncan/.emacs" nil t)  ; Reading at buffer position 1768
  load-with-code-conversion("/home/duncan/.emacs" "/home/duncan/.emacs" t t)
  load("~/.emacs" t t)
  #[nil "\205\264

我在这里有点困惑......特别是,我不明白ruby-mode是如何无效的&amp;所以无法传递给函数,但可以cons编成一对吗?

任何指针(嘿)都会非常感激。

3 个答案:

答案 0 :(得分:4)

表格形式:

(cons '("\\.rb$" . ruby-mode) ...

ruby-mode是引用列表的一部分。这意味着它被读作符号名称,而不是作为变量计算。换句话说,Emacs将其视为符号ruby-mode并按原样接受它。

表格形式:

(set-mode-for-filename-patterns 
   ruby-mode
   '("\\.rb$"
     "\\.rsel$"
     ...

ruby-mode未被引用,因此被读作函数的参数。函数参数被评估。这意味着Emacs读取ruby-mode,将其识别为符号,并尝试对其进行评估。评估符号意味着查找它指向的值,在这种情况下不存在。

编辑:

你的功能仍然不起作用,还有另外一个问题。您在set-mode-for-filename-patterns中使用了引用列表。这在原始代码中可以正常工作:

(setq auto-mode-alist (cons '("\\.rb$" . ruby-mode) auto-mode-alist))

因为您实际上为filename-patternmode提供了。在您的函数内部,您需要评估 这些符号,这些符号在引用时不会发生!结果是,不是将列表中的每个不同字符串添加到自动模式列表中,而是改为使用符号filename-pattern

要解决此问题,您需要认识到'(文件名模式。模式)是为了生成包含filename-patternmode的cons单元格。我们可以用(cons filename-pattern模式)生成。所以纠正的功能是:

(defun set-mode-for-filename-patterns (mode filename-pattern-list)
  (mapcar
    (lambda (filename-pattern)
      (setq 
        auto-mode-alist 
        (cons (cons filename-pattern mode) auto-mode-alist)))
    filename-pattern-list))

更正的函数调用是:

(set-mode-for-filename-patterns 
  'ruby-mode
  '("\\.rb$"
    "\\.rsel$"
    "\\.rhtml$"
    "\\.erb$" 
    "\\.prawn$"
    "Rakefile$"
    "Gemfile$"))

答案 1 :(得分:1)

看这里:

(setq auto-mode-alist (cons '("\\.rb$" . ruby-mode) auto-mode-alist))
----------------------------^

这是quote,这意味着您无法评估下一个 表单,因此'("\\.rb$" . ruby-mode)相当于(cons '"hello" 'ruby-mode)

但是当你调用函数set-mode-for-filename-patterns时 参数首先进行评估,然后将结果传递给 功能。这就是评估(set-mode-for-filename-patterns ruby-mode ..)引发错误的原因,因为emacs-lisp解释器会尝试 将ruby-mode评估为变量,但ruby-mode没有值 这个上下文因此错误(void-variable ruby-mode)。你是什​​么 想要传递符号ruby-mode,所以你必须引用它 像这样(set-mode-for-filename-patterns 'ruby-mode ...)

请注意,您可以使用ruby-mode将值设置为let模式。

(let ((ruby-mode 'ruby-mode))
  (set-mode-for-filename-patterns ruby-mode ...))

这里,当评估形式(set-...)的参数时,它会进行评估 ruby-mode并且可以找到它的值(这是符号 ruby-mode)然后将其传递给函数。

答案 2 :(得分:1)

我认为set-mode-for-filename-patterns是一个有趣的功能。我要将它添加到我的配置中,但使用更优化的实现。

这里的实现都为每个文件后缀的auto-mode-alist变量添加了一个项目。 Emacs每次找到文件时都会搜索此列表。因此auto-mode-alist越短,Emacs找到文件的速度就越快。

此版本在启动时可能较慢,但在查找文件时速度更快:

(defun set-mode-for-filename-patterns (mode filename-pattern-list)
  (push (cons (regexp-opt filename-pattern-list) mode)
        auto-mode-alist))`

这将适用于同一个电话:

(set-mode-for-filename-patterns 
   'ruby-mode
   '("\\.rb$"
     "\\.rsel$"
     "\\.rhtml$"
     "\\.erb$" 
     "\\.prawn$"
     "Rakefile$"
     "Gemfile$"))

如果您查看auto-mode-alist的值,您会发现许多内置模式使用regexp是出于同样的性能原因。

顺便说一下,我建议你只要相信regexp-opt做正确的事。它所做的正则表达对眼睛(和大脑)非常困难。