怎样给 Ivy 添加拼音支持

怎样给 Ivy 添加拼音支持呢?

比如下面这样:

在 org 中,只要输入 rw 就可以过滤出“任务”

这个应该不难吧,我记得以前 ido 还是什么有类似的功能

这个有没有已经造好的轮子? :smile:

1 个赞

Packages that Use pinyinlib

第三方库 pinyinlib.el 可以建立这样的正则表达式:

(require 'pinyinlib)
;; => pinyinlib

(string-match (pinyinlib-build-regexp-string "rw") "任务")
;; => 0

不过我不用 Ivy,不清楚怎么结合起来用。

等有时间了研究一下,看来又得自己造轮子了

不是很了解 ivypinyinlib,可能有更好的方法,先来抛个砖:

(ivy-read "test: " '("张三和李四" "foo张三" "bar李四" "baz王五")
          :re-builder #'pinyinlib-build-regexp-string)

输入zs可以匹配到“张三和李四”和“foo张三”,但是这样做不能像一般的命令那样用单个空格表示任意数量字符,两个空格表示真正的单个空格。

用以下代码可以(临时)解决上述问题,输入zs ls匹配到“张三和李四”:

(defun ivy--regex-pinyin (str)
  (ivy--regex (pinyinlib-build-regexp-string str)))

(ivy-read "test: " '("张三和李四" "foo张三" "bar李四" "baz王五")
          :re-builder #'ivy--regex-pinyin)

但是看了一下 ivy--regexpinyinlib-build-regexp-string 的实现,隐隐觉得 ivy--regex-hash 可能会爆掉!所以最好还是自己参考 ivy--regex 实现一个不用 hashtable 的版本,可能会慢一些?也可能是这个整合拼音的方案行不通 :worried:


要在 swiper 等命令中使用 ivy--regex-pinyin,可以设置变量 ivy-re-builders-alist,用法可以参考文档 Ivy - Completion Styles 部分(优先级那里我有点看不懂):

(add-to-list 'ivy-re-builders-alist
             '(swiper . ivy--regex-pinyin))

在我这里这样还不行,参考 swiper--re-builder 的实现,需要设置 search-default-mode 为非 'char-fold-to-regexp(不清楚这么用是否合适)。

pinyinlib 的作者更有发言权,以上仅供参考 :blush:

2 个赞

这个确实可以有。

1 个赞

谢谢!我就感觉肯定有人已经造过这个轮子了。有点疑问,请问为啥要多输入一个感叹号?

如果开头是叹号,就用拼音查找规则。

(use-package pyim
  :after ivy
  :config

  (defun eh-ivy-cregexp (str)
    (if (string-match-p "^\\." str)
        (pyim-cregexp-build (substring str 1))
      (ivy--regex-plus str)))

  (setq ivy-re-builders-alist
        '((t . eh-ivy-cregexp))))
3 个赞

谈不上造轮子吧,以 ivy-read 为例,就是把喂给 ivy--regex-function 的数据改一下而已:

(ivy-read
 "[拼音] "
 (lambda (input)
   (cl-remove-if-not
    (lambda (cmd)
      (string-match-p (funcall ivy--regex-function
--                             input
++                             (pinyinlib-build-regexp-string input)
                               ) cmd))
    '("标题" "任务" "子任务")))
 :dynamic-collection t)

00_AM 20_AM

1 个赞

这样写相当于 advise 了 ivy--regex-plus,如果不用 ivy--regex-plus 就得再写新的函数,比如我就用的是 ivy--regex-ignore-order。不能向 ivy-re-builders-alist 中添加一条规则吗?这样不管使用哪个函数都可以用,会不会更 clean 一些?

感谢大家的回复,我没想到 ivy 的可定制性这么强,这么多方法都可以实现这一目的。

@fontux@tumashu 的方案比较接近,也比较 clean,一个用到 pinyinlib,一个用 pyim,目前我比较倾向于这两位的方法。这两个包我都用到,所以依赖不成问题。稍后我会把比较的结果发上来。

Edit:

@tumashu 的方法在我这不 work,@fontux 的方法就是我要的,非常 nice!

根据 @fontux 的稍微修改了一下:

目前使用还没有发现什么问题,赞一个!

Edit 2:

看来这样全局修改还是有一定问题的,重启之后我的 M-x 等等都没有候选项了。我还是单个函数修改吧。

修改单个函数(用Ivy 自带的 :re-builder property,谢谢 @fontux

我用的前缀是· 而不是问号

难怪我没试出来,我最后采用只修改一个函数的方式,所以没有用前缀。

我现在更新了我的拼音搜索设置,有兴趣的可以试试,中文英文同时搜出,支持首字母搜索或者全拼搜索

(use-package pyim
  :after ivy
  :config

  (defun eh-ivy-cregexp (str)
    (let ((a (ivy--regex-plus str))
          (b (let ((case-fold-search nil))
               (pyim-cregexp-build str))))
      (if (and a (stringp a))
          (concat a "\\|" b)
        a)))

  (setq ivy-re-builders-alist
        '((t . eh-ivy-cregexp))))
6 个赞

我也遇到了M-x没有候选项的情况,请问兄弟最后是如何解决的?

今天又更新了一下 ivy pinyin 搜索支持,感觉更好用了,不过要配合最新的 pyim,可能 melpa 还没有更新

(defun eh-ivy-cregexp (str)
  (let ((x (ivy--regex-plus str))
        (case-fold-search nil))
    (if (listp x)
        (mapcar (lambda (y)
                  (if (cdr y)
                      y
                    (list (pyim-cregexp-build (car y))))
                  x))
      (pyim-cregexp-build x))))

(setq ivy-re-builders-alist
      '((t . eh-ivy-cregexp)))
6 个赞

6 个赞

现在得用pyim-cregexp-build-1才可以了

用了这个配置,当同时搜索多个词时,为啥中间部分被阴影覆盖了呀?

ivy