/ emacs.d / inf-ruby.el
inf-ruby.el
  1  ;;; -*-Emacs-Lisp-*-
  2  ;;;
  3  ;;;  $Id$
  4  ;;;  $Author$
  5  ;;;  $Date$
  6  ;;;
  7  ;;; Inferior Ruby Mode - ruby process in a buffer.
  8  ;;;                      adapted from cmuscheme.el
  9  ;;;
 10  ;;; Usage:
 11  ;;;
 12  ;;; (0) check ruby-program-name variable that can run your environment.
 13  ;;;
 14  ;;; (1) modify .emacs to use ruby-mode 
 15  ;;;     for example :
 16  ;;;
 17  ;;;    (autoload 'ruby-mode "ruby-mode"
 18  ;;;      "Mode for editing ruby source files" t)
 19  ;;;    (setq auto-mode-alist
 20  ;;;          (append '(("\\.rb$" . ruby-mode)) auto-mode-alist))
 21  ;;;    (setq interpreter-mode-alist (append '(("ruby" . ruby-mode))
 22  ;;;    				     interpreter-mode-alist))
 23  ;;;    
 24  ;;; (2) set to load inf-ruby and set inf-ruby key definition in ruby-mode.
 25  ;;;
 26  ;;;    (autoload 'run-ruby "inf-ruby"
 27  ;;;      "Run an inferior Ruby process")
 28  ;;;    (autoload 'inf-ruby-keys "inf-ruby"
 29  ;;;      "Set local key defs for inf-ruby in ruby-mode")
 30  ;;;    (add-hook 'ruby-mode-hook
 31  ;;;          '(lambda ()
 32  ;;;             (inf-ruby-keys)
 33  ;;;    ))
 34  ;;;
 35  ;;; HISTORY
 36  ;;; senda -  8 Apr 1998: Created.
 37  ;;;	 $Log$
 38  ;;;	 Revision 1.7  2004/07/27 08:11:36  matz
 39  ;;;	 * eval.c (rb_eval): copy on write for argument local variable
 40  ;;;	   assignment.
 41  ;;;
 42  ;;;	 * eval.c (assign): ditto.
 43  ;;;
 44  ;;;	 * eval.c (rb_call0): update ruby_frame->argv with the default
 45  ;;;	   value used for the optional arguments.
 46  ;;;
 47  ;;;	 * object.c (Init_Object): "===" calls rb_obj_equal() directly.
 48  ;;;	   [ruby-list:39937]
 49  ;;;
 50  ;;;	 Revision 1.6  2002/09/07 14:35:46  nobu
 51  ;;;	 * misc/inf-ruby.el (inferior-ruby-error-regexp-alist): regexp
 52  ;;;	   alist for error message from ruby.
 53  ;;;	
 54  ;;;	 * misc/inf-ruby.el (inferior-ruby-mode): fixed for Emacs.
 55  ;;;	
 56  ;;;	 * misc/inf-ruby.el (ruby-send-region): compilation-parse-errors
 57  ;;;	   doesn't parse first line, so insert separators before each
 58  ;;;	   evaluations.
 59  ;;;	
 60  ;;;	 Revision 1.5  2002/08/19 10:05:47  nobu
 61  ;;;	 * misc/inf-ruby.el (inf-ruby-keys): ruby-send-definition
 62  ;;;	   conflicted with ruby-insert-end.
 63  ;;;	
 64  ;;;	 * misc/inf-ruby.el (inferior-ruby-mode): compilation-minor-mode.
 65  ;;;	
 66  ;;;	 * misc/inf-ruby.el (ruby-send-region): send as here document to
 67  ;;;	   adjust source file/line.  [ruby-talk:47113], [ruby-dev:17965]
 68  ;;;	
 69  ;;;	 * misc/inf-ruby.el (ruby-send-terminator): added to make unique
 70  ;;;	   terminator.
 71  ;;;	
 72  ;;;	 Revision 1.4  2002/01/29 07:16:09  matz
 73  ;;;	 * file.c (rb_stat_rdev_major): added. [new]
 74  ;;;	
 75  ;;;	 * file.c (rb_stat_rdev_minor): added. [new]
 76  ;;;	
 77  ;;;	 * file.c (rb_stat_inspect): print mode in octal.
 78  ;;;	
 79  ;;;	 Revision 1.3  1999/12/01 09:24:18  matz
 80  ;;;	 19991201
 81  ;;;	
 82  ;;;	 Revision 1.2  1999/08/13 05:45:18  matz
 83  ;;;	 1.4.0
 84  ;;;	
 85  ;;;	 Revision 1.1.1.1.2.1  1999/07/15 07:59:59  matz
 86  ;;;	 990715
 87  ;;;	
 88  ;;;	 Revision 1.1.1.1  1999/01/20 04:59:36  matz
 89  ;;;	 ruby 1.3 cycle
 90  ;;;	
 91  ;;;	 Revision 1.1.2.1  1998/12/16 07:30:36  matz
 92  ;;;	 first public release of 1.1d (pre1.2) series
 93  ;;;	
 94  ;;;	 Revision 1.4  1998/05/20 02:45:58  senda
 95  ;;;	 default program to irb
 96  ;;;
 97  ;;;	 Revision 1.3  1998/04/10 04:11:30  senda
 98  ;;;	 modification by Matsumoto san (1.1b9_09)
 99  ;;;	 remove-in-string defined
100  ;;;	 global variable :
101  ;;;	 	 inferior-ruby-first-prompt-pattern
102  ;;;	       inferior-ruby-prompt-pattern
103  ;;;	 defined
104  ;;;
105  ;;;	 Revision 1.2  1998/04/09 07:53:42  senda
106  ;;;	 remove M-C-x in inferior-ruby-mode
107  ;;;
108  ;;;	 Revision 1.1  1998/04/09 07:28:36  senda
109  ;;;	 Initial revision
110  ;;;
111  ;;;
112  
113  (require 'comint)
114  (require 'compile)
115  (require 'ruby-mode)
116  
117  ;;
118  ;; you may change these variables
119  ;;
120  ;(defvar ruby-program-name "rbc --noreadline"
121  ;  "*Program invoked by the run-ruby command")
122  ;
123  ;(defvar inferior-ruby-first-prompt-pattern "^rbc0> *"
124  ;  "first prompt regex pattern of ruby interpreter.")
125  ;
126  ;(defvar inferior-ruby-prompt-pattern "^\\(rbc.[>*\"'] *\\)+"
127  ;  "prompt regex pattern of ruby interpreter.")
128  
129  ;;;; for irb
130  (defvar ruby-program-name "irb --inf-ruby-mode"
131    "*Program invoked by the run-ruby command")
132  
133  (defvar inferior-ruby-first-prompt-pattern "^irb(.*)[0-9:]+0> *"
134    "first prompt regex pattern of ruby interpreter.")
135  
136  (defvar inferior-ruby-prompt-pattern "^\\(irb(.*)[0-9:]+[>*\"'] *\\)+"
137    "prompt regex pattern of ruby interpreter.")
138  
139  ;;
140  ;; mode variables
141  ;;
142  (defvar inferior-ruby-mode-hook nil
143    "*Hook for customising inferior-ruby mode.")
144  (defvar inferior-ruby-mode-map nil
145    "*Mode map for inferior-ruby-mode")
146  
147  (defconst inferior-ruby-error-regexp-alist
148         '(("SyntaxError: compile error\n^\\([^\(].*\\):\\([1-9][0-9]*\\):" 1 2)
149  	 ("^\tfrom \\([^\(].*\\):\\([1-9][0-9]*\\)\\(:in `.*'\\)?$" 1 2)))
150  
151  (cond ((not inferior-ruby-mode-map)
152         (setq inferior-ruby-mode-map
153  	     (copy-keymap comint-mode-map))
154  ;       (define-key inferior-ruby-mode-map "\M-\C-x" ;gnu convention
155  ;	           'ruby-send-definition)
156  ;       (define-key inferior-ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
157         (define-key inferior-ruby-mode-map "\C-c\C-l" 'ruby-load-file)
158  ))
159  
160  (defun inf-ruby-keys ()
161    "Set local key defs for inf-ruby in ruby-mode"
162    (define-key ruby-mode-map "\M-\C-x" 'ruby-send-definition)
163  ;  (define-key ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
164    (define-key ruby-mode-map "\C-c\C-b" 'ruby-send-block)
165    (define-key ruby-mode-map "\C-c\M-b" 'ruby-send-block-and-go)
166    (define-key ruby-mode-map "\C-c\C-x" 'ruby-send-definition)
167    (define-key ruby-mode-map "\C-c\M-x" 'ruby-send-definition-and-go)
168    (define-key ruby-mode-map "\C-c\C-r" 'ruby-send-region)
169    (define-key ruby-mode-map "\C-c\M-r" 'ruby-send-region-and-go)
170    (define-key ruby-mode-map "\C-c\C-z" 'switch-to-ruby)
171    (define-key ruby-mode-map "\C-c\C-l" 'ruby-load-file)
172    (define-key ruby-mode-map "\C-c\C-s" 'run-ruby)
173  )
174  
175  (defvar ruby-buffer nil "current ruby (actually irb) process buffer.")
176  
177  (defun inferior-ruby-mode ()
178    "Major mode for interacting with an inferior ruby (irb) process.
179  
180  The following commands are available:
181  \\{inferior-ruby-mode-map}
182  
183  A ruby process can be fired up with M-x run-ruby.
184  
185  Customisation: Entry to this mode runs the hooks on comint-mode-hook and
186  inferior-ruby-mode-hook (in that order).
187  
188  You can send text to the inferior ruby process from other buffers containing
189  Ruby source.
190      switch-to-ruby switches the current buffer to the ruby process buffer.
191      ruby-send-definition sends the current definition to the ruby process.
192      ruby-send-region sends the current region to the ruby process.
193  
194      ruby-send-definition-and-go, ruby-send-region-and-go,
195          switch to the ruby process buffer after sending their text.
196  For information on running multiple processes in multiple buffers, see
197  documentation for variable ruby-buffer.
198  
199  Commands:
200  Return after the end of the process' output sends the text from the 
201      end of process to point.
202  Return before the end of the process' output copies the sexp ending at point
203      to the end of the process' output, and sends it.
204  Delete converts tabs to spaces as it moves back.
205  Tab indents for ruby; with argument, shifts rest
206      of expression rigidly with the current line.
207  C-M-q does Tab on each line starting within following expression.
208  Paragraphs are separated only by blank lines.  # start comments.
209  If you accidentally suspend your process, use \\[comint-continue-subjob]
210  to continue it."
211    (interactive)
212    (comint-mode)
213    ;; Customise in inferior-ruby-mode-hook
214    ;(setq comint-prompt-regexp "^[^>\n]*>+ *")
215    (setq comint-prompt-regexp inferior-ruby-prompt-pattern)
216    ;;(scheme-mode-variables)
217    (ruby-mode-variables)
218    (setq major-mode 'inferior-ruby-mode)
219    (setq mode-name "Inferior Ruby")
220    (setq mode-line-process '(":%s"))
221    (use-local-map inferior-ruby-mode-map)
222    (setq comint-input-filter (function ruby-input-filter))
223    (setq comint-get-old-input (function ruby-get-old-input))
224    (compilation-shell-minor-mode t)
225    (make-local-variable 'compilation-error-regexp-alist)
226    (setq compilation-error-regexp-alist inferior-ruby-error-regexp-alist)
227    (run-hooks 'inferior-ruby-mode-hook))
228  
229  (defvar inferior-ruby-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'"
230    "*Input matching this regexp are not saved on the history list.
231  Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.")
232  
233  (defun ruby-input-filter (str)
234    "Don't save anything matching inferior-ruby-filter-regexp"
235    (not (string-match inferior-ruby-filter-regexp str)))
236  
237  ;; adapted from replace-in-string in XEmacs (subr.el)
238  (defun remove-in-string (str regexp)
239    "Remove all matches in STR for REGEXP and returns the new string."
240    (let ((rtn-str "") (start 0) match prev-start)
241      (while (setq match (string-match regexp str start))
242        (setq prev-start start
243  	    start (match-end 0)
244  	    rtn-str (concat rtn-str (substring str prev-start match))))
245      (concat rtn-str (substring str start))))
246  
247  (defun ruby-get-old-input ()
248    "Snarf the sexp ending at point"
249    (save-excursion
250      (let ((end (point)))
251        (re-search-backward inferior-ruby-first-prompt-pattern)
252        (remove-in-string (buffer-substring (point) end)
253  			inferior-ruby-prompt-pattern)
254        )))
255  
256  (defun ruby-args-to-list (string)
257    (let ((where (string-match "[ \t]" string)))
258      (cond ((null where) (list string))
259  	  ((not (= where 0))
260  	   (cons (substring string 0 where)
261  		 (ruby-args-to-list (substring string (+ 1 where)
262  						 (length string)))))
263  	  (t (let ((pos (string-match "[^ \t]" string)))
264  	       (if (null pos)
265  		   nil
266  		 (ruby-args-to-list (substring string pos
267  						 (length string)))))))))
268  
269  (defun run-ruby (cmd)
270    "Run an inferior Ruby process, input and output via buffer *ruby*.
271  If there is a process already running in `*ruby*', switch to that buffer.
272  With argument, allows you to edit the command line (default is value
273  of `ruby-program-name').  Runs the hooks `inferior-ruby-mode-hook'
274  \(after the `comint-mode-hook' is run).
275  \(Type \\[describe-mode] in the process buffer for a list of commands.)"
276  
277    (interactive (list (if current-prefix-arg
278  			 (read-string "Run Ruby: " ruby-program-name)
279  			 ruby-program-name)))
280    (if (not (comint-check-proc "*ruby*"))
281        (let ((cmdlist (ruby-args-to-list cmd)))
282  	(set-buffer (apply 'make-comint "ruby" (car cmdlist)
283  			   nil (cdr cmdlist)))
284  	(inferior-ruby-mode)))
285    (setq ruby-program-name cmd)
286    (setq ruby-buffer "*ruby*")
287    (pop-to-buffer "*ruby*"))
288  
289  (defconst ruby-send-terminator "--inf-ruby-%x-%d-%d-%d--"
290    "Template for irb here document terminator.
291  Must not contain ruby meta characters.")
292  
293  (defconst ruby-eval-separator "")
294  
295  (defun ruby-send-region (start end)
296    "Send the current region to the inferior Ruby process."
297    (interactive "r")
298    (let (term (file (buffer-file-name)) line)
299      (save-excursion
300        (save-restriction
301  	(widen)
302  	(goto-char start)
303  	(setq line (+ start (forward-line (- start)) 1))
304  	(goto-char start)
305  	(while (progn
306  		 (setq term (apply 'format ruby-send-terminator (random) (current-time)))
307  		 (re-search-forward (concat "^" (regexp-quote term) "$") end t)))))
308      ;; compilation-parse-errors parses from second line.
309      (save-excursion
310        (let ((m (process-mark (ruby-proc))))
311  	(set-buffer (marker-buffer m))
312  	(goto-char m)
313  	(insert ruby-eval-separator "\n")
314  	(set-marker m (point))))
315      (comint-send-string (ruby-proc) (format "eval <<'%s', nil, %S, %d\n" term file line))
316      (comint-send-region (ruby-proc) start end)
317      (comint-send-string (ruby-proc) (concat "\n" term "\n"))))
318  
319  (defun ruby-send-definition ()
320    "Send the current definition to the inferior Ruby process."
321    (interactive)
322    (save-excursion
323      (ruby-end-of-defun)
324      (let ((end (point)))
325        (ruby-beginning-of-defun)
326        (ruby-send-region (point) end))))
327  
328  ;(defun ruby-send-last-sexp ()
329  ;  "Send the previous sexp to the inferior Ruby process."
330  ;  (interactive)
331  ;  (ruby-send-region (save-excursion (backward-sexp) (point)) (point)))
332  
333  (defun ruby-send-block ()
334    "Send the current block to the inferior Ruby process."
335    (interactive)
336    (save-excursion
337      (ruby-end-of-block)
338      (end-of-line)
339      (let ((end (point)))
340        (ruby-beginning-of-block)
341        (ruby-send-region (point) end))))
342  
343  (defun switch-to-ruby (eob-p)
344    "Switch to the ruby process buffer.
345  With argument, positions cursor at end of buffer."
346    (interactive "P")
347    (if (get-buffer ruby-buffer)
348        (pop-to-buffer ruby-buffer)
349        (error "No current process buffer. See variable ruby-buffer."))
350    (cond (eob-p
351  	 (push-mark)
352  	 (goto-char (point-max)))))
353  
354  (defun ruby-send-region-and-go (start end)
355    "Send the current region to the inferior Ruby process.
356  Then switch to the process buffer."
357    (interactive "r")
358    (ruby-send-region start end)
359    (switch-to-ruby t))
360  
361  (defun ruby-send-definition-and-go ()
362    "Send the current definition to the inferior Ruby. 
363  Then switch to the process buffer."
364    (interactive)
365    (ruby-send-definition)
366    (switch-to-ruby t))
367  
368  (defun ruby-send-block-and-go ()
369    "Send the current block to the inferior Ruby. 
370  Then switch to the process buffer."
371    (interactive)
372    (ruby-send-block)
373    (switch-to-ruby t))
374  
375  (defvar ruby-source-modes '(ruby-mode)
376    "*Used to determine if a buffer contains Ruby source code.
377  If it's loaded into a buffer that is in one of these major modes, it's
378  considered a ruby source file by ruby-load-file.
379  Used by these commands to determine defaults.")
380  
381  (defvar ruby-prev-l/c-dir/file nil
382    "Caches the last (directory . file) pair.
383  Caches the last pair used in the last ruby-load-file command.
384  Used for determining the default in the 
385  next one.")
386  
387  (defun ruby-load-file (file-name)
388    "Load a Ruby file into the inferior Ruby process."
389    (interactive (comint-get-source "Load Ruby file: " ruby-prev-l/c-dir/file
390  				  ruby-source-modes t)) ; T because LOAD 
391                                                            ; needs an exact name
392    (comint-check-source file-name) ; Check to see if buffer needs saved.
393    (setq ruby-prev-l/c-dir/file (cons (file-name-directory    file-name)
394  				       (file-name-nondirectory file-name)))
395    (comint-send-string (ruby-proc) (concat "(load \""
396  					    file-name
397  					    "\"\)\n")))
398  
399  (defun ruby-proc ()
400    "Returns the current ruby process. See variable ruby-buffer."
401    (let ((proc (get-buffer-process (if (eq major-mode 'inferior-ruby-mode)
402  				      (current-buffer)
403  				    ruby-buffer))))
404      (or proc
405  	(error "No current process. See variable ruby-buffer"))))
406  
407  ;;; Do the user's customisation...
408  
409  (defvar inf-ruby-load-hook nil
410    "This hook is run when inf-ruby is loaded in.
411  This is a good place to put keybindings.")
412  	
413  (run-hooks 'inf-ruby-load-hook)
414  
415  (provide 'inf-ruby)
416  
417  ;;; inf-ruby.el ends here