基于 ripgrep 的代码搜索和重构工具

看你报错,感觉你自己的advice有问题啊,my-advice-compilation-filter

你用emacs -Q对比测试一下?

感谢,确实是自己配置的问题。

旅游还写代码,真爱 ,佩服佩服:smile:

写代码最重要的是想象力,放松的时候反而灵感比较多.

念念不忘,才有所回响!

个人使用 color-rg 的感受是:简洁、快速、有针对性。主要用于查询中、英、韩文关键字,在搜索结果中跳转到需要的位置查看。
但有时候不得不在 windows 上使用 emacs,那么 color-rg 搜索中、英文关键字一切正常,就是到了其它语种的关键字比如 韩文,就没有效果了!这也是 windows gbk 带来的麻烦!

自己想了一个办法,就是调用 python 脚本。在 emacs 中将需要搜索的韩文转码,作为参数传递给 python ,在 python 中使用 rg -E utf-8 来搜索!得到了正确的结果!

(setq blove/rg-keywd  "니트")
(setq blove/rg-search-path-or-file  "c:/rg-search-path")
(setq blove/rg-keywd (url-encode-url blove/rg-keywd))
(setq blove/rg-cmd (concat "python c:/py-rg.py " blove/rg-keywd " " blove/rg-search-path-or-file))
(shell-command-to-string blove/rg-cmd)

pythong 脚本

import os,sys
import platform
from urllib.parse import unquote

if platform.system() == "Windows":
    #  cmd = "rg -E utf-8 --column --color=always -H --heading --max-columns 3000 --no-ignore -g '!node_modules' -g '!dist' --smart-case -e " + unquote(sys.argv[1]) + " "
    cmd = "rg -E utf-8 " + unquote(sys.argv[1]) + " " + sys.argv[2]
else:
    cmd = "rg -E utf-8 " + sys.argv[1] + " " + sys.argv[2]

if __name__ == "__main__":
    ret_str = os.system(cmd)
    print(ret_str)



研究了一下 color-rg.el 的第 775 行,尝试简单写一个嵌入函数,但没效果。

(defun color-rg-search-symbol-in-dest-file (py-script rg-search-path-or-file)
  (interactive)
  (setq command-line (concat "powershell python " py-script " " (url-encode-url (color-rg-pointer-string))))
  ;; (color-rg-search-input (url-encode-url (color-rg-pointer-string)) rg-search-path-or-file))
  (color-rg-search-input (color-rg-pointer-string) rg-search-path-or-file))

(setq blove/rg-search-path-or-file  "c:/rg-search-path")
(global-set-key (kbd "C-c C-g") '(lambda () (interactive)
								   (color-rg-search-symbol-in-dest-file "c:/py-rg.py" blove/rg-search-path-or-file)
								   ))

那么,请问这种调用 python 执行 ripgrep 的方式,是否适合嵌入到 color-rg 中?如果可以,应该定义哪个变量?

这个函数可以加个选项

感谢大佬!但是我个人能力有限,还是没太研究明白!
目前的困惑是,具体不知道该把 “python c:/py-rg.py” 这样的调用脚本命令加在哪! :laughing: :rofl:
目前我先大概参考了一下大佬的 color-rg 整体功能思路,自己写了一个丐版的先暂时在 windows 上用着!等腾出时间再仔细研究 ,以 color-rg 为基础写入自己的一些想要的功能!

;;;
;;; ~ rg for ZH-CN | Eng | Korean ~
;;;
(defun blove/make-rg-open-f-link (label link line)
  "click, open the fiel, goto the line"
  (insert-button label
				 'action (lambda (_) (find-file link)(goto-line (string-to-number line)))
				 'follow-link t
				 )
  (insert "\n")
  )
(defun blove/open-rg-ret-in-buffer (item-list)
  "popup a buffer, list the results & file-open-link"
  (let ((buf (get-buffer-create "*RipGrep Blove Buffer*")))
	(with-current-buffer buf
	  (erase-buffer)
	  (mapcar (lambda (item)
				(setq label (concat item " " "ﰺ"))
				(setq dest-line (nth 2 (split-string item ":")))
				(blove/make-rg-open-f-link label (concat (nth 0 (split-string item ":")) ":" (nth 1 (split-string item ":"))) dest-line)
				)
			  item-list))
	(pop-to-buffer buf t)
	)
  )
