thing-at-point を応用したファイルの検索
clmemo elispTable of Contents
#+File Created:
#+Last Updated:
1 はじめに
私はここ 10 年ほど, 普段の業務日誌を ChangeLog メモで書いている.
シンプルな形式なので自分で色々加工が出来るし, 1 ファイルなので管理も楽で気にいっている.
一時期 org-mode でメモをとろうかなと思って色々やってみたこともあったが, 結局続かなかった.
自分にとって, やっぱり母艦となるメモは 1 つのファイルにしておくのが性にあってると思った.
2 問題点
メモは 1 つのファイルが良いとは言え, 作業自身はいろんなところでやるわけで, その場所を忘れないように書いておくことがある.
あるいは「メモ」では書ききれない「文書」を org-mode 等で書いてその場所を書いておくこともある.
以下のような感じである.
2016-06-28 (Tue) Taro YAMADA <taro@example.jp> * 設定 elisp の変更 [elisp][setup]: find-file (file: ~/.emacs.d/inits/51-file.el) 上記ファイルを変更して fugafuga 出来るようにした. * hogehoge 開発 [log]: 進捗確認 (file: /work/foo/bar/hogehoge.py) * ○○の改善案 [foo][log]: 文書下書きの作成 (file: /work/doc/2016/foo.org) あす 4 章目の修正を行う. 文字数のチェック.
作成したファイルのある場所を書いておくことで, 作業場所を確認すると共に必要なファイルをさっさと取り出すことが出来る.
ここから M-x find-file-at-point で直接ファイルが開ける.
例えば hogehoge.pl というプログラムを書いたらその場所をメモしておく.
$: pwd /work/theme/ $: ls hogehoge.pl
2016-06-28 (Tue) Taro YAMADA <taro@example.jp> * theme 開発 [log]: 一応完成 (file: /work/theme/hogehoge.pl)
しかし私の場合, ファイルのパスは結構変わることも多いのだ.
例えば, 最初は場所として /work/theme で問題なかったものの,
後日 version2 のデータがやってきたので
最初のやつは version1 以下に置いておきたい, ということがあったり.
version1 のデータ用プログラム hogehoge.pl は version1 という directory を作ってそこに入れとこう.
$: pwd /work/theme/ $: mkdir version01 $: mv hogehoge.pl version01/
パスが変わってしまうと, ChangeLog との整合性が保てなくなってしまう!!
directory の構成を変える度に一々 ChangeLog の該当部分を探して修正をするのはめんどくさすぎるし.
2016-06-28 (Tue) Taro YAMADA <taro@example.jp> * theme 開発 [log]: 一応完成 (file: /work/theme/hogehoge.pl)
どーしたらいいんだろう…
と思って色々探してみましたが問題を解決してくれそーなプログラムは落ちてないようだった.
3 解決策
ファイルを移動したらそれに連動して ChangeLog のリンクの内容を変える.
そんな魔法のようなことが出来れば超ナイスなのだが,
やっぱ無理っぽいんじゃないかなぁと思ったので,
以下のように動くプログラムを作ることでお茶を濁すことにする.
- ChangeLog のリンク部分からパスを抽出し, その存在をチェックする.
- 抽出したパスにファイルが存在すればファイルの存在を示して終了する.
- 無ければその近辺のディレクトリを探して同名のファイルがあるかどうか検索.
- 同名のファイルがあればそのパスに ChangeLog を書き換える.
注: ChangeLog めも上じゃなくても使えるようにしたい.
4 elisp プログラム
ChangeLog 上に書いてあるファイルが無ければその近辺の dir を探して,
同名のファイルを探してくれる elisp プログラムを色々調べながらつくってみた.
以下の 3 つの関数を導入する.
ChangeLog 上じゃなくても動くかと思う.
1: (ffap-bindings) 2: ; main function 3: (defun search-file-at-point-with-dir-search() 4: (interactive) 5: (let* ( 6: ;; カーソル上のファイル名らしき文字列をとる 7: (my-path (thing-at-point 'filename)) ;; fname with dir 8: ;; (最初の位置, 最後の位置)をリストで取得 9: (bounds (bounds-of-thing-at-point 'filename)) 10: (my-dir nil) 11: (my-pdir nil) ;; 親 directory 12: (my-fname nil) 13: (stt nil) 14: (end nil) 15: ) 16: (setq stt (car bounds)) 17: (setq end (cdr bounds)) 18: (setq my-dir (file-name-directory my-path)) ;; dir name 19: (setq my-fname (file-name-nondirectory my-path)) ;; file name 20: ;(y-or-n-p (message "my-path =%s" my-path)) 21: ;; 親 dir でも検索(dir が二つずれてるときも対処するように 22: (setq my-pdir (file-name-directory (directory-file-name my-dir))) 23: ;(y-or-n-p (message "my-path =%s" my-path)) 24: (cond 25: ;; ファイルが存在してれば開く 26: ;((file-exists-p my-path) (find-file my-path)) 27: ;; やっぱ開くのはやめて存在を主張して終了 28: ((file-exists-p my-path) (message "file %s exists" my-path)) 29: ;; ファイルが存在してなくて dir があれば検索 30: ((file-directory-p my-dir) (my-search-path-file my-dir my-fname stt end)) 31: ;; my-dir が存在してなくてももう少し頑張る. 親 dir があれば検索をする. 32: ((file-directory-p my-pdir) (my-search-path-file my-pdir my-fname stt end)) 33: ;; 親 dir も存在してなければ諦めて試合終了 34: (t (message "dir %s is not found" my-dir)) 35: ) 36: ) 37: )
1: (defun my-search-path-file(my-dir my-fname stt end) 2: (let ((files nil) 3: (fpath nil) 4: (maxdepth nil) 5: (home-dir (substitute-in-file-name "$HOME")) ;; see clmemo.el 6: ) 7: ;; 調査する dir の深さを minifuffer から入れたければ 8: ;(setq maxdepth (string-to-number (read-string "depth:" "3" nil "3"))) 9: (setq maxdepth 3) 10: ;; 条件にあうファイルをリストで取り出す 11: (setq files (directory-files-recursive my-dir my-fname maxdepth nil)) 12: ;; 最初のファイルを取り出して, $HOME -> "~" に変更 13: (setq fpath (replace-regexp-in-string home-dir "~" (car files))) 14: ;; 書き直し 15: (save-excursion 16: (delete-region stt end) 17: (goto-char stt) 18: (insert fpath) 19: ) 20: ) 21: )
directory-files-recursive は以下の URL から拾ってきた.
1: ;; Recursively listing directories in elisp - turingMachine 2: ;; http://turingmachine.org/bl/2013-05-29-recursively-listing-directories-in-elisp.html 3: (defun directory-files-recursive(directory match maxdepth ignore) 4: (let* ((files-list '()) 5: (current-directory-list (directory-files directory t)) 6: ) 7: ;; while we are in the current directory 8: (while current-directory-list 9: (let ((f (car current-directory-list))) 10: (cond 11: ((and 12: ignore ;; make sure it is not nil 13: (string-match ignore f)) 14: ; ignore 15: nil 16: ) 17: ((and 18: (file-regular-p f) 19: (file-readable-p f) 20: (string-match match f)) 21: (setq files-list (cons f files-list)) 22: ) 23: ((and 24: (file-directory-p f) 25: (file-readable-p f) 26: (not (string-equal ".." (substring f -2))) 27: (not (string-equal "." (substring f -1))) 28: (> maxdepth 0)) 29: ;; recurse only if necessary 30: (setq files-list (append files-list (directory-files-recursive f match (- maxdepth -1) ignore))) 31: ;(setq files-list (cons f files-list)) 32: ) 33: (t) 34: ) 35: ) 36: (setq current-directory-list (cdr current-directory-list)) 37: ) 38: files-list 39: ) 40: )
5 使い方例
(file: /work/theme/hogehoge.pl)
パス名がある場所にカーソルがある状態で M-x search-file-at-point-with-dir-search
もしファイルがあれば "/work/theme/hogehoge.el exists." というメッセージを書いて終わる.
無ければ周辺の dir を探して hogehoge.pl が存在すれば
(file: /work/theme/version1/hogehoge.pl)
のようにメモを書き換えてくれます.
探しても無ければ "not found" と書いて何もしません(のでその場合は手動で何とかするしかない…).
注意点: dir を探して最初に見つかった hogehoge.pl の場所を返す仕様になっている.
/work/theme/version0/hogehoge.pl とか /work/theme/version1/hogehoge.pl とかがある場合は, 実際には一意に特定出来ていない.
先に見つかった方を書き出す.
6 elisp 書き方めも
(setq pdir (file-name-directory (directory-file-name dir))) ;; dir = /foo/bar/hoge/ ;; /foo/bar/hoge = (directory-file-name dir) ;; /foo/bar/ = (file-name-directory "/foo/bar/hoge")
(setq hoge (read-string "prompt:" default値 nil 何も入れなかたときの値)) (setq num (string-to-number hoge)) ;; 数値にする
;; (file: /work/theme/hogehoge.pl) ;; 'filename にすると, 最後の ")" はとっていい感じでファイル名を取り出せる (setq st (thing-at-point 'filename)) (setq bounds (bounds-of-thing-at-point 'filename))
;; カーソルは /foo/bar/hoge/file.pptm の上にあって, ;; /foo/bar/hoge 以下には実際は file.pptm というファイルが無い場合には (setq fname (ffap-file-at-point)) ;; fname="/foo/bar/hoge/" となる.
(message "%s" dir-list)
7 参照 URL
ChangeLog memo
横着プログラミング 第1回: Unixのメモ技術
ChangeLogメモによる自分データベースの構築
clmemo
clmemo@aka: Change Log メモを試してみよう
chalow
chalow - CHAngeLog On the Web
たつをの ChangeLog
quasi-howm
clmemo@aka: ChangeLog メモで Howm を使う quasi-howm |ChangeLogメモ|Howm|Emacs|
私は大きなメモは quasi-howm を利用して書いてます
(howm の中で org-mode 形式でかいています. howm の機能はほとんど使ってません).
emacs-lisp 関連
ディレクトリ上位方向へ進む際のイディオム - syohex’s diary
(url: http://syohex.hatenablog.com/entry/20121008/1349681293)
GNU Emacs Lispリファレンスマニュアル: String Conversion
(url: http://www.geocities.co.jp/SiliconValley-Bay/9285/ELISP-JA/elisp_76.html)
GNU Emacs Lispリファレンス・マニュアル: 27. 位置
(url: http://www.fan.gr.jp/~ring/doc/elisp_19/elisp-jp_29.html)
GNU Emacs Lisp Reference Manual - ファイルに関する情報
(url: http://flex.phys.tohoku.ac.jp/texi/eljman/eljman_154.html)
再帰的に file を探す elisp プログラム
Recursively listing directories in elisp - turingMachine
(url: http://turingmachine.org/bl/2013-05-29-recursively-listing-directories-in-elisp.html)
cond の使い方
GNU Emacs Lispリファレンスマニュアル: 9. 制御構造
(url: http://www.fan.gr.jp/~ring/doc/elisp_20/elisp_10.html)
その他参考 URL:
Emacs Lisp デバッグ — ありえるえりあ
(url: http://dev.ariel-networks.com/articles/software-design-200802/elisp-debug/)
カーソル位置の情報で遊ぼう。 - 日々、とんは語る。
(url: http://d.hatena.ne.jp/tomoya/20101213/1292166026)
[Home] Thing At Point
(url: https://www.emacswiki.org/emacs/ThingAtPoint)
GNU Emacs Lisp Reference Manual - ファイルに関する情報
(url: http://flex.phys.tohoku.ac.jp/texi/eljman/eljman_154.html)
GNU Emacs Lispリファレンスマニュアル: ファイル
(url: http://www.bookshelf.jp/texi/elisp-manual-20-2.5-jp/elisp_25.html#SEC385)