Created: 25 Feb 2023
Last modified: 26 Mar 2023
Extend the switching effect in projectile
背景
projectile
已本身是个很优秀的多项目文件管理插件,同时因为预留了很多 hook 点便给我们带来了更多的可扩展性。
需求
[X]
使用rg
代替grep
进行匹配查找[X]
如果在开启了NeoTree
buffer,则在切换项目时自动更新根目录[X]
快速复制文件相对路径[X]
在项目切换时,恢复至最后的 window configuration
配置 rg 至 commander
通过 C-c p m c r
即可快速调用:
(when (executable-find "rg") (setq-default projectile-generic-command "rg --files --hidden")) (with-eval-after-load 'projectile (define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map) (global-set-key (kbd "s-p") 'projectile-command-map ) (when (executable-find "rg") (def-projectile-commander-method ?c "Grep files in project by rg, with context." (rg-menu)))
自动更新 NewTree buffer
在 after-switch
hook 添加即可:
(defun my/neotree-dir-hook () (if (get-buffer " *NeoTree*") (progn (neotree-dir (projectile-project-root)) (windmove-right)))) (add-hook 'projectile-after-switch-project-hook #'my/neotree-dir-hook)
复制相对路径
如果需要复制绝对路径, C-x C-j 0 w
由 dired
快速完成。
(defun my/projectile-copy-relative-filename-as-kill () (interactive) (kill-new (file-relative-name buffer-file-name (projectile-project-root))))
切换项目时还原 window configuration
项目间的切换行为是 projectile-find-file
即文件模糊匹配 ,这确实为我带来了些不便。第一次尝试的改造是,打开该项目的第一条最近访问文件。
(defun my/projectile-find-first-recently-file () "Open first recently file." (find-file (concat (projectile-project-root) (car (projectile-recently-active-files)))))
着重介绍下窗口配置的还原部分,主要思路是以 projectile-project-root
为 key,hash 存储最新的 window configuration
:
(defvar my/projectile-hash-newest-wc (make-hash-table :test 'equal) "Newest window configuration of each project.") (defun my/projectile-puthash-current-wc () (puthash (projectile-project-root) (current-window-configuration) my/projectile-hash-newest-wc)) (defun my/projectile-gethash-wc () (gethash (projectile-project-root) my/projectile-hash-newest-wc))
由实际的使用中发现问题,在 killed 一个项目后,也必须的从该 hash 中清除,否则无法正确地再次打开该项目。
(defun if-opend-file-name-match-p (regexp) (let ((found '())) (maphash (lambda (_ data) (cond ((string-match-p regexp (file-notify--watch-absolute-filename data)) (setq found t)))) file-notify-descriptors) found)) (defun my/projectile-remhash-killed-projects () "Delete killed projects' window configurations." (interactive) (maphash (lambda (pkey _) (cond ((or (not pkey) (not (if-opend-file-name-match-p pkey))) (remhash pkey my/projectile-hash-newest-wc)))) my/projectile-hash-newest-wc))
由于 projectile
未提供有关 kill 的回调,我们只能将其放到 after switch
钩子中。
最后再考虑一种某项目还未被缓存时情况,我们则进行返回最近最新使用文件。
(add-hook 'projectile-before-switch-project-hook #'my/projectile-puthash-current-wc) ;; store or update (add-hook 'projectile-after-switch-project-hook #'my/projectile-remhash-killed-projects) (defun my/projectile-switch-project () (let ((configuration (my/projectile-gethash-wc))) (cond ((not configuration) (progn (my/projectile-find-first-recently-file) (delete-other-windows))) (t (set-window-configuration configuration))))) (setq projectile-switch-project-action 'my/projectile-switch-project)
file notify 扩展
file-notify-descriptors
获取当前 Emacs 进程所占用的文件描述符 - file-notify--watch
映射,具体请参考 filenotify.el.gz
文件。
renyddd by Renyddd is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.