/ emacs.d / rails / rails-navigation.el
rails-navigation.el
  1  ;;; rails-navigation.el --- emacs-rails navigation functions
  2  
  3  ;; Copyright (C) 2006 Dmitry Galinsky <dima dot exe at gmail dot com>
  4  
  5  ;; Authors: Dmitry Galinsky <dima dot exe at gmail dot com>,
  6  ;;          Rezikov Peter <crazypit13 (at) gmail.com>
  7  
  8  ;; Keywords: ruby rails languages oop
  9  ;; $URL: svn://rubyforge.org/var/svn/emacs-rails/trunk/rails-navigation.el $
 10  ;; $Id: rails-navigation.el 150 2007-03-29 20:48:17Z dimaexe $
 11  
 12  ;;; License
 13  
 14  ;; This program is free software; you can redistribute it and/or
 15  ;; modify it under the terms of the GNU General Public License
 16  ;; as published by the Free Software Foundation; either version 2
 17  ;; of the License, or (at your option) any later version.
 18  
 19  ;; This program is distributed in the hope that it will be useful,
 20  ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 21  ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 22  ;; GNU General Public License for more details.
 23  
 24  ;; You should have received a copy of the GNU General Public License
 25  ;; along with this program; if not, write to the Free Software
 26  ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 27  
 28  (defun rails-nav:create-goto-menu (items title &optional append-to-menu)
 29    (when append-to-menu
 30      (dolist (l append-to-menu items)
 31        (add-to-list 'items l t)))
 32    (let ((selected
 33           (when items
 34             (rails-core:menu
 35              (list title (cons title items))))))
 36      (if selected selected (message "No files found"))))
 37  
 38  (defun rails-nav:goto-file-with-menu (dir title &optional ext no-inflector append-to-menu)
 39    "Make a menu to choose files from and find-file it."
 40    (let* (file
 41           files
 42           (ext (if ext ext "rb"))
 43           (ext (concat "\\." ext "$"))
 44           (dir (rails-core:file dir)))
 45      (setq files (find-recursive-directory-relative-files dir "" ext))
 46      (setq files (sort files 'string<))
 47      (setq files (mapcar
 48                   #'(lambda(f)
 49                       (list
 50                        (if no-inflector f (rails-core:class-by-file f))
 51                        f))
 52                   files))
 53      (when-bind
 54       (selected (rails-nav:create-goto-menu files title append-to-menu))
 55       (if (symbolp selected)
 56           (apply selected (list))
 57         (rails-core:find-file-if-exist (concat dir selected))))))
 58  
 59  (defun rails-nav:goto-file-with-menu-from-list (items title func &optional append-to-menu)
 60    (when-bind
 61     (selected (rails-nav:create-goto-menu (list->alist items) title append-to-menu))
 62     (when-bind
 63      (file (apply func (list selected)))
 64      (rails-core:find-file-if-exist file))))
 65  
 66  (defun rails-nav:goto-controllers ()
 67    "Go to controllers."
 68    (interactive)
 69    (rails-nav:goto-file-with-menu-from-list
 70     (rails-core:controllers t)
 71     "Go to controller"
 72     'rails-core:controller-file))
 73  
 74  (defun rails-nav:goto-models ()
 75    "Go to models."
 76    (interactive)
 77    (rails-nav:goto-file-with-menu-from-list
 78     (rails-core:models)
 79     "Go to model.."
 80     'rails-core:model-file))
 81  
 82  (defun rails-nav:goto-functional-tests ()
 83    "Go to functional tests."
 84    (interactive)
 85    (rails-nav:goto-file-with-menu-from-list
 86     (rails-core:functional-tests)
 87     "Go to functional test."
 88     'rails-core:functional-test-file))
 89  
 90  (defun rails-nav:goto-unit-tests ()
 91    "Go to functional tests."
 92    (interactive)
 93    (rails-nav:goto-file-with-menu-from-list
 94     (rails-core:unit-tests)
 95     "Go to unit test."
 96     'rails-core:unit-test-file))
 97  
 98  (defun rails-nav:goto-observers ()
 99    "Go to observers."
100    (interactive)
101    (rails-nav:goto-file-with-menu-from-list
102     (rails-core:observers)
103     "Go to observer.."
104     'rails-core:observer-file))
105  
106  (defun rails-nav:goto-mailers ()
107    "Go to mailers."
108    (interactive)
109    (rails-nav:goto-file-with-menu-from-list
110     (rails-core:mailers)
111     "Go to mailers.."
112     'rails-core:mailer-file))
113  
114  (defun rails-nav:goto-migrate ()
115    "Go to migrations."
116    (interactive)
117    (rails-nav:goto-file-with-menu-from-list
118     (rails-core:migrations)
119     "Go to migrate.."
120     'rails-core:migration-file))
121  
122  (defun rails-nav:goto-helpers ()
123    "Go to helpers."
124    (interactive)
125    (rails-nav:goto-file-with-menu-from-list
126     (rails-core:helpers)
127     "Go to helper.."
128     'rails-core:helper-file))
129  
130  (defun rails-nav:goto-plugins ()
131    "Go to plugins."
132    (interactive)
133    (rails-nav:goto-file-with-menu-from-list
134     (rails-core:plugins)
135     "Go to plugin.."
136     (lambda (plugin)
137       (concat "vendor/plugins/" plugin "/init.rb"))))
138  
139  (defun rails-nav:create-new-layout (&optional name)
140    "Create a new layout."
141    (let ((name (or name (read-string "Layout name? "))))
142      (when name
143        (rails-core:find-file (rails-core:layout-file name))
144        (if (y-or-n-p "Insert initial template? ")
145            (insert rails-layout-template)))))
146  
147  (defun rails-nav:goto-layouts ()
148    "Go to layouts."
149    (interactive)
150    (let ((items (list (cons "--" "--")
151                       (cons "Create new layout" 'rails-nav:create-new-layout))))
152      (rails-nav:goto-file-with-menu-from-list
153       (rails-core:layouts)
154       "Go to layout.."
155       (lambda (l)
156         (if (stringp l)
157             (rails-core:layout-file l)
158           (apply l (list))))
159       items)))
160  
161  (defun rails-nav:goto-fixtures ()
162    "Go to fixtures."
163    (interactive)
164    (rails-nav:goto-file-with-menu-from-list
165     (rails-core:fixtures)
166     "Go to fixture.."
167     'rails-core:fixture-file))
168  
169  (defun rails-nav:goto-stylesheets ()
170    "Go to stylesheets."
171    (interactive)
172    (rails-nav:goto-file-with-menu "public/stylesheets/" "Go to stylesheet.." "css" t))
173  
174  (defun rails-nav:goto-javascripts ()
175    "Go to JavaScripts."
176    (interactive)
177    (rails-nav:goto-file-with-menu "public/javascripts/" "Go to stylesheet.." "js" t))
178  
179  ;;;;;;;;;; Goto file on current line ;;;;;;;;;;
180  
181  (defmacro* def-goto-line (name (&rest conditions) &rest body)
182    "Go to the file specified by the current line. Parses the
183  current line for a series of patterns."
184    (let ((line (gensym))
185          (field (gensym))
186          (prefix (gensym)))
187      `(progn
188         (defun ,name (,line ,prefix)
189           (block ,name
190             ,@(loop for (sexpr . map) in conditions
191                     collect
192                     `(when (string-match ,sexpr ,line)
193                        (let ,(loop for var-acc in map collect
194                                    (if (listp var-acc)
195                                        `(,(first var-acc) (match-string ,(second var-acc) ,line))
196                                      var-acc))
197                          (return-from ,name (progn ,@body))))))))))
198  
199  (defun rails-goto-file-on-current-line (prefix)
200    "Analyze a string (or ERb block) and open some file related with it.
201  For example, on a line with \"render :partial\" runing this
202  function will open the partial file.  The function works with
203  \"layout 'name'\", \"render/redirect-to [:action => 'name',]
204  [controller => 'n']\", stylesheet_link_tag and other common
205  patterns.
206  
207  Rules for actions/controllers:
208   If you are in a controller, the cursor will be placed on the controller action.
209   If you in view, the view file related to the action will be opened.
210   Use prefix before the command to change this navigation direction."
211    (interactive "P")
212    (rails-project:with-root
213     (root)
214     (save-match-data
215       (unless
216           (when-bind
217            (line (save-excursion
218                    (if (rails-core:rhtml-buffer-p)
219                        (rails-core:erb-block-string)
220                      (current-line-string))))
221            (loop for func in rails-on-current-line-gotos
222                  until (when (funcall func line prefix) (return t))))
223         (message "Can't switch to some file form this line.")))))
224  
225  (defvar rails-on-current-line-gotos
226    '(rails-line-->partial
227      rails-line-->action
228      rails-line-->controller+action
229      rails-line-->layout
230      rails-line-->stylesheet
231      rails-line-->js)
232    "Functions that will ne called to analyze the line when
233  rails-goto-file-on-current-line is run.")
234  
235  (def-goto-line rails-line-->stylesheet (("[ ]*stylesheet_link_tag[ ][\"']\\([^\"']*\\)[\"']"
236                                           (name 1)))
237    (rails-core:find-or-ask-to-create
238     (format "Stylesheet \"%s\" does not exist do you whant to create it? " name)
239     (rails-core:stylesheet-name name)))
240  
241  (def-goto-line rails-line-->partial (("\\([ ]*render\\|replace_html\\|insert_html\\).*:partial[ ]*=>[ ]*[\"']\\([^\"']*\\)[\"']"
242                                        (name 2)))
243    (rails-core:find-or-ask-to-create
244     (format "Partial \"%s\" does not exist do you whant to create it? " name)
245     (rails-core:partial-name name)))
246  
247  (def-goto-line rails-line-->action (("\\([ ]*render\\|replace_html\\|insert_html\\).*:action[ ]*=>[ ]*[\"'\:]\\([^\"']*\\)"
248                                       (name 2)))
249    (rails-core:find-or-ask-to-create
250     (format "View \"%s\" does not exist do you whant to create it? " name)
251     (rails-core:view-name name)))
252  
253  (def-goto-line rails-line-->layout (("^[ ]*layout[ ]*[\"']\\(.*\\)[\"']" (name 1)))
254    (let ((file-name (rails-core:layout-file name)))
255      (if (file-exists-p (rails-core:file file-name))
256          (rails-core:find-file file-name)
257        (rails-nav:create-new-layout name))))
258  
259  (def-goto-line rails-line-->js (("^[ ]*javascript_include_tag[ ]*[\"']\\(.*\\)[\"']"
260                                   (name  1)))
261    (rails-core:find-or-ask-to-create
262     (format "JavaScript file \"%s\" does not exist do you whant to create it? " name)
263     (rails-core:js-file name)))
264  
265  (defvar rails-line-to-controller/action-keywords
266    '("render" "redirect_to" "link_to" "form_tag" "start_form_tag" "render_component"
267      "form_remote_tag" "link_to_remote"))
268  
269  (defun rails-line-->controller+action (line prefix)
270    (when (loop for keyword in rails-line-to-controller/action-keywords
271                when (string-match (format "^[ ]*%s " keyword) line) do (return t))
272      (let (action controller)
273        (when (string-match ":action[ ]*=>[ ]*[\"']\\([^\"']*\\)[\"']" line)
274          (setf action (match-string 1 line)))
275        (when (string-match ":controller[ ]*=>[ ]*[\"']\\([^\"']*\\)[\"']" line)
276          (setf controller (match-string 1 line)))
277        (rails-controller-layout:switch-to-action-in-controller
278         (if controller controller
279           (rails-core:current-controller))
280         action))))
281  
282  (provide 'rails-navigation)