123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- ;;; inf-ruby.el --- Run a ruby process in a buffer
- ;; Copyright (C) 1999-2008 Yukihiro Matsumoto, Nobuyoshi Nakada
- ;; Authors: Yukihiro Matsumoto, Nobuyoshi Nakada
- ;; URL: http://www.emacswiki.org/cgi-bin/wiki/RubyMode
- ;; Created: 8 April 1998
- ;; Keywords: languages ruby
- ;; Version: 2.1
- ;; Package-Requires: ((ruby-mode "1.1"))
- ;;; Commentary:
- ;;
- ;; inf-ruby.el provides a REPL buffer connected to an IRB subprocess.
- ;;
- ;; If you're installing manually, you'll need to:
- ;; * drop the file somewhere on your load path (perhaps ~/.emacs.d)
- ;; * Add the following lines to your .emacs file:
- ;; (autoload 'inf-ruby "inf-ruby" "Run an inferior Ruby process" t)
- ;; (autoload 'inf-ruby-keys "inf-ruby" "" t)
- ;; (eval-after-load 'ruby-mode
- ;; '(add-hook 'ruby-mode-hook 'inf-ruby-keys))
- ;;; TODO:
- ;;
- ;; inferior-ruby-error-regexp-alist doesn't match this example
- ;; SyntaxError: /home/eschulte/united/org/work/arf/arf/lib/cluster.rb:35: syntax error, unexpected '~', expecting kEND
- ;; similarity = comparison_cache[m][n] ||= clusters[m] ~ clusters[n]
- ;;
- ;; M-p skips the first entry in the input ring.
- ;;
- (require 'comint)
- (require 'compile)
- (require 'ruby-mode)
- (defvar inf-ruby-default-implementation "ruby"
- "Which ruby implementation to use if none is specified.")
- (defvar inf-ruby-first-prompt-pattern "^irb(.*)[0-9:]+0> *"
- "First prompt regex pattern of ruby interpreter.")
- (defvar inf-ruby-prompt-pattern "^\\(irb(.*)[0-9:]+[>*\"'] *\\)+"
- "Prompt regex pattern of ruby interpreter.")
- (defvar inf-ruby-mode-hook nil
- "*Hook for customising inf-ruby mode.")
- (defvar inf-ruby-mode-map
- (let ((map (copy-keymap comint-mode-map)))
- (define-key map (kbd "C-c C-l") 'inf-ruby-load-file)
- (define-key map (kbd "C-x C-e") 'ruby-send-last-sexp)
- (define-key map (kbd "TAB") 'inf-ruby-complete-or-tab)
- map)
- "*Mode map for inf-ruby-mode")
- (defvar inf-ruby-implementations
- '(("ruby" . "irb --inf-ruby-mode -r irb/completion")
- ("jruby" . "jruby -S irb -r irb/completion")
- ("rubinius" . "rbx -r irb/completion")
- ("yarv" . "irb1.9 --inf-ruby-mode -r irb/completion")) ;; TODO: ironruby?
- "An alist of ruby implementations to irb executable names.")
- ;; TODO: do we need these two defvars?
- (defvar ruby-source-modes '(ruby-mode)
- "*Used to determine if a buffer contains Ruby source code.
- If it's loaded into a buffer that is in one of these major modes, it's
- considered a ruby source file by ruby-load-file.
- Used by these commands to determine defaults.")
- (defvar ruby-prev-l/c-dir/file nil
- "Caches the last (directory . file) pair.
- Caches the last pair used in the last ruby-load-file command.
- Used for determining the default in the
- next one.")
- (defconst inf-ruby-error-regexp-alist
- '(("SyntaxError: compile error\n^\\([^\(].*\\):\\([1-9][0-9]*\\):" 1 2)
- ("^\tfrom \\([^\(].*\\):\\([1-9][0-9]*\\)\\(:in `.*'\\)?$" 1 2)))
- ;;;###autoload
- (defun inf-ruby-keys ()
- "Set local key defs to invoke inf-ruby from ruby-mode."
- (define-key ruby-mode-map "\M-\C-x" 'ruby-send-definition)
- (define-key ruby-mode-map "\C-x\C-e" 'ruby-send-last-sexp)
- (define-key ruby-mode-map "\C-c\C-b" 'ruby-send-block)
- (define-key ruby-mode-map "\C-c\M-b" 'ruby-send-block-and-go)
- (define-key ruby-mode-map "\C-c\C-x" 'ruby-send-definition)
- (define-key ruby-mode-map "\C-c\M-x" 'ruby-send-definition-and-go)
- (define-key ruby-mode-map "\C-c\C-r" 'ruby-send-region)
- (define-key ruby-mode-map "\C-c\M-r" 'ruby-send-region-and-go)
- (define-key ruby-mode-map "\C-c\C-z" 'ruby-switch-to-inf)
- (define-key ruby-mode-map "\C-c\C-l" 'ruby-load-file)
- (define-key ruby-mode-map "\C-c\C-s" 'inf-ruby))
- (defvar inf-ruby-buffer nil "Current irb process buffer.")
- (defun inf-ruby-mode ()
- "Major mode for interacting with an inferior ruby (irb) process.
- The following commands are available:
- \\{inf-ruby-mode-map}
- A ruby process can be fired up with M-x inf-ruby.
- Customisation: Entry to this mode runs the hooks on comint-mode-hook and
- inf-ruby-mode-hook (in that order).
- You can send text to the inferior ruby process from other buffers containing
- Ruby source.
- ruby-switch-to-inf switches the current buffer to the ruby process buffer.
- ruby-send-definition sends the current definition to the ruby process.
- ruby-send-region sends the current region to the ruby process.
- ruby-send-definition-and-go, ruby-send-region-and-go,
- switch to the ruby process buffer after sending their text.
- Commands:
- Return after the end of the process' output sends the text from the
- end of process to point.
- Return before the end of the process' output copies the sexp ending at point
- to the end of the process' output, and sends it.
- Delete converts tabs to spaces as it moves back.
- Tab indents for ruby; with arugment, shifts rest
- of expression rigidly with the current line.
- C-M-q does Tab on each line starting within following expression.
- Paragraphs are separated only by blank lines. # start comments.
- If you accidentally suspend your process, use \\[comint-continue-subjob]
- to continue it."
- (interactive)
- (comint-mode)
- (setq comint-prompt-regexp inf-ruby-prompt-pattern)
- (ruby-mode-variables)
- (setq major-mode 'inf-ruby-mode)
- (setq mode-name "Inf-Ruby")
- (setq mode-line-process '(":%s"))
- (use-local-map inf-ruby-mode-map)
- (setq comint-input-filter (function inf-ruby-input-filter))
- (setq comint-get-old-input (function inf-ruby-get-old-input))
- (make-local-variable 'compilation-error-regexp-alist)
- (setq compilation-error-regexp-alist inf-ruby-error-regexp-alist)
- (compilation-shell-minor-mode t)
- (run-hooks 'inf-ruby-mode-hook))
- (defvar inf-ruby-filter-regexp "\\`\\s *\\S ?\\S ?\\s *\\'"
- "*Input matching this regexp are not saved on the history list.
- Defaults to a regexp ignoring all inputs of 0, 1, or 2 letters.")
- (defun inf-ruby-input-filter (str)
- "Don't save anything matching inf-ruby-filter-regexp"
- (not (string-match inf-ruby-filter-regexp str)))
- ;; adapted from replace-in-string in XEmacs (subr.el)
- (defun inf-ruby-remove-in-string (str regexp)
- "Remove all matches in STR for REGEXP and returns the new string."
- (let ((rtn-str "") (start 0) match prev-start)
- (while (setq match (string-match regexp str start))
- (setq prev-start start
- start (match-end 0)
- rtn-str (concat rtn-str (substring str prev-start match))))
- (concat rtn-str (substring str start))))
- (defun inf-ruby-get-old-input ()
- "Snarf the sexp ending at point"
- (save-excursion
- (let ((end (point)))
- (re-search-backward inf-ruby-first-prompt-pattern)
- (inf-ruby-remove-in-string (buffer-substring (point) end)
- inf-ruby-prompt-pattern))))
- ;;;###autoload
- (defun inf-ruby (&optional impl)
- "Run an inferior Ruby process in a buffer.
- With prefix argument, prompts for which Ruby implementation
- \(from the list `inf-ruby-implementations') to use. Runs the
- hooks `inf-ruby-mode-hook' \(after the `comint-mode-hook' is
- run)."
- (interactive (list (if current-prefix-arg
- (completing-read "Ruby Implementation: "
- (mapc #'car inf-ruby-implementations))
- inf-ruby-default-implementation)))
- (setq impl (or impl "ruby"))
- (let ((command (cdr (assoc impl inf-ruby-implementations))))
- (run-ruby command impl)))
- ;;;###autoload
- (defun run-ruby (&optional command name)
- "Run an inferior Ruby process, input and output via buffer *ruby*.
- If there is a process already running in `*ruby*', switch to that buffer.
- With argument, allows you to edit the command line (default is value
- of `ruby-program-name'). Runs the hooks `inferior-ruby-mode-hook'
- \(after the `comint-mode-hook' is run).
- \(Type \\[describe-mode] in the process buffer for a list of commands.)"
- (interactive)
- (setq command (or command (cdr (assoc inf-ruby-default-implementation
- inf-ruby-implementations))))
- (setq name (or name "ruby"))
- (if (not (comint-check-proc inf-ruby-buffer))
- (let ((commandlist (split-string command)))
- (set-buffer (apply 'make-comint name (car commandlist)
- nil (cdr commandlist)))
- (inf-ruby-mode)))
- (pop-to-buffer (setq inf-ruby-buffer (format "*%s*" name))))
- (defun inf-ruby-proc ()
- "Returns the current IRB process. See variable inf-ruby-buffer."
- (or (get-buffer-process (if (eq major-mode 'inf-ruby-mode)
- (current-buffer)
- inf-ruby-buffer))
- (error "No current process. See variable inf-ruby-buffer")))
- ;; These commands are added to the ruby-mode keymap:
- (defconst ruby-send-terminator "--inf-ruby-%x-%d-%d-%d--"
- "Template for irb here document terminator.
- Must not contain ruby meta characters.")
- (defconst ruby-eval-separator "")
- (defun ruby-send-region (start end)
- "Send the current region to the inferior Ruby process."
- (interactive "r")
- (let (term (file (buffer-file-name)) line)
- (save-excursion
- (save-restriction
- (widen)
- (goto-char start)
- (setq line (+ start (forward-line (- start)) 1))
- (goto-char start)
- (while (progn
- (setq term (apply 'format ruby-send-terminator (random) (current-time)))
- (re-search-forward (concat "^" (regexp-quote term) "$") end t)))))
- ;; compilation-parse-errors parses from second line.
- (save-excursion
- (let ((m (process-mark (inf-ruby-proc))))
- (set-buffer (marker-buffer m))
- (goto-char m)
- (insert ruby-eval-separator "\n")
- (set-marker m (point))))
- (comint-send-string (inf-ruby-proc) (format "eval <<'%s', nil, %S, %d\n" term file line))
- (comint-send-region (inf-ruby-proc) start end)
- (comint-send-string (inf-ruby-proc) (concat "\n" term "\n"))))
- (defun ruby-send-definition ()
- "Send the current definition to the inferior Ruby process."
- (interactive)
- (save-excursion
- (ruby-end-of-defun)
- (let ((end (point)))
- (ruby-beginning-of-defun)
- (ruby-send-region (point) end))))
- (defun ruby-send-last-sexp ()
- "Send the previous sexp to the inferior Ruby process."
- (interactive)
- (ruby-send-region (save-excursion (backward-sexp) (point)) (point)))
- (defun ruby-send-block ()
- "Send the current block to the inferior Ruby process."
- (interactive)
- (save-excursion
- (ruby-end-of-block)
- (end-of-line)
- (let ((end (point)))
- (ruby-beginning-of-block)
- (ruby-send-region (point) end))))
- (defun ruby-switch-to-inf (eob-p)
- "Switch to the ruby process buffer.
- With argument, positions cursor at end of buffer."
- (interactive "P")
- (if (get-buffer inf-ruby-buffer)
- (pop-to-buffer inf-ruby-buffer)
- (error "No current process buffer. See variable inf-ruby-buffer."))
- (cond (eob-p
- (push-mark)
- (goto-char (point-max)))))
- (defun ruby-send-region-and-go (start end)
- "Send the current region to the inferior Ruby process.
- Then switch to the process buffer."
- (interactive "r")
- (ruby-send-region start end)
- (ruby-switch-to-inf t))
- (defun ruby-send-definition-and-go ()
- "Send the current definition to the inferior Ruby.
- Then switch to the process buffer."
- (interactive)
- (ruby-send-definition)
- (ruby-switch-to-inf t))
- (defun ruby-send-block-and-go ()
- "Send the current block to the inferior Ruby.
- Then switch to the process buffer."
- (interactive)
- (ruby-send-block)
- (ruby-switch-to-inf t))
- (defun ruby-load-file (file-name)
- "Load a Ruby file into the inferior Ruby process."
- (interactive (comint-get-source "Load Ruby file: " ruby-prev-l/c-dir/file
- ruby-source-modes t)) ;; T because LOAD needs an exact name
- (comint-check-source file-name) ; Check to see if buffer needs saved.
- (setq ruby-prev-l/c-dir/file (cons (file-name-directory file-name)
- (file-name-nondirectory file-name)))
- (comint-send-string (inf-ruby-proc) (concat "(load \""
- file-name
- "\"\)\n")))
- (defun inf-ruby-completions (seed)
- "Return a list of completions for the line of ruby code starting with SEED."
- (let* ((proc (get-buffer-process inf-ruby-buffer))
- (comint-filt (process-filter proc))
- (kept "") completions)
- (set-process-filter proc (lambda (proc string) (setf kept (concat kept string))))
- (process-send-string proc (format "puts IRB::InputCompletor::CompletionProc.call('%s').compact\n" seed))
- (while (not (string-match inf-ruby-prompt-pattern kept)) (accept-process-output proc))
- (if (string-match "^[[:alpha:]]+?Error: " kept) (error kept))
- (setf completions (butlast (split-string kept "[\r\n]") 2))
- (set-process-filter proc comint-filt)
- completions))
- (defun inf-ruby-complete-or-tab (&optional command)
- "Either complete the ruby code at point or call
- `indent-for-tab-command' if no completion is available. Relies
- on the irb/completion Module used by readline when running irb
- through a terminal."
- (interactive (list (let* ((curr (thing-at-point 'line))
- (completions (inf-ruby-completions curr)))
- (case (length completions)
- (0 nil)
- (1 (car completions))
- (t (completing-read "possible completions: " completions nil 'confirm-only curr))))))
- (if (not command)
- (call-interactively 'indent-for-tab-command)
- (move-beginning-of-line 1)
- (kill-line 1)
- (insert command)))
- ;;;###autoload
- (eval-after-load 'ruby-mode
- '(add-hook 'ruby-mode-hook 'inf-ruby-keys))
- (provide 'inf-ruby)
- ;;; inf-ruby.el ends here
|