关于ivy的使用技巧


ivy默认的配置已经足够了,但还是可以通过一些技巧来提升使用体验

ivy中的tab

ivy提供了好几个有关补全的命令,分别是ivy-done, ivy-partial-or-done, ivy-immediate-done, ivy-dispatching-done, 但不幸,想要获得高效的补全,我不得不记住这n多个命令,这常常使效率变得更低, 所以,我自定义了一个 maple/ivy-done, 仅使用 tab 这一个按键就可以获得以上所有的体验

(defun maple/ivy-done()
  (interactive)
  (let ((dir ivy--directory))
    (ivy-partial-or-done)
    (when (string= dir ivy--directory)
      (ivy-insert-current)
      (when (and (eq (ivy-state-collection ivy-last) #'read-file-name-internal)
                 (setq dir (ivy-expand-file-if-directory (ivy-state-current ivy-last))))
        (ivy--cd dir)
        (setq this-command 'ivy-cd)))))
  • 预设场景一

    dirs目录下有

    init-a.el
    init-b.el
    init-c.el
    

    等多个文件, 我需要把init-c.el重命名为init-c.el.bak, 默认的ivy会怎么做呢

    • 输入 init c 进行查找
    • 使用 C-M-j (ivy-immediate-done) 插入当前选中项
    • 修改

    而使用 maple/ivy-done 后呢

    • 输入 init c 进行查找
    • 使用 tab 插入当前选中项
    • 修改
  • 预设场景二

    有一个n级子目录,需要切换到第n个子目录下, 默认的ivy会怎么做呢

    • 使用 tab 选中
    • 再次使用 tab 切换目录

    而使用 maple/ivy-done 后呢

    • 使用 tab 选中并切换目录

ivy中的C-h

由于我是邪恶的 evil 用户, 所以我正常情况会使用 C-j, C-k来选择待选项,同样的,我希望能够使用 C-h 来进行某些操作,比如在counsel-ag中使用C-h代替backspace删除输入的字符,而在counsel-find-file中使用C-h切换到上一级目录

ivy默认提供了 counsel-up-directory 命令可以切换到上级目录, 我希望能更进一步,当有插入的字符时, 调用C-h能够直接清除插入的字符,再次调用才切换到上一级目录

(defun maple/ivy-c-h ()
  (interactive)
  (if (eq (ivy-state-collection ivy-last) #'read-file-name-internal)
      (if (string-equal (ivy--input) "")
          (counsel-up-directory)
        (delete-minibuffer-contents))
    (ivy-backward-delete-char)))

ivy-occur批量操作

自动打开wgrep-mode

ivy默认调用ivy-occur后, 还需要调用ivy-wgrep-change-to-wgrep-mode才能进行批量修改

;; ivy-occur custom
(defun maple/ivy-edit ()
  "Edit the current search results in a buffer using wgrep."
  (interactive)
  (run-with-idle-timer 0 nil 'ivy-wgrep-change-to-wgrep-mode)
  (ivy-occur))

批量修改文件名

预设场景: 当某个项目下有许多使用 aaa.py的文件,我需要把 aaa.py 全部修改为 bbb.py, 也许某个 aaa.py 需要修改成 ccc.py,我在使用 projectile-find-file 搜索完全部的 aaa.py 后,打开ivy默认的ivy-occur并不能对文件进行修改(提示Text read only), 所以我采用 find-name-dired 来批量修改文件名(目前还有些问题)

(defun maple/ivy-dired-occur()
  (interactive)
  (find-name-dired
   (or (projectile-project-root) default-directory) (concat (ivy--input) "*"))
  (ivy-exit-with-action
   (lambda (_)
     (pop-to-buffer (get-buffer "*Find*"))
     (dired-hide-details-mode)
     (wdired-change-to-wdired-mode)
     (when (bound-and-true-p evil-local-mode) (evil-normal-state)))))

counsel-find-file

重命名

预设场景: 有一个dirs目录, 我需要在同级目录下创建一个 dirs-aa.el 的文件, 默认的ivy会怎么做呢

  • 切换到 dirs 目录的上级目录
  • 使用 ivy-immediate-done 插入目录名
  • 新建文件

但还有一种方式

  • 切换到 dirs 目录
  • 使用 backspace 键删除目录末尾的 =/=
  • 新建文件

不幸的是,ivy中 backspace 的按键绑定了 ivy-backward-delete-char, 在 counsel-find-file 中使用 ivy会直接返回上级目录, 所以需要对 ivy-backward-delete-char 作简单的修改

(defun maple/ivy-backward-delete-char ()
  (interactive)
  (let ((dir ivy--directory)
        (p (and ivy--directory (= (minibuffer-prompt-end) (point)))))
    (ivy-backward-delete-char)
    (when p (insert (file-name-nondirectory (directory-file-name dir))))))

自动创建不存在的目录

不仅用于ivy, 其它文件操作命令也需要

(defun maple/ivy-make-directory-maybe ()
  "Create parent directory if not exists while visiting file."
  (let ((dir (file-name-directory buffer-file-name)))
    (unless (file-exists-p dir)
      (if (y-or-n-p (format "Directory %s does not exist,do you want you create it? " dir))
          (make-directory dir t)
        (keyboard-quit)))))

(add-to-list 'find-file-not-found-functions 'maple/ivy-make-directory-maybe nil #'eq)

ivy自动使用选中的内容

在使用 counsel-ag 或者 swiper, 我需要快速搜索选中的关键词,然而,ivy默认并不支持这么做,ivy需要打开counsel-ag后使用 M-n 插入选中的关键词,当然,也许是作者认为这样的方式很方便,但我并不认同,我为何需要重复一次操作呢,为何还需要记住 M-n 这样的按键呢?

(defun maple/region-string()
  "Get region string."
  (if (not (use-region-p)) ""
    (let* ((beg (region-beginning))
           (end (region-end))
           (eol (save-excursion (goto-char beg) (line-end-position))))
      (deactivate-mark) (buffer-substring-no-properties beg (min end eol)))))

;; custom counsel-ag
(defun maple/counsel-ag(-counsel-ag &optional initial-input initial-directory extra-ag-args ag-prompt)
  (funcall -counsel-ag
           (or initial-input (maple/region-string))
           (or initial-directory default-directory)
           extra-ag-args
           ag-prompt))

(advice-add 'counsel-ag :around #'maple/counsel-ag)

这样,调用 counsel-ag 时,如何已经有选中的关键词,就会直接调用该关键词进行查找,避免重复操作, 同样的对于swiper 也可以使用另一种方式

(defun maple/ivy-search-at-point (func)
  (let ((ivy-initial-inputs-alist (list (cons func (maple/region-string)))))
    (funcall func)))

(defun maple/swiper()
  (interactive)
  (maple/ivy-search-at-point 'swiper))

搜索某个目录

counsel-ag默认搜索当前目录,想要搜索某个目录,需要切换到该目录下调用counsel-ag,无疑的,这非常不方便,所以,我希望可以选择某个目录进行搜索

(defun maple/counsel-ag-directory()
  (interactive)
  (counsel-ag nil (read-directory-name "Search in directory: ")))

ivy的显示避免出现在视线外

详见 自定义helm式的ivy.html 中的

吐嘈六: minibuffer

作者: honmaple
链接: https://honmaple.me/articles/2019/05/关于ivy的使用技巧.html
版权: 知识共享署名-非商业性使用-相同方式共享4.0国际许可协议
wechat