changes to the "literate programming" section of ob-worg
[org-mode.git] / util / inf-ruby.el
blob182d93a32d83cd7b00324a5d6cb96a01c4fd9fed
1 ;;; inf-ruby.el --- Run a ruby process in a buffer
3 ;; Copyright (C) 1999-2008 Yukihiro Matsumoto, Nobuyoshi Nakada
5 ;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada
6 ;; URL: http://www.emacswiki.org/cgi-bin/wiki/RubyMode
7 ;; Created: 8 April 1998
8 ;; Keywords: languages ruby
9 ;; Version: 2.1
10 ;; Package-Requires: ((ruby-mode "1.1"))
12 ;;; Commentary:
14 ;; inf-ruby.el provides a REPL buffer connected to an IRB subprocess.
16 ;; If you're installing manually, you'll need to:
17 ;; * drop the file somewhere on your load path (perhaps ~/.emacs.d)
18 ;; * Add the following lines to your .emacs file:
19 ;; (autoload 'inf-ruby "inf-ruby" "Run an inferior Ruby process" t)
20 ;; (autoload 'inf-ruby-keys "inf-ruby" "" t)
21 ;; (eval-after-load 'ruby-mode
22 ;; '(add-hook 'ruby-mode-hook 'inf-ruby-keys))
24 ;;; TODO:
26 ;; inferior-ruby-error-regexp-alist doesn't match this example
27 ;; SyntaxError: /home/eschulte/united/org/work/arf/arf/lib/cluster.rb:35: syntax error, unexpected '~', expecting kEND
28 ;; similarity = comparison_cache[m][n] ||= clusters[m] ~ clusters[n]
30 ;; M-p skips the first entry in the input ring.
33 (require 'comint)
34 (require 'compile)
35 (require 'ruby-mode)
37 (defvar inf-ruby-default-implementation "ruby"
38 "Which ruby implementation to use if none is specified.")
40 (defvar inf-ruby-first-prompt-pattern "^irb(.*)[0-9:]+0> *"
41 "First prompt regex pattern of ruby interpreter.")
43 (defvar inf-ruby-prompt-pattern "^\\(irb(.*)[0-9:]+[>*\"'] *\\)+"
44 "Prompt regex pattern of ruby interpreter.")
46 (defvar inf-ruby-mode-hook nil
47 "*Hook for customising inf-ruby mode.")
49 (defvar inf-ruby-mode-map
50 (let ((map (copy-keymap comint-mode-map)))
51 (define-key map (kbd "C-c C-l") 'inf-ruby-load-file)
52 (define-key map (kbd "C-x C-e") 'ruby-send-last-sexp)
53 (define-key map (kbd "TAB") 'inf-ruby-complete-or-tab)
54 map)
55 "*Mode map for inf-ruby-mode")
57 (defvar inf-ruby-implementations
58 '(("ruby" . "irb --inf-ruby-mode -r irb/completion")
59 ("jruby" . "jruby -S irb -r irb/completion")
60 ("rubinius" . "rbx -r irb/completion")
61 ("yarv" . "irb1.9 --inf-ruby-mode -r irb/completion")) ;; TODO: ironruby?
62 "An alist of ruby implementations to irb executable names.")
64 ;; TODO: do we need these two defvars?
65 (defvar ruby-source-modes '(ruby-mode)
66 "*Used to determine if a buffer contains Ruby source code.
67 If it's loaded into a buffer that is in one of these major modes, it's
68 considered a ruby source file by ruby-load-file.
69 Used by these commands to determine defaults.")
71 (defvar ruby-prev-l/c-dir/file nil
72 "Caches the last (directory . file) pair.
73 Caches the last pair used in the last ruby-load-file command.
74 Used for determining the default in the
75 next one.")
77 (defconst inf-ruby-error-regexp-alist
78 '(("SyntaxError: compile error\n^\\([^\(].*\\):\\([1-9][0-9]*\\):" 1 2)
79 ("^\tfrom \\([^\(].*\\):\\([1-9][0-9]*\\)\\(:in `.*'\\)?$" 1 2)))
81 ;;;###autoload
82 (defun inf-ruby-keys ()
83 "Set local key defs to invoke inf-ruby from ruby-mode."
84 (define-key ruby-mode-map "\M-\C-x" 'ruby-send-definition)
85 (define-key ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
86 (define-key ruby-mode-map "\C-c\C-b" 'ruby-send-block)
87 (define-key ruby-mode-map "\C-c\M-b" 'ruby-send-block-and-go)
88 (define-key ruby-mode-map "\C-c\C-x" 'ruby-send-definition)
89 (define-key ruby-mode-map "\C-c\M-x" 'ruby-send-definition-and-go)
90 (define-key ruby-mode-map "\C-c\C-r" 'ruby-send-region)
91 (define-key ruby-mode-map "\C-c\M-r" 'ruby-send-region-and-go)
92 (define-key ruby-mode-map "\C-c\C-z" 'ruby-switch-to-inf)
93 (define-key ruby-mode-map "\C-c\C-l" 'ruby-load-file)
94 (define-key ruby-mode-map "\C-c\C-s" 'inf-ruby))
96 (defvar inf-ruby-buffer nil "Current irb process buffer.")
98 (defun inf-ruby-mode ()
99 "Major mode for interacting with an inferior ruby (irb) process.
101 The following commands are available:
102 \\{inf-ruby-mode-map}
104 A ruby process can be fired up with M-x inf-ruby.
106 Customisation: Entry to this mode runs the hooks on comint-mode-hook and
107 inf-ruby-mode-hook (in that order).
109 You can send text to the inferior ruby process from other buffers containing
110 Ruby source.
111 ruby-switch-to-inf switches the current buffer to the ruby process buffer.
112 ruby-send-definition sends the current definition to the ruby process.
113 ruby-send-region sends the current region to the ruby process.
115 ruby-send-definition-and-go, ruby-send-region-and-go,
116 switch to the ruby process buffer after sending their text.
118 Commands:
119 Return after the end of the process' output sends the text from the
120 end of process to point.
121 Return before the end of the process' output copies the sexp ending at point
122 to the end of the process' output, and sends it.
123 Delete converts tabs to spaces as it moves back.
124 Tab indents for ruby; with arugment, shifts rest
125 of expression rigidly with the current line.
126 C-M-q does Tab on each line starting within following expression.
127 Paragraphs are separated only by blank lines. # start comments.
128 If you accidentally suspend your process, use \\[comint-continue-subjob]
129 to continue it."
130 (interactive)
131 (comint-mode)
132 (setq comint-prompt-regexp inf-ruby-prompt-pattern)
133 (ruby-mode-variables)
134 (setq major-mode 'inf-ruby-mode)
135 (setq mode-name "Inf-Ruby")
136 (setq mode-line-process '(":%s"))
137 (use-local-map inf-ruby-mode-map)
138 (setq comint-input-filter (function inf-ruby-input-filter))
139 (setq comint-get-old-input (function inf-ruby-get-old-input))
140 (make-local-variable 'compilation-error-regexp-alist)
141 (setq compilation-error-regexp-alist inf-ruby-error-regexp-alist)
142 (compilation-shell-minor-mode t)
143 (run-hooks 'inf-ruby-mode-hook))
145 (defvar inf-ruby-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'"
146 "*Input matching this regexp are not saved on the history list.
147 Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.")
149 (defun inf-ruby-input-filter (str)
150 "Don't save anything matching inf-ruby-filter-regexp"
151 (not (string-match inf-ruby-filter-regexp str)))
153 ;; adapted from replace-in-string in XEmacs (subr.el)
154 (defun inf-ruby-remove-in-string (str regexp)
155 "Remove all matches in STR for REGEXP and returns the new string."
156 (let ((rtn-str "") (start 0) match prev-start)
157 (while (setq match (string-match regexp str start))
158 (setq prev-start start
159 start (match-end 0)
160 rtn-str (concat rtn-str (substring str prev-start match))))
161 (concat rtn-str (substring str start))))
163 (defun inf-ruby-get-old-input ()
164 "Snarf the sexp ending at point"
165 (save-excursion
166 (let ((end (point)))
167 (re-search-backward inf-ruby-first-prompt-pattern)
168 (inf-ruby-remove-in-string (buffer-substring (point) end)
169 inf-ruby-prompt-pattern))))
171 ;;;###autoload
172 (defun inf-ruby (&optional impl)
173 "Run an inferior Ruby process in a buffer.
174 With prefix argument, prompts for which Ruby implementation
175 \(from the list `inf-ruby-implementations') to use. Runs the
176 hooks `inf-ruby-mode-hook' \(after the `comint-mode-hook' is
177 run)."
179 (interactive (list (if current-prefix-arg
180 (completing-read "Ruby Implementation: "
181 (mapc #'car inf-ruby-implementations))
182 inf-ruby-default-implementation)))
183 (setq impl (or impl "ruby"))
185 (let ((command (cdr (assoc impl inf-ruby-implementations))))
186 (run-ruby command impl)))
188 ;;;###autoload
189 (defun run-ruby (&optional command name)
190 "Run an inferior Ruby process, input and output via buffer *ruby*.
191 If there is a process already running in `*ruby*', switch to that buffer.
192 With argument, allows you to edit the command line (default is value
193 of `ruby-program-name'). Runs the hooks `inferior-ruby-mode-hook'
194 \(after the `comint-mode-hook' is run).
195 \(Type \\[describe-mode] in the process buffer for a list of commands.)"
197 (interactive)
198 (setq command (or command (cdr (assoc inf-ruby-default-implementation
199 inf-ruby-implementations))))
200 (setq name (or name "ruby"))
202 (if (not (comint-check-proc inf-ruby-buffer))
203 (let ((commandlist (split-string command)))
204 (set-buffer (apply 'make-comint name (car commandlist)
205 nil (cdr commandlist)))
206 (inf-ruby-mode)))
207 (pop-to-buffer (setq inf-ruby-buffer (format "*%s*" name))))
209 (defun inf-ruby-proc ()
210 "Returns the current IRB process. See variable inf-ruby-buffer."
211 (or (get-buffer-process (if (eq major-mode 'inf-ruby-mode)
212 (current-buffer)
213 inf-ruby-buffer))
214 (error "No current process. See variable inf-ruby-buffer")))
216 ;; These commands are added to the ruby-mode keymap:
218 (defconst ruby-send-terminator "--inf-ruby-%x-%d-%d-%d--"
219 "Template for irb here document terminator.
220 Must not contain ruby meta characters.")
222 (defconst ruby-eval-separator "")
224 (defun ruby-send-region (start end)
225 "Send the current region to the inferior Ruby process."
226 (interactive "r")
227 (let (term (file (buffer-file-name)) line)
228 (save-excursion
229 (save-restriction
230 (widen)
231 (goto-char start)
232 (setq line (+ start (forward-line (- start)) 1))
233 (goto-char start)
234 (while (progn
235 (setq term (apply 'format ruby-send-terminator (random) (current-time)))
236 (re-search-forward (concat "^" (regexp-quote term) "$") end t)))))
237 ;; compilation-parse-errors parses from second line.
238 (save-excursion
239 (let ((m (process-mark (inf-ruby-proc))))
240 (set-buffer (marker-buffer m))
241 (goto-char m)
242 (insert ruby-eval-separator "\n")
243 (set-marker m (point))))
244 (comint-send-string (inf-ruby-proc) (format "eval <<'%s', nil, %S, %d\n" term file line))
245 (comint-send-region (inf-ruby-proc) start end)
246 (comint-send-string (inf-ruby-proc) (concat "\n" term "\n"))))
248 (defun ruby-send-definition ()
249 "Send the current definition to the inferior Ruby process."
250 (interactive)
251 (save-excursion
252 (ruby-end-of-defun)
253 (let ((end (point)))
254 (ruby-beginning-of-defun)
255 (ruby-send-region (point) end))))
257 (defun ruby-send-last-sexp ()
258 "Send the previous sexp to the inferior Ruby process."
259 (interactive)
260 (ruby-send-region (save-excursion (backward-sexp) (point)) (point)))
262 (defun ruby-send-block ()
263 "Send the current block to the inferior Ruby process."
264 (interactive)
265 (save-excursion
266 (ruby-end-of-block)
267 (end-of-line)
268 (let ((end (point)))
269 (ruby-beginning-of-block)
270 (ruby-send-region (point) end))))
272 (defun ruby-switch-to-inf (eob-p)
273 "Switch to the ruby process buffer.
274 With argument, positions cursor at end of buffer."
275 (interactive "P")
276 (if (get-buffer inf-ruby-buffer)
277 (pop-to-buffer inf-ruby-buffer)
278 (error "No current process buffer. See variable inf-ruby-buffer."))
279 (cond (eob-p
280 (push-mark)
281 (goto-char (point-max)))))
283 (defun ruby-send-region-and-go (start end)
284 "Send the current region to the inferior Ruby process.
285 Then switch to the process buffer."
286 (interactive "r")
287 (ruby-send-region start end)
288 (ruby-switch-to-inf t))
290 (defun ruby-send-definition-and-go ()
291 "Send the current definition to the inferior Ruby.
292 Then switch to the process buffer."
293 (interactive)
294 (ruby-send-definition)
295 (ruby-switch-to-inf t))
297 (defun ruby-send-block-and-go ()
298 "Send the current block to the inferior Ruby.
299 Then switch to the process buffer."
300 (interactive)
301 (ruby-send-block)
302 (ruby-switch-to-inf t))
304 (defun ruby-load-file (file-name)
305 "Load a Ruby file into the inferior Ruby process."
306 (interactive (comint-get-source "Load Ruby file: " ruby-prev-l/c-dir/file
307 ruby-source-modes t)) ;; T because LOAD needs an exact name
308 (comint-check-source file-name) ; Check to see if buffer needs saved.
309 (setq ruby-prev-l/c-dir/file (cons (file-name-directory file-name)
310 (file-name-nondirectory file-name)))
311 (comint-send-string (inf-ruby-proc) (concat "(load \""
312 file-name
313 "\"\)\n")))
315 (defun inf-ruby-completions (seed)
316 "Return a list of completions for the line of ruby code starting with SEED."
317 (let* ((proc (get-buffer-process inf-ruby-buffer))
318 (comint-filt (process-filter proc))
319 (kept "") completions)
320 (set-process-filter proc (lambda (proc string) (setf kept (concat kept string))))
321 (process-send-string proc (format "puts IRB::InputCompletor::CompletionProc.call('%s').compact\n" seed))
322 (while (not (string-match inf-ruby-prompt-pattern kept)) (accept-process-output proc))
323 (if (string-match "^[[:alpha:]]+?Error: " kept) (error kept))
324 (setf completions (butlast (split-string kept "[\r\n]") 2))
325 (set-process-filter proc comint-filt)
326 completions))
328 (defun inf-ruby-complete-or-tab (&optional command)
329 "Either complete the ruby code at point or call
330 `indent-for-tab-command' if no completion is available. Relies
331 on the irb/completion Module used by readline when running irb
332 through a terminal."
333 (interactive (list (let* ((curr (thing-at-point 'line))
334 (completions (inf-ruby-completions curr)))
335 (case (length completions)
336 (0 nil)
337 (1 (car completions))
338 (t (completing-read "possible completions: " completions nil 'confirm-only curr))))))
339 (if (not command)
340 (call-interactively 'indent-for-tab-command)
341 (move-beginning-of-line 1)
342 (kill-line 1)
343 (insert command)))
345 ;;;###autoload
346 (eval-after-load 'ruby-mode
347 '(add-hook 'ruby-mode-hook 'inf-ruby-keys))
349 (provide 'inf-ruby)
350 ;;; inf-ruby.el ends here