Emacs builtin mode 功能介绍

昨天 7月13号加的

dired 不是用 a 进入文件夹嘛(怎么是 RET 呢

多谢,看来得再编译一波了。

按 a 不如 RET 方便)

精华帖子,顶一下,避免沉默

好贴。自己攒配置人的福音! :two_hearts:

补充两篇博客

BATTERIES INCLUDED WITH EMACS

MORE BATTERIES INCLUDED WITH EMACS

6 个赞

这个作者写得真好,今天刚刚看到他写的 avy can do anything

6 个赞

我也是收藏了那篇还没看,先扫了下前面的几篇

文章好长,先收藏了。avy 目前只用来大范围的跳转,其他功能还没探索。

1 个赞

Changelog

  • 2021-10-31 新增了 file cache, filesets, shadow file

文件的艺术

file cache

如果在 C-x C-f 时不小心误按 C-TAB (默认是 file-cache-minibuffer-complete,不过好像 helm 把这个绑定给去掉了) 就会发现,其实 Emacs 还自带了一个叫做 file cache 的东西。

假设当前目录结构是这样的:

$ tree

.
├── proj1
│   ├── main.cpp
│   └── Makefile
└── proj2
    ├── main.cpp
    └── Makefile

2 directories, 4 files

在使用 M-x file-cache-add-directory-recursively (如果嫌太慢可以用 file-cache-add-directory-using-find, 实际是调用的 find . -name '*') 将这个目录以及子目录下的文件添加到 file cache 中。

然后在 C-x C-f 的时候,即使当前目录是在 /usr/share/include/,输入 main 然后再按 C-TAB 仍然可以补全成 /path/to/proj1/main.cpp. 因为 file cache 里存在多个候选项,再次按 C-TAB 就会切换成 /path/to/proj2/main.cpp.

如果想把 file cache 给清除掉,那么就直接 M-x file-cache-clear-cache 即可。另还可以使用 M-x file-cache-display 来查看 file cache 的内容。

注意,file cache 本身没有做数据的持久化,重启 Emacs 会丢失 file cache,因此如果想每次都让 Emacs 加载一些文件的话得在配置里人工指定。实际上 file cache 存储在 file-cache-alist 变量中,因此可以自己保存此变量。

;; 将 ~/projects 下的所有文件都加入
(file-cache-add-directory-using-find "~/projects")

;; 通常是 ~/.emacs.d/elpa/ 包下文件
(file-cache-add-directory-list load-path)

单项目下的文件跳转更适合用 projectile/project,如果频繁在多个项目间跳转,那么可以尝试将多个项目的文件都加入到 file cache 中。

filesets

如果你频繁地在一些 buffers 中操作,那么可以把这些 buffers 加入到 filesets 组中。Emacs manual 中推荐的用法需要用户手工调用 filesets-init,但是它会额外地构建 menu-bar 菜单,作为一个不使用 menu-bar 的人来说这点可以算是无用功了。

;; 如果你不需要数据持久化,那么就不需要下面的 hook 了
(use-package filesets
  :ensure nil
  :commands filesets-run-cmd ;; ...
  :hook (kill-emacs . filesets-save-config))

因为 filesets 库里默认标记 autoload 的只有 filesets-init 函数, 而我们又没有使用这函数,导致 filesets 无法被动加载,于是只能手动导出需要的命令了。

然后就可以通过 M-x filesets-add-buffer 来将当前 buffer 加入至一个 filesets 组中;使用 M-x filesets-remove-buffer 来将当前 buffer 从 filesets 组删除。需要注意,filesets 会使用 custom system 将组信息 (filesets-data) 持久化。如果不想弄脏 custom.el 的话可以不调用 filesets-save-config

个人觉得比较常用的命令就只有如下几个:

  • filesets-run-cmd (属 Run Shell Command 最常用)
  • filesets-open 当 filesets 组过大时需注意
  • filesets-close

剩下的都的都是 filesets 所提供的必要性功能。

  • filesets-edit (使用 custom interface 编辑 filesets-data,不过直接编辑可能更快一点)
  • filesets-add-buffer
  • filesets-remove-buffer
  • filesets-save-config

由于 Emacs 本身就提供了 multi-isearch-files, multi-isearch-files-regexpmulti-occur 等函数,用 filesets 的好处则是将操作的文件组给记录下来方便后期的操作。

Shadow files

Shadow files 可以算是 Emacs 内的单向同步机制。例如在本地写完代码后想上传至服务器,那么就可以通过这个机制来完成。为什么不直接用 tramp 呢?因为 lsp-mode 默认没法在 tramp 上补全,还需另外设置,而且 tramp 在每次保存文件的时候都会同步一次,shadow file 则是将此文件加入到写队列中,最后通过 M-x shadow-copy-files 来同步。

