projectile有个很好的特性,是在git控制的项目里可以自动识别并过滤gitignore里面的内容;可是在普通目录里,只好编辑.projectile文件,十分麻烦;有些我从github上下载下来的项目,我想让projectile过滤掉gitignore中的文件,可是又不想每次都写一遍.projectile文件,而且也不想把这样的目录加入git控制,只是读一读代码;这些目录下本身就带着gitignore文件;不知道能不能让projectile在只要包含又gitconfig的,或gitignore文件的,就认为是项目根目录呢?就和被git管控的目录一样?
谢谢;
projectile默认就是支持gitignore的,可以检查下 projectile-git-command
和 projectile-git-ignored-command
的值。
只有一个孤零零的 .gitignore
时,项目的根目录无法确定:
$ cd ~/Downloads/test-orphan-gitignore && ls -a
./ ../ .gitignore foo.el bar.el
$ cat .gitignore
*~
bar.el
$ emacsq.sh -P projectile -M projectile-mode -nw --eval "(print (projectile-project-root))" --batch
"/Users/john/"
当执行 -find-file
或获取文件列表时就会卡住,因为 ~
底下文件太多了:
$ time emacsq.sh -P projectile -M projectile-mode -nw --eval "(print (projectile-project-files (projectile-project-root)))" --batch
^C
________________________________________________________
Executed in 90.21 secs fish external
usr time 2.22 secs 192.00 micros 2.22 secs
sys time 0.63 secs 1692.00 micros 0.63 secs
所以,
首先是解决项目根的问题
在缺失 .git
的情况下,让 projectile 认 .gitignore
为根:
$ emacsq.sh -P projectile -M projectile-mode -nw --eval \
"(progn
(add-to-list 'projectile-project-root-files-bottom-up \".gitignore\")
(print (list :project-root (projectile-project-root)))
(print (list :project-files (projectile-project-files (projectile-project-root)))))" --batch
(:project-root "/Users/johon/Downloads/test-orphan-gitignore/")
(:project-files ("./.gitignore" "./.gitignore~" "./bar.el" "./foo.el"))
然后再解决如何应用 .gitignore
规则
然而 projectile 并不直接处理 .gitignore
, 而是直接从 git ls-files
获得文件列表。所幸它和 .projectile
差别不是很大,所以这里又有 3 种可能:
-
git ls-files
可否应用于一个非项目文件夹?
- 实现一个
projectile-gitignore-to-dirconfig
转换函数?
- 参考
projectile-parse-dirconfig-file
实现一个 projectile-parse-gitignore-file
?
如果 1
可行的话就最简单,2
和 3
差不多。
我试了一下直接把 .gitignore
当作 .projectile
用(暂不考虑 .gitignore
文件里存在注释的情况)是可行的:
$ emacs -Q -nw -l /path/to/test-projectile-orphan-gitignore.el --batch
(:project-root "/Users/johon/Downloads/test-orphan-gitignore/")
(:project-files (".gitignore" "foo.el"))
test-project-orphan-gitignore.el
;;; Usage: /path/to/emacs -nw -Q -l /path/to/test-projectile-orphan-gitignore.el
;;; Date: 2021-08-22_22.43.53
(toggle-debug-on-error)
(global-set-key (kbd "C-h") 'delete-backward-char)
(global-set-key (kbd "M-h") 'backward-kill-word)
(global-set-key (kbd "<f1>") 'help-command)
(define-key isearch-mode-map "\C-h" 'isearch-delete-char)
(require 'seq)
(setq user-emacs-directory
(car (seq-filter
#'file-exists-p
(list (format "~/.emacs.d/%s/" emacs-version)
(format "~/.emacs.d/%s/" emacs-major-version)
"~/.emacs.d/"))))
(setq package-user-dir (concat user-emacs-directory "elpa/"))
(package-initialize)
(require 'projectile)
(projectile-mode 1)
(require 'ido)
(ido-mode 1)
(progn
;; (setq projectile-indexing-method 'native)
(global-set-key (kbd "C-x C-f") #'projectile-find-file)
;; 1. 确定项目的根
(add-to-list 'projectile-project-root-files-bottom-up ".gitignore")
(defun projectile-orphan-gitignore-p (project-root)
(let ((default-directory project-root))
(and (file-exists-p ".gitignore") (not (file-exists-p ".git")))))
(define-advice projectile-project-vcs (:filter-return (return) +orphan-gitignore)
(or (if (equal return 'none)
(when (projectile-orphan-gitignore-p (projectile-project-root))
'orphan-gitignore))
return))
;; 2. 应用 .gitnore 规则
(define-advice projectile-dirconfig-file (:filter-return (return) +orphan-gitignore)
(if (file-exists-p return) return
(let ((default-directory (projectile-project-root)))
(when (projectile-orphan-gitignore-p default-directory)
(expand-file-name ".gitignore" default-directory)))))
;; --- 强制修改 projectile-indexing-method 为 native
;; (define-advice projectile-project-files (:around (orig-fn project-root) +orphan-gitignore)
;; (let ((projectile-indexing-method
;; (if (projectile-orphan-gitignore-p project-root)
;; 'native
;; projectile-indexing-method)))
;; (funcall orig-fn project-root)))
;; --- 或在 `projectile-dir-files-alien' 内增加 orphan-gitignore 类型
(define-advice projectile-dir-files-alien (:override (directory) +orphan-gitignore)
(let ((vcs (projectile-project-vcs directory)))
(cond
((eq vcs 'orphan-gitignore)
(projectile-dir-files-native directory))
((eq vcs 'git)
(nconc (projectile-files-via-ext-command directory (projectile-get-ext-command vcs))
(projectile-get-sub-projects-files directory vcs)))
(t (projectile-files-via-ext-command directory (projectile-get-ext-command vcs))))))
(print (list :project-root (projectile-project-root)))
(print (list :project-files (projectile-project-files (projectile-project-root)))))
;;; test-projectile-orphan-gitignore.el ends here
1 个赞
加了 (add-to-list 'projectile-project-root-files-bottom-up “.gitignore”),好像可以自动识别ignore文件了。
歪个楼(不过正好也能解决楼主的问题)。
最近在优化我的配置,发现 projectile 还比较耗时,于是研究了下自带的 project.el 作为替代的可能性。
结论: 28 版本的 project.el 完全可以替代,并且速度非常快
27 版本如何使用
主要特点
- 可以把 “.project” “README.org” “README.md” “Makefile” “pom.xml” “go.mod” “cargo.toml” “project.clj” 这些文件作为 project-root
- 使用 fd 找 project 内文件
- my/project-discover, 自动把指定目录下的项目追加到 project 中,方便 switch
相关配置
(transient-define-prefix my/project-command
"Project"
[["Find"
("f" "File" project-find-file)
("s" "Search" project-search)
("d" "Dired" project-dired)
("v" "discoVer" my/project-discover)
("b" "buffer" project-switch-to-buffer)]
["Manage"
("p" "Project" my/project-switch)
("e" "Eshell" project-eshell)
("r" "Remove" project-remove-known-project)
("i" "Info" my/project-info)
("c" "Compile" project-compile)]])

你试一下孤立的 .gitinore
规则是否生效。不要使用常见的被过滤的后缀名(例如 .elc / .o 等等)测试。