相关文章推荐
老实的烈酒  ·  使用Node.js ...·  3 月前    · 
严肃的柚子  ·  Android ...·  3 年前    · 
越狱的煎饼果子  ·  SQL ...·  3 年前    · 
  • dired-single.el 让 dired 只使用一个 buffer 。
  • dired+.el 扩展 dired.el 、 dired-x.el 和 dired-aux.el 的功能。
  • dired-x.el 这是 Emacs 自带的扩展,安装方法可以查看 Emacs 的 info 文
  • dired-isearch.el 在 dired 里面只针对文件名做 i-search 。
  • dired-details.el 隐藏多余的信息,显示简洁的文件列表。
  • dired-details+.el dired-details.el 的扩展。
  • dired-view.el 输入文件名的首字母快速跳转。
  • dired-sort-menu.el 方便地使用各种方式进行排序,并提供一个菜单用于执
  • dired-sort-menu+.el dired-sort-menu.el 的扩展。
  • dired 在每打开一个新的目录之后总会打开一个新的 buffer ,这样,在浏览了 很多目录之后, Emacs 里面就会充满了目录的 buffer ,很不舒服,不过有很多 方法可以解决这个问题。

    不用任何扩展

    Emacs 22 里面,可以使用 a 来访问目录,这样可以避免打开多余的buffer ,不过使用回车或者鼠标点击打开目录的话,还是会产生多出来的 buffer 。

    对内置的函数进行 advice

    可以通过对 Emacs 内置的函数进行 advice 也可以达到这个目的,其实就是对 进入其他目录的动作进行截获,判断如果进入了一个目录,则把原来目录的那个 buffer 删掉:

    -->( defadvice dired-find-file (around dired-find-file-single-buffer activ ate) "Replace current buffer if file is a directory." (interactive) ( let ((orig (current-buffer)) (filename (dired-get-file-for-visit))) ad-do-it ( when (and (file-directory-p filename) (not (eq (current-buffer) orig))) (kill-buffer orig)))) ( defadvice dired-up-directory (around dired-up-directory-single-buffer activate) "Replace current buffer if file is a directory." (interactive) ( let ((orig (current-buffer))) ad-do-it (kill-buffer orig)))

    dired+.el 提供 toggle-dired-find-file-reuse-dir ,用于打开和关闭默认按 键是否重用当前 buffer ,如果你不想影响默认新闻,它还提供了了两个函数, diredp-find-file-reuse-dir-buffer diredp-mouse-find-file-reuse-dir-buffer ,你可以把他们绑定到其他的按键 上面。不过,使用 ^ 跳到上一级目录的时候仍然是没有重用当前的 buffer 的,当 然,你可以使用在 .. 上按回车跳到上一级的办法。

    使用 dired-single.el

    dired-single.el 专门为这个问题而设计,他可以保证所有的 dired buffer 都 使用同一个名字,换句话说,总是只会有一个 dired buffer 存在,并且我们可 以方便地跳到那个 buffer ,如果 buffer 不存在的话, dired-single.el 会自 动帮我们创建。

    作为基本使用,在 dired 里面重新绑定 回车、鼠标点击和 ^ 键:

    (add-hook 'dired-mode-hook ( lambda () (define-key dired-mode-map (kbd "RET" ) 'joc-dired-single-b uffer) (define-key dired-mode-map (kbd "<mouse-1>" ) 'joc-dired-si ngle-buffer-mouse) (define-key dired-mode-map (kbd "^" ) ( lambda () (interactive) (joc-dired-single-buffer ".." )))))

    另外,如果想使用单一的 dired buffer 模式,首先要固定住 dired buffer 的 名字,这样才能方便地在任何时候跳转到原来存在的 dired buffer 那里。定制 joc-dired-magic-buffer-name 可以设定这个值,他的默认值为 *dired* 。然后 要把 joc-dired-use-magic-buffer 设定为 t 以启用 magic-buffer 机制。最后 我们绑定 C-x d joc-dired-magic-buffer 上,这样,就可以使用 C-x d 来 跳转回存在的 dired buffer ,如果事先没有这个 buffer 存在,他会询问你要 访问的目录并自动创建这个 buffer 。

    (add-hook 'dired-load-hook ( lambda () (define-key dired-mode-map (kbd "RET" ) 'joc-dired-single-b uffer) (define-key dired-mode-map (kbd "<mouse-1>" ) 'joc-dired-si ngle-buffer-mouse) (define-key dired-mode-map (kbd "^" ) ( lambda () (interactive) (joc-dired-single-buffer ".." ))) (setq joc-dired-use-magic-buffer t) (setq joc-dired-magic-buffer-name "*dired*" ))) (global-set-key (kbd "C-x d" ) 'joc-dired-magic-buffer)

    但是由于 dired-single 的实现方法的原因,现在不能在 .. 上回车跳到上一级 目录了,不过这不是什么大问题,可以使用 ^ 键直接跳到上一级目录。但是如 果觉得不爽,可以把这个 patch 打到 dired-single.el 上去以修复这个问题:

    --> *** dired-single.el 2006-10-02 23:47:52.000000000 +0800 --- dired-single.el.new 2006-10-03 14:19:42.000000000 +0800 *************** *** 244,250 **** its name will not change every time a new directory is entered)." (interactive) ;; use arg passed in or find name of current line ! (let ((name (or default-dirname (dired-get-filename)))) (save-excursion (save-match-data ;; See if the selection is a directory or not. --- 244,250 ---- its name will not change every time a new directory is entered)." (interactive) ;; use arg passed in or find name of current line ! (let ((name (or default-dirname (dired-get-file-for-visit)))) (save-excursion (save-match-data ;; See if the selection is a directory or not.

    另外, dired-single.el 是根据 dired 列出的文件类型那个 d 来判断是否是 目录的,这样对于处理软链接的目录就不行了,我们可以使用 Emacs 提供的 file-directory-p 来判断,这个函数会跟踪软链接,只要把这个 patch 打到 dired-single.el 上就可以解决这个问题:

    --> *** dired-single.el 2006-10-05 11:17:06.000000000 +0800 --- dired-single.el.new 2006-10-05 11:17:06.000000000 +0800 *************** *** 252,258 **** (let ((eol (point))) (beginning-of-line) ;; assume directory if arg passed in ! (if (or default-dirname (re-search-forward "^ d" eol t)) ;; save current buffer's name (let ((current-buffer-name (buffer-name))) ;; go ahead and read in the directory --- 252,258 ---- (let ((eol (point))) (beginning-of-line) ;; assume directory if arg passed in ! (if (or default-dirname (file-directory-p name)) ;; save current buffer's name (let ((current-buffer-name (buffer-name))) ;; go ahead and read in the directory

    既然 dired-single.el 可以让 dired 使用单一的 buffer ,并随时方便跳转到 那个 buffer ,我们也可以稍微扩展一下让它能做到跳转到当前 buffer 说对应 的文件的目录。

    (interactive "D" ) ( let ((win-list (window-list))) ( when (null (cdr win-list)) ; only one window (split-window-vertically)) (other-window 1) (joc-dired-magic-buffer directory))))

    dired-view.el 提供一个方便的功能,在打开 dired-view-minor-mode 之后, 可以输入文件名的首字母快速定位到文件。通常这个 minor-mode 并不是一直打 开,事实上,他是重新定义了所有的字母以及数字键说绑定的函数,所以如果文 件名是中文的话也没有办法,而且按键会和 dired 本身的很多按键冲突,于是 他提供了方便的打开和关闭的函数:

    -->( require ' dired-isearch ) (define-key dired-mode-map (kbd "C-s" ) 'dired-isearch-forward) (define-key dired-mode-map (kbd "C-r" ) 'dired-isearch-backward) (define-key dired-mode-map (kbd "M-C-s" ) 'dired-isearch-forward-regexp ) (define-key dired-mode-map (kbd "M-C-r" ) 'dired-isearch-backward-regex p)

    可是这个东西并不能和 dired+.el 共同工作,默认情况下,dired 在文件显示 的地方可以用鼠标进行点击,而 dired-isearch.el 也正是依靠这个进行判断 的,可是 dired+.el 让整行都处于可用鼠标点击进行操作(事实上, dired+.el 提供了很强大的鼠标操作功能,例如,可以像常见的文件管理器那样区域选择、 用 Shift 加鼠标来选择以及用 Ctrl 加鼠标来选择等), dired-isearch.el 的 方法就不管用了,不过其实只要一点小小的改动,把 dired-isearch.el 里面的 所有的 help-echo 改为 dired-filename 就可以正常使用了。

  • dired-omit-files 是一个正则表达式,凡是匹配这个正则表达式的文件就会 被忽略掉,默认情况下会忽略自动保存的文件(以 # 开头),当前目录和父目 录(也就是 . .. )。
  • dired-omit-extensions 是一个字符串列表,凡是文件名结尾是这个列表中 的某一个的时就会被忽略。
  • 我通常把 dired-omit-files 设置为 "^#\\|^\\..*" ,这样可以忽略掉以 . 开 头的文件,在 Linux 下面这通常表示隐藏文件, ls 默认情况下也不会显示他 们,而且主目录下面通常有一大堆这样的文件,显示出来看起来非常恐怖。

    -->(add-hook 'dired-mode-hook ( lambda () (setq dired-omit-files "^# \\ | ^\\..*" ) ; omit all hidden fi le which starts with `.' (dired-omit-mode 1))) ; initially omit uni ntrested files

    在 Windows 上,文件相关联的程序都是在系统那里注册了的,可以直接执行相 关程序而不用自己再去定义关联列表。我没有在 Windows 下用这个,所以没有 办法做什么测试,也不多说, EmacsWiki 上有比较详细的介绍。这里我说一下几 种在 Linux 下实现使用关联的程序打开文件的方法,都是需要自己定义关联关 系的,因为 Linux 下并没有统一的关联程序的标准,而且,如果只是使用 dired 作为文件管理器的话,自己定义一些自己最常用的关联关系也是很方便的。

    使用 dired-x.el

    dired-x.el 提供猜测 shell command 的功能,在某一个文件上按 ! dired-x.el 会把猜测的命令在方括号中显示出来,可以直接回车执行,也可 以用 M-p 来取得默认命令,并进行适当的编辑再执行,如果有几个可选的默认 命令,可以用 M-p M-n 来进行轮换选择。

    dired-x.el 根据 dired-guess-shell-alist-default 变量里面定义的命令来进 行猜测,另外还有一个 dired-guess-shell-alist-user 用于自定义猜测的命 令,这里面的命令会覆盖掉 dired-guess-shell-alist-default 里面的命令。

    这个变量是一个列表,表的每一项格式为 (REGEXP COMMAND ...) ,其中每一个 COMMAND 都可以是一个字符串或者是一个可以求值得到一个字符串的 Elisp 表 达式。在字符串里面如果有 * 出现则会被替换成文件名,另外,也可以直接在 Elisp 表达式里面使用 file 这个变量,查看 dired-guess-shell-alist-default 就可以得到很多例子,例如:

    (file-name-sans-extension file) "; " dired-guess-shell-gnutar " -C " (file-name-sans-extension file) " -zxvf" ) (concat "mkdir " (file-name-sans-extension file) "; gunzip -qc * | tar -C " (file-name-sans-extension file) " -xvf -" ))

    不足的地方是只能对单个文件进行猜测,如果 mark 了一堆文件的话,这个功能 就不太好用了。

    另外,对于 X 下的应用程序,我们通常不希望它把 Emacs 阻塞掉,而是同步执 行,只需要在末尾加上 & 即可同步执行,同时 Emacs 会收集程序输出,例如:

    可是有些程序的输出含有很多终端控制字符,mplayer 就是一个例子,我在这样 运行 mplayer 的时候显得十分卡,我想可能是输出被 Emacs 捕获到 buffer 里 面的原因。这些输出本身就没有什么用,如果还会让程序运行缓慢的话,就更可

    一个办法是直接在 shell 命令那里下手,例如:

    -->( defadvice dired-run-shell-command (around kid-dired-run-shell-command (command)) "run a shell command COMMAND . If the COMMAND ends with `&' then run it in background and *discard* t he output, otherwise simply let the original ` dired-run-shell-command ' ru n it." ( if (string-match "&[[:blank:]]*$" command) ( let ((proc (start-process "kid-shell" nil shell-file-name shell-command-switch (substring command 0 (match-beginni ng 0))))) (set-process-sentinel proc 'shell-command-sentinel)) ad-do-it)) (ad-activate 'dired-run-shell-command)

    这个缺点就是所有以 & 结尾的程序的输出都直接被丢弃了,但是我觉得几乎这 样的程序输出都是不需要的,而且常规的文件管理器也通常都直接丢弃这些输出 吧。如果实在是不愿意,可以另外定义一种语法(比如末尾是 &! 的才丢弃输出) 并修改那个函数里面相应的匹配的正则表达式就可以了。

    另外,其实有许多不同的类型的文件使用相同的命令打开,可以把他们归到一组 (例如,各种 video 文件都用 mplayer 打开),但是 dired-x.el 并不提供这个 功能,所以我写了一个宏来从“表面”上实现这个功能:

    -->( defmacro kid-dired-define-assoc-group (patterns actions &optional nam e) "define an assoc entry to help dired guess the shell command. PATTERN is a list of regexps used to match the filename. NAME is a list of string or expression which eval to a string to denote what shell command to execute. Optional NAME is the name of this group , just for documentation purpose." ( let ((item (gensym))) `( dolist (,item ',patterns) (setq dired-guess-shell-alist-user (cons (list ,item ,@actions) dired-guess-shell-alist-user ))))) (add-hook 'dired-load-hook ( lambda () (setq dired-guess-shell-alist-user nil) (kid-dired-define-assoc-group ( "rm" "rmvb" "RM" "RMVB" "avi" "mpg" "mpeg" ) ( "mplayer * &" ) video) (kid-dired-define-assoc-group ( "pdf" "PDF" ) ( "acroread * &" "xpdf * &" ) pdf-document) (kid-dired-define-assoc-group ( "png" "jpg" "jpeg" "gif" ) ( "xloadimage * &" "gqview * &" ) image) (kid-dired-define-assoc-group ( "chm" "CHM" ) ( "xchm * &" ) chm-document) (kid-dired-define-assoc-group ( "html" "HTML" "htm" "HTML" ) ( "firefox * &" ))))

    trivial-mode.el 允许你定义一个 "trivial mode" ,并把他添加到 auto-mode-alist 里面去,比如,定义了一个所有 pdf 文件用 xpdf 打开的 trivial mode ,那么当在 Emacs 里面打开 pdf 的时候会自动调用 xpdf 打开 这个文件。当然这个扩展可以完全独立于 dired 而使用,还是比较方便的。

    dired-sort-menu.el 提供了强大的排序功能,可以按照名称、修改时间、访问时 间、大小、扩展名等进行排序,还可以逆序排序和递归排序。同时,它还提供了 一个菜单以及一个对话框(使用 Emacs 的 widget 库来实现的,就是说,在终端 下面也是可以用的)来选择排序功能。可以使用 C-d 来打开这个对话框,或者用 Shift+mouse-2 来打开弹出菜单选择排序项目。 dired-sort-menu+.el dired-sort-menu.el 做了一定的修改。

    自己对 dired 进行扩展

    我们可以通过 dired-sort-other 给 ls 程序传递参数来达到排序的目的, gnu 的 ls 程序有很多选项可用: [tSXUucrR]

    我们这里使用 dired 提供的 dired-sort-other 来调用 gnu ls 来进行排序, 为了方便记忆,我覆盖掉 dired 的 s 键(原本用于切换按照时间还是文件名进 行排序),作为一个前缀,然后后面的键着直接对应于 ls 的相关参数,例如 s X 则是对扩展名进行排序。另外,还定义了两个开关, s r 用于打开或关闭逆 序排序,而 s R 用于打开或关闭递归排序。

    -->( defmacro kid-dired-define-sort (key switch) "define the key `s KEY' to sort use switch to ls." `(define-key dired-mode-map ,(concat "s" key) ( lambda () (interactive) (dired-sort-other (concat dired-listing-switches ,switch ( if kid-dired-sort-reverse ( if kid-dired-sort-recursive "" )))))) ( defmacro kid-dired-define-toggle (key var) `(define-key dired-mode-map ,(concat "s" key) ( lambda () (interactive) (setq ,var (not ,var)) (message "%s %s." (get ',var 'variable-documentation) ( if ,var "enabled" "disabled" ))))) (add-hook 'dired-mode-hook ( lambda () ;; dired use `s' to switch sort by name/time, we undefine it so ;; that it can be used as prefix (define-key dired-mode-map (kbd "s" ) ( defvar kid-dired-sort-reverse nil "sort reversely" ) ( defvar kid-dired-sort-recursive nil "sort recursively" ) (kid-dired-define-sort "X" "X" ) (kid-dired-define-sort "t" "t" ) (kid-dired-define-sort "S" "S" ) (kid-dired-define-sort "U" "U" ) (kid-dired-define-sort "u" "ut" ) ; sort by access time (kid-dired-define-sort "c" "ct" ) ; sort by ctime (kid-dired-define-sort "n" "" ) ; sort by name :) (kid-dired-define-toggle "r" kid-dired-sort-reverse) (kid-dired-define-toggle "R" kid-dired-sort-recursive)))