(use-package shadowfile
  :ensure nil
  :config
  (shadow-initialize)
  (setq shadow-literal-groups
        '(("/Youmu:/tmp/a.cpp"                  ;; 本机,本机前缀需要跟 `shadow-system-name' 一样
           "/ssh:the-remote-machine:/tmp/a.cpp" ;; 跟 tramp 的格式一样
           ))))

shadow-literal-groups 用于单文件的同步,如上配置表示本机和 the-remote-machine/tmp/a.cpp 需要同步。尝试一下从本机同步到远程的机器上,在 /tmp/a.cpp 里随便写入点东西。由于前面已经调用过 shadow-initialize 了,所以当一个文件需要同步的时候,会在 C-x C-s 时弹出提醒 Use C-x 4 s to update shadows, 当然也可以直接调用 M-x shadow-copy-files. shadowfile 同步依赖 tramp,所以无法避免 tramp 自身卡顿的问题,但是它可以避免通过 tramp 直接编辑时频繁保存带来的同步问题

上图显示 shadowfile 成功地将本地的文件同步至了远端。

当然如果仅仅是同步一个文件,那么简单的 scp 即可,也没必须引入这么多的复杂度了。

(use-package shadowfile
  :ensure nil
  :config
  (shadow-initialize)
  (setq shadow-regexp-groups
        '(("/Youmu:\\`/tmp/shadow/.+\\.[ch]pp\\'" ;; 本机
           "/ssh:the-remote-machine:\\`/tmp/shadow/.+\\.[ch]pp\\'"))))

如上这个例子可能更符合日常用途,它默认将本机上的 /tmp/shadow/ 下的 hpp/cpp 文件与远端机器同步。这里需要注意,如果远端没有目录则需要自己提前创建一个不然会同步失败。

其实 shadow 有提供命令来构建规则

  • shadow-define-literal-group
  • shadow-define-regexp-group

在使用命令的时候需要注意, shadow 里的 SITE 是一个 /ssh:the-remote-machine: 这种形式的字符串,如果想指代本机的话用 shadow-system-name 的值就行了。

16 个赞

写的太好了,感谢,先收藏了

今天复习这个帖子,发现use-package用法不解:

  1. :ensure nil 是默认行为吧,不写还省一行代码,为啥要写?
  2. :hook (after-init winner-mode)改成:init (winner-mode)不是更简单吗?

我怀疑你这样写是有好处的,但我没有证据。。。 :laughing:

烦请释疑

这个是给用package.el 的用户用的,对内置包用这个是因为他的个人配置改变了默认行为,应该是设置了 (setq use-package-always-ensure t),这样在第三方包就可以省略很多 :ensure t

你提的第二点是加载顺序问题,加hook 是希望init完成之后再加载指定的包。差别不大

其实我没设置 use-package-always-ensuret,主要是想分清楚哪些包是内置包,哪些包是三方包。这样无论用户是否修改 use-package-always-ensure 都能看得明白一点。

这个是正解。这里有个加载顺序的问题,在 :init 里是在这个包加载前就会执行的,如果对应的函数不是 autoloaded 的话就报找不到符号的号,虽然多数情况下都是满足要求的。

2 个赞
(defun newcomment-toggle (n)
  (interactive "*p")
  (if (or (use-region-p)
          (save-excursion
            (beginning-of-line)
            (looking-at "\\s-*$")))
      (call-interactively 'comment-dwim)
    (let ((range
           (list (line-beginning-position)
                 (goto-char (line-end-position n)))))
      (comment-or-uncomment-region
       (apply #'min range)
       (apply #'max range)))))

非空单行可加数字前缀,如 C-u 3 M-; 可以注释3行,更贴近 evil-nerd-comment

1 个赞

这个参数现在不需要设置了,默认就是这个,只要开启 isearch-lazy-count t。我用的 Emacs 29

2 个赞
(lazy-count-prefix-format nil)
(lazy-count-suffix-format " [%s/%s]")

我现在改成这样了,在使用 evil-mode 的 / 搜索时(evil-search-moduleisearch),将这个次数比放在后面显得比较好看。

2 个赞

怎么开启? 这是好多编辑器默认开启的,emacs咋不默认开启?

看了帮助是这样写的

goto-address-mode is an autoloaded interactive byte-compiled Lisp function in ‘goto-addr.el’. (goto-address-mode &optional ARG) Minor mode to buttonize URLs and e-mail addresses in the current buffer. If called from Lisp, toggle the mode if ARG is ‘toggle’. Enable the mode if ARG is nil, omitted, or is a positive number. Disable the mode if ARG is a negative number.

但在配置中显示写了(goto-address-mode 1) 还是不生效,最后是靠M-x goto-address-mode来开启。是配置写得有问题吗?

环境:Windows10 原生 GNU Emacs 28.2。