折腾了好久,实在没办法了。
需要修改一下下面这个函数的定义,修改的地方就是在unless那行加了个or。
(defun persp-add-buffer (buffer)
"Associate BUFFER with the current perspective.
See also `persp-switch' and `persp-remove-buffer'."
(interactive
(list
(let ((read-buffer-function nil))
(read-buffer "Add buffer to perspective: "))))
(let ((buffer (get-buffer buffer)))
(unless (or (not (persp-curr)) (memq buffer (persp-buffers (persp-curr))))
(push buffer (persp-buffers (persp-curr))))))
但是无论是def还是加advice,都报错,symbol’s function definition is void: \(setf\ persp-buffers\),感觉像是宏展开的错误。还有这里面的persp-buffers不是一个函数,而是由setf定义的一个位置,不是很懂。
源文件在这里 perspective-el/perspective.el at master · nex3/perspective-el · GitHub
把配置编译了之后就没问题了,去除编译文件运行还是有问题。
perspective这个包总是遇到莫名其妙的问题,还是自己对elisp了解不够。
因为你把这个放在 perspective 加载之前了。
我放在use-package的:config里了,这样子不行吗?
不行,因为 setf
的宏展开在 use-package
的时候就调用了
吃完午饭 略饱 然后温饱思淫欲 想睡觉, 幸好随手点开emacs-china看看想学习的东西,碰巧楼主问题置顶,然后…
我看到了这个问题后习惯性的去看源码学习,然后看到 cl-defstruct
, 看不懂,于是看文档,惊喜来了:
感谢楼主大佬给了我学到一个elisp面向对象的编程方法机会!
虽然很多lisp概念还是无法深识,但是我目前想到了一个笨办法,鉴于要使用use-package
管理perspective,得出我的原始方法是:
(use-package perspective
:config
(cl-defstruct (perspective
(:conc-name persp-)
(:constructor make-persp-internal))
name buffers killed local-variables
(buffer-history buffer-name-history)
(window-configuration (current-window-configuration))
(point-marker (point-marker)))
;; redefine persp-add-buffer
(defun persp-add-buffer (buffer)
"Associate BUFFER with the current perspective.
See also `persp-switch' and `persp-remove-buffer'."
(interactive
(list
(let ((read-buffer-function nil))
(read-buffer "Add buffer to perspective: "))))
(let ((buffer (get-buffer buffer)))
(unless (or (not (persp-curr)) (memq buffer (persp-buffers (persp-curr))))
(push buffer (persp-buffers (persp-curr)))))))
就如 @LdBeth 大佬所教的, 我想是因为自定义数据类型(或者叫‘’类‘’,我不是很清楚)的缺失,导致成员方法的无法引用,也就产生了找不到定义的问题,因此我把结构体随重定义一起塞进use-package
的:config
中了。
虽然我不用perspective, 但是可能有效,如果有效也不知道这个方法是不是规范。
不需要这么麻煩(?),而且虽然为了方便调试 elisp 允許这么做,重定义 struct
的確是不规范的。
byte compile 是最理想解。但如果完全不想 byte compile 配置,可以:
...
:config
(eval '(byte-compile
(defun persp-add-buffer (buffer)
"Associate BUFFER with the current perspective.
See also `persp-switch' and `persp-remove-buffer'."
(interactive
(list
(let ((read-buffer-function nil))
(read-buffer "Add buffer to perspective: "))))
(let ((buffer (get-buffer buffer)))
(unless (or (not (persp-curr)) (memq buffer (persp-buffers (persp-curr))))
(push buffer (persp-buffers (persp-curr))))))))
没看明白,跟宏有什么关系。
像下边这样使用 advice 会有问题?
(defun my-persp-add-buffer (buffer)
;; ...
)
(with-eval-after-load 'perspective
(advice-add 'persp-add-buffer :override 'my-persp-add-buffer))
;; or
(defadvice persp-add-buffer (around my-persp-add-buffer (buffer) activate)
"Associate BUFFER with the current perspective.
See also `persp-switch' and `persp-remove-buffer'."
(interactive
(list
(let ((read-buffer-function nil))
(read-buffer "Add buffer to perspective: "))))
(let ((buffer (get-buffer buffer)))
(unless (or (not (persp-curr)) (memq buffer (persp-buffers (persp-curr))))
(push buffer (persp-buffers (persp-curr))))))
LdBeth
11
会。问題不在于 advice,在于 push
。
(push buffer (persp-buffers (persp-curr)))
单从行为上可以理解为 push
调用了 setf
的內部宏展開,即
(defmacro push (new place)
`(setf ,place ,new))
而在用 setf
修改一个 struct
时,宏展开要用到对应的 self expander
,以上面的 persp-buffers
为例,会调用一名为 \(setf\ persp-buffers\)
的 local function。
而 \(setf\ persp-buffers\)
由 cl-defstruct
定义,在 perspective
未加載,以及配置未 byte compile 以提前宏展开时,就会出現如題所示的
Lisp error: (void-function \(setf\ persp-buffers\))
所以这里跟宏和 advice 都没有关系,是楼主的加载流程出了问题。
persp-add-buffer
不应该在 perspective
加载之前调用,基于错误的加载流程提出的解决方案也是不可取。
大佬 感谢你的纠正,但是 我还是不明白为什么 byte-compile 后就没问题。
重点是!!!!!! 我怀疑楼主的问题根本不会出现,因为我想复现楼主的问题,但是复现不出来?
很抱歉我在没有试验问题的可复现性的时候就草率的回复楼主(那时第一次看到 cl-defstruct
高兴坏了 ),但是确实这对于我以下的复现实验结果是相悖的,又由于我稍微看了一下use-packge
源码,因为发现use-package本身是宏,因此猜测它只是生成配置的form-list,然后按条件把list中的元素分配给其他函。再来(defun ....)
在我的实验下,这个表达式的执行时不会进行定义检查的,我觉得可能只是建立一个symbol的函数域中数据结构罢了,好了前面说的是猜测,我摆上我的测试代码( 偷了 @twlz0ne 大佬的一部分, 懒的手写了 :
(use-package perspective
:init (persp-mode)
:config
;; (defadvice persp-add-buffer (around my-persp-add-buffer (buffer) activate)
;; "Associate BUFFER with the current perspective.
;; See also `persp-switch' and `persp-remove-buffer'."
;; (interactive
;; (list
;; (let ((read-buffer-function nil))
;; (read-buffer "Add buffer to perspective: "))))
;; (let ((buffer (get-buffer buffer)))
;; (unless (or (not (persp-curr)) (memq buffer (persp-buffers (persp-curr))))
;; (push buffer (persp-buffers (persp-curr))))))
(advice-add 'persp-add-buffer :override #'my/persp-add-buffer)
(defun my/persp-add-buffer (buffer)
"Associate BUFFER with the current perspective.
See also `persp-switch' and `persp-remove-buffer'."
(interactive
(list
(let ((read-buffer-function nil))
(read-buffer "Add buffer to perspective: "))))
(let ((buffer (get-buffer buffer)))
(unless (or (not (persp-curr)) (memq buffer (persp-buffers (persp-curr))))
(push buffer (persp-buffers (persp-curr)))))))
重新启动emacs结果一切正常。。。。
LdBeth
14
你没有用 autoload,或者说用了也没延迟加载,因为 :init
里面直接把这个包加载了。
把 :init
那行去了再试试。
yes 是的 我考虑到启动就要加载perspective就这么配置了,
但是我不明白如果不启动 (persp-mode)
当然就没法使用这个包的内部函数啊,也就是说大佬教我怎么用autoload 换init:
吧
使用 (add-hook 'after-init-hook #'persp-mode)
我再试一下。
LdBeth
16
反正题主没一开始就启动。你要复现问题研究一下就都去了,要是只是确认自己配置有没有文件结论已经很明显了,没问题。
用了这个和在 :init
开没区别。
使用 (add-hook 'after-init-hook #'persp-mode) 我再试一下。
let*: Symbol’s function definition is void: (setf\ persp-buffers) [2 times]
出错了