Mercurial > dotfiles
comparison .elisp/textmate.el @ 141:e30655eb7050
textmate.el: synced with upstream
Includes discovering that ido-imenu was a better textmate-goto-symbol
and support for excluding files from grepping that are otherwise
tracked in VCS. Useful for symlinks to large trees, and other such things.
author | Augie Fackler <durin42@gmail.com> |
---|---|
date | Tue, 15 Sep 2009 20:28:22 -0400 |
parents | fe9d358b9fe8 |
children | a24d5587386f 95b7dc384677 |
comparison
equal
deleted
inserted
replaced
140:7fa84e297c84 | 141:e30655eb7050 |
---|---|
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> | 3 ;; Copyright (C) 2008 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 | 7 ;; Version: 0.1.0 |
8 ;; Keywords: textmate osx mac | 8 ;; Keywords: textmate osx mac |
9 ;; Created: 22 Nov 2008 | 9 ;; Created: 22 Nov 2008 |
10 ;; Author: Chris Wanstrath <chris@ozmm.org> | 10 ;; Author: Chris Wanstrath <chris@ozmm.org> and others |
11 | 11 |
12 ;; This file is NOT part of GNU Emacs. | 12 ;; This file is NOT part of GNU Emacs. |
13 | |
14 ;; Licensed under the same terms as Emacs. | |
15 | 13 |
16 ;;; Commentary: | 14 ;;; Commentary: |
17 | 15 |
18 ;; This minor mode exists to mimick TextMate's awesome | 16 ;; This minor mode exists to mimick TextMate's awesome |
19 ;; features. | 17 ;; features. |
20 | 18 |
21 ;; ⌘T - Go to File | 19 ;; ⌘T - Go to File |
22 ;; ⇧⌘T - Go to Symbol | 20 ;; ⇧⌘T - Go to Symbol |
23 ;; ⌘L - Go to Line | 21 ;; ⌘L - Go to Line |
24 ;; ⌘/ - Comment Line (or Selection/Region) | 22 ;; ⌘/ - Comment Line (or Selection/Region) |
25 ;; ⌘] - Shift Right (currently indents region) | 23 ;; ⌘] - Shift Right |
26 ;; ⌘[ - Shift Left (not yet implemented) | 24 ;; ⌘[ - Shift Left |
27 ;; ⌥⌘] - Align Assignments | 25 ;; ⌥⌘] - Align Assignments |
28 ;; ⌥⌘[ - Indent Line | 26 ;; ⌥⌘[ - Indent Line |
29 ;; ⌘RET - Insert Newline at Line's End | 27 ;; ⌘RET - Insert Newline at Line's End |
30 ;; ⌥⌘T - Reset File Cache (for Go to File, cache unused if using git/hg root) | 28 ;; ⌥⌘T - Reset File Cache (for Go to File, cache unused if using git/hg root, |
29 ;; but resets cached root location, useful if roots | |
30 ;; are nested) | |
31 | 31 |
32 ;; A "project" in textmate-mode is determined by the presence of | 32 ;; A "project" in textmate-mode is determined by the presence of |
33 ;; a .git directory. If no .git directory is found in your current | 33 ;; a .git directory. If no .git directory is found in your current |
34 ;; directory, textmate-mode will traverse upwards until one (or none) | 34 ;; directory, textmate-mode will traverse upwards until one (or none) |
35 ;; is found. The directory housing the .git directory is presumed | 35 ;; is found. The directory housing the .git directory is presumed |
52 ;; (textmate-mode) | 52 ;; (textmate-mode) |
53 | 53 |
54 ;;; Depends on imenu | 54 ;;; Depends on imenu |
55 (require 'imenu) | 55 (require 'imenu) |
56 | 56 |
57 ;;; Needed for flet | |
58 (eval-when-compile | |
59 (require 'cl)) | |
60 | |
57 ;;; Minor mode | 61 ;;; Minor mode |
58 | 62 |
59 (defvar textmate-use-file-cache t | 63 (defvar textmate-use-file-cache t |
60 "* Should `textmate-goto-file' keep a local cache of files?") | 64 "* Should `textmate-goto-file' keep a local cache of files?") |
61 | 65 |
71 `((ido ,(lambda (a) (progn (ido-mode a) (setq ido-enable-flex-matching t)))) | 75 `((ido ,(lambda (a) (progn (ido-mode a) (setq ido-enable-flex-matching t)))) |
72 (icicles ,(lambda (a) (icy-mode a))) | 76 (icicles ,(lambda (a) (icy-mode a))) |
73 (none ,(lambda (a) ()))) | 77 (none ,(lambda (a) ()))) |
74 "The list of functions to enable and disable completing minor modes") | 78 "The list of functions to enable and disable completing minor modes") |
75 | 79 |
76 (defvar *textmate-mode-map* (make-sparse-keymap)) | 80 (defvar *textmate-mode-map* |
81 (let ((map (make-sparse-keymap))) | |
82 (cond ((featurep 'aquamacs) | |
83 (define-key map [A-return] 'textmate-next-line) | |
84 (define-key map (kbd "A-M-t") 'textmate-clear-cache) | |
85 (define-key map (kbd "A-M-]") 'align) | |
86 (define-key map (kbd "A-M-[") 'indent-according-to-mode) | |
87 (define-key map (kbd "A-]") 'textmate-shift-right) | |
88 (define-key map (kbd "A-[") 'textmate-shift-left) | |
89 (define-key map (kbd "A-/") 'comment-or-uncomment-region-or-line) | |
90 (define-key map (kbd "A-t") 'textmate-goto-file) | |
91 (define-key map (kbd "A-T") 'textmate-goto-symbol)) | |
92 ((and (featurep 'mac-carbon) (eq window-system 'mac) mac-key-mode) | |
93 (define-key map [(alt meta return)] 'textmate-next-line) | |
94 (define-key map [(alt meta t)] 'textmate-clear-cache) | |
95 (define-key map [(alt meta \])] 'align) | |
96 (define-key map [(alt meta \[)] 'indent-according-to-mode) | |
97 (define-key map [(alt \])] 'textmate-shift-right) | |
98 (define-key map [(alt \[)] 'textmate-shift-left) | |
99 (define-key map [(meta /)] 'comment-or-uncomment-region-or-line) | |
100 (define-key map [(alt t)] 'textmate-goto-file) | |
101 (define-key map [(alt shift t)] 'textmate-goto-symbol)) | |
102 ((featurep 'ns) ;; Emacs.app | |
103 (define-key map [(super meta return)] 'textmate-next-line) | |
104 (define-key map [(super meta t)] 'textmate-clear-cache) | |
105 (define-key map [(super meta \])] 'align) | |
106 (define-key map [(super meta \[)] 'indent-according-to-mode) | |
107 (define-key map [(super \])] 'textmate-shift-right) | |
108 (define-key map [(super \[)] 'textmate-shift-left) | |
109 (define-key map [(super /)] 'comment-or-uncomment-region-or-line) | |
110 (define-key map [(super t)] 'textmate-goto-file) | |
111 (define-key map [(super shift t)] 'textmate-goto-symbol)) | |
112 (t ;; Any other version | |
113 (define-key map [(meta return)] 'textmate-next-line) | |
114 (define-key map [(control c)(control t)] 'textmate-clear-cache) | |
115 (define-key map [(control c)(control a)] 'align) | |
116 (define-key map [(control tab)] 'textmate-shift-right) | |
117 (define-key map [(control shift tab)] 'textmate-shift-left) | |
118 (define-key map [(control c)(control k)] 'comment-or-uncomment-region-or-line) | |
119 (define-key map [(meta t)] 'textmate-goto-file) | |
120 (define-key map [(meta shift t)] 'textmate-goto-symbol))) | |
121 map)) | |
122 | |
123 | |
77 (defvar *textmate-project-root* nil) | 124 (defvar *textmate-project-root* nil) |
78 (defvar *textmate-project-files* '()) | 125 (defvar *textmate-project-files* '()) |
126 | |
79 (defvar *textmate-gf-exclude* | 127 (defvar *textmate-gf-exclude* |
80 "/\\.|vendor|fixtures|tmp|log|build|\\.xcodeproj|\\.nib|\\.framework|\\.app|\\.pbproj|\\.pbxproj|\\.xcode|\\.xcodeproj|\\.bundle") | 128 "/\\.|vendor|fixtures|tmp|log|build|\\.xcodeproj|\\.nib|\\.framework|\\.app|\\.pbproj|\\.pbxproj|\\.xcode|\\.xcodeproj|\\.bundle|\\.pyc") |
81 | 129 |
82 (defvar *textmate-keybindings-list* `((textmate-next-line | 130 (defvar *textmate-project-roots* |
83 [A-return] [M-return]) | 131 '(".git" ".hg" "Rakefile" "Makefile" "README" "build.xml")) |
84 (textmate-clear-cache | 132 |
85 ,(kbd "A-M-t") [(control c)(control t)]) | 133 (defvar *textmate-vcs-exclude* nil |
86 (align | 134 "string to give to grep -V to exclude some VCS paths from being grepped." |
87 ,(kbd "A-M-]") [(control c)(control a)]) | 135 ) |
88 (indent-according-to-mode | |
89 ,(kbd "A-M-[") nil) | |
90 (indent-region | |
91 ,(kbd "A-]") [(control tab)]) | |
92 (comment-or-uncomment-region-or-line | |
93 ,(kbd "A-/") [(control c)(control k)]) | |
94 (textmate-goto-file | |
95 ,(kbd "A-t") [(meta t)]) | |
96 (textmate-goto-symbol | |
97 ,(kbd "A-T") [(meta T)]) | |
98 (textmate-toggle-camel-case | |
99 ,(kbd "C-_") [(control _)]))) | |
100 | |
101 (defvar *textmate-project-root-p* | |
102 #'(lambda (coll) (or (member ".git" coll) | |
103 (member ".hg" coll) | |
104 )) | |
105 "*Lambda that, given a collection of directory entries, returns | |
106 non-nil if it represents the project root.") | |
107 | 136 |
108 (defvar *textmate-find-in-project-default* nil) | 137 (defvar *textmate-find-in-project-default* nil) |
138 | |
109 (defvar *textmate-find-in-project-type-default* nil) | 139 (defvar *textmate-find-in-project-type-default* nil) |
110 | 140 |
111 ;;; Bindings | 141 ;;; Bindings |
112 | 142 |
113 (defun textmate-ido-fix () | 143 (defun textmate-ido-fix () |
114 "Add up/down keybindings for ido." | 144 "Add up/down keybindings for ido." |
115 (define-key ido-completion-map [up] 'ido-prev-match) | 145 (define-key ido-completion-map [up] 'ido-prev-match) |
116 (define-key ido-completion-map [down] 'ido-next-match)) | 146 (define-key ido-completion-map [down] 'ido-next-match)) |
117 | 147 |
118 (defun textmate-bind-keys () | |
119 (add-hook 'ido-setup-hook 'textmate-ido-fix) | |
120 | |
121 ; weakness until i figure out how to do this right | |
122 (when (boundp 'osx-key-mode-map) | |
123 (define-key osx-key-mode-map (kbd "A-t") 'textmate-goto-file) | |
124 (define-key osx-key-mode-map (kbd "A-T") 'textmate-goto-symbol)) | |
125 | |
126 (let ((member) (i 0) (access (if (boundp 'aquamacs-version) 'cadr 'caddr))) | |
127 (setq member (nth i *textmate-keybindings-list*)) | |
128 (while member | |
129 (if (funcall access member) | |
130 (define-key *textmate-mode-map* (funcall access member) (car member))) | |
131 (setq member (nth i *textmate-keybindings-list*)) | |
132 (setq i (+ i 1))))) | |
133 | |
134 (defun textmate-completing-read (&rest args) | 148 (defun textmate-completing-read (&rest args) |
135 (let ((reading-fn (cadr (assoc textmate-completing-library *textmate-completing-function-alist*)))) | 149 (let ((reading-fn (cadr (assoc textmate-completing-library *textmate-completing-function-alist*)))) |
136 (apply (symbol-function reading-fn) args))) | 150 (apply (symbol-function reading-fn) args))) |
137 | 151 |
152 ;;; allow-line-as-region-for-function adds an "-or-line" version of | |
153 ;;; 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 | |
155 ;;; and is licensed under the GPL | |
156 | |
157 (defmacro allow-line-as-region-for-function (orig-function) | |
158 `(defun ,(intern (concat (symbol-name orig-function) "-or-line")) | |
159 () | |
160 ,(format "Like `%s', but acts on the current line if mark is not active." orig-function) | |
161 (interactive) | |
162 (if mark-active | |
163 (call-interactively (function ,orig-function)) | |
164 (save-excursion | |
165 ;; define a region (temporarily) -- so any C-u prefixes etc. are preserved. | |
166 (beginning-of-line) | |
167 (set-mark (point)) | |
168 (end-of-line) | |
169 (call-interactively (function ,orig-function)))))) | |
170 | |
171 (defun textmate-define-comment-line () | |
172 "Add or-line (un)comment function if not already defined" | |
173 (unless (fboundp 'comment-or-uncomment-region-or-line) | |
174 (allow-line-as-region-for-function comment-or-uncomment-region))) | |
175 | |
138 ;;; Commands | 176 ;;; Commands |
139 | 177 |
140 (defun textmate-next-line () | 178 (defun textmate-next-line () |
141 (interactive) | 179 (interactive) |
142 (end-of-line) | 180 (end-of-line) |
143 (newline-and-indent)) | 181 (newline-and-indent)) |
144 | 182 |
145 ;; http://chopmo.blogspot.com/2008/09/quickly-jumping-to-symbols.html | 183 ;; http://chopmo.blogspot.com/2008/09/quickly-jumping-to-symbols.html |
146 (defun textmate-goto-symbol () | 184 (defun textmate-goto-symbol () |
147 "Will update the imenu index and then use ido to select a symbol to navigate to" | 185 "Update the imenu index and then use ido to select a symbol to navigate to. |
186 Symbols matching the text at point are put first in the completion list." | |
148 (interactive) | 187 (interactive) |
149 (imenu--make-index-alist) | 188 (imenu--make-index-alist) |
150 (let ((name-and-pos '()) | 189 (let ((name-and-pos '()) |
151 (symbol-names '())) | 190 (symbol-names '())) |
152 (flet ((addsymbols (symbol-list) | 191 (flet ((addsymbols (symbol-list) |
167 | 206 |
168 (unless (or (null position) (null name)) | 207 (unless (or (null position) (null name)) |
169 (add-to-list 'symbol-names name) | 208 (add-to-list 'symbol-names name) |
170 (add-to-list 'name-and-pos (cons name position)))))))) | 209 (add-to-list 'name-and-pos (cons name position)))))))) |
171 (addsymbols imenu--index-alist)) | 210 (addsymbols imenu--index-alist)) |
172 (let* ((selected-symbol (textmate-completing-read "Symbol: " symbol-names)) | 211 ;; If there are matching symbols at point, put them at the beginning of `symbol-names'. |
212 (let ((symbol-at-point (thing-at-point 'symbol))) | |
213 (when symbol-at-point | |
214 (let* ((regexp (concat (regexp-quote symbol-at-point) "$")) | |
215 (matching-symbols (delq nil (mapcar (lambda (symbol) | |
216 (if (string-match regexp symbol) symbol)) | |
217 symbol-names)))) | |
218 (when matching-symbols | |
219 (sort matching-symbols (lambda (a b) (> (length a) (length b)))) | |
220 (mapc (lambda (symbol) (setq symbol-names (cons symbol (delete symbol symbol-names)))) | |
221 matching-symbols))))) | |
222 (let* ((selected-symbol (ido-completing-read "Symbol? " symbol-names)) | |
173 (position (cdr (assoc selected-symbol name-and-pos)))) | 223 (position (cdr (assoc selected-symbol name-and-pos)))) |
174 (goto-char position)))) | 224 (goto-char position)))) |
175 | 225 |
176 (defun textmate-goto-file () | 226 (defun textmate-goto-file () |
177 (interactive) | 227 (interactive) |
178 (let ((root (textmate-project-root))) | 228 (let ((root (textmate-project-root))) |
179 (when (null root) | 229 (when (null root) |
180 (error "Can't find any .git directory")) | 230 (error |
231 (concat | |
232 "Can't find a sutiable project root (" | |
233 (string-join " " *textmate-project-roots* ) | |
234 ")"))) | |
181 (find-file | 235 (find-file |
182 (concat | 236 (concat |
183 (expand-file-name root) "/" | 237 (expand-file-name root) "/" |
184 (textmate-completing-read | 238 (textmate-completing-read |
185 "Find file: " | 239 "Find file: " |
217 (concat "cd " | 271 (concat "cd " |
218 root | 272 root |
219 " ; " | 273 " ; " |
220 (cond ((string= type "git") "git ls-files") | 274 (cond ((string= type "git") "git ls-files") |
221 ((string= type "hg") "hg manifest")) | 275 ((string= type "hg") "hg manifest")) |
276 (if *textmate-vcs-exclude* | |
277 (concat " | grep -v " (shell-quote-argument *textmate-vcs-exclude*)) | |
278 "") | |
222 " | xargs grep -nR " | 279 " | xargs grep -nR " |
223 (if pattern (concat " --include='" pattern "' ") "") | 280 (if pattern (concat " --include='" pattern "' ") "") |
224 " -- " | 281 " -- " |
225 (shell-quote-argument re))) | 282 (shell-quote-argument re))) |
226 (t (concat "cd " root "; egrep -nR --exclude='" | 283 (t (concat "cd " root "; egrep -nR --exclude='" |
269 (downcase-region start end) | 326 (downcase-region start end) |
270 ))))) | 327 ))))) |
271 | 328 |
272 ;;; Utilities | 329 ;;; Utilities |
273 | 330 |
274 (defun textmate-also-ignore (pattern) | |
275 "Also ignore PATTERN in project files." | |
276 (setq *textmate-gf-exclude* | |
277 (concat *textmate-gf-exclude* "|" pattern))) | |
278 | |
279 (defun textmate-project-root-type (root) | 331 (defun textmate-project-root-type (root) |
280 (cond ((member ".git" (directory-files root)) "git") | 332 (cond ((member ".git" (directory-files root)) "git") |
281 ((member ".hg" (directory-files root)) "hg") | 333 ((member ".hg" (directory-files root)) "hg") |
282 (t "unknown") | 334 (t "unknown") |
283 )) | 335 )) |
321 (if root | 373 (if root |
322 (setq *textmate-project-root* (expand-file-name (concat root "/"))) | 374 (setq *textmate-project-root* (expand-file-name (concat root "/"))) |
323 (setq *textmate-project-root* nil)))) | 375 (setq *textmate-project-root* nil)))) |
324 *textmate-project-root*) | 376 *textmate-project-root*) |
325 | 377 |
378 (defun root-match(root names) | |
379 (member (car names) (directory-files root))) | |
380 | |
381 (defun root-matches(root names) | |
382 (if (root-match root names) | |
383 (root-match root names) | |
384 (if (eq (length (cdr names)) 0) | |
385 'nil | |
386 (root-matches root (cdr names)) | |
387 ))) | |
388 | |
326 (defun textmate-find-project-root (&optional root) | 389 (defun textmate-find-project-root (&optional root) |
327 (when (null root) (setq root default-directory)) | 390 (when (null root) (setq root default-directory)) |
328 (cond | 391 (cond |
329 ((funcall *textmate-project-root-p* (directory-files root)) | 392 ((root-matches root *textmate-project-roots*) |
330 (expand-file-name root)) | 393 (expand-file-name root)) |
331 ((equal (expand-file-name root) "/") nil) | 394 ((equal (expand-file-name root) "/") nil) |
332 (t (textmate-find-project-root (concat (file-name-as-directory root) ".."))))) | 395 (t (textmate-find-project-root (concat (file-name-as-directory root) ".."))))) |
396 | |
397 (defun textmate-shift-right (&optional arg) | |
398 "Shift the line or region to the ARG places to the right. | |
399 | |
400 A place is considered `tab-width' character columns." | |
401 (interactive) | |
402 (let ((deactivate-mark nil) | |
403 (beg (or (and mark-active (region-beginning)) | |
404 (line-beginning-position))) | |
405 (end (or (and mark-active (region-end)) (line-end-position)))) | |
406 (indent-rigidly beg end (* (or arg 1) tab-width)))) | |
407 | |
408 (defun textmate-shift-left (&optional arg) | |
409 "Shift the line or region to the ARG places to the left." | |
410 (interactive) | |
411 (textmate-shift-right (* -1 (or arg 1)))) | |
333 | 412 |
334 ;;;###autoload | 413 ;;;###autoload |
335 (define-minor-mode textmate-mode "TextMate Emulation Minor Mode" | 414 (define-minor-mode textmate-mode "TextMate Emulation Minor Mode" |
336 :lighter " mate" :global t :keymap *textmate-mode-map* | 415 :lighter " mate" :global t :keymap *textmate-mode-map* |
337 (textmate-bind-keys) | 416 (add-hook 'ido-setup-hook 'textmate-ido-fix) |
417 (textmate-define-comment-line) | |
338 ; activate preferred completion library | 418 ; activate preferred completion library |
339 (dolist (mode *textmate-completing-minor-mode-alist*) | 419 (dolist (mode *textmate-completing-minor-mode-alist*) |
340 (if (eq (car mode) textmate-completing-library) | 420 (if (eq (car mode) textmate-completing-library) |
341 (funcall (cadr mode) t) | 421 (funcall (cadr mode) t) |
342 (when (fboundp | 422 (when (fboundp |