2016年1月20日水曜日

helm-swoopを使ってemacsバッファ全体を一括検索

helm-swoopというemacs-lispを導入すれば、開いているバッファ全体から検索がかけられるという情報を見たので、試してみました。

emacsでLaTeXファイルを編集する際に、文書を幾つかのファイルに分割してmainファイルにincludeしているのですが、演習問題として扱っていたものを本文中の命題に格上げしたりするときに、labelを変更するとrefで引用した部分を一つ一つ探して修正していかなければならず、かなり面倒でした。これが一括して行えるようになりました。

YaTeXを使ってTeXファイルを編集しているときには、[prefix] dでmainファイルがincludeしているファイルを全て開くことができます。このようにmainファイルがincludeしているファイルを全て開いておいて、検索したい文字列を選択してM-iでファイル中の文字列の検索結果が表示され、さらにM-iを押せば開いているバッファ全体からの検索結果が表示されます。


検索結果の表示画面でC-c C-eを押せば編集画面に移行し、検索した文字列を含む行を一度に編集することができます。C-c C-eを押す前に編集したい行でC-spaceを押せば、行が選択されハイライト表示になり、C-c C-eで選択した部分だけを編集できます。また語句をスペースを空けて複数記述していくと、それらの語句を全て含む行が検索でき、語句の前に!をつけるとその語句を含まない行が表示されます。検索語を含む行の前後で表示させたい行数は設定で変更できるようです。これで参照部分の変更が非常に楽になりました。

YaTeX自身の機能としても、labelもしくはrefの部分にカーソルを置いた状態で[prefix] gを打鍵すれば、対応する部分へジャンブすることができます。labelからrefへのジャンプは、最初に参照した部分にしか飛ばないみたいです(使い方がわかっていないだけかも)。またLaTeXのパッケージでcleveref.styというものもあり、こちらは参照元の環境が定理から命題に変更されれば、参照している部分も自動で定理hogehogeを命題hogehogeに変更してくれ便利なのですが、mathtools.styのshowonlyrefオプションとの相性が悪く使えていません。しかしhelm-swoopの導入で気にならなくなりました。



参考にしたのは以下の記事です。

  1. helm を使うための設定
  2. Helm をストレスなく使うための個人的な設定
  3. Helm をストレスなく使うための個人的な設定 (2)
  4. Helm v1.8.0 をストレスなく使うための個人的な設定
  5. helm-swoopでEmacsを位置同期検索
  6. emacs helm-swoop.el : 【これはすごい】バッファ全体をMigemo絞り込み検索して走り回れ!

helmやhelm-swoopの導入方法についてはpackage.elを使って導入したので省略します。
設定ファイルは上記リスト1の設定をほぼそのまま使わせていただきました。
helm初心者としては、tabキーで補完が効かないのがかなり気持ち悪いので、
helm-completing-read-handlers-alistにfind-fileなどを登録してtabで補完ができるようにしました。
設定は以下の通りです。

;;helmの設定
;; 候補を作って描写するまでのタイムラグを設定する(デフォルトは 0.01)
(setq helm-idle-delay 0.2)

(require 'helm-config)
(require 'helm-descbinds)

(helm-mode 1)
;(helm-migemo-mode 1)



;;; 処理を変更したいコマンドをリストに登録していく
(add-to-list 'helm-completing-read-handlers-alist '(find-file . nil))
(add-to-list 'helm-completing-read-handlers-alist '(execute-extended-command . nil))
(add-to-list 'helm-completing-read-handlers-alist '(YaTeX-visit-main . nil))
(add-to-list 'helm-completing-read-handlers-alist '(YaTeX-display-hierarchy . nil))


;; 文字列を入力してから検索するまでのタイムラグを設定する(デフォルトは 0.01)
(setq helm-input-idle-delay 0.2)

;; helm-source-locate でワードの and 検索ができるようにする
;; ・locate を使えない場合は、「locate ''」の部分を「cat /tmp/all.filelist」に置き換えてください。
;;  そして、find 等を使って /tmp/all.filelist(ファイル名が並んだファイル)を作成してください。
;;  locate.db を作成した場合でも、「locate '' > /tmp/all.filelist」として /tmp/all.filelist を
;;  作成し、そのファイルを検索した方がファイルキャッシュが効くので高速なようです。お試しください。
;; ・検索の高速化のために、検索件数を helm-candidate-number-limit までに headコマンドで絞っています。
;;  helm-source-locate情報源 の candidate-number-limit設定値 を設定する方法もあります。
;;  (後半の helm-before-initialize-hook の箇所でコメント化している部分となります。)
;; ・helm の検索パターン入力ルールに従い、2つのスペースの並びを一つのスペースとして認識して検索
;;  します。また、「!検索パターン」(マッチしない)を指定可能です。
(setq helm-locate-command
      ;; (concat "locate_case=$(echo '%s' | sed 's/-//'); cat /tmp/all.filelist |"
      (concat "locate_case=$(echo '%s' | sed 's/-//'); locate '' |"
              "perl -ne \"$(echo '%s' |"
                           "sed -r -e 's/[\\\\ ] /__SpAcE__/g' "
                                  "-e 's/^ +//' "
                                  "-e 's/ +$//' "
                                  "-e 's_/_\\\\&_g' "
                                  "-e 's_ +_/'$locate_case' \\&\\& m/_g' "
                                  "-e 's_.*_$| = 1; print if (m/&/'$locate_case')_' "
                                  "-e 's_m/!_!m/_g' "
                                  "-e 's/__SpAcE__/ /g')\" 2> /dev/null |"
              "head -n " (number-to-string helm-candidate-number-limit)))

;; tramp で remote-directory を開いているときに、helm-for-files を起動すると反応が悪い
;; 原因は helm-source-files-in-current-dir だったので、この情報源の指定を削除する
;; また、一部表示順を変更する
(setq helm-for-files-preferred-list
      '(helm-source-buffers-list
        helm-source-bookmarks
        helm-source-recentf
        helm-source-file-cache
        ;; helm-source-files-in-current-dir
        helm-source-locate))

;; 表示する最大候補数を指定する(デフォルトで 50)
;; (setq helm-candidate-number-limit 100)

;; 候補表示画面で改行しないようにする
;; (setq helm-truncate-lines t)

;; helm-source-buffers-list を詳細に表示しない
(setq helm-buffer-details-flag nil)

;; helm-source-buffers-list でバッファ名を表示する幅を調整する
(setq helm-buffer-max-length 50)

;; helm-follow-mode (C-c C-f で ON/OFF)の前回の状態を維持する
(setq helm-follow-mode-persistent t)

;; ミニバッファで C-k 入力時にカーソル以降を削除する(C-u C-k でも同様の動きをする)
(setq helm-delete-minibuffer-contents-from-point t)

;; http://fukuyama.co/nonexpansion
;; 自動補完を無効にする
(setq helm-ff-auto-update-initial-value nil)

;; C-h でバックスペースと同じように文字を削除できるようにする
;; (define-key helm-read-file-map (kbd "C-h") 'delete-backward-char)

;; TAB で補完する
;; For find-file etc.
(define-key helm-read-file-map (kbd "TAB") 'helm-execute-persistent-action)
;; For helm-find-files etc.
(define-key helm-find-files-map (kbd "TAB") 'helm-execute-persistent-action)





;; C-o は IME変換用として使っているので、helm-next-source を C-l に変更する
;(define-key helm-map (kbd "C-o") nil)
;(define-key helm-map (kbd "C-l") 'helm-next-source)

;; http://d.hatena.ne.jp/sugyan/20120104/1325604433
;; プレフィックスキーを C-; に設定する
(custom-set-variables '(helm-command-prefix-key "C-;"))

;; キーバインドを設定する。コマンド起動後は、以下のキーが利用可能となる
;;  ・M-n     :カーソル位置の単語を検索パターンに追加
;;  ・C-z     :チラ見
;;  ・C-c C-f :helm-follow-mode の ON/OFF
(global-set-key (kbd "C-x C-b") 'helm-for-files)
(global-set-key (kbd "C-x C-;") 'helm-for-files)
(define-key helm-command-map (kbd "C-;") 'helm-resume)
(define-key helm-command-map (kbd "y")   'helm-show-kill-ring)
(define-key helm-command-map (kbd "o")   'helm-occur)
(define-key helm-command-map (kbd "C-s") 'helm-occur-from-isearch)
(define-key helm-command-map (kbd "g")   'helm-do-grep) ; C-u 付で起動すると、recursive となる

;; helm-occurコマンドの起動時に helm--maybe-use-default-as-input(helmコマンドに :input パラメータが
;; 指定されていなければ、:default の値を使って表示を更新する)を設定する
(advice-add 'helm-occur
            :around (lambda (orig-fun &rest args)
                      (let ((helm--maybe-use-default-as-input t))
                        (apply orig-fun args))))

;; 情報源 helm-source-occur と helm-source-grep について、利用開始時点から helm-follow-mode を ON にする
;; 情報源 helm-source-locate と helm-source-grep について、検索必要最低文字数を 2 とする。
(add-hook 'helm-before-initialize-hook
          (lambda ()
            (when helm-source-locate
              ;; (setcdr (assq 'candidate-number-limit helm-source-locate) helm-candidate-number-limit)
              (setcdr (assq 'requires-pattern helm-source-locate) 2))
            (when helm-source-occur
              (helm-attrset 'follow 1 helm-source-occur))
            (when helm-source-grep
              (helm-attrset 'follow 1 helm-source-grep)
              ;; (setcdr (assq 'candidate-number-limit helm-source-grep) helm-candidate-number-limit)
              (setcdr (assq 'requires-pattern helm-source-grep) 2))))



;;;helm-swoopの設定
;; helm from https://github.com/emacs-helm/helm
;; helm-swoopを使うためにはhelmが必要です
(require 'helm)

;; helm-swoopフォルダを任意の位置に置きます
;(add-to-list 'load-path "~/.emacs.d/elisp/helm-swoop")
(require 'helm-swoop)

;; キーバインドはお好みで
(global-set-key (kbd "M-i") 'helm-swoop)
(global-set-key (kbd "M-I") 'helm-swoop-back-to-last-point)
(global-set-key (kbd "C-c M-i") 'helm-multi-swoop)
(global-set-key (kbd "C-x M-i") 'helm-multi-swoop-all)

;; isearch実行中にhelm-swoopに移行
(define-key isearch-mode-map (kbd "M-i") 'helm-swoop-from-isearch)
;; helm-swoop実行中にhelm-multi-swoop-allに移行
(define-key helm-swoop-map (kbd "M-i") 'helm-multi-swoop-all-from-helm-swoop)

;; Save buffer when helm-multi-swoop-edit complete
(setq helm-multi-swoop-edit-save t)

;; 値がtの場合はウィンドウ内に分割、nilなら別のウィンドウを使用
(setq helm-swoop-split-with-multiple-windows nil)

;; ウィンドウ分割方向 'split-window-vertically or 'split-window-horizontally
(setq helm-swoop-split-direction 'split-window-vertically)

;; nilなら一覧のテキストカラーを失う代わりに、起動スピードをほんの少し上げる
(setq helm-swoop-speed-or-color t)