dired-mode 下按 Return 进入文件夹就会产生错误

我个人使用 macOS, spacemacs 的版本是 26.2. 我的 dire-mode 相关配置如下:

(defun czqhurricane-better-defaults/init-dired-mode ()
  (use-package dired-mode
    :defer t
    :init
    (progn
      (require 'dired-x)
      (require 'dired-aux)
      (setq dired-dwin-target 1)
      (setq dired-listing-switches "-alh")
      (setq dired-guess-shell-alist-user
            '(("\\.pdf\\'" "open")
              ("\\.docx\\'" "open")
              ("\\.\\(?:djvu\\|eps\\)\\'" "open")
              ("\\.\\(?:jpg\\|jpeg\\|png\\|gif\\|xpm\\)\\'" "open")
              ("\\.\\(?:xcf\\)\\'" "open")
              ("\\.csv\\'" "open")
              ("\\.tex\\'" "open")
              ("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\|ogv\\)\\(?:\\.part\\)?\\'"
               "open")
              ("\\.\\(?:mp3\\|flac\\)\\'" "open")
              ("\\.html?\\'" "open")
              ("\\.md\\'" "open")))

      (setq dired-omit-files
            (concat dired-omit-files "\\|^.DS_Store$\\|^.projectile$\\|\\.js\\.meta$\\|\\.meta$"))

      ;; Always delete and copy recursively.
      (setq dired-recursive-deletes 'always)
      (setq dired-recursive-copies 'always)

      ;; {{
      ;; @see: https://oremacs.com/2017/03/18/dired-ediff/
      (defun ora-ediff-files ()
        (interactive)
        (let ((files (dired-get-marked-files))
              (wnd (current-window-configuration)))
          (if (<= (length files) 2)
              (let ((file1 (car files))
                    (file2 (if (cdr files)
                               (cadr files)
                             (read-file-name
                              "file: "
                              (dired-dwim-target-directory)))))
                (if (file-newer-than-file-p file1 file2)
                    (ediff-files file2 file1)
                  (ediff-files file1 file2))
                (add-hook 'ediff-after-quit-hook-internal
                          (lambda ()
                            (setq ediff-after-quit-hook-internal nil)
                            (set-window-configuration wnd))))
            (error "no more than 2 files should be marked"))))
      ;; }}

      (defvar dired-filelist-cmd
        '(("vlc" "-L")))

      ;; FIXME: Evilify dired mode will lead to startup warnings.
      (evilified-state-evilify-map dired-mode-map
        :mode dired-mode
        :bindings
        (kbd "C-k") 'czqhurricane/dired-up-directory
        "E" 'dired-toggle-read-only
        "C" 'dired-do-copy
        "<mouse-2>" 'my-dired-find-file
        "`" 'dired-open-terminal
        "p" 'peep-dired-prev-file
        "n" 'peep-dired-next-file
        "z" 'dired-get-size
        "c" 'dired-copy-file-here
        "J" 'counsel-find-file
        "f" 'czqhurricane/open-file-with-projectile-or-counsel-git
        ")" 'dired-omit-mode)
      )))

这段配置是抄自子龙山人的配置. 最近使用过程中经常出现, 使用 SPC-f-j 进入 dired-mode 后, 在文件夹的高亮行, 按 Return 回车进入, 或者按 a 进入, 都会出现错误.

错误栈的代码如下:

Debugger entered--Lisp error: (wrong-type-argument stringp nil)
  file-name-nondirectory(nil)
  file-name-extension(nil)
  ad-Advice-find-file(#f(compiled-function (filename &optional wildcards) "Edit file FILENAME.\nSwitch to a buffer visiting file FILENAME,\ncreating one if none already exists.\nInteractively, the default if you just type RET is the current directory,\nbut the visited file name is available through the minibuffer history:\ntype \\[next-history-element] to pull it into the minibuffer.\n\nThe first time \\[next-history-element] is used after Emacs prompts for\nthe file name, the result is affected by `file-name-at-point-functions',\nwhich by default try to guess the file name by looking at point in the\ncurrent buffer.  Customize the value of `file-name-at-point-functions'\nor set it to nil, if you want only the visited file name and the\ncurrent directory to be available on first \\[next-history-element]\nrequest.\n\nYou can visit files on remote machines by specifying something\nlike /ssh:SOME_REMOTE_MACHINE:FILE for the file name.  You can\nalso visit local files as a different user by specifying\n/sudo::FILE for the file name.\nSee the Info node `(tramp)File name Syntax' in the Tramp Info\nmanual, for more about this.\n\nInteractively, or if WILDCARDS is non-nil in a call from Lisp,\nexpand wildcards (if any) and visit multiple files.  You can\nsuppress wildcard expansion by setting `find-file-wildcards' to nil.\n\nTo visit a file without any kind of conversion and without\nautomatically choosing a major mode, use \\[find-file-literally]." (interactive #f(compiled-function () #<bytecode 0x43144fdd>)) #<bytecode 0x4009fe3f>) "/Users/c/适航管理/screenshotImg" nil)
  apply(ad-Advice-find-file #f(compiled-function (filename &optional wildcards) "Edit file FILENAME.\nSwitch to a buffer visiting file FILENAME,\ncreating one if none already exists.\nInteractively, the default if you just type RET is the current directory,\nbut the visited file name is available through the minibuffer history:\ntype \\[next-history-element] to pull it into the minibuffer.\n\nThe first time \\[next-history-element] is used after Emacs prompts for\nthe file name, the result is affected by `file-name-at-point-functions',\nwhich by default try to guess the file name by looking at point in the\ncurrent buffer.  Customize the value of `file-name-at-point-functions'\nor set it to nil, if you want only the visited file name and the\ncurrent directory to be available on first \\[next-history-element]\nrequest.\n\nYou can visit files on remote machines by specifying something\nlike /ssh:SOME_REMOTE_MACHINE:FILE for the file name.  You can\nalso visit local files as a different user by specifying\n/sudo::FILE for the file name.\nSee the Info node `(tramp)File name Syntax' in the Tramp Info\nmanual, for more about this.\n\nInteractively, or if WILDCARDS is non-nil in a call from Lisp,\nexpand wildcards (if any) and visit multiple files.  You can\nsuppress wildcard expansion by setting `find-file-wildcards' to nil.\n\nTo visit a file without any kind of conversion and without\nautomatically choosing a major mode, use \\[find-file-literally]." (interactive #f(compiled-function () #<bytecode 0x44a7814d>)) #<bytecode 0x4009fe3f>) ("/Users/c/适航管理/screenshotImg" nil))
  find-file("/Users/c/适航管理/screenshotImg" nil)
  find-alternate-file("/Users/c/适航管理/screenshotImg")
  dired-find-alternate-file()
  funcall-interactively(dired-find-alternate-file)
  call-interactively(dired-find-alternate-file nil nil)
  command-execute(dired-find-alternate-file)

自己也进行了分析, 发现这个过程中其实有调用函数 dired-find-alternate-file, 如下所示(dired.el 中的源码):

(defun dired-find-alternate-file ()
  "In Dired, visit file or directory on current line via `find-alternate-file'.
This kills the Dired buffer, then visits the current line's file or directory."
  (interactive)
  (set-buffer-modified-p nil)
  (find-alternate-file (dired-get-file-for-visit)))

但是 dired-get-file-for-visit 函数在光标所在为文件夹时, 返回了 nil, 进而导致 find-alternate-file 调用产生错误?

自己尝试了一些方法, 比如说使用 defadvice 在调用 dired-find-alternate-file 前先判断光标所在处是否指向的是文件夹, 但是没有效果.

这个问题困扰了我好久, 不知道哪位能指点下?

这个错误的直接原因是 file-name-nondirectory(nil),来自于 file-name-extension(nil),最终来自于你在 find-file 上添加的 Advice 函数。所以调查下你的配置对 find-file 做了那些修改(就是那个 Advice 函数有问题)。

找到错误原因了, 确实是在 find-fileAdvice 函数有问题, 谢谢.

(defadvice find-file (after insert-header-to-org-buffer
                            activate compile)
  "When a new 'org' buffer is created, then insert a header to it."
  ((if (equal "org" (file-name-extension buffer-file-name))
      (progn
        (save-excursion
        (goto-char (point-min))
        (when (not (re-search-forward "# -\\*- eval: (setq org-download-image-dir (concat default-directory \\\"/screenshotImg\\\")); -\\*-" nil t))
          (insert "# -*- eval: (setq org-download-image-dir (concat default-directory \"/screenshotImg\")); -*-\n"))
        (save-buffer))))))

buffer-file-name 返回 nil, 就会出现错误.

这个需求其实可以用 auto-insert 实现

有没有可以参考的? 我这个自己写的比较 hard code.

对着改改就好

好的, 十分感谢.