Helm, Ripgrep và Emacs dùng... helm-ag
Có một thủ thuật khá là tiện dụng khi dùng helm
trong Emacs đó là lệnh helm-resume
giúp khôi phục lại lệnh đã chạy trước đó, lệnh này đặc biệt hữu dụng khi đang search trong toàn project.
Lại nói về search, thường chúng ta hay xài ag
(hay còn gọi là the_silver_searcher
) vì nó có performance rất tốt, nhưng còn có một công cụ xịn hơn cả ag
đó là rg
(gọi là ripgrep
, build bằng Rust, tất nhiên ). Để tích hợp ag
vào Emacs/Helm chúng ta có gói helm-ag
, với rg
thì chúng ta có helm-rg
.
Nhưng helm-rg
không play nice lắm với lệnh helm-resume
, nó thường xảy ra lỗi đại loại là helm-rg--make-process should run a process
sau khi resume, dẫn tới việc không jump tới buffer trong kết quả search được. Anh tác giả không chịu fix vì cho rằng đó là lỗi của helm
hay helm-projectile
gì đó, nói chung là đổ thừa, mà mình thì rất ghét dev có tính đổ thừa. Trong khi sự thật rõ rành rành, helm-ag
vẫn chạy tốt khi resume!
Mà ghét rồi thì sao? Không lẽ bỏ không xài rg
nữa, về xài ag
? Đời nào lại thế?
Hóa ra, tác giả của helm-ag
đã rất tinh ý khi hướng dẫn cả cách xài rg
thay thế cho ag
, documented ngay trong code của helm-ag
! Đó là:
- Vào màn hình tùy biến giá trị
M-x customize-set-variable
- Chọn giá trị
helm-ag-base-command
- Đặt giá trị mới thành:
rg --vimgrep --no-heading --smart-case
Ba bước trên thì ai cũng biết rồi, không có gì đáng nói.
Cái đáng nói là khi kết hợp với helm-projectile
(ai xài projectile
thì sẽ gặp liền), rg
sẽ phun ra một đống lỗi:
.#*: No such file or directory (os error 2)
*.o: No such file or directory (os error 2)
*~: No such file or directory (os error 2)
*.bin: No such file or directory (os error 2)
*.lbin: No such file or directory (os error 2)
*.so: No such file or directory (os error 2)
*.a: No such file or directory (os error 2)
*.ln: No such file or directory (os error 2)
*.blg: No such file or directory (os error 2)
*.bbl: No such file or directory (os error 2)
*.elc: No such file or directory (os error 2)
*.lof: No such file or directory (os error 2)
*.glo: No such file or directory (os error 2)
*.idx: No such file or directory (os error 2)
*.lot: No such file or directory (os error 2)
*.fmt: No such file or directory (os error 2)
*.tfm: No such file or directory (os error 2)
...
Nguyên nhân là vì helm-projectile
quá thông minh và nhiệt tình, tới mức nhanh nhẩu, tự add thêm vài cái option kiểu --ignore *.o ...
vô khi chạy helm-projectile-ag
, và tất nhiên rg
không có option này, nên nó hiểu nhầm các giá trị kia là đường dẫn cần search.
Đến đây có hai cách để giải quyết:
- Là sửa code của
helm-projectile
để nó khỏi thêm vào các option "lạ" nữa - Là wrap cái lệnh
rg
trong máy lại thành một lệnh khác để loại bỏ thuộc tính "lạ" kia đi
Cách thứ 1 nếu làm theo thì sẽ ngầu hơn, nhưng khó maintain về sau, nếu không ngại việc tự xài một bản fork riêng cho mọi thứ mình dùng thì cứ chơi.
An toàn nhất là chọn cách 2. Wrap lệnh rg
lại tức là tạo ra một lệnh mới, đặt ở, ví dụ /usr/local/bin/rgemacs
, với nội dung như sau:
#!/usr/bin/env bash
set -euo pipefail
newargs="$(echo "$@" | sed 's/\-\-ignore .* //')"
rg $newargs
Đừng quên cấp quyền execute cho nó:
$ chmod +x /usr/local/bin/rgemacs
Rồi thay giá trị của helm-ag-base-command
thành rgemacs --vimgrep --no-heading --smart-case
, thế là xong.
Update 19/11/2018: Cách trên gây ra một vấn đề đó là khi trong search query có nhiều từ và khoảng trắng, thì chỉ có một từ duy nhất ở cuối query được ghi nhận, ví dụ, search với query: "day la con ga" thì chỉ có chữ "ga" là được search.
Cách tốt hơn đó là vẫn xài rg
thay cho rgemacs
trong helm-ag-base-command
và ghi đè hàm helm-projectile-ag
bằng cách chèn đoạn code sau vào Emacs config của bạn, tất nhiên là sau khi load helm-ag
và projectile
:
(defun helm-projectile-ag (&optional options)
"Helm version of projectile-ag."
(interactive (if current-prefix-arg (list (read-string "option: " "" 'helm-ag--extra-options-history))))
(if (require 'helm-ag nil 'noerror)
(if (projectile-project-p)
(let ((helm-ag-command-option options)
(current-prefix-arg nil))
(helm-do-ag (projectile-project-root) (car (projectile-parse-dirconfig-file))))
(error "You're not in a project"))
(error "helm-ag not available")))
Biên lại dựa trên kinh nghiệm và hướng dẫn đọc được từ một cái gist.