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.