保存/恢复 frame 位置和大小

(破事水) 简单写了段代码, 帮助使用 server 的各位提高生活幸福感:

;;; your _emacs   -*- lexical-binding: t; -*-
;; 当最后一个 frame 关闭时, 存入它的位置和尺寸; 当桌面上没有 frame 时, 下一个打开的 frame 将使用那个被存入的位置和尺寸.
(let ((size&position-relayer `(,(cons 'top 0) ,(cons 'left 0)
                               ;; ‘fullscreen’ 放最后, 以覆盖 ‘width’&‘height’ 的设置.
                               ,(cons 'width 0) ,(cons 'height 0) ,(cons 'fullscreen nil))))
  (put 'size&position-relayer :holding? nil)
  (letrec ((get-size&position (lambda ()
                                (when (get 'size&position-relayer :holding?)
                                  (dolist (parameter-value size&position-relayer)
                                    (set-frame-parameter nil (car parameter-value) (cdr parameter-value))))
                                (remove-hook 'server-after-make-frame-hook get-size&position)
                                (   add-hook 'delete-frame-functions       put-size&position)))
           (put-size&position (lambda (frame-to-be-deleted)
                                (when (length= (frames-on-display-list) 1)
                                  (dolist (parameter-value size&position-relayer)
                                    (setcdr parameter-value (frame-parameter frame-to-be-deleted (car parameter-value))))
                                  (put 'size&position-relayer :holding? t)
                                  (remove-hook 'delete-frame-functions       put-size&position)
                                  ;; 当需要调用该 λ 时, 必然没有除此以外的其它 frame 了, 因此之后新建的 frame 必然是 server 弹出的,
                                  ;; 所以此处没有使用 ‘after-make-frame-functions’, 而是指向性更强的 ‘server-after-make-frame-hook’.
                                  (   add-hook 'server-after-make-frame-hook get-size&position)))))
    (add-hook 'server-after-make-frame-hook get-size&position)))
2 个赞

毫无必要的 backquote,看yue了

都用 closure 了,怎么还用这样全局变量的方式交流状态

每次加钩子删钩子属于不必要的 consing。

直接在钩子函数里用条件变量控制实现还简单,还不会在 debug 时候看到钩子变量里一坨 lambda

为什么不应该给钩子变量里加 lambda?因为如果有重新加载配置文件的需求的话这样健壮性会很差

个人喜好: 我不喜欢写 listcons.

这样写是有性能影响?

这个确实有点纠结, 我是认为存没存值是变量自己的属性. 改掉了.

这样比较好玩. 同一时间最多只有一个闭包存在于 hook 中, 把自己删了换另一个.

没太懂, 能不能细🔒? 是指修改 hook 中的 lambda 后, 重新加载, 原来那个 lambda 还在 hook 里吗?

'((top . 0) (left . 0)
  (width . 0) (height . 0) (fullscreen . nil))

这样写从语义上看是常量的意思, 虽然一般能正常使用, 但是如果要编译的文件中的其它地方也有一个 '((top . 0) ...), 是不是会指向同一个列表?

Emacs 里不会,Chez Scheme 里也不会

https://www.gnu.org/software/emacs/manual/html_node/elisp/Modifying-Lists.html:

Lists created by quoting are part of the program and should not be changed by destructive operations.

如果你的结论是通过看源码得来的, 那我暂时还是维持我原本的写法. 万一哪天实现改了呢?

很简单,用 copy-alist 浅复制就行。

更合理的方式是用 (setf (alist-get <attr> <alist>) <newval>) 而不是用 setcdr。当然这样用你这 dolist 也要改。

又比如可以重新生成一个 alist

(setq size&position-relayer
      (cl-loop for pair in size&position-relayer
               collect (cons (car pair) (frame-parameter frame-to-be-deleted (car pair)))))
(defun foo ()
  (let ((a '(1 2 3))
        (b '(1 2 3)))
    (cons a b)))

(defun bar ()
  (let ((a "abc")
        (b "abc"))
    (cons a b)))

(byte-compile 'foo)
#[0 "..." [(1 2 3) (1 2 3)] 4]

(byte-compile 'bar)
#[0 "..." ["abc"] 2]

只是 Emacs Lisp 的编译器会把 string 当常量优化而已。并不会把 quoted list 也当常量。

1 个赞