如何定义函数来嵌入after-make-frame-functions

elisp

#1

怎么定义一个elisp函数,比如my-load-theme,使得(my-load-theme 'solarized)实现以下这段代码的功能:

(add-hook 'after-make-frame-functions
    (lambda (frame)
        (select-frame frame)
        (when (display-graphic-p frame)
            (load-theme 'solarized t))))

我怎么都搞不定,问题出在after-make-frame-functions需要frame作为参数的lambda函数,但load-theme是嵌套在这个lambda函数中,然后我就搞不定了。。。

多谢大牛现身~

我也在stack-overflow上提问了:


[elisp新手历险] 在function里add-hook ... lambda ...
#2

用 macro


#3

Stack Overflow有人答了,用;; -*- lexical-binding:t -*-可以简单搞定。如果一定要用dyanmic binding的话,我试了试它对一个问题的“等价”方案:

但不行,也许因为after-make-frame-functions特殊性,也许我用错了。


#4
lexical-binding
     => nil

(defun foo (x)
  (lambda () x))

(foo 123)
     => (lambda nil x)

(defun bar (x)
  `(lambda () ,x))

(bar 123)
     => (lambda nil 123)

至于 Dynamic v.s. Lexical binding,我的感觉是你应该总是用 Lexical binding,无论是写 Package 还是写自己的配置 init.el


#5
(defun foo (y)
(add-hook 'after-make-frame-functions
    `(lambda (frame)
        (select-frame frame)
        (when (display-graphic-p frame)
            (load-theme ,y t)))))

这样?


#6

不是 after-make-frame-functions 有什么特殊,也不是 hook 带参数的问题,是 lambda 的问题。

当你 (my-load-theme 'solarized) 的时候,theme 变量是有效的,但是这个时候只是定义了 lambda,并没有执行。等到 lambda 真正执行的时候,变量 theme 已经不存在了

所以这个时候,你需要 -*- lexical-binding:t -*- 来维持 theme 变量的有效性:(请在 --batch 模式下执行示范代码)

-*- lexical-binding:t -*-
(defvar foo-hook nil)

(defun set-hook (s)
  (add-hook 'foo-hook
            (lambda ()
              (princ s))))

(set-hook "foo")
(run-hooks 'foo-hook)

如果不想 lexical-binding 作用于整个文件, 可以在局部使用:

(require 'cl)
(defvar foo-hook nil)

(defun set-hook (s)
  (lexical-let ((ss s))
    (add-hook 'foo-hook
              (lambda ()
                (princ ss)))))

(set-hook "foo")
(run-hooks 'foo-hook)

如果根本不想使用 lexical-binding,就把 theme 定义成全局变量。总之就是要想办法,让 theme 变量在回调的时候仍然有效。


楼上的方法也是可以的,在 (my-load-theme 'solarized) 的时候立即把 theme 变量展开:

...
(load-theme ,theme t) ;; => (load-theme 'solarized t)
...

就不存在变量是否有效的问题了。


#7

这种情况还是用全局变量好,可以很方便地更换theme。否则的话,第二次调用时,如何卸载之前的? 这样的话这个函数调用两次就会加载两个theme。

如果不需要换theme,也就不需要函数了,直接写死就行了。


#8

是的,所以楼主有两个问题需要厘清:

  1. 关于 lambda 和变量作用域的问题。
  2. 真实意图是什么。如果是想达到切换 theme 的效果,这么做就有问题。

#9

对的,不过不工作啊


#10

那麼 after-make-frame-functions 值是啥?


大概明白你的问題在哪了

(defun foo (y)
  (add-hook 'after-make-frame-functions
            `(lambda (frame)
               (select-frame frame)
               (when (display-graphic-p frame)
                 (load-theme ,y t)))))
;; => foo

(foo '(quote spacemacs-light)) ;; (quote 'spacemacs-light), ''spacemacs-light 皆可
;; => ((lambda (frame) (select-frame frame) (when (display-graphic-p frame) (load-theme (quote spacemacs-light) t))))

#11

and @netjune 多谢啊!

恩,我没有真正读过elisp的文档,都是用多少看多少;去仔细了解下lambda

哦,我同时开多个emacs server(平时使用两个,给daemon不同名字),对每个只加载一个theme的。只不过配置里,配了大概4个server,所以想写个函数(除了加载theme,还有modeline修改,字体什么的)。我用Alfred方便的打开不同的emacs client,这样我就可以文档(大量latex)用light theme,码代码用dark theme了:slight_smile:


#12

多谢多谢!有估计是变量传递的问题,实在基础知识不够啊。你的解释解决了很多问题那。

不过 @LdBeth 的那个方案,我试过,不知道为什么不工作。(目前我用的是-*- lexical-binding:t -*-).


更新:那个替代方案已解决,是我调用时的错误,应该传参数'(quote solarized)


#13

是的!谢谢啊!

原来是调用问题。好吧,我还是把elisp, an introduction打出来都看一遍吧,elisp基础几乎没有啊。碰到语法问题没基础知识帮助解决啊。


#14

你还得先明白指针在 Lisp 中是怎麼用的。


#15

恩,我一直以来都是用C/C++的概念处理elisp的,但lisp中的新玩意儿好多啊,得从基础抽空看看了。多谢啊!有解决了一个心腹大患 :smiley:


#16

也可以定义为

(defun foo (y)
  (add-hook 'after-make-frame-functions
            `(lambda (frame)
               (select-frame frame)
               (when (display-graphic-p frame)
                 (load-theme ',y t)))))

这样就可以依然直接调用 (foo 'solaried)。说白了,都是我基础知识缺乏整出来的问题,得补补基础了。


#17

搜帖子时顺带发现: @ReimuXMX 你的主页竟然有背景图!


#18

@ReimuXMX 主页挂了