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