LoginSignup
1
1

More than 5 years have passed since last update.

Emacsでカーソル位置の相対パスのファイル名を他のバッファのディレクトリを基準に探して開く方法

Last updated at Posted at 2017-09-30

相対パスのファイル名をコピペしたファイルが別ディレクトリにあっても、次の順序でがんばって探すので、ファイルが簡単に開けるようになります。

  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
~/.emacs.d/init.el:
~/.emacs.d/init.el:2
~/.emacs.d/init.el:3:
~/.emacs.d/init.el:3:in
~/.emacs.d/init.el(5)
~/.emacs.d/init.el (3 hit)       ; この数字は行番号ではない
./spec/models/user_spec.rb:6:in  ; がんばって探す
./spec/models/xxxx_spec.rb:6:in  ; がんばって探すがファイルがない
"
  (interactive)
  (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)

    ;; 後ろに行番号らしきものがあれば拾う
    (save-excursion
      (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)))
          (progn
            ;; カレントからの相対パス
            (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)
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1