Skip to content

Latest commit

 

History

History
148 lines (128 loc) · 6.93 KB

2017-04-10Emacs是如何实现快速查找和定位代码的?.org

File metadata and controls

148 lines (128 loc) · 6.93 KB

Emacs是如何实现快速查找和定位代码的?.org

实际编程时,不仅需要写代码,还需要快速地查找代码,定位日志等。这些事情在命令行下可以使用 find 和 grep 等工具的编合来完成。很多 IDE 也都集成了这些功能。平时主要还是使用 Emacs,还是希望在 Emacs 中实现这些功能。平时以下功能对我很重要:

  1. 根据文件名查找文件。
  2. 根据字符串搜索代码。
  3. 快速、快速、快速。

之前我主要使用 helm-ls-git-lshelm-grep-do-git-grep 来实现。但是发现一般只要 Emacs 工作一天,打开的文件很多。到下午的时候,helm 就会变得很卡。于是想使用 ivy 试试。

也是两个函数:

  • counsel-git:在一个 git 项目中 find-file。
  • counsel-git-grep:在一个 git 项目中 grep。

我希望的是,到一个我常用的项目目录里执行这两个函数。最初的想法是直接 find-file 到对应目录。然后调函数就可以了。如:

(defun test ()
  (interactive)
  (find-file "projectdir")
  (counsel-git))

但是有一个很麻烦的事情:如果我在执行两个函数的时候后悔了,不想找了。按下 C-g 不会回到我原来的 buffer,而是停留在我打开的 projectdir 。所以我的需求是如果按下 C-g ,取消的同时还要把 projectdir 这个 buffer 干掉。


最后发现, ivy-read 函数本来就有一个 unwind 选项,可以在 C-g=后也执行对应的函数。所以直接把 =counsel-gitcounsel-git-grep=函数拿来改一下就可以了(需要注意的是:peng-root-dir 不能以 =/ 结束,因为我需要使用 file-name-base 来找到打开 project 后的 buffer-name):

(defcustom peng-root-dir "~/src/project"
  "the root directorie of your project"
  :type 'string
  )
(defun peng-asp-engine-project-and-ivy-ls ()
  "Find file in the current Git repository."
  (interactive)
  (find-file peng-root-dir)
  (setq peng-temp-buffer-name (file-name-base peng-root-dir))
  (setq counsel--git-dir (locate-dominating-file
                          default-directory ".git"))
  (ivy-set-prompt 'counsel-git counsel-prompt-function)
  (if (null counsel--git-dir)
      (error "Not in a git repository")
    (setq counsel--git-dir (expand-file-name
                            counsel--git-dir))
    (let* ((default-directory counsel--git-dir)
           (cands (split-string
                   (shell-command-to-string counsel-git-cmd)
                   "\n"
                   t)))
      (ivy-read "Find file" cands
                :action #'counsel-git-action
                :caller 'counsel-git
                :unwind #'(lambda ()
                            (kill-buffer peng-temp-buffer-name))))))

(defun peng-asp-engine-project-and-ivy-grep (&optional cmd initial-input)
  "Grep for a string in the current git repository.
When CMD is a string, use it as a \"git grep\" command.
When CMD is non-nil, prompt for a specific \"git grep\" command.
INITIAL-INPUT can be given as the initial minibuffer input."
  (interactive "P")
  (find-file peng-root-dir)
  (setq peng-temp-buffer-name (file-name-base peng-root-dir))
  (ivy-set-prompt 'counsel-git-grep counsel-prompt-function)
  (let ((dd (expand-file-name default-directory))
        proj)
    (cond
      ((stringp cmd)
       (setq counsel-git-grep-cmd cmd))
      (cmd
       (if (setq proj
                 (cl-find-if
                  (lambda (x)
                    (string-match (car x) dd))
                  counsel-git-grep-projects-alist))
           (setq counsel-git-grep-cmd (cdr proj))
         (setq counsel-git-grep-cmd
               (ivy-read "cmd: " counsel-git-grep-cmd-history
                         :history 'counsel-git-grep-cmd-history
                         :re-builder #'ivy--regex
                         :unwind #'(lambda ()
                                     (kill-buffer peng-temp-buffer-name))))
         (setq counsel-git-grep-cmd-history
               (delete-dups counsel-git-grep-cmd-history))))
      (t
       (setq counsel-git-grep-cmd counsel-git-grep-cmd-default)))
    (setq counsel--git-grep-dir
          (if proj
              (car proj)
            (locate-dominating-file default-directory ".git")))
    (if (null counsel--git-grep-dir)
        (error "Not in a git repository")
      (unless proj
        (setq counsel--git-grep-count
              (if (eq system-type 'windows-nt)
                  0
                (counsel--gg-count "" t))))
      (ivy-read "git grep" (if proj
                               'counsel-git-grep-proj-function
                             'counsel-git-grep-function)
                :initial-input initial-input
                :matcher #'counsel-git-grep-matcher
                :dynamic-collection (or proj (> counsel--git-grep-count 20000))
                :keymap counsel-git-grep-map
                :action #'counsel-git-grep-action
                ;; :unwind #'swiper--cleanup
                :history 'counsel-git-grep-history
                :caller 'counsel-git-grep
                :unwind #'(lambda ()
                            (kill-buffer peng-temp-buffer-name)
                            (swiper--cleanup))))))

最后再来一个函数,预定几个 project 后,一调用就可以方便地在几个 project 中切换啦:

(defun peng-set-root-project ()
  "selete a projec through `ivy-read'"
  (interactive)
  (let* ((project-list (ivy-read "Please selete a project: "
                                 (list
                                  ;; do not end with slash
                                  "~/src/xxx"
                                  "~/src/yyy"
                                  "~/src/zzz"
                                  ))))
    (setq peng-root-dir project-list)))

最后附加 ivy 的一些实用的按键绑定介绍,也算是总结(基本就是把翻译了一些 ivy-help 里面的东西):

M-i (ivy-insert-current) :插入当前选中的目标。

M-j (ivy-yank-word) : 插入当前光标下的 word。

S-SPC (ivy-restrict-to-matches) : 保持当前输入过滤候选项,并清空当前输入。这个功能很实用。一般找文件,如果有重名,选输入文件名后,按下 S-SPC 后简单再过滤一下目录就出来了。

C-c C-o (ivy-occur) :把当前候选项保存到一个 buffer 中。然后你可以慢慢查找你想要的,最后回车即可。

C-o (hydra-ivy/body) :可以调出一个辅助小窗口,基本都有提示,kj 上下移动等等。