1
;;; rails-ui.el --- emacs-rails user interface
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$
10
;; $Id$
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
29
;;;;;;;;;; Some init code ;;;;;;;;;;
30
31
(defconst rails-minor-mode-log-menu-bar-map
32
  (let ((map (make-sparse-keymap)))
33
    (define-keys map
34
      ([test]      '("test.log"         . rails-log:open-test))
35
      ([pro]       '("production.log"   . rails-log:open-production))
36
      ([dev]       '("development.log"  . rails-log:open-development))
37
      ([separator] '("---"))
38
      ([open]      '("Open Log File..." . rails-log:open)))
39
    map))
40
41
(defconst rails-minor-mode-config-menu-bar-map
42
  (let ((map (make-sparse-keymap)))
43
    (define-keys map
44
      ([routes]      '("routes.rb" .
45
                       (lambda () (interactive)
46
                         (rails-core:find-file "config/routes.rb"))))
47
      ([environment] '("environment.rb" .
48
                       (lambda() (interactive)
49
                         (rails-core:find-file "config/environment.rb"))))
50
      ([database]    '("database.yml" .
51
                       (lambda() (interactive)
52
                         (rails-core:find-file "config/database.yml"))))
53
      ([boot]        '("boot.rb" .
54
                       (lambda() (interactive)
55
                         (rails-core:find-file "config/boot.rb"))))
56
      ([env] (cons "environments" (make-sparse-keymap "environments")))
57
      ([env test]        '("test.rb" .
58
                           (lambda() (interactive)
59
                             (rails-core:find-file "config/environments/test.rb"))))
60
      ([env production]  '("production.rb" .
61
                           (lambda() (interactive)
62
                             (rails-core:find-file "config/environments/production.rb"))))
63
      ([env development] '("development.rb" .
64
                           (lambda()(interactive)
65
                             (rails-core:find-file "config/environments/development.rb")))))
66
    map))
