Created
August 24, 2010 02:26
-
-
Save sbenhaim/546797 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; The Common Lisp library for Emacs Lisp gives us keyword arguments | |
;; for defun* | |
(require 'cl) | |
(defvar projects (list) "This keeps track of all available projects.") | |
(defvar project (list) "And here's our current project.") | |
(defvar project-index (list) "This will store the project index of files.") | |
(defvar project-default-filetypes | |
'("*.el" "*.rb" "*.py" "*.rb" "*.clj" "*.php" "*.js" "*.html") | |
"Files for indexing.") | |
;; Before we can build `anything-project-files', we need to have an | |
;; idea of what a project is. For the purposes of this post, a | |
;; project will have a name, a root directory, and a list of filetypes | |
;; we want to index. Change this default file list to suit your own | |
;; needs. | |
;; | |
;; By the way, `&key' gives us keyword arguments, you can use a symbol | |
;; or a list of the form `(keyname default-value)' to designate a | |
;; keyword. | |
;; | |
;; Anything candidates can be a list of `(DISPLAY . REAL)' pairs, so | |
;; we throw the name up front to serve as the `DISPLAY' component." | |
(defun* project-create (name &key root (indexed-files project-default-filetypes)) | |
"Add a project to the list or projects." | |
(add-to-list 'projects | |
(cons name | |
`((:name . ,name) | |
(:root . ,root) | |
(:indexed-files . ,indexed-files))))) | |
;; The handy, dandy function below makes it real easy to get a project | |
;; component in the form of `(project :root)' or `(project :name)'. | |
;; (Elisp is a Lisp-2, so function can have the same name as | |
;; variables.) | |
(defun project (key) | |
(cdr (assoc key project))) | |
;; Here's the code that indexes our project. | |
(defun project-reindex () | |
"Update your projects index of files." | |
(interactive) | |
;; Travel to the project root | |
(cd (project :root)) | |
;; Using our `indexed-files', create a string that we can toss into a | |
;; find command. It'll look like "'*.el' -or -name '*.rb'...". | |
;; | |
;; For a big project, we don't want to be stuck waiting while it | |
;; indexes, so we have to do a little jig-hoolery to make this work | |
;; async. Firstly, that means keeping track of the current buffer, | |
;; and switching to a temporary one in which to do our work. | |
(let* ((b (current-buffer)) | |
(file-types (mapconcat #'identity (project :indexed-files) " -or -name "))) | |
;; And here's our temp buffer. | |
(switch-to-buffer "*project-index*") | |
;; I'll use `tramp-handle-shell-command' as it returns a process | |
(let ((process (tramp-handle-shell-command | |
"find . -type f -name '*.el' &" (current-buffer)))) | |
;; And that lets me set a sentinel (callback) for when the | |
;; process is complete | |
(set-process-sentinel process 'project-load-index)) | |
(message "Indexing...") | |
;; The work has been started, so we switch back to where we were | |
;; when we called the command. | |
(switch-to-buffer b))) | |
;; Here's my callback function. `p' is the process and `s' is a | |
;; message string, which I'll ignore here. | |
(defun project-load-index (p s) | |
;; Now we'll just split-string on newline for our process buffer. | |
(setq project-index | |
(split-string | |
(with-current-buffer (process-buffer p) | |
(buffer-string)))) | |
;; Goodbye | |
(kill-buffer (process-buffer p)) | |
(message "Indexing complete.")) | |
;; We load the project by selecting from a list an anything | |
;; buffer. Nice. | |
(defun project-load () | |
(interactive) | |
(setq project | |
(anything | |
'((name . "Load Project") | |
(candidates . projects) | |
;; `anything' usually wants to do something with our selected candidate, | |
;; but here I'm just going to return it. | |
(action . (("Return" . identity)))))) | |
;; The project is set, let's index it. | |
(project-reindex)) | |
;; And finally, the payload. | |
(defun anything-project-files () | |
(interactive) | |
(anything | |
'((name . "Project Find File") | |
(candidates . project-index) | |
;; `display-to-real' will take the filename selection and add our | |
;; project root before any action is performed on it. | |
(display-to-real . (lambda (c) (concat (project :root) "/" c))) | |
(type . file)))) | |
;; Now we'll set up our `emacs-config' project, accepting defaults for `:indexed-files', | |
;; though we could certainly use `'("*.el")' if we wanted to. | |
(project-create "emacs-config" | |
:root "~/.emacs.d") | |
;; And there you have it. Load your `emacs-config' project, give it | |
;; the old `M-x anything-project-files', and you're off to the races. | |
;; | |
;; Next steps: | |
;; | |
;; Once you've got project context, you can let your imagination run | |
;; wild coming up with project-related tasks Emacs could be doing for | |
;; you. | |
;; | |
;; Also `project-load' could do more, like tagging the project. Hell, | |
;; you could create an `anything-project-tags' command, which would | |
;; rock out pretty hard. | |
;; | |
;; Indexing (and tagging) might take a long time on large projects and | |
;; may not be necessary on every project load. One could consider | |
;; saving a project index file and loading from that until you | |
;; explicitly reindex. It'd be pretty simple, but I'll leave that up | |
;; to you. | |
;; | |
;; Until next time, buenos dias! Buenos Aires! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment