Personal tools
You are here: Home ブログ matsuyama 巨大なディレクトリ構造でもフラットに find-file する
Document Actions

巨大なディレクトリ構造でもフラットに find-file する

find-file でいちいちディレクトリを補完するのが面倒なので以下のツール( + elisp )を書きました。ありそうなアイデアですが探してもなかったので自作しました。

できること

  • locate の部分的適用 + α
  • 巨大なディレクトリ構造でもフラットに find-file ができる

使い方(コマンドライン)

あらかじめ path/usr/bin/ あたりにインストールしておいてください。

データベース作成

データベースを作成するとカレントディレクトリに PATH というデータベースファイルが作られます。内部的には slocatelocatedb です。

% path -u

検索

クエリ文字列に一致するファイルのフルパスを得ることができます。 PATH があるディレクトリ配下ならどこからでも利用できます。

% pwd
/path/to/
% ls
PATH hoge1 foo/
% path hoge
/path/to/hoge1
/path/to/foo/hoge2

zsh を使ってる場合は

% less =path hoge

で一発で hoge を less することができます。

一覧

ディレクトリ・ファイル名を一覧します( elisp で使っています)。

% path -l

その他

  • -e オプションでクエリ対象の排除ができます
  • -E.svn とか .cvs をクエリ対象から排除できます
  • -r で正規表現が使えます
  • -i で case-insensitive になります

使い方( Emacs )

初期設定