67
68
(defconst rails-minor-mode-nav-menu-bar-map
69
  (let ((map (make-sparse-keymap)))
70
    (define-keys map
71
      ([goto-fixtures]    '("Go to Fixtures"         . rails-nav:goto-fixtures))
72
      ([goto-plugins]     '("Go to Plugins"          . rails-nav:goto-plugins))
73
      ([goto-migrate]     '("Go to Migrations"       . rails-nav:goto-migrate))
74
      ([goto-layouts]     '("Go to Layouts"          . rails-nav:goto-layouts))
75
      ([goto-stylesheets] '("Go to Stylesheets"      . rails-nav:goto-stylesheets))
76
      ([goto-javascripts] '("Go to Javascripts"      . rails-nav:goto-javascripts))
77
      ([goto-helpers]     '("Go to Helpers"          . rails-nav:goto-helpers))
78
      ([goto-mailers]     '("Go to Mailers"          . rails-nav:goto-mailers))
79
      ([goto-observers]   '("Go to Observers"        . rails-nav:goto-observers))
80
      ([goto-unit-tests]  '("Go to Unit Tests"       . rails-nav:goto-unit-tests))
81
      ([goto-func-tests]  '("Go to Functional Tests" . rails-nav:goto-functional-tests))
82
      ([goto-models]      '("Go to Models"           . rails-nav:goto-models))
83
      ([goto-controllers] '("Go to Controllers"      . rails-nav:goto-controllers))
84
      ([goto-rspec-controllers] '("Go to RSpec Controllers" . rails-nav:goto-rspec-controllers))
85
      ([goto-rspec-lib]         '("Go to RSpec Lib"         . rails-nav:goto-rspec-lib))
86
      ([goto-rspec-models]      '("Go to RSpec Models"      . rails-nav:goto-rspec-models))
87
      ([goto-rspec-fixtures]    '("Go to RSpec Fixtures"    . rails-nav:goto-rspec-fixtures)))
88
    map))
89
90
(defconst rails-minor-mode-tests-menu-bar-map
91
  (let ((map (make-sparse-keymap)))
92
    (define-keys map
93
      ([kill-current] '(menu-item "Kill Test" rails-script:kill-script :enable (rails-script:running-p)))
94
      ([separator0]   '("--"))
95
      ([integration] '("Integration Tests" . rails-test:run-integration))
96
      ([unit]        '("Unit Tests"        . rails-test:run-units))
97
      ([functional]  '("Functional Tests"  . rails-test:run-functionals))
98
      ([recent]      '("Recent Tests"      . rails-test:run-recent))
99
      ([tests]       '("All"               . rails-test:run-all))
100
      ([separator1]   '("--"))
101
      ([toggle]      '(menu-item "Toggle Output Window" rails-script:toggle-output-window
102
                                 :enable (get-buffer rails-script:buffer-name)))
103
      ([run-current] '("Test Current Model/Controller/Mailer" . rails-test:run-current))
104
      ([run]         '("Run Tests ..."                        . rails-test:run)))
105
    map))
106
107
(defconst rails-minor-mode-db-menu-bar-map
108
  (let ((map (make-sparse-keymap)))
109
    (define-keys map
110
      ([clone-db]    '("Clone Development DB to Test DB" . rails-rake:clone-development-db-to-test-db))
111
      ([load-schema] '("Load schema.rb to DB"               . (lambda() (interactive) (rails-rake:task "db:schema:load"))))
112
      ([dump-schema] '("Dump DB to schema.rb"               . (lambda() (interactive) (rails-rake:task "db:schema:dump"))))
113
      ([sep]         '("--"))
114
      ([prev]        '("Migrate to Previous Version" . rails-rake:migrate-to-prev-version))
115
      ([version]     '("Migrate to Version ..."      . rails-rake:migrate-to-version))
116
      ([migrate]     '("Migrate"                     . rails-rake:migrate)))
117
    map))
118
119
(define-keys rails-minor-mode-menu-bar-map
120
121
  ([rails] (cons "RoR" (make-sparse-keymap "RubyOnRails")))
122
123
  ([rails rails-customize] '("Customize" . (lambda () (interactive) (customize-group 'rails))))
124
  ([rails separator0] '("--"))
125
  ([rails scm-status]        '("SCM Status"             . rails-scm-status-into-root))
126
  ([rails api-doc]           '("Rails API Doc at Point" . rails-browse-api-at-point))
127
  ([rails sql]               '("SQL Rails Buffer"       . rails-run-sql))
128
  ([rails tag]               '("Update TAGS File"       . rails-create-tags))
129
  ([rails ri]                '("Search Documentation"   . rails-search-doc))
130
  ([rails goto-file-by-line] '("Go to File by Line"     . rails-goto-file-on-current-line))
131
  ([rails switch-file-menu]  '("Switch file Menu..."    . rails-lib:run-secondary-switch))
132
  ([rails switch-file]       '("Switch File"            . rails-lib:run-primary-switch))
133
  ([rails separator1]        '("--"))
134
135
  ([rails scr] (cons "Scripts" (make-sparse-keymap "Scripts")))
136
  ([rails scr kill]    '(menu-item "Kill current script" rails-script:kill-script :enable (rails-script:running-p)))
137
  ([rails scr separator]  '("--"))
138
139
  ([rails scr gen] (cons "Generate" (make-sparse-keymap "Generate")))
140
  ([rails scr destr] (cons "Destroy" (make-sparse-keymap "Generators")))
141
142
  ([rails scr destr resource]   '("Resource"        . rails-script:destroy-resource))
143
  ([rails scr destr observer]   '("Observer"        . rails-script:destroy-observer))
144
  ([rails scr destr mailer]     '("Mailer"          . rails-script:destroy-mailer))
145
  ([rails scr destr plugin]     '("Plugin"          . rails-script:destroy-plugin))
146
  ([rails scr destr migration]  '("Migration"       . rails-script:destroy-migration))
147
  ([rails scr destr scaffold]   '("Scaffold"        . rails-script:destroy-scaffold))
148
  ([rails scr destr model]      '("Model"           . rails-script:destroy-model))
149
  ([rails scr destr controller] '("Controller"      . rails-script:destroy-controller))
150
  ([rails scr destr separator]  '("--"))
151
  ([rails scr destr run]        '("Run Destroy ..." . rails-script:destroy))
152
153
  ([rails scr gen resource]   '("Resource"         . rails-script:generate-resource))
154
  ([rails scr gen observer]   '("Observer"         . rails-script:generate-observer))
155
  ([rails scr gen mailer]     '("Mailer"           . rails-script:generate-mailer))
156
  ([rails scr gen plugin]     '("Plugin"           . rails-script:generate-plugin))
157
  ([rails scr gen migration]  '("Migration"        . rails-script:generate-migration))
158
  ([rails scr gen scaffold]   '("Scaffold"         . rails-script:generate-scaffold))
159
  ([rails scr gen model]      '("Model"            . rails-script:generate-model))
160
  ([rails scr gen controller] '("Controller"       . rails-script:generate-controller))
161
  ([rails scr gen separator]  '("--"))
162
  ([rails scr gen run]        '("Run Generate ..." . rails-script:generate))
163
164
  ([rails scr break]   '("Breakpointer"         . rails-script:breakpointer))
165
  ([rails scr console] '("Console"              . rails-script:console))
166
  ([rails scr rake]    '("Rake..."              . rails-rake:task))
167
168
  ([rails nav]    (cons "Navigation"    rails-minor-mode-nav-menu-bar-map))
169
  ([rails config] (cons "Configuration" rails-minor-mode-config-menu-bar-map))
170
  ([rails log]    (cons "Log Files"     rails-minor-mode-log-menu-bar-map))
171
172
  ([rails ws] (cons "WebServer" (make-sparse-keymap "WebServer")))
173
174
  ([rails ws use-webrick]  '(menu-item "Use WEBrick" (lambda() (interactive)
175
                                                       (rails-ws:switch-default-server-type "webrick"))
176
                                       :button (:toggle . (rails-ws:default-server-type-p "webrick"))))
177
  ([rails ws use-lighttpd] '(menu-item "Use Lighty" (lambda() (interactive)
178
                                                      (rails-ws:switch-default-server-type "lighttpd"))
179
                                       :button (:toggle . (rails-ws:default-server-type-p "lighttpd"))))
180
  ([rails ws use-mongrel]  '(menu-item "Use Mongrel" (lambda() (interactive)
181
                                                       (rails-ws:switch-default-server-type "mongrel"))
182
                                       :button (:toggle . (rails-ws:default-server-type-p "mongrel"))))
183
  ([rails ws use-thin]  '(menu-item "Use Thin" (lambda() (interactive)
184
                                                    (rails-ws:switch-default-server-type "thin"))
185
                                       :button (:toggle . (rails-ws:default-server-type-p "thin"))))
186
  ([rails ws separator] '("--"))
187
188
  ([rails ws brows]      '(menu-item "Open Browser..." rails-ws:open-browser-on-controller
189
                                     :enable (rails-ws:running-p)))
190
  ([rails ws auto-brows] '(menu-item "Open Browser on Current Action" rails-ws:auto-open-browser
191
                                     :enable (rails-ws:running-p)))
192
  ([rails ws url]        '(menu-item "Open Browser" rails-ws:open-browser
193
                                     :enable (rails-ws:running-p)))
194
  ([rails ws separator2] '("--"))
195
196
  ([rails ws test]        '(menu-item "Start Test" rails-ws:start-test
197
                                      :enable (not (rails-ws:running-p))))
198
  ([rails ws production]  '(menu-item "Start Production" rails-ws:start-production
199
                                      :enable (not (rails-ws:running-p))))
200
  ([rails ws development] '(menu-item "Start Development" rails-ws:start-development
201
                                      :enable (not (rails-ws:running-p))))
202
  ([rails ws separator3] '("--"))
203
204
  ([rails ws status]  '(menu-item "Print Status"                                     rails-ws:print-status))
205
  ([rails ws default] '(menu-item "Start/Stop Web Server (With Default Environment)" rails-ws:toggle-start-stop))
206
  )
207
208
(defcustom rails-minor-mode-prefix-key "\C-c"
209
  "Key prefix for rails minor mode."
210
  :group 'rails)
211
212
(defmacro rails-key (key)
213
  `(kbd ,(concat rails-minor-mode-prefix-key " " key)))
214
215
(defconst rails-minor-mode-test-current-method-key (rails-key "\C-c ,"))
216
217
(defvar rails-minor-mode-map
218
  (make-sparse-keymap))
219
220
(define-keys rails-minor-mode-map
221
  ([menu-bar] rails-minor-mode-menu-bar-map)
222
  ([menu-bar rails-tests] (cons "Tests" rails-minor-mode-tests-menu-bar-map))
223
  ([menu-bar rails-db] (cons "Database" rails-minor-mode-db-menu-bar-map))
224
225
  ;; Goto
226
  ((rails-key "\C-c g m") 'rails-nav:goto-models)
227
  ((rails-key "\C-c g c") 'rails-nav:goto-controllers)
228
  ((rails-key "\C-c g o") 'rails-nav:goto-observers)
229
  ((rails-key "\C-c g n") 'rails-nav:goto-mailers)
230
  ((rails-key "\C-c g h") 'rails-nav:goto-helpers)
231
  ((rails-key "\C-c g l") 'rails-nav:goto-layouts)
232
  ((rails-key "\C-c g s") 'rails-nav:goto-stylesheets)
233
  ((rails-key "\C-c g j") 'rails-nav:goto-javascripts)
234
  ((rails-key "\C-c g g") 'rails-nav:goto-migrate)
235
  ((rails-key "\C-c g p") 'rails-nav:goto-plugins)
236
  ((rails-key "\C-c g x") 'rails-nav:goto-fixtures)
237
  ((rails-key "\C-c g f") 'rails-nav:goto-functional-tests)
238
  ((rails-key "\C-c g u") 'rails-nav:goto-unit-tests)
239
  ((rails-key "\C-c g r c") 'rails-nav:goto-rspec-controllers)
240
  ((rails-key "\C-c g r f") 'rails-nav:goto-rspec-fixtures)
241
  ((rails-key "\C-c g r l") 'rails-nav:goto-rspec-lib)
242
  ((rails-key "\C-c g r m") 'rails-nav:goto-rspec-models)
243
244
  ;; Switch
245
  ((kbd "<M-S-up>")      'rails-lib:run-primary-switch)
246
  ((kbd "<M-S-down>")    'rails-lib:run-secondary-switch)
247
  ((rails-key "<up>")     'rails-lib:run-primary-switch)
248
  ((rails-key "<down>")   'rails-lib:run-secondary-switch)
249
  ((kbd "<C-return>")    'rails-goto-file-on-current-line)
250
251
  ;; Scripts & SQL
252
  ((rails-key "\C-c k")   'rails-script:kill-script)
253
  ((rails-key "\C-c e")   'rails-script:generate)
254
  ((rails-key "\C-c x")   'rails-script:destroy)
255
  ((rails-key "\C-c s c") 'rails-script:console)
256
  ((kbd "C-c C-s")        'rails-script:console)
257
  ((kbd "C-c C-z")        'rails-script:console)
258
  ((rails-key "\C-c s b") 'rails-script:breakpointer)
259
  ((rails-key "\C-c s s") 'rails-run-sql)
260
  ((rails-key "\C-c w s") 'rails-ws:toggle-start-stop)
261
  ((rails-key "\C-c w d") 'rails-ws:start-development)
262
  ((rails-key "\C-c w p") 'rails-ws:start-production)
263
  ((rails-key "\C-c w t") 'rails-ws:start-test)
264
  ((rails-key "\C-c w i") 'rails-ws:print-status)
265
  ((rails-key "\C-c w a") 'rails-ws:auto-open-browser)
266
267
  ;; Rails finds
268
  ((rails-key "\C-c f m") 'rails-find:models)
269
  ((rails-key "\C-c f c") 'rails-find:controller)
270
  ((rails-key "\C-c f h") 'rails-find:helpers)
271
  ((rails-key "\C-c f l") 'rails-find:layout)
272
  ((rails-key "\C-c f s") 'rails-find:stylesheets)
273
  ((rails-key "\C-c f j") 'rails-find:javascripts)
274
  ((rails-key "\C-c f g") 'rails-find:migrate)
275
  ((rails-key "\C-c f b") 'rails-find:lib)
276
  ((rails-key "\C-c f t") 'rails-find:tasks)
277
  ((rails-key "\C-c f v") 'rails-find:view)
278
  ((rails-key "\C-c f d") 'rails-find:db)
279
  ((rails-key "\C-c f p") 'rails-find:public)
280
  ((rails-key "\C-c f f") 'rails-find:fixtures)
281
  ((rails-key "\C-c f o") 'rails-find:config)
282
  ;; Spec finds
283
  ((rails-key "\C-c f r s") 'rails-find:spec)
284
  ((rails-key "\C-c f r c") 'rails-find:spec-controllers)
285
  ((rails-key "\C-c f r m") 'rails-find:spec-models)
286
  ((rails-key "\C-c f r h") 'rails-find:spec-helpers)
287
  ((rails-key "\C-c f r v") 'rails-find:spec-views)
288
  ((rails-key "\C-c f r f") 'rails-find:spec-fixtures)
289
290
  ((rails-key "\C-c d m") 'rails-rake:migrate)
291
  ((rails-key "\C-c d v") 'rails-rake:migrate-to-version)
292
  ((rails-key "\C-c d p") 'rails-rake:migrate-to-prev-version)
293
  ((rails-key "\C-c d u") 'rails-rake:migration-version-up)
294
  ((rails-key "\C-c d d") 'rails-rake:migration-version-down)
295
  ((rails-key "\C-c d t") 'rails-rake:clone-development-db-to-test-db)
296
297
  ;; Tests
298
  ((rails-key "\C-c r")   'rails-rake:task)
299
  ((rails-key "\C-c t")   'rails-test:run)
300
  ((rails-key "\C-c .")   'rails-test:run-current)
301
  ((rails-key "\C-c /")   'rails-test:rerun-single)
302
  ((rails-key "\C-c y i") 'rails-test:run-integration)
303
  ((rails-key "\C-c y u") 'rails-test:run-units)
304
  ((rails-key "\C-c y f") 'rails-test:run-functionals)
305
  ((rails-key "\C-c #")   'rails-test:run-recent)
306
  ((rails-key "\C-c y a") 'rails-test:run-all)
307
308
  ;; RSpec
309
  ((rails-key "\C-c z f") 'rails-spec:run-files)
310
  ((rails-key "\C-c z .") 'rails-spec:run-current)
311
  ((rails-key "\C-c z a") 'rails-spec:run-all)
312
  ((rails-key "\C-c z l") 'rails-spec:run-last)
313
  ((rails-key "\C-c z s") 'rails-spec:run-this-spec)
314
315
  ;; Log files
316
  ((rails-key "\C-c l")    'rails-log:open)
317
  ((rails-key "\C-c o t")    'rails-log:open-test)
318
  ((rails-key "\C-c o p")    'rails-log:open-production)
319
  ((rails-key "\C-c o d")    'rails-log:open-development)
320
321
  ;; Tags
322
  ((rails-key "\C-c \C-t") 'rails-create-tags)
323
324
  ;; Documentation
325
  ([f1]                  'rails-search-doc)
326
  ((kbd "<C-f1>")        'rails-browse-api-at-point)
327
  ((rails-key "<f1>")     'rails-browse-api)
328
  ((rails-key "/")        'rails-script:toggle-output-window)
329
330
  ;; Other
331
  ((rails-key "s")       'rails-grep-project)
332
  ((rails-key "C-M-z")   'rails-shell)
333
  ([f9]                  'rails-scm-status-into-root))
334
335
;; Global keys and menubar
336
337
(global-set-key (rails-key "\C-c j") 'rails-script:create-project)
338
339
(when-bind (map (lookup-key global-map  [menu-bar file]))
340
  (define-key-after
341
    map
342
    [create-rails-project]
343
    '("Create Rails Project" . rails-script:create-project) 'insert-file))
344
345
;; mode-line info
346
347
(defvar rails-ui:mode-line-script-name ""
348
  "A short name for the currently running script")
349
350
(defvar rails-ui:num-errors 0
351
  "Number of errors in the latest test task.")
352
(defvar rails-ui:num-failures 0
353
  "Number of failures in the latest test task.")
354
(defvar rails-ui:num-ok 0
355
  "Number of tests in the latest test task.")
356
357
(defun rails-ui:reset-error-count ()
358
  "Reset number of tests/errors/failures to 0."
359
  (setq rails-ui:num-ok 0
360
	rails-ui:num-errors 0
361
	rails-ui:num-failures 0))
362
363
(defvar rails-ui:mode-line '(:propertize (" " rails-ui:mode-line-script-string rails-ui:mode-line-test-results))
364
  "`rails-mode''s mode-line construct")
365
366
(put 'rails-ui:mode-line 'risky-local-variable t)
367
368
(defvar rails-ui:idle-script-line (propertize "idle"
369
					      'help-echo "No script running.\nmouse-1: toggle output window"
370
					      'mouse-face 'mode-line-highlight
371
					      'local-map '(keymap (mode-line keymap
372
							 (mouse-1 . rails-script:toggle-output-window)))))
373
374
(defvar rails-ui:mode-line-script-string
375
  '(:eval (if (rails-script:running-p)
376
	      (propertize rails-ui:mode-line-script-name
377
			  'help-echo (concat "running "
378
					     rails-script:running-script-name
379
					     "\nmouse-1: toggle output window\nmouse-2: kill script")
380
			  'face 'bold
381
			  'mouse-face 'mode-line-highlight
382
			  'local-map '(keymap (mode-line keymap
383
							 (mouse-1 . rails-script:toggle-output-window))
384
					      (mouse-2 . rails-script:kill-script)))
385
	    rails-ui:idle-script-line)))
386
387
(put 'rails-ui:mode-line-script-string 'risky-local-variable t)
388
389
(defcustom rails-ui:show-mode-line
390
  't
391
  "Show test and script status in the mode-line"
392
  :type 'boolean
393
  :group 'rails
394
  :tag "Rails show mode line")
395
396
(defvar rails-ui:mode-line-test-results
397
  '(:eval
398
    (let ((results (propertize
399
		    (format "%dE%dF%d" rails-ui:num-ok rails-ui:num-errors rails-ui:num-failures)
400
		    'help-echo (format "%d Tests, %d Errors, %d Failures."
401
				       rails-ui:num-ok
402
				       rails-ui:num-errors
403
				       rails-ui:num-failures))))
404
      (if (> (+ rails-ui:num-errors rails-ui:num-failures) 0)
405
	  (setq results (propertize results 'face 'compilation-error)))
406
      (concat " [" results "]"))))
407
408
(put 'rails-ui:mode-line-test-results 'risky-local-variable t)
409
410
(defun rails-ui:enable-mode-line ()
411
  "Add the rails mode-line to the global mode-string."
412
  (interactive)
413
  (unless (memq 'rails-ui:mode-line global-mode-string)
414
    (setq global-mode-string
415
	  (append global-mode-string '(rails-ui:mode-line)))))
416
417
(defun rails-ui:disable-mode-line ()
418
  "Remove the rails mode-line from the global mode-string."
419
  (interactive)
420
  (setq global-mode-string
421
	(remove 'rails-ui:mode-line global-mode-string)))
422
423
(add-hook 'rails-minor-mode-hook (lambda ()
424
				   (if rails-ui:show-mode-line
425
				       (rails-ui:enable-mode-line))))
426
427
(provide 'rails-ui)