Add ruby stuff
[emacs.git] / .emacs.d / elisp / local / inf-ruby.el
1 ;;; inf-ruby.el --- Run a Ruby process in a buffer
2
3 ;; Copyright (C) 1999-2008 Yukihiro Matsumoto, Nobuyoshi Nakada
4
5 ;; Author: Yukihiro Matsumoto
6 ;; Nobuyoshi Nakada
7 ;; Cornelius Mika <cornelius.mika@gmail.com>
8 ;; Dmitry Gutov <dgutov@yandex.ru>
9 ;; Kyle Hargraves <pd@krh.me>
10 ;; URL: http://github.com/nonsequitur/inf-ruby
11 ;; Created: 8 April 1998
12 ;; Keywords: languages ruby
13 ;; Version: 2.3.2
14
15 ;; This program is free software: you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation, either version 3 of the License, or
18 ;; (at your option) any later version.
19
20 ;; This program is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 ;; GNU General Public License for more details.
24
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28 ;; This file is not part of GNU Emacs.
29
30 ;;; Commentary:
31 ;;
32 ;; inf-ruby provides a REPL buffer connected to a Ruby subprocess.
33 ;;
34 ;; If you're installing manually, you'll need to:
35 ;; * drop the file somewhere on your load path (perhaps ~/.emacs.d)
36 ;; * Add the following lines to your .emacs file:
37 ;;
38 ;; (autoload 'inf-ruby "inf-ruby" "Run an inferior Ruby process" t)
39 ;; (add-hook 'ruby-mode-hook 'inf-ruby-minor-mode)
40 ;;
41 ;; Or, for enh-ruby-mode:
42 ;;
43 ;; (add-hook 'enh-ruby-mode-hook 'inf-ruby-minor-mode)
44 ;;
45 ;; Installation via ELPA interface does the above for you
46 ;; automatically.
47 ;;
48 ;; Additionally, consider adding
49 ;;
50 ;; (add-hook 'after-init-hook 'inf-ruby-switch-setup)
51 ;;
52 ;; to your init file to easily switch from common Ruby compilation
53 ;; modes to interact with a debugger.
54
55 ;;; Code:
56
57 (require 'comint)
58 (require 'compile)
59 (require 'ruby-mode)
60 (require 'thingatpt)
61
62 (defgroup inf-ruby nil
63 "Run Ruby process in a buffer"
64 :group 'languages)
65
66 (defcustom inf-ruby-prompt-read-only t
67 "If non-nil, the prompt will be read-only.
68
69 Also see the description of `ielm-prompt-read-only'.")
70
71 (defvar inf-ruby-default-implementation "ruby"
72 "Which Ruby implementation to use if none is specified.")
73
74 (defconst inf-ruby-prompt-format
75 (concat
76 (mapconcat
77 #'identity
78 '("\\(^%s> *\\)" ; Simple
79 "\\(^(rdb:1) *\\)" ; Debugger
80 "\\(^\\(irb([^)]+)" ; IRB default
81 "\\([[0-9]+] \\)?[Pp]ry ?([^)]+)" ; Pry
82 "\\(jruby-\\|JRUBY-\\)?[1-9]\\.[0-9]\\.[0-9]+\\(-?p?[0-9]+\\)?" ; RVM
83 "^rbx-head\\)") ; RVM continued
84 "\\|")
85 ;; Statement and nesting counters, common to the last four.
86 " ?[0-9:]* ?%s *\\)")
87 "Format string for the prompt regexp pattern.
88 Two placeholders: first char in the Simple prompt, and the last
89 graphical char in all other prompts.")
90
91 (defvar inf-ruby-first-prompt-pattern (format inf-ruby-prompt-format ">" ">")
92 "First prompt regex pattern of Ruby interpreter.")
93
94 (defvar inf-ruby-prompt-pattern (format inf-ruby-prompt-format "[?>]" "[\]>*\"'/`]")
95 "Prompt regex pattern of Ruby interpreter.")
96
97 (defvar inf-ruby-mode-hook nil
98 "Hook for customizing `inf-ruby-mode'.")
99
100 (defvar inf-ruby-mode-map
101 (let ((map (copy-keymap comint-mode-map)))
102 (define-key map (kbd "C-c C-l") 'ruby-load-file)
103 (define-key map (kbd "C-x C-e") 'ruby-send-last-sexp)
104 (define-key map (kbd "TAB") 'inf-ruby-complete)
105 (define-key map (kbd "C-x C-q") 'inf-ruby-maybe-switch-to-compilation)
106 map)
107 "Mode map for `inf-ruby-mode'.")
108
109 (defvar inf-ruby-implementations
110 '(("ruby" . "irb --prompt default -r irb/completion")
111 ("jruby" . "jruby -S irb --prompt default -r irb/completion")
112 ("rubinius" . "rbx -r irb/completion")
113 ("yarv" . "irb1.9 -r irb/completion")
114 ("macruby" . "macirb -r irb/completion")
115 ("pry" . "pry"))
116 "An alist of ruby implementations to irb executable names.")
117
118 ;;;###autoload
119 (defvar ruby-source-modes '(ruby-mode enh-ruby-mode)
120 "Used to determine if a buffer contains Ruby source code.
121 If it's loaded into a buffer that is in one of these major modes, it's
122 considered a ruby source file by `ruby-load-file'.
123 Used by these commands to determine defaults.")
124
125 (defvar ruby-prev-l/c-dir/file nil
126 "Caches the last (directory . file) pair.
127 Caches the last pair used in the last `ruby-load-file' command.
128 Used for determining the default in the
129 next one.")
130
131 (defvar inf-ruby-at-top-level-prompt-p t)
132
133 (defconst inf-ruby-error-regexp-alist
134 '(("SyntaxError: \\(?:compile error\n\\)?\\([^\(].*\\):\\([1-9][0-9]*\\):" 1 2)
135 ("^\tfrom \\([^\(].*\\):\\([1-9][0-9]*\\)\\(:in `.*'\\)?$" 1 2)))
136
137 ;;;###autoload
138 (defun inf-ruby-setup-keybindings ()
139 "Hook up `inf-ruby-minor-mode' to each of `ruby-source-modes'."
140 (warn "`inf-ruby-setup-keybindings' is deprecated, please don't use it anymore.")
141 (warn "If you're using `inf-ruby' from Git, please look up the new usage instructions."))
142
143 (make-obsolete 'inf-ruby-setup-keybindings 'add-hook "2.3.1")
144
145 (defvar inf-ruby-minor-mode-map
146 (let ((map (make-sparse-keymap)))
147 (define-key map (kbd "C-M-x") 'ruby-send-definition)
148 (define-key map (kbd "C-x C-e") 'ruby-send-last-sexp)
149 (define-key map (kbd "C-c C-b") 'ruby-send-block)
150 (define-key map (kbd "C-c M-b") 'ruby-send-block-and-go)
151 (define-key map (kbd "C-c C-x") 'ruby-send-definition)
152 (define-key map (kbd "C-c M-x") 'ruby-send-definition-and-go)
153 (define-key map (kbd "C-c C-r") 'ruby-send-region)
154 (define-key map (kbd "C-c M-r") 'ruby-send-region-and-go)
155 (define-key map (kbd "C-c C-z") 'ruby-switch-to-inf)
156 (define-key map (kbd "C-c C-l") 'ruby-load-file)
157 (define-key map (kbd "C-c C-s") 'inf-ruby)
158 map))
159
160 ;;;###autoload
161 (define-minor-mode inf-ruby-minor-mode
162 "Minor mode for interacting with the inferior process buffer.
163
164 The following commands are available:
165
166 \\{inf-ruby-minor-mode-map}"
167 :lighter "" :keymap inf-ruby-minor-mode-map)
168
169 (defvar inf-ruby-buffer nil "Current Ruby process buffer.")
170
171 (defun inf-ruby-mode ()
172 "Major mode for interacting with an inferior Ruby REPL process.
173
174 A simple IRB process can be fired up with \\[inf-ruby].
175
176 To launch a REPL with project-specific console instead, type
177 \\[inf-ruby-console-auto]. It recognizes several
178 project types, including Rails, gems and anything with `racksh'
179 in their Gemfile.
180
181 Customization: When entered, this mode runs `comint-mode-hook' and
182 `inf-ruby-mode-hook' (in that order).
183
184 You can send text to the inferior Ruby process from other buffers containing
185 Ruby source.
186
187 `ruby-switch-to-inf' switches the current buffer to the ruby process buffer.
188 `ruby-send-definition' sends the current definition to the ruby process.
189 `ruby-send-region' sends the current region to the ruby process.
190 `ruby-send-definition-and-go' and `ruby-send-region-and-go'
191 switch to the ruby process buffer after sending their text.
192
193 Commands:
194 `RET' after the end of the process' output sends the text from the
195 end of process to point.
196 `RET' before the end of the process' output copies the sexp ending at point
197 to the end of the process' output, and sends it.
198 `DEL' converts tabs to spaces as it moves back.
199 `TAB' completes the input at point. IRB, Pry and Bond completion is supported.
200 `C-M-q' does `TAB' on each line starting within following expression.
201 Paragraphs are separated only by blank lines. # start comments.
202 If you accidentally suspend your process, use \\[comint-continue-subjob]
203 to continue it.
204
205 The following commands are available:
206
207 \\{inf-ruby-mode-map}"
208 (interactive)
209 (let ((orig-mode-line-process mode-line-process))
210 (comint-mode)
211 (when orig-mode-line-process
212 (setq mode-line-process orig-mode-line-process)))
213 (setq comint-prompt-regexp inf-ruby-prompt-pattern)
214 (ruby-mode-variables)
215 (setq major-mode 'inf-ruby-mode)
216 (setq mode-name "Inf-Ruby")
217 (use-local-map inf-ruby-mode-map)
218 (add-hook 'comint-output-filter-functions 'inf-ruby-output-filter nil t)
219 (setq comint-get-old-input 'inf-ruby-get-old-input)
220 (set (make-local-variable 'compilation-error-regexp-alist)
221 inf-ruby-error-regexp-alist)
222 (set (make-local-variable 'comint-prompt-read-only) inf-ruby-prompt-read-only)
223 (when (eq system-type 'windows-nt)
224 (setq comint-process-echoes t))
225 (compilation-shell-minor-mode t)
226 (run-hooks 'inf-ruby-mode-hook))
227
228 (defun inf-ruby-output-filter (output)
229 "Check if the current prompt is a top-level prompt."
230 (unless (zerop (length output))
231 (setq inf-ruby-at-top-level-prompt-p
232 (string-match inf-ruby-first-prompt-pattern
233 (car (last (split-string output "\n")))))))
234
235 ;; adapted from replace-in-string in XEmacs (subr.el)
236 (defun inf-ruby-remove-in-string (str regexp)
237 "Remove all matches in STR for REGEXP and returns the new string."
238 (let ((rtn-str "") (start 0) match prev-start)
239 (while (setq match (string-match regexp str start))
240 (setq prev-start start
241 start (match-end 0)
242 rtn-str (concat rtn-str (substring str prev-start match))))
243 (concat rtn-str (substring str start))))
244
245 (defun inf-ruby-get-old-input ()
246 "Snarf the sexp ending at point."
247 (save-excursion
248 (let ((end (point)))
249 (re-search-backward inf-ruby-first-prompt-pattern)
250 (inf-ruby-remove-in-string (buffer-substring (point) end)
251 inf-ruby-prompt-pattern))))
252
253 ;;;###autoload
254 (defun inf-ruby (&optional impl)
255 "Run an inferior Ruby process in a buffer.
256 With prefix argument, prompts for which Ruby implementation
257 \(from the list `inf-ruby-implementations') to use. Runs the
258 hooks `inf-ruby-mode-hook' \(after the `comint-mode-hook' is
259 run)."
260
261 (interactive (list (if current-prefix-arg
262 (completing-read "Ruby Implementation: "
263 (mapc #'car inf-ruby-implementations))
264 inf-ruby-default-implementation)))
265 (setq impl (or impl "ruby"))
266
267 (let ((command (cdr (assoc impl inf-ruby-implementations))))
268 (run-ruby command impl)))
269
270 ;;;###autoload
271 (defun run-ruby (&optional command name)
272 "Run an inferior Ruby process, input and output via buffer `*NAME*'.
273 If there is a process already running in `*NAME*', switch to that buffer.
274
275 NAME defaults to \"ruby\". COMMAND defaults to the default entry
276 in `inf-ruby-implementations'.
277
278 \(Type \\[describe-mode] in the process buffer for the list of commands.)"
279
280 (interactive)
281 (setq command (or command (cdr (assoc inf-ruby-default-implementation
282 inf-ruby-implementations))))
283 (setq name (or name "ruby"))
284
285 (if (not (comint-check-proc inf-ruby-buffer))
286 (let ((commandlist (split-string-and-unquote command))
287 (process-environment process-environment))
288 ;; http://debbugs.gnu.org/15775
289 (setenv "PAGER" (executable-find "cat"))
290 (set-buffer (apply 'make-comint name (car commandlist)
291 nil (cdr commandlist)))
292 (inf-ruby-mode)))
293 (pop-to-buffer (setq inf-ruby-buffer (format "*%s*" name))))
294
295 (defun inf-ruby-proc ()
296 "Return the current inferior Ruby process.
297
298 See variable `inf-ruby-buffer'."
299 (or (get-buffer-process (if (eq major-mode 'inf-ruby-mode)
300 (current-buffer)
301 inf-ruby-buffer))
302 (error "No current process. See variable inf-ruby-buffer")))
303
304 ;; These commands are added to the ruby-mode keymap:
305
306 (defconst ruby-send-terminator "--inf-ruby-%x-%d-%d-%d--"
307 "Template for irb here document terminator.
308 Must not contain ruby meta characters.")
309
310 (defconst inf-ruby-eval-binding
311 (concat "(IRB.conf[:MAIN_CONTEXT] && IRB.conf[:MAIN_CONTEXT].workspace.binding) || "
312 "(defined?(Pry) && Pry.toplevel_binding)"))
313
314 (defconst ruby-eval-separator "")
315
316 (defun ruby-send-region (start end)
317 "Send the current region to the inferior Ruby process."
318 (interactive "r")
319 (let (term (file (or buffer-file-name (buffer-name))) line)
320 (save-excursion
321 (save-restriction
322 (widen)
323 (goto-char start)
324 (setq line (+ start (forward-line (- start)) 1))
325 (goto-char start)
326 (while (progn
327 (setq term (apply 'format ruby-send-terminator (random) (current-time)))
328 (re-search-forward (concat "^" (regexp-quote term) "$") end t)))))
329 ;; compilation-parse-errors parses from second line.
330 (save-excursion
331 (let ((m (process-mark (inf-ruby-proc))))
332 (set-buffer (marker-buffer m))
333 (goto-char m)
334 (insert ruby-eval-separator "\n")
335 (set-marker m (point))))
336 (comint-send-string (inf-ruby-proc) (format "eval <<'%s', %s, %S, %d\n"
337 term inf-ruby-eval-binding
338 file line))
339 (comint-send-region (inf-ruby-proc) start end)
340 (comint-send-string (inf-ruby-proc) (concat "\n" term "\n"))))
341
342 (defun ruby-send-definition ()
343 "Send the current definition to the inferior Ruby process."
344 (interactive)
345 (save-excursion
346 (ruby-end-of-defun)
347 (let ((end (point)))
348 (ruby-beginning-of-defun)
349 (ruby-send-region (point) end))))
350
351 (defun ruby-send-last-sexp ()
352 "Send the previous sexp to the inferior Ruby process."
353 (interactive)
354 (ruby-send-region (save-excursion (ruby-backward-sexp) (point)) (point)))
355
356 (defun ruby-send-block ()
357 "Send the current block to the inferior Ruby process."
358 (interactive)
359 (save-excursion
360 (ruby-end-of-block)
361 (end-of-line)
362 (let ((end (point)))
363 (ruby-beginning-of-block)
364 (ruby-send-region (point) end))))
365
366 (defun ruby-switch-to-inf (eob-p)
367 "Switch to the ruby process buffer.
368 With argument, positions cursor at end of buffer."
369 (interactive "P")
370 (if (and inf-ruby-buffer (get-buffer inf-ruby-buffer))
371 (pop-to-buffer inf-ruby-buffer)
372 (error "No current process buffer, see variable inf-ruby-buffer"))
373 (cond (eob-p
374 (push-mark)
375 (goto-char (point-max)))))
376
377 (defun ruby-send-region-and-go (start end)
378 "Send the current region to the inferior Ruby process.
379 Then switch to the process buffer."
380 (interactive "r")
381 (ruby-send-region start end)
382 (ruby-switch-to-inf t))
383
384 (defun ruby-send-definition-and-go ()
385 "Send the current definition to the inferior Ruby.
386 Then switch to the process buffer."
387 (interactive)
388 (ruby-send-definition)
389 (ruby-switch-to-inf t))
390
391 (defun ruby-send-block-and-go ()
392 "Send the current block to the inferior Ruby.
393 Then switch to the process buffer."
394 (interactive)
395 (ruby-send-block)
396 (ruby-switch-to-inf t))
397
398 (defun ruby-load-file (file-name)
399 "Load a Ruby file into the inferior Ruby process."
400 (interactive (comint-get-source "Load Ruby file: " ruby-prev-l/c-dir/file
401 ruby-source-modes t)) ;; T because LOAD needs an exact name
402 (comint-check-source file-name) ; Check to see if buffer needs saved.
403 (setq ruby-prev-l/c-dir/file (cons (file-name-directory file-name)
404 (file-name-nondirectory file-name)))
405 (comint-send-string (inf-ruby-proc) (concat "(load \""
406 file-name
407 "\"\)\n")))
408
409 (defun ruby-escape-single-quoted (str)
410 "Escape single quotes, double quotes and newlines in STR."
411 (replace-regexp-in-string "'" "\\\\'"
412 (replace-regexp-in-string "\n" "\\\\n"
413 (replace-regexp-in-string "\\\\" "\\\\\\\\" str))))
414
415 (defun inf-ruby-completions (expr)
416 "Return a list of completions for the Ruby expression starting with EXPR."
417 (let* ((proc (inf-ruby-proc))
418 (line (buffer-substring (save-excursion (beginning-of-thing 'line))
419 (point)))
420 (comint-filt (process-filter proc))
421 (kept "") completions
422 ;; Guard against running completions in parallel:
423 inf-ruby-at-top-level-prompt-p)
424 (set-process-filter proc (lambda (proc string) (setq kept (concat kept string))))
425 (unwind-protect
426 (let ((completion-snippet
427 (format (concat "proc { |expr, line| "
428 "if defined?(Pry.config) then "
429 "completor = Pry.config.completer"
430 ".build_completion_proc(binding, defined?(_pry_) ? _pry_ : Pry.new)"
431 " elsif defined?(Bond.agent) && Bond.started? then "
432 "completor = Bond.agent"
433 " end ? (puts completor.call(expr, line).compact) : "
434 "if defined?(IRB::InputCompletor::CompletionProc) then "
435 "puts IRB::InputCompletor::CompletionProc.call(expr).compact "
436 "end }.call('%s', '%s')\n")
437 (ruby-escape-single-quoted expr)
438 (ruby-escape-single-quoted line))))
439 (process-send-string proc completion-snippet)
440 (while (and (not (string-match inf-ruby-prompt-pattern kept))
441 (accept-process-output proc 2)))
442 (setq completions (butlast (split-string kept "\r?\n") 2))
443 ;; Subprocess echoes output on Windows and OS X.
444 (when (and completions (string= (concat (car completions) "\n") completion-snippet))
445 (setq completions (cdr completions))))
446 (set-process-filter proc comint-filt))
447 completions))
448
449 (defconst inf-ruby-ruby-expr-break-chars " \t\n\"\'`><,;|&{(")
450
451 (defun inf-ruby-completion-bounds-of-expr-at-point ()
452 "Return bounds of expression at point to complete."
453 (save-excursion
454 (let ((end (point)))
455 (skip-chars-backward (concat "^" inf-ruby-ruby-expr-break-chars))
456 (cons (point) end))))
457
458 (defun inf-ruby-completion-expr-at-point ()
459 "Return expression at point to complete."
460 (let ((bounds (inf-ruby-completion-bounds-of-expr-at-point)))
461 (buffer-substring (car bounds) (cdr bounds))))
462
463 (defun inf-ruby-completion-at-point ()
464 "Retrieve the list of completions and prompt the user.
465 Returns the selected completion or nil."
466 (if inf-ruby-at-top-level-prompt-p
467 (let* ((expr (inf-ruby-completion-expr-at-point))
468 (completions (inf-ruby-completions expr)))
469 (if completions
470 (if (= (length completions) 1)
471 (car completions)
472 (completing-read "possible completions: "
473 completions nil t expr))))
474 (message "Completion aborted: Not at a top-level prompt")
475 nil))
476
477 (defun inf-ruby-complete ()
478 "Complete the Ruby code at point.
479 Uses the first one available of Pry, Bond and the default IRB
480 completion."
481 (interactive)
482 (let ((replacement (inf-ruby-completion-at-point)))
483 (when replacement
484 (inf-ruby-complete-replace-expr replacement))))
485
486 (defun inf-ruby-complete-replace-expr (str)
487 "Replace expression at point with STR."
488 (let ((bounds (inf-ruby-completion-bounds-of-expr-at-point)))
489 (delete-region (car bounds) (cdr bounds)))
490 (insert str))
491
492 (defun inf-ruby-complete-or-tab ()
493 "Complete the Ruby code at point or call `indent-for-tab-command'."
494 (interactive)
495 (let ((replacement (inf-ruby-completion-at-point)))
496 (if (not replacement)
497 (call-interactively 'indent-for-tab-command)
498 (inf-ruby-complete-replace-expr replacement))))
499
500 (defvar inf-ruby-orig-compilation-mode nil
501 "Original compilation mode before switching to `inf-ruby-mode'.")
502
503 (defvar inf-ruby-orig-process-filter nil
504 "Original process filter before switching to `inf-ruby-mode`.")
505
506 (defun inf-ruby-switch-from-compilation ()
507 "Make the buffer writable and switch to `inf-ruby-mode'.
508 Recommended for use when the program being executed enters
509 interactive mode, i.e. hits a debugger breakpoint."
510 (interactive)
511 (setq buffer-read-only nil)
512 (buffer-enable-undo)
513 (let ((mode major-mode))
514 (inf-ruby-mode)
515 (make-local-variable 'inf-ruby-orig-compilation-mode)
516 (setq inf-ruby-orig-compilation-mode mode))
517 (let ((proc (get-buffer-process (current-buffer))))
518 (when proc
519 (make-local-variable 'inf-ruby-orig-process-filter)
520 (setq inf-ruby-orig-process-filter (process-filter proc))
521 (set-process-filter proc 'comint-output-filter))
522 (when (looking-back inf-ruby-prompt-pattern (line-beginning-position))
523 (let ((line (match-string 0)))
524 (delete-region (match-beginning 0) (point))
525 (comint-output-filter proc line)))))
526
527 (defun inf-ruby-maybe-switch-to-compilation ()
528 "Switch to compilation mode this buffer was in before
529 `inf-ruby-switch-from-compilation' was called, if it was.
530 Otherwise, just toggle read-only status."
531 (interactive)
532 (if inf-ruby-orig-compilation-mode
533 (let ((orig-mode-line-process mode-line-process)
534 (proc (get-buffer-process (current-buffer)))
535 (filter inf-ruby-orig-process-filter))
536 (funcall inf-ruby-orig-compilation-mode)
537 (setq mode-line-process orig-mode-line-process)
538 (when proc
539 (set-process-filter proc filter)))
540 (toggle-read-only)))
541
542 ;;;###autoload
543 (defun inf-ruby-switch-setup ()
544 "Modify `rspec-compilation-mode' and `ruby-compilation-mode'
545 keymaps to bind `inf-ruby-switch-from-compilation' to `ะก-x C-q'."
546 (eval-after-load 'rspec-mode
547 '(define-key rspec-compilation-mode-map (kbd "C-x C-q")
548 'inf-ruby-switch-from-compilation))
549 (eval-after-load 'ruby-compilation
550 '(define-key ruby-compilation-mode-map (kbd "C-x C-q")
551 'inf-ruby-switch-from-compilation)))
552
553 (defvar inf-ruby-console-patterns-alist
554 '(("config/application.rb" . rails)
555 ("*.gemspec" . gem)
556 ("Gemfile" . default))
557 "Mapping from file name patterns to name symbols.
558 `inf-ruby-console-auto' walks up from the current directory until
559 one of the patterns matches, then calls `inf-ruby-console-NAME',
560 passing it the found directory.")
561
562 (defun inf-ruby-console-match (dir)
563 "Find matching console command for DIR, if any."
564 (catch 'type
565 (dolist (pair inf-ruby-console-patterns-alist)
566 (let ((default-directory dir))
567 (when (file-expand-wildcards (car pair))
568 (throw 'type (cdr pair)))))))
569
570 ;;;###autoload
571 (defun inf-ruby-console-auto ()
572 "Run the appropriate Ruby console command.
573 The command and and the directory to run it from are detected
574 automatically."
575 (interactive)
576 (let* ((dir (locate-dominating-file default-directory
577 #'inf-ruby-console-match))
578 (type (inf-ruby-console-match dir))
579 (fun (intern (format "inf-ruby-console-%s" type))))
580 (unless type (error "No matching directory found"))
581 (funcall fun dir)))
582
583 ;;;###autoload
584 (defun inf-ruby-console-rails (dir)
585 "Run Rails console in DIR."
586 (interactive "D")
587 (let ((default-directory dir))
588 (run-ruby "rails console" "rails")))
589
590 ;;;###autoload
591 (defun inf-ruby-console-gem (dir)
592 "Run IRB console for the gem in DIR.
593 The main module should be loaded automatically. If DIR contains a
594 Gemfile, it should use the `gemspec' instruction."
595 (interactive "D")
596 (let* ((default-directory dir)
597 (base-command (if (file-exists-p "Gemfile")
598 "bundle exec irb"
599 "irb -I lib"))
600 files)
601 (unless (file-exists-p "lib")
602 (error "The directory must contain a 'lib' subdirectory"))
603 (dolist (item (directory-files "lib"))
604 (unless (file-directory-p item)
605 (setq files (cons item files))))
606 (run-ruby (concat base-command " "
607 ;; If there are several files under 'lib'
608 ;; (unlikely), load them all.
609 (mapconcat
610 (lambda (file)
611 (concat " -r " (file-name-sans-extension file)))
612 files
613 ""))
614 "gem")))
615
616 ;;;###autoload
617 (defun inf-ruby-console-default (dir)
618 "Run racksh, custom console.rb, or just IRB, in DIR."
619 (interactive "D")
620 (let ((default-directory dir))
621 (unless (file-exists-p "Gemfile")
622 (error "The directory must contain a Gemfile"))
623 (cond
624 ((with-temp-buffer
625 (insert-file-contents "Gemfile")
626 (re-search-forward "[\"']racksh[\"']" nil t))
627 (run-ruby "bundle exec racksh" "racksh"))
628 ((file-exists-p "console.rb")
629 (run-ruby "ruby console.rb" "console.rb"))
630 (t
631 (run-ruby "bundle console")))))
632
633 ;;;###autoload (dolist (mode ruby-source-modes) (add-hook (intern (format "%s-hook" mode)) 'inf-ruby-minor-mode))
634
635 (provide 'inf-ruby)
636 ;;; inf-ruby.el ends here