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)