`wrong-type-argument consp`是什么原因?

背景

于是现在在写一个定位project的函数:

(defvar aucoda/project-files
  '("compile_commands.json" ".clang_complete" ".cquery" ".ccls")
  "Files to locate project root.
Root could be folders including these files or
their parent folder if they are build folders.")

(defun aucoda/find-files-in (dir files)
  "Return the full path of found file or nil if not found.
DIR and its parents will be searched.
FILES is a list of files to be found."
  (let ((json-cdb-p (lambda (json-cdb-p dir file skip-children-p)
                      (let ((file-path (concat dir (concat "/" file))))
                        (if (file-exists-p file-path) file-path
                          (if skip-children-p nil
                            (or (mapcan
                                 (lambda (dir)
                                   (funcall json-cdb-p json-cdb-p dir file t))
                                 (directory-files dir t))))))))
        (aux (lambda (aux json-cdb-p dir)
               (let ((dir (directory-file-name dir)))
                 (let ((file-path
                        (or (mapcan
                             (lambda (file)
                               (funcall json-cdb-p json-cdb-p dir file nil))
                             files))))
                   (if file-path file-path
                     (let ((parent-dir (file-name-directory dir)))
                       (when parent-dir
                         (funcall aux aux json-cdb-p parent-dir)))))))))
    (funcall aux aux json-cdb-p (expand-file-name dir))))

(defun aucoda/project-aucoda (dir)
  "Return nil if project files not found or project instance.
DIR is required by `project-find-functions`."
  (let ((file (aucoda/find-files-in dir aucoda/project-files)))
    (if file (cons 'transient (file-name-directory file)) nil)))

但是会报:

File mode specification error: (wrong-type-argument consp /absolute/path/to/.cquery)
or: Wrong type argument: consp, "/absolute/path/to/.cquery"
Error in post-command-hook (#[0 "\303\304\301\242\305#\210\300\306!\205\0r\211q\210
?\205\0\307\310\311 \")\207" [#<buffer file.h> (#0) eglot--managed-mode remove-hook post-command-hook nil buffer-live-p apply eglot--connect eglot--guess-contact] 4]): (wrong-type-argument consp "/absolute/path/to/.cquery")

但是,自己在一个buffer里执行(aucoda/find-files-in default-directory '("aucoda.el"))是会正确返回找到的文件路径的。这个错误怎么来的呢?

1 个赞

解决了,用cl-some

@et2010 昨天睡觉前给项目的查找结果搞了个缓存

递归向上查找文件可以用内置函数 locate-dominating-file

单个文件

(locate-dominating-file "~/.emacs.d/site-lisp/foo" "custom.el")
;; => "~/.emacs.d/"

多个文件

(locate-dominating-file "~/.emacs.d/site-lisp/foo"
                        (lambda (dir)
                          (when (or (file-exists-p (expand-file-name "custom.el" dir))
                                    (file-exists-p (expand-file-name "foo.el" dir)))
                            dir)))
;; => "~/.emacs.d/site-lisp/foo/"

唯一的问题是,这个函数只返回 dir。多个文件的时候,如果想要知道到底匹配到哪些文件,要在 (lambda (dir) ...) 里面记录一下:

(let* (files
       (prj-root
        (locate-dominating-file "~/.emacs.d/site-lisp/foo"
                                (lambda (dir)
                                  (let ((matchs (seq-filter
                                                 (lambda (file)
                                                   (when (file-exists-p (expand-file-name file dir))
                                                     file))
                                                 '("custom.el" "foo.el"))))
                                    (when matchs
                                      (setq files matchs)
                                      dir))))))
  (cons prj-root files))
;; => ("~/.emacs.d/site-lisp/foo/" "foo.el")
2 个赞