comparison .elisp/textmate.el @ 152:a24d5587386f

textmate.el: update to latest version
author Augie Fackler <durin42@gmail.com>
date Fri, 09 Oct 2009 21:48:03 -0400
parents e30655eb7050
children 668268f29a88
comparison
equal deleted inserted replaced
151:bf6b5a0dc1f3 152:a24d5587386f
1 ;; textmate.el --- TextMate minor mode for Emacs 1 ;; textmate.el --- TextMate minor mode for Emacs
2 2
3 ;; Copyright (C) 2008 Chris Wanstrath <chris@ozmm.org> and others 3 ;; Copyright (C) 2008, 2009 Chris Wanstrath <chris@ozmm.org> and others
4 4
5 ;; Licensed under the same terms as Emacs. 5 ;; Licensed under the same terms as Emacs.
6 6
7 ;; Version: 0.1.0
8 ;; Keywords: textmate osx mac 7 ;; Keywords: textmate osx mac
9 ;; Created: 22 Nov 2008 8 ;; Created: 22 Nov 2008
10 ;; Author: Chris Wanstrath <chris@ozmm.org> and others 9 ;; Author: Chris Wanstrath <chris@ozmm.org> and others
11 10
12 ;; This file is NOT part of GNU Emacs. 11 ;; This file is NOT part of GNU Emacs.
28 ;; ⌥⌘T - Reset File Cache (for Go to File, cache unused if using git/hg root, 27 ;; ⌥⌘T - Reset File Cache (for Go to File, cache unused if using git/hg root,
29 ;; but resets cached root location, useful if roots 28 ;; but resets cached root location, useful if roots
30 ;; are nested) 29 ;; are nested)
31 30
32 ;; A "project" in textmate-mode is determined by the presence of 31 ;; A "project" in textmate-mode is determined by the presence of
33 ;; a .git directory. If no .git directory is found in your current 32 ;; a .git directory, an .hg directory, a Rakefile, or a Makefile.
34 ;; directory, textmate-mode will traverse upwards until one (or none) 33
35 ;; is found. The directory housing the .git directory is presumed 34 ;; You can configure what makes a project root by appending a file
36 ;; to be the project's root. 35 ;; or directory name onto the `*textmate-project-roots*' list.
36
37 ;; If no project root indicator is found in your current directory,
38 ;; textmate-mode will traverse upwards until one (or none) is found.
39 ;; The directory housing the project root indicator (e.g. a .git or .hg
40 ;; directory) is presumed to be the project's root.
37 41
38 ;; In other words, calling Go to File from 42 ;; In other words, calling Go to File from
39 ;; ~/Projects/fieldrunners/app/views/towers/show.html.erb will use 43 ;; ~/Projects/fieldrunners/app/views/towers/show.html.erb will use
40 ;; ~/Projects/fieldrunners/ as the root if ~/Projects/fieldrunners/.git 44 ;; ~/Projects/fieldrunners/ as the root if ~/Projects/fieldrunners/.git
41 ;; exists. 45 ;; exists.
46
47 ;; In the event that the project root was defined by either .git or .hg,
48 ;; fast file-listing with no caching is provided by the version control
49 ;; system.
50
51 ;; Not bound to keys, but available are textmate-find-in-project and
52 ;; textmate-find-in-project-type, which use grep, the file listing,
53 ;; and grep-mode to provide excellent (and blindingly fast with git and
54 ;; hg!) grep integration with emacs and your project.
55
56 ;; Also available (and unbound) is textmate-compile, which is like
57 ;; compile but prepends a cd to the project root to the command. It is
58 ;; used to build the find-in-project commands, but has other possible
59 ;; uses as well (eg, a test runner or some kind of compile command).
42 60
43 ;;; Installation 61 ;;; Installation
44 62
45 ;; $ cd ~/.emacs.d/vendor 63 ;; $ cd ~/.emacs.d/vendor
46 ;; $ git clone git://github.com/defunkt/textmate.el.git 64 ;; $ git clone git://github.com/defunkt/textmate.el.git
58 (eval-when-compile 76 (eval-when-compile
59 (require 'cl)) 77 (require 'cl))
60 78
61 ;;; Minor mode 79 ;;; Minor mode
62 80
81 (defvar *textmate-gf-exclude*
82 "/\\.|vendor|fixtures|tmp|log|build|\\.xcodeproj|\\.nib|\\.framework|\\.app|\\.pbproj|\\.pbxproj|\\.xcode|\\.xcodeproj|\\.bundle|\\.pyc"
83 "Regexp of files to exclude from `textmate-goto-file'.")
84
85 (defvar *textmate-project-roots*
86 '(".git" ".hg" "Rakefile" "Makefile" "README" "build.xml")
87 "The presence of any file/directory in this list indicates a project root.")
88
63 (defvar textmate-use-file-cache t 89 (defvar textmate-use-file-cache t
64 "* Should `textmate-goto-file' keep a local cache of files?") 90 "Should `textmate-goto-file' keep a local cache of files?")
65 91
66 (defvar textmate-completing-library 'ido 92 (defvar textmate-completing-library 'ido
67 "The library `textmade-goto-symbol' and `textmate-goto-file' should use for completing filenames and symbols (`ido' by default)") 93 "The library `textmade-goto-symbol' and `textmate-goto-file' should use for
94 completing filenames and symbols (`ido' by default)")
68 95
69 (defvar *textmate-completing-function-alist* '((ido ido-completing-read) 96 (defvar *textmate-completing-function-alist* '((ido ido-completing-read)
70 (icicles icicle-completing-read) 97 (icicles icicle-completing-read)
71 (none completing-read)) 98 (none completing-read))
72 "The function to call to read file names and symbols from the user") 99 "The function to call to read file names and symbols from the user")
118 (define-key map [(control c)(control k)] 'comment-or-uncomment-region-or-line) 145 (define-key map [(control c)(control k)] 'comment-or-uncomment-region-or-line)
119 (define-key map [(meta t)] 'textmate-goto-file) 146 (define-key map [(meta t)] 'textmate-goto-file)
120 (define-key map [(meta shift t)] 'textmate-goto-symbol))) 147 (define-key map [(meta shift t)] 'textmate-goto-symbol)))
121 map)) 148 map))
122 149
123 150 (defvar *textmate-project-root* nil
124 (defvar *textmate-project-root* nil) 151 "Used internally to cache the project root.")
125 (defvar *textmate-project-files* '()) 152 (defvar *textmate-project-files* '()
126 153 "Used internally to cache the files in a project.")
127 (defvar *textmate-gf-exclude*
128 "/\\.|vendor|fixtures|tmp|log|build|\\.xcodeproj|\\.nib|\\.framework|\\.app|\\.pbproj|\\.pbxproj|\\.xcode|\\.xcodeproj|\\.bundle|\\.pyc")
129
130 (defvar *textmate-project-roots*
131 '(".git" ".hg" "Rakefile" "Makefile" "README" "build.xml"))
132 154
133 (defvar *textmate-vcs-exclude* nil 155 (defvar *textmate-vcs-exclude* nil
134 "string to give to grep -V to exclude some VCS paths from being grepped." 156 "string to give to grep -V to exclude some VCS paths from being grepped."
135 ) 157 )
136 158
137 (defvar *textmate-find-in-project-default* nil) 159 (defvar *textmate-find-in-project-default* nil)
138 160
139 (defvar *textmate-find-in-project-type-default* nil) 161 (defvar *textmate-find-in-project-type-default* nil)
162
163 (defvar *textmate-compile-default* nil)
140 164
141 ;;; Bindings 165 ;;; Bindings
142 166
143 (defun textmate-ido-fix () 167 (defun textmate-ido-fix ()
144 "Add up/down keybindings for ido." 168 "Add up/down keybindings for ido."
145 (define-key ido-completion-map [up] 'ido-prev-match) 169 (define-key ido-completion-map [up] 'ido-prev-match)
146 (define-key ido-completion-map [down] 'ido-next-match)) 170 (define-key ido-completion-map [down] 'ido-next-match))
147 171
148 (defun textmate-completing-read (&rest args) 172 (defun textmate-completing-read (&rest args)
149 (let ((reading-fn (cadr (assoc textmate-completing-library *textmate-completing-function-alist*)))) 173 "Uses `*textmate-completing-function-alist*' to call the appropriate completing
174 function."
175 (let ((reading-fn
176 (cadr (assoc textmate-completing-library
177 *textmate-completing-function-alist*))))
150 (apply (symbol-function reading-fn) args))) 178 (apply (symbol-function reading-fn) args)))
151 179
152 ;;; allow-line-as-region-for-function adds an "-or-line" version of 180 ;;; allow-line-as-region-for-function adds an "-or-line" version of
153 ;;; the given comment function which (un)comments the current line is 181 ;;; the given comment function which (un)comments the current line is
154 ;;; the mark is not active. This code comes from Aquamac's osxkeys.el 182 ;;; the mark is not active. This code comes from Aquamac's osxkeys.el
174 (allow-line-as-region-for-function comment-or-uncomment-region))) 202 (allow-line-as-region-for-function comment-or-uncomment-region)))
175 203
176 ;;; Commands 204 ;;; Commands
177 205
178 (defun textmate-next-line () 206 (defun textmate-next-line ()
207 "Inserts an indented newline after the current line and moves the point to it."
179 (interactive) 208 (interactive)
180 (end-of-line) 209 (end-of-line)
181 (newline-and-indent)) 210 (newline-and-indent))
182 211
183 ;; http://chopmo.blogspot.com/2008/09/quickly-jumping-to-symbols.html 212 ;; http://chopmo.blogspot.com/2008/09/quickly-jumping-to-symbols.html
222 (let* ((selected-symbol (ido-completing-read "Symbol? " symbol-names)) 251 (let* ((selected-symbol (ido-completing-read "Symbol? " symbol-names))
223 (position (cdr (assoc selected-symbol name-and-pos)))) 252 (position (cdr (assoc selected-symbol name-and-pos))))
224 (goto-char position)))) 253 (goto-char position))))
225 254
226 (defun textmate-goto-file () 255 (defun textmate-goto-file ()
256 "Uses your completing read to quickly jump to a file in a project."
227 (interactive) 257 (interactive)
228 (let ((root (textmate-project-root))) 258 (let ((root (textmate-project-root)))
229 (when (null root) 259 (when (null root)
230 (error 260 (error
231 (concat 261 (concat
232 "Can't find a sutiable project root (" 262 "Can't find a suitable project root ("
233 (string-join " " *textmate-project-roots* ) 263 (string-join " " *textmate-project-roots* )
234 ")"))) 264 ")")))
235 (find-file 265 (find-file
236 (concat 266 (concat
237 (expand-file-name root) "/" 267 (expand-file-name root) "/"
238 (textmate-completing-read 268 (textmate-completing-read
239 "Find file: " 269 "Find file: "
240 (textmate-project-files root)))))) 270 (textmate-project-files root))))))
241 271
242 (defun textmate-find-in-project-type () 272 (defun textmate-find-in-project-type ()
273 "Run grep over project files of a specific type and put the results
274 in a grep-mode buffer."
243 (interactive) 275 (interactive)
244 (let ((pat (read-string (concat "Suffix" 276 (let ((pat (read-string (concat "Suffix"
245 (if *textmate-find-in-project-type-default* 277 (if *textmate-find-in-project-type-default*
246 (format " [\"%s\"]" *textmate-find-in-project-type-default*) 278 (format " [\"%s\"]" *textmate-find-in-project-type-default*)
247 "") 279 "")
248 ": " 280 ": "
249 ) nil nil *textmate-find-in-project-type-default*))) 281 ) nil nil *textmate-find-in-project-type-default*)))
250 (setq *textmate-find-in-project-type-default* pat) 282 (setq *textmate-find-in-project-type-default* pat)
251 (textmate-find-in-project (concat "*." pat)))) 283 (textmate-find-in-project (concat "*." pat))))
252 284
253 (defun textmate-find-in-project (&optional pattern) 285 (defun textmate-start-compile-in-root (command &optional mode
254 (interactive) 286 name-function
255 (let ((root (textmate-project-root)) 287 highlight-regexp)
256 (default *textmate-find-in-project-default*) 288 "Idential to compilation-start, except it automatically changes to the
257 ) 289 project root directory before starting the command."
290 (let ((root (textmate-project-root)))
258 (when (null root) 291 (when (null root)
259 (error "Not in a project area.")) 292 (error "Not in a project area."))
260 (let ((re (read-string (concat "Search for " 293 (let ((realcommand (concat "cd " root " ; " command)))
294 (compilation-start realcommand mode name-function highlight-regexp))))
295
296 (defun textmate-compile ()
297 "Run a command in compilation-mode rooted at the project root."
298 (interactive)
299 (let* ((default *textmate-compile-default*)
300 (command (read-string
301 (concat "Command"
302 (if default (format " [\"%s\"]" default) "")
303 ": ") nil 'textmate-compile-history default)))
304 (setq *textmate-compile-default* command)
305 (textmate-start-compile-in-root command)))
306
307 (defun textmate-find-in-project (&optional pattern)
308 "Run grep over project files with results in grep-mode.
309
310 Takes an optional argument (see also textmate-find-in-project-type)
311 of a file extension to limit the search. Useful for finding results in only a
312 specific type of file."
313 (interactive)
314 (let* ((default *textmate-find-in-project-default*)
315 (re (read-string (concat "Search for "
261 (if (and default (> (length default) 0)) 316 (if (and default (> (length default) 0))
262 (format "[\"%s\"]" default)) ": ") 317 (format "[\"%s\"]" default)) ": ")
263 nil 'textmate-find-in-project-history default) 318 nil 'textmate-find-in-project-history default))
264 ) 319 (incpat (if pattern pattern "*"))
265 (incpat (if pattern pattern "*"))) 320 (type (textmate-project-root-type (textmate-project-root)))
266 (append textmate-find-in-project-history (list re)) 321 (command
267 (setq *textmate-find-in-project-default* re) 322 (cond ((not (string= type "unknown"))
268 (let ((type (textmate-project-root-type root))) 323 (concat (cond ((string= type "git") "git ls-files")
269 (let ((command
270 (cond ((not (string= type "unknown"))
271 (concat "cd "
272 root
273 " ; "
274 (cond ((string= type "git") "git ls-files")
275 ((string= type "hg") "hg manifest")) 324 ((string= type "hg") "hg manifest"))
276 (if *textmate-vcs-exclude* 325 (if *textmate-vcs-exclude*
277 (concat " | grep -v " (shell-quote-argument *textmate-vcs-exclude*)) 326 (concat " | grep -v "
327 (shell-quote-argument *textmate-vcs-exclude*))
278 "") 328 "")
279 " | xargs grep -nR " 329 " | xargs grep -nR "
280 (if pattern (concat " --include='" pattern "' ") "") 330 (if pattern (concat " --include='" pattern "' ") "")
281 " -- " 331 " -- "
282 (shell-quote-argument re))) 332 (shell-quote-argument re)))
283 (t (concat "cd " root "; egrep -nR --exclude='" 333 (t (concat "egrep -nR --exclude='"
284 *textmate-gf-exclude* 334 *textmate-gf-exclude*
285 "' --include='" 335 "' --include='"
286 incpat 336 incpat
287 "' -- " 337 "' -- "
288 (shell-quote-argument re) 338 (shell-quote-argument re)
289 " . | grep -vE '" 339 " . | grep -vE '"
290 *textmate-gf-exclude* 340 *textmate-gf-exclude*
291 "' | sed s:./::" 341 "' | sed s:./::"
292 ))))) 342 )))))
293 (compilation-start command 'grep-mode))) 343 (setq *textmate-find-in-project-default* re)
294 ))) 344 (textmate-start-compile-in-root command 'grep-mode)))
295 345
296 (defun textmate-clear-cache () 346 (defun textmate-clear-cache ()
347 "Clears the project root and project files cache. Use after adding files."
297 (interactive) 348 (interactive)
298 (setq *textmate-project-root* nil) 349 (setq *textmate-project-root* nil)
299 (setq *textmate-project-files* nil) 350 (setq *textmate-project-files* nil)
300 (message "textmate-mode cache cleared.")) 351 (message "textmate-mode cache cleared."))
301 352
333 ((member ".hg" (directory-files root)) "hg") 384 ((member ".hg" (directory-files root)) "hg")
334 (t "unknown") 385 (t "unknown")
335 )) 386 ))
336 387
337 (defun textmate-project-files (root) 388 (defun textmate-project-files (root)
389 "Finds all files in a given project using either hg, git, or find."
338 (let ((type (textmate-project-root-type root))) 390 (let ((type (textmate-project-root-type root)))
339 (cond ((string= type "git") (split-string 391 (cond ((string= type "git") (split-string
340 (shell-command-to-string 392 (shell-command-to-string
341 (concat "cd " root " && git ls-files")) "\n" t)) 393 (concat "cd " root " && git ls-files")) "\n" t))
342 ((string= type "hg") (split-string 394 ((string= type "hg") (split-string
344 (concat "cd " root " && hg manifest")) "\n" t)) 396 (concat "cd " root " && hg manifest")) "\n" t))
345 ((string= type "unknown") (textmate-cached-project-files-find root)) 397 ((string= type "unknown") (textmate-cached-project-files-find root))
346 ))) 398 )))
347 399
348 (defun textmate-project-files-find (root) 400 (defun textmate-project-files-find (root)
401 "Finds all files in a given project using find."
349 (split-string 402 (split-string
350 (shell-command-to-string 403 (shell-command-to-string
351 (concat 404 (concat
352 "find " 405 "find "
353 root 406 root
356 "' | sed 's:" 409 "' | sed 's:"
357 *textmate-project-root* 410 *textmate-project-root*
358 "/::'")) "\n" t)) 411 "/::'")) "\n" t))
359 412
360 (defun textmate-cached-project-files-find (&optional root) 413 (defun textmate-cached-project-files-find (&optional root)
414 "Finds and caches all files in a given project using find."
361 (cond 415 (cond
362 ((null textmate-use-file-cache) (textmate-project-files root)) 416 ((null textmate-use-file-cache) (textmate-project-files root))
363 ((equal (textmate-project-root) (car *textmate-project-files*)) 417 ((equal (textmate-project-root) (car *textmate-project-files*))
364 (cdr *textmate-project-files*)) 418 (cdr *textmate-project-files*))
365 (t (cdr (setq *textmate-project-files* 419 (t (cdr (setq *textmate-project-files*
366 `(,root . ,(textmate-project-files-find root))))))) 420 `(,root . ,(textmate-project-files-find root)))))))
367 421
368 (defun textmate-project-root () 422 (defun textmate-project-root ()
423 "Returns the current project root."
369 (when (or 424 (when (or
370 (null *textmate-project-root*) 425 (null *textmate-project-root*)
371 (not (string-match *textmate-project-root* default-directory))) 426 (not (string-match *textmate-project-root* default-directory)))
372 (let ((root (textmate-find-project-root))) 427 (let ((root (textmate-find-project-root)))
373 (if root 428 (if root
385 'nil 440 'nil
386 (root-matches root (cdr names)) 441 (root-matches root (cdr names))
387 ))) 442 )))
388 443
389 (defun textmate-find-project-root (&optional root) 444 (defun textmate-find-project-root (&optional root)
445 "Determines the current project root by recursively searching for an indicator."
390 (when (null root) (setq root default-directory)) 446 (when (null root) (setq root default-directory))
391 (cond 447 (cond
392 ((root-matches root *textmate-project-roots*) 448 ((root-matches root *textmate-project-roots*)
393 (expand-file-name root)) 449 (expand-file-name root))
394 ((equal (expand-file-name root) "/") nil) 450 ((equal (expand-file-name root) "/") nil)