1. フルパスだったらそのまま開く
  2. 現在のディレクトリを基準に探す
  3. 現在のディレクトリから見て先祖のディレクトリに .git があればそのディレクトリからの相対パスとして探す
  4. 直近で開いているバッファを順に切り替えながら、そのディレクトリを基準に 2 と 3 を試す

dashs という便利ライブラリを利用しているので M-x package-list-packages からのインストールが必要です。

(require 'dash)
(require 's)

(defun my-file-jump ()
~/.emacs.d/init.el (3 hit)       ; この数字は行番号ではない
./spec/models/user_spec.rb:6:in  ; がんばって探す
./spec/models/xxxx_spec.rb:6:in  ; がんばって探すがファイルがない
  (let ((thing-at-point-file-name-chars "-~/[:alnum:]_.${}#%,") ; 後ろのコロンまで拾ってしまうためコロンだけ除去
        original-path path line git-root vpath)

    ;; とりあえずファイル名を拾う
    (setq original-path (thing-at-point 'filename))
    (unless original-path (error "?"))
    (setq original-path (s-chop-prefix "./" original-path))
    (setq path original-path)

    ;; 後ろに行番号らしきものがあれば拾う
      (end-of-thing 'filename)
      (unless (= (following-char) (string-to-char " "))
        (when (re-search-forward "[^[:digit:]]*\\([[:digit:]]+\\)" (line-end-position) t)
          (setq line (buffer-substring-no-properties (match-beginning 1) (match-end 1))))))

    ;; ファイルがなければ .git からの相対パスとみなす
    (unless (file-exists-p path)
      (setq git-root (locate-dominating-file default-directory ".git"))
      (when git-root
        (setq path (concat git-root original-path))))

    ;; それでもなければバッファリストから探す
    (unless (file-exists-p path)
      (catch 'loop
        (--each (-uniq (--map (with-current-buffer it default-directory) (buffer-list)))
            ;; カレントからの相対パス
            (setq vpath (concat it original-path))
            (when (file-exists-p vpath)
              (setq path vpath)
              (throw 'loop nil))
            ;; .git からの相対パス
            (setq git-root (locate-dominating-file it ".git"))
            (when git-root
              (setq vpath (concat git-root original-path))
              (when (file-exists-p vpath)
                (setq path vpath)
                (throw 'loop nil)))))))

    (unless (file-exists-p path)
      (error "ファイルが見つかりません: %s" original-path))

    ;; ファイルが見つかったので
    (find-file path)
    (if line
        (goto-line (string-to-int line)))
    (delete-other-windows)              ; 他のWindowを消す (お好みで)
    (recenter-top-bottom)               ; 画面中央に配置   (お好みで)

キーバインドは C-j を無効にして C-j C-j に私は割り当てています。これもお好みです。

(global-unset-key "\C-j")
(global-set-key "\C-j\C-j" 'my-file-jump)
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.