(autoload 'path-find-file "path" nil t)
(global-set-key "\C-xp" 'path-find-file)

M-x path-find-file

PATH から取得したディレクトリ・ファイル名をミニバッファで補完するだけで find-file できます。

メモ

  • 処理が強引かつ適当なのはわかってる
  • slocate わけわからんから許して
  • elisp 始めてだから許して
  • completin-read 使わないバージョンを作るべき( read-string など)
  • バグだらけ

本体( perl スクリプトと elisp )

path:

#!/usr/bin/perl

use strict;
use Getopt::Long;
use File::Basename;

my $print;
my $list;
my $update;
my $icase;
my $regex;
my @excludes = ();
my $exclude_vc;
GetOptions('print'          => \$print,
           'list'           => \$list,
           'update'         => \$update,
           'icase'          => \$icase,
           'regex'          => \$regex,
           'exclude=s'      => \@excludes,
           'exclude-vc|E'   => \$exclude_vc) or exit(1);

if ($exclude_vc) {
    push @excludes, '\.svn', '\.cvs';
}

if ($print) {
    my ($dir) = find_database();
    print "$dir\n";
} elsif ($update) {
    my ($dir, $name) = find_database();
    my $database = join_path($dir, $name);
    my @args = ('slocate', '-U', $dir, '-o', $database, '-l', '0');
    shift @args, '-e', join(',', @excludes) if @excludes;
    system(@args);
} else {
    my ($dir, $name) = find_database();
    my $database = join_path($dir, $name);
    if (-e $database) {
        my @args = ('slocate', '-d', $database);
        push @args, '-i' if $icase;
        push @args, '-r' if $regex;
        if ($list) {
            push @ARGV, '/';
            my $cmd = join(' ', @args, @ARGV);
            my @list = grep { !match_excludes($_) } map { basename $_ } split /\n/, `$cmd`;
            my %seen = ();
            for (@list) {
                next if $seen{$_};
                $seen{$_} = 1;
                print "$_\n";
            }
        } elsif (@ARGV) {
            my $cmd = join(' ', @args, @ARGV);
            for (split /\n/, `$cmd`) {
                next if match_excludes($_);
                print "$_\n";
            }
        } else {
            die "No query string";
        }
    } else {
        die "Cannot find database: $database";
    }

}

sub match_excludes {
    my $str = shift;
    for (@excludes) {
        return 1 if $str =~ /$_/;
    }
    return 0;
}

sub unique {
    my %seen = ();
    $seen{$_} = 1 for @_;
    return \(keys %seen);
}

sub split_path {
    return split '/', shift;
}

sub join_path {
    return join '/', @_;
}

sub find_database {
    my $name = 'PATH';
    my $pwd = `pwd`;
    chomp $pwd;
    my @parts = split_path($pwd);
    while (@parts) {
        my $dir = join_path(@parts);
        if (-e join_path($dir, $name)) {
            return ($dir, $name);
        }
        pop @parts;
    }
    return ($pwd, $name);
}

path.el:

;;; path.el

;; Author: MATSUYAMA Tomohiro
;; Version: 1.0
;; Licence: GPLv2

;;; Code

(defconst path-db-name "PATH") 
(defconst path-buffer-name "*PATH*")

(defvar path-do-cache t)
(defvar path-hashtab (make-hash-table :test 'equal))

(defvar path-db-rootdir nil)
(defvar path-options "-E")
(defvar path-saved-buffer nil)

(defvar path-select-mode-map (make-sparse-keymap))
(define-key path-select-mode-map "n" 'next-line)
(define-key path-select-mode-map "p" 'previous-line)
(define-key path-select-mode-map "q" 'path-quit-selection)
(define-key path-select-mode-map "\C-m" 'path-select-path)

(defun path-clear-hashtab ()
  (clrhash path-hashtab))

(defun path-get-buffer ()
  (get-buffer path-buffer-name))

(defun path-call-process (&rest args)
  (let ((buffer (path-get-buffer))
        (saved-buffer (current-buffer))
        result)
    (set-buffer (progn
                  (when (null buffer)
                    (setq buffer (generate-new-buffer (generate-new-buffer-name path-buffer-name)))
                    (buffer-disable-undo buffer))
                  buffer))
    (when path-db-rootdir
        (cd path-db-rootdir))
    (kill-region (point-min) (point-max))
    (setq result
          (if (= 0 (apply 'call-process "path" nil t nil args))
              (buffer-substring (point-min) (1- (point-max)))
            nil))
    (set-buffer saved-buffer)
    result))

(defun path-find-db-rootdir (dir)
  (setq dir (directory-file-name dir))
  (if dir
      (if (file-exists-p (concat (file-name-as-directory dir) path-db-name))
          dir
        (progn
          (let ((parent (file-name-directory dir)))
            (if (equal dir parent)
                nil
              (path-find-db-rootdir parent)))))
    nil))

(defun path-visit-db-rootdir ()
  (interactive)
  (setq path-db-rootdir (path-find-db-rootdir (expand-file-name default-directory))))

(defun path-list-names ()
  (path-visit-db-rootdir)
  (let ((lst (gethash path-db-rootdir path-hashtab)))
    (when (null lst)
      (setq lst (split-string (path-call-process "-l" path-options) "\n"))
      (when path-do-cache
        (puthash path-db-rootdir lst path-hashtab)))
    lst))

(defun path-goto-path (name)
  (path-visit-db-rootdir)
  (let ((str (path-call-process path-options "-r" (format "'/%s$'" name))))
    (when str
      (let* ((lst (split-string str "\n"))
             (cnt (safe-length lst)))
        (cond
         ((= cnt 1)
          (find-file (car lst)))
         ((> cnt 1)
          (switch-to-buffer (path-get-buffer))
          (path-select-mode)))))))

(defun path-select-mode ()
  (interactive)
  (use-local-map path-select-mode-map)
  (setq major-mode 'path-select-mode
        mode-name "Path-Select")
  (goto-char (point-min))
  (run-hooks 'path-select-mode-hook))

(defun path-quit-selection ()
  (interactive)
  (switch-to-buffer path-saved-buffer))

(defun path-select-path ()
  (interactive)
  (setq path-saved-buffer (current-buffer))
  (find-file (buffer-substring (point-at-bol) (point-at-eol))))

;;;###autoload
(defun path-find-file ()
  (interactive)
  (setq input (completing-read "File file: " (path-list-names)))
  (when input
    (path-goto-path input)))
Category(s)
emacs
The URL to Trackback this entry is:
http://dev.ariel-networks.com/Members/matsuyama/flat-find-file/tbping
Add comment

You can add a comment by filling out the form below. Plain text formatting.

(Required)
(Required)
(Required)
(Required)
(Required)
This helps us prevent automated spamming.
Captcha Image


Copyright(C) 2001 - 2006 Ariel Networks, Inc. All rights reserved.