(defun blove/rg-search-by-sel-region (py-script path-or-file)
  "main function for sel-region"
  (interactive)
  (let* (
		 (selection
		  (if (memq window-system '(w32 pgtk))
			  (url-encode-url (buffer-substring-no-properties (region-beginning) (region-end)))
			(buffer-substring-no-properties (region-beginning) (region-end))
			)
		  )
		 (rg-cmd (concat py-script " \"" selection "\" \"" path-or-file "\""))
		 (item-list (split-string (shell-command-to-string rg-cmd) "\n" "\n" "0"))
		 )
	(if (not (= (string-to-number (car item-list)) 1))
		(blove/open-rg-ret-in-buffer item-list)
	  (message "No matches were found!!!")
	  )
	)
  )
(defun blove/rg-search-by-word-point (py-script path-or-file)
  "main function for word-at-point"
  (interactive)
  (let* (
		 (keywd
		  (if (memq window-system '(w32 pgtk))
			  (url-encode-url (word-at-point))
			(word-at-point)
			)
		  )
		 (rg-cmd (concat py-script " \"" keywd "\" \"" path-or-file "\""))
		 (item-list (split-string (shell-command-to-string rg-cmd) "\n" "\n" "0"))
		 )
	(if (not (= (string-to-number (car item-list)) 1))
		(blove/open-rg-ret-in-buffer item-list)
	  (message "No matches were found!!!")
	  )
	)
  )

python 脚本

import os,sys
import platform
from urllib.parse import unquote

if platform.system() == "Windows":
    cmd = "rg -E utf-8 -H --no-heading -n " + unquote(sys.argv[1]) + " " + sys.argv[2]
else:
    cmd = "rg -E utf-8 " + sys.argv[1] + " " + sys.argv[2]

if __name__ == "__main__":
    print(os.system(cmd))

调用方式

(global-set-key (kbd "C-c C-g") (lambda () (interactive) (blove/rg-search-by-sel-region "c:/py-rg.py" "c:/rg-search-path")))
(global-set-key (kbd "C-x C-g") (lambda () (interactive) (blove/rg-search-by-word-point "c:/py-rg.py" "c:/rg-search-path")))

在 windows 中实现效果大致是这样的

你其实就想加一个 -E utf-8 的参数给rg撒?

不是的!如果那样就简单了! 因为我发现即使在 color-rg.el 中的 775 行,加 -E utf-8 也是无法搜索到韩文的

也就是说,目前在windows上经由 emacs 调用 powershell、cmd 运行 rg 搜索韩文都是不能获得有效结果的!系统会将关键字变成空格!即使大佬根据系统判断,设置了

    (when (memq system-type '(cygwin windows-nt ms-dos))
      (setq command-line (encode-coding-string command-line locale-coding-system)))

也只是对搜索中文有效、结果正确!但对于搜索韩文是无效的!
而且我发现,在 828 和 877 行都已经设定了使用 powershell

现在我所能实现的唯一有效办法是绕开 windows的 powershell、cmd,借助 python 脚本运行 rg 进行搜索:

1-将需要搜索的 韩文关键字 通过 url-encode-url 转码出去(这可以保证关键字能被搜索到) →
2-将其作为参数传递给 python,并在python脚本中使用 unquote 解码,供 rg 搜索 →

import os,sys
from urllib.parse import unquote
cmd = "rg -E utf-8 -H --no-heading -n " + unquote(sys.argv[1]) + " " + "rg-search-path"
os.system(cmd)

3-然后使用 shell-command-to-string 命令调用 python 脚本去运行 rg 搜索(而不是用 powershell 运行 rg),就能正确搜索到韩文了

(setq rg-cmd (concat "c:/py-rg.py" " \"" keywd "\" \"" path-or-file "\""))
(shell-command-to-string rg-cmd)

经过 url-encode-url 转码和 python中介执行,似乎在 windows 就绕开了 gbk :rofl:虽然不懂其原理,也许有点绕、有点麻烦,但目前只知道这个方法而且也实现了想要的效果!

不懂韩文,我测试了一下,windows cmd中运行rg搜索韩文,发现能搜索到 image

但以前应该确实不能,甚至连cmd rg里搜索中文都有问题,应该可能是因为我把 系统语言设成了英文 ,然后各种gbk问题我好像都没有在遇到过了

1 个赞

是我没有描述清楚! :blush:
如果直接使用 powershell 或 cmd, 用 rg 搜索韩文,即使系统语言是中文,我这边也是可以有正确结果的!

我这边是通过 emacs 调用 powershell rg … 这样的命令时, 不管系统语言是中文或英文,都无法得到正确的结果!就是经由emacs 运行 rg 搜索 韩文 无效! :thinking:

所以我感觉,可能还是从emacs中,抓取、输出的韩文 编码 无法被 powershell 正确识别!

有时,搜索到结果后,打开文件时,因为文件很大又或是windows平台会卡顿!所以根据我自己的需要,简单改写了 color-rg 的

color-rg-open-file (&optional stay)

函数。并配合 python 脚本,读取文件的指定部分内容(根据设定的数值,只选择匹配行的前几行和后几行),打印在 buffer 中!不再直接打开整个文件。

;;;
;;; dec line-thr, the lines of the match_line + -
;;;
(setq line-thr 5)
;;;
;;; def my match-hl-face
;;;
(defface blove-rg-match-hl-face
  '((t (
		:underline nil
		:foreground "#FF0000"
		:background "#000000"
		:bold t :height 1.2)))
  "for rg-match-hl"
  )
