0%

Question-and-solutions

1. 路径设置中的问题

最近在写一个windows上使用的emacs terminal插件,让emacs act as vscode的terminal,遇到一个查找父路径的问题,比如一个java项目,项目路径即父路径应该是有 settings和graldew的目录,而source code放在 src/main/Main.java 这样子,那么当我编辑了该文件后要运行 gradle run ,就应该在父路径下运行,所以我找到了下面的方法

1
2
3
4
(defun get-parent-dir (name)
"Get the parent name dir."
(locate-dominating-file default-directory name))

理论上可以直接用这个,但是我希望自己指定父路径,这时,我需要一个方法能够返回list中第一个非nil,想到用or 但是不知道怎么把list 拆开传给or。找到了 cl-some 这个方法

1
2
3
4
5
6
7
8
9
(defvar pop-find-parent-directory nil "find the files in parent directory,if find,return the path as parent directory,else try the next")

(defun get-project-root-directory (buffer)
"find current project root,for git or gradle."
(with-current-buffer buffer
(if (setq parent (cl-some #'get-parent-dir pop-find-parent-directory))
parent
(get-current-directory))))

之后又详细的研究了macro 补一个 or的实现

1
2
3
4
5
6
(defmacro get-not-nil (list)
`(progn
(or ,@list)))

(get-not-nil (nil nil "he" "hi") )

2. 使用关键字

我希望我的方法能像use-package一样使用 :init :config来配置,所以我查询了function的keyword。发现方法或者宏后面加*的都是 cl.el 的实现,cl的实现里支持 &key.

1
2
3
4
5
6
7
8
(defun* example (&rest args &key (a 1) (b 2) &allow-other-keys)
(format "%s %s %s" args a b))
(example :b "hello" 2)

(defun* my-example (name &key (a ) &allow-other-keys)
(format "%s %s " name a))

(my-example "gradle" :b (message "hello"))

上面的方法确实可以传递keyword,但是并不能传递 (message “hello”),这种形式会被eval成 “hello”传递,所以又想到宏

1
2
3
4
5
6
7
8
9
10
(defmacro* test-macro (name &key (init) (config) &allow-other-keys)
`(progn
(message "name is :%s" ,name)
(if (> ,(length name) 5)
,init
,config)))

(macroexpand '(test-macro "hi" :init (message "ok") :config (message "config")))
(test-macro "hi1234" :init (message "ok") :config (message "config"))
(test-macro "ojbk")

如上就可以使用关键字来初始化了。然而关键字后面有两个表达式的情况下不能解析,keyword 只认:init之后的第一个表达式.

3 关键字解析问题

通过宏将quote形式的参数传入,调用while时,内部不需要宏参数解析。参考了 defcustom宏的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
(defmacro handle-params-macro (&rest args)
`(let ((params ',args)
current-arg
(init (list 'progn))
(config (list 'progn)))
(while params
(let ((value (pop params)))
(cond
((symbolp value) (setq current-arg value))
((eq current-arg :init) (setq init (append init (list value))))
((eq current-arg :config) (setq config (append config (list value))))
)))
(eval config)
config;init back value.
))

(setq x (handle-params-macro :init (message "hi") (message "hello") :config "ok" (message "ok")))
x

上面的方法解决了2中多form解析的问题,但是参数固定了,只有:init :config,要添加就需要修改代码,为了方便,让该宏自动查找:init这种参数并返回一个plist

4 keywords-to-plist

解决上面 #3 的 keyways 解析问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
(defmacro keywords-to-plist (&rest args)
"handle the input keywords to progn plist."
`(let ((params ',args)
current-arg
return-plist
tmp-value-arg
(init-arg (list 'progn)))
(while params
(let ((value (pop params)))
(cond
((and (symbolp value) (not (eq nil value)))
(setq current-arg value)
(setq tmp-value-arg init-arg))
(t (setq tmp-value-arg (append tmp-value-arg (list value))))
)
(if (or (eq 0 (length params)) (and (symbolp (car params)) (not (eq nil (car params)))))
(setq return-plist (plist-put return-plist current-arg tmp-value-arg)))
))
return-plist
))


(setq x (keywords-to-plist "hello" "world" :init (message "hi") (message "hello") :config "ok" (message "ok") :some (message "this is some") nil (message "new some")))

(eval (plist-get x :some))

That’s all.