summaryrefslogtreecommitdiffstats
path: root/emacs.d/nxhtml/util/ffip.el
diff options
context:
space:
mode:
Diffstat (limited to 'emacs.d/nxhtml/util/ffip.el')
-rw-r--r--emacs.d/nxhtml/util/ffip.el304
1 files changed, 304 insertions, 0 deletions
diff --git a/emacs.d/nxhtml/util/ffip.el b/emacs.d/nxhtml/util/ffip.el
new file mode 100644
index 0000000..42d1893
--- /dev/null
+++ b/emacs.d/nxhtml/util/ffip.el
@@ -0,0 +1,304 @@
+;;; ffip.el --- Find files in project
+;;
+;; Authors: extracted from rinari by Phil Hagelberg and Doug Alcorn
+;; Changed by Lennart Borgman
+;; Created: 2008-08-14T23:46:22+0200 Thu
+;; Version: 0.3
+;; Last-Updated: 2008-12-28 Sun
+;; URL:
+;; Keywords:
+;; Compatibility:
+;;
+;; Features that might be required by this library:
+;;
+;; None
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Commentary:
+;;
+;;
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Change log:
+;;
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;; This program is free software; you can redistribute it and/or
+;; modify it under the terms of the GNU General Public License as
+;; published by the Free Software Foundation; either version 2, or
+;; (at your option) any later version.
+;;
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with this program; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
+;; Floor, Boston, MA 02110-1301, USA.
+;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Project data
+
+;; Fix-me: Change the inner structure of ffip projects
+(defvar ffip-project-name nil "Project name.")
+(defvar ffip-project-roots nil "Project directory roots.")
+(defvar ffip-project-type nil "Project type, `ffip-project-file-types'.")
+(defcustom ffip-project-file-types
+ (list
+ '(ruby "\\(\\.el$\\|\\.rb$\\|\\.js$\\|\\.emacs\\)")
+ (list 'nxhtml (concat
+ (regexp-opt '(".html" ".htm" ".xhtml"
+ ".css"
+ ".js"
+ ".png" ".gif"
+ ))
+ "\\'"))
+ )
+ "Project types and file types.
+The values in this list are used to determine if a file belongs
+to the current ffip project. Entries have the form
+
+ \(TYPE FILE-REGEXP)
+
+TYPE is the parameter set by `ffip-set-current-project'. Files
+matching FILE-REGEXP within the project roots are members of the
+project."
+ :type '(repeat (list
+ (symbol :tag "Type")
+ (regexp :tag "File regexp")))
+ :group 'ffip)
+
+(defvar ffip-project-file-matcher nil "Project file matcher.")
+(defvar ffip-project-files-table nil "Project file cache.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Project handling
+
+(defun ffip-reset-project ()
+ "Clear project data."
+ (remove-hook 'after-save-hook 'ffip-after-save)
+ (setq ffip-project-name nil)
+ (setq ffip-project-roots nil)
+ (setq ffip-project-files-table nil)
+ (setq ffip-project-type nil)
+ (setq ffip-project-file-matcher nil))
+;;(ffip-reset-project)
+
+(defun ffip-is-current (name root type)
+ "Return non-nil if NAME, ROOT and TYPE match current ffip project.
+See `ffip-set-current-project'."
+ (and name
+ (string= ffip-project-name name)
+ (eq ffip-project-type type)
+ (equal ffip-project-roots root)))
+
+;;;###autoload
+(defun ffip-set-current-project (name root type)
+ "Setup ffip project NAME with top directory ROOT of type TYPE.
+ROOT can either be just a directory or a list of directory where
+the first used just for prompting purposes and the files in the
+rest are read into the ffip project.
+
+Type is a type in `ffip-project-file-types'."
+ (unless (ffip-is-current name root type)
+ (ffip-reset-project)
+ (setq ffip-project-name name)
+ (setq ffip-project-type type)
+ (setq ffip-project-roots root)
+ (message "Project %s with %s files setup for find-files-in-project"
+ name (length ffip-project-files-table))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; File cache handling
+
+(defun ffip-cache-project-files (file-regexp)
+ "Read files and cache their names within the ffip project."
+ (let ((root ffip-project-roots))
+ (message "... reading files in %s ..." root)
+ (add-hook 'after-save-hook 'ffip-after-save)
+ (if (not (listp root))
+ (ffip-populate-files-table root file-regexp)
+ (setq root (cdr root))
+ (dolist (r root)
+ (ffip-populate-files-table r file-regexp)))))
+
+(defun ffip-file-matcher ()
+ (when ffip-project-type
+ (cadr (assoc ffip-project-type ffip-project-file-types))))
+
+(defun ffip-project-files ()
+ "Get a list of all files in ffip project.
+The members in the list has the format
+
+ \(SHORT-NAME . FULL-NAME)
+
+where SHORT-NAME is a unique name (normally file name without
+directory) and FULL-NAME is the full file name."
+ (unless ffip-project-files-table
+ (let ((file-regexp (ffip-file-matcher)))
+ (ffip-cache-project-files file-regexp)))
+ ffip-project-files-table)
+
+;; Fix-me: Seems better to rewrite this to use
+;; project-find-settings-file.
+(defun ffip-project-root (&optional dir)
+ (setq dir (or dir
+ ffip-project-roots
+ default-directory))
+ ;;(locate-dominating-file "." "\\`\\find-file-in-project.el\\'")
+ (let ((root (locate-dominating-file dir
+ ;;"\\`\\.emacs-project\\'"
+ "\\`\\.dir-settings\\.el\\'"
+ )))
+ (if root
+ (file-name-directory root)
+ dir)))
+
+(defun ffip-populate-files-table (file file-regexp)
+ ;;(message "ffip-populate-files-table.file=%s" file)
+ (if (file-directory-p file)
+ (mapc (lambda (file)
+ (ffip-populate-files-table file file-regexp))
+ (directory-files (expand-file-name file) t "^[^\.]"))
+ (let* ((file-name (file-name-nondirectory file))
+ (existing-record (assoc file-name ffip-project-files-table))
+ (unique-parts (ffip-get-unique-directory-names file
+ (cdr existing-record))))
+ (when (or (not file-regexp)
+ (string-match file-regexp file-name))
+ (if existing-record
+ (let ((new-key (concat file-name " - " (car unique-parts)))
+ (old-key (concat (car existing-record) " - "
+ (cadr unique-parts))))
+ (setf (car existing-record) old-key)
+ (setq ffip-project-files-table
+ (acons new-key file ffip-project-files-table)))
+ (setq ffip-project-files-table
+ (acons file-name file ffip-project-files-table)))))))
+
+(defun ffip-get-unique-directory-names (path1 path2)
+ (let* ((parts1 (and path1 (split-string path1 "/" t)))
+ (parts2 (and path2 (split-string path2 "/" t)))
+ (part1 (pop parts1))
+ (part2 (pop parts2))
+ (looping t))
+ (while (and part1 part2 looping)
+ (if (equal part1 part2)
+ (setq part1 (pop parts1) part2 (pop parts2))
+ (setq looping nil)))
+ (list part1 part2)))
+
+(defun ffip-file-is-in-project (file-name)
+ "Return non-nil if file is in current ffip project."
+ (save-match-data
+ (let ((file-regexp (ffip-file-matcher))
+ (roots ffip-project-roots)
+ regexp)
+ (if (not (listp roots))
+ (setq roots (list roots))
+ (setq roots (cdr roots)))
+ (catch 'found
+ (dolist (root roots)
+ (setq file-regexp (concat root ".*" file-regexp))
+ (when (string-match file-regexp file-name)
+ (throw 'found t)))))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Updating on file changes
+
+(defun ffip-add-file-if-in-project (file-name)
+ "Add file to cache if it in ffip project."
+ (when (ffip-file-is-in-project file-name)
+ ;; We have already checked so just use nil for the matcher.
+ (ffip-populate-files-table file-name nil)))
+
+;; For after-save-hook
+(defun ffip-after-save ()
+ "Check if a file should be added to cache."
+ (condition-case err
+ (ffip-add-file-if-in-project buffer-file-name)
+ (error (message "%s" (error-message-string err)))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Interactive functions
+
+;;;###autoload
+(defun ffip-find-file-in-dirtree (root)
+ "Find files in directory tree ROOT."
+ (interactive "DFind file in directory tree: ")
+ ;; Setup a temporary
+ (let ((ffip-project-name nil)
+ (ffip-project-roots nil)
+ (ffip-project-files-table nil)
+ (ffip-project-type nil)
+ (ffip-project-file-matcher nil))
+ (ffip-set-current-project "(temporary)" root nil)
+ (call-interactively 'ffip-find-file-in-project)))
+
+(defun ffip-find-file-in-project (file)
+ "Find files in current ffip project."
+ (interactive
+ (list
+ (let* ((prompt (format "Find file in project %s: "
+ ffip-project-name)))
+ (if (memq ido-mode '(file 'both))
+ (ido-completing-read prompt
+ (mapcar 'car (ffip-project-files)))
+ (let ((files (mapcar 'car (ffip-project-files))))
+ (completing-read prompt
+ files
+ (lambda (elem) (member elem files))
+ t))))))
+ (find-file (cdr (assoc file ffip-project-files-table))))
+
+;;(global-set-key (kbd "C-x C-M-f") 'find-file-in-project)
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Fix-me: This part should go somewhere else
+(eval-after-load 'ruby-mode
+ '(progn
+ (defun ffip-rails-project-files (&optional file)
+ (let ((default-directory (or file (rails-root))))
+ (unless (and ffip-project-roots
+ (string= default-directory ffip-project-roots))
+ (ffip-set-current-project
+ "Rails proj"
+ root
+ (list default-directory
+ (expand-file-name "app")
+ (expand-file-name "lib")
+ (expand-file-name "test"))
+ 'ruby
+ )))
+ (ffip-project-files))
+
+ (defun ffip-find-file-in-rails (file)
+ (interactive
+ (list (if (memq ido-mode '(file 'both))
+ (ido-completing-read
+ "Find file in project: "
+ (mapcar 'car (ffip-rails-project-files)))
+ (completing-read "Find file in project: "
+ (mapcar 'car (rails-project-files))))))
+ (find-file (cdr (assoc file ffip-project-files-table))))
+
+ (define-key ruby-mode-map (kbd "C-x C-M-f") 'find-file-in-rails)
+ (eval-after-load 'nxhtml-mode
+ '(define-key nxhtml-mode-map (kbd "C-x C-M-f") 'find-file-in-rails))))
+
+(provide 'ffip)
+;;; ffip.el ends here