;;;
;;; fork original-function
;;;
(defun color-rg-open-file (&optional stay)
  "Open file, but read part of the file only, in a buffer."
  (interactive)
  ;; (setq color-rg-window-configuration-before-open (current-window-configuration))
  (let* ((match-file (color-rg-get-match-file))
         (match-line (color-rg-get-match-line))
		 (buf (get-buffer-create "**Part Of the File**"))
		 )
	(with-current-buffer buf
	  (erase-buffer)
      (insert (shell-command-to-string (concat "python ~/tmp/read-part-of-the-file.py " "\"" match-file "\" " (int-to-string match-line) " " (int-to-string line-thr))))
	  (beginning-of-buffer)
	  (while (re-search-forward the-keywd nil t)
		(let ((ov (make-overlay (match-beginning 0) (match-end 0))))
		  (overlay-put ov 'face 'blove-rg-match-hl-face))
		)
	  )
	(switch-to-buffer buf)
    ))
;;;
;;; use
;;;
(defun color-rg-search-eng-symbol-in-dest-path ()
  (interactive)
  (setq the-keywd (word-at-point))
  (color-rg-search-input (color-rg-pointer-string) "~/to-search-path")
  )

python 脚本

import sys

def ReadFilePart(f,match_line,line_thr):
    with open(f,"r",encoding="utf-8") as fr:
        lines = fr.readlines()
        all_lines = len(lines)
        match_line = int(match_line)
        line_thr = int(line_thr)
        if (match_line - line_thr) <= 0:
            start_line = 0
        else:
            start_line = match_line - line_thr
        if (match_line + line_thr) > all_lines:
            end_line = all_lines - 1
        else:
            end_line = match_line + line_thr
        for i in range(start_line,end_line + 1):
            print(lines[i].split("\n")[0])

if __name__ == "__main__":
    ReadFilePart(sys.argv[1],sys.argv[2],sys.argv[3])

color-rg 的很多实现,都是很实用的,成为了我使用 rg 搜索的基础依赖!
而且在 linux 上可以开心得搜索 中 英 韩 关键字。
在 windows 上搜索 中 英 关键字也很精准。搜索韩文暂时先用了我自己的独立丐版实现!
总之,平时使用 color-rg 的时间是越来越多了!真的是越来越习惯、喜欢用了! :blush:

1 个赞

今天推送的这个补丁主要新增了两个功能:

  1. 增加了多行搜索: 如果用 find-file.*\n(goto-char 这样的正则搜索, 可以搜索到第一行是 find-file 并且第二行是 goto-char 的代码块, 以前要把 find-file 的地方找出来然后人工去找匹配的代码块, 对于语义搜索非常方便
  2. 增加了color-rg-show-lines-before-match 和 color-rg-show-lines-after-match 这两个选项, 对应的是 rg 的 -A NUM 和 -B NUM 两个选项, 这两个选项的好处是可以直接在搜索结果的页面内显示上下文
2 个赞

感谢大佬,这个好顶赞。元旦的时候本来想研究一下这个以及替换的改进

替换在isearch和vscode里都支持case sensitive以及capture group的变量。等我有时间了一定(逃

1 个赞

我仔细看了看search跟replace的实现,好像是在color-rg的buffer里直接调用了query-replace-regexp?要实现capture group感觉要略大改?毕竟pcre跟emacs regex还是很多不同的,虽然是可以用 pcre2el,但是搜出来的不保证可以替换。 @manateelazycat

啥意思,没看懂

我意思是如果要实现vscode那样方便的替换,可能要绕开emacs的API。这工作量比我想象的要大不少。

在color rg现在的实现里做capture group的搜索替换会碰到两个正则表达式的语法不同的问题。例如我们用pcre在rg 里搜索color-rg-(\w+)-(.*),目标是把匹配项替换成color-rg-\u$2$1,现在因为color-rg是用了emacs的query-replace-regexp 去实现替换的,其实不太好改。

首先是pcre的语法要改成emacs regex color-rg-\([[:alnum:]]+\)-\(.*\),其次$1要改成\1

pcre2el好像能解决一部分问题。但是pcre跟emacs regex不能一一对应,例如替换上emacs就不支持\u \l这样的modifier

感觉还是要保存好rg的匹配结果并在那个基础上去实现这个功能

;; color-rg/color-rg.el:1305

(defun color-rg-replace-all-matches ()
  "Replace all matched results."
  (interactive)
  (save-excursion
    (let (changed-line-number)
      (let ((inhibit-message t)) ; don't flush to echo area when apply changed, optimise for color-rg
        (with-current-buffer color-rg-buffer
          (let* ((search-keyword (color-rg-search-keyword color-rg-cur-search))
                 (replace-text (read-string (format "Replace '%s' all matches with: " search-keyword) search-keyword)))
            (color-rg-switch-to-edit-mode)
            (if (color-rg-search-literal color-rg-cur-search)
                (query-replace search-keyword replace-text nil (point-min) (point-max))
              (query-replace-regexp search-keyword replace-text nil (point-min) (point-max)))
            (setq changed-line-number (length color-rg-changed-lines))
            (color-rg-apply-changed)
            (color-rg-switch-to-view-mode)
            (when (> changed-line-number 0)
              (setf (color-rg-search-keyword color-rg-cur-search) replace-text)))))
      (message "Replace %s lines" changed-line-number))))