Initial Commit
[temp.git] / site-lisp / ri / ri-ruby.el
blob16e1eb56b346cdb8e14169032f45cc6849376413
1 ;;;; ri-ruby.el emacs wrapper around ri
2 ;;
3 ;; Author: Kristof Bastiaensen <kristof@vleeuwen.org>
4 ;;
5 ;;
6 ;; Copyright (C) 2004,2006 Kristof Bastiaensen
7 ;;
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation; either version 2 of the License, or
11 ;; (at your option) any later version.
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program; if not, write to the Free Software
20 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 ;;----------------------------------------------------------------------
24 ;; Installing:
25 ;; ===========
27 ;; add the following to your init.el, replacing the filenames with
28 ;; their correct locations:
30 ;; (setq ri-ruby-script "/home/kristof/.xemacs/ri-emacs.rb")
31 ;; (autoload 'ri "/home/kristof/.xemacs/ri-ruby.el" nil t)
33 ;; You may want to bind the ri command to a key.
34 ;; For example to bind it to F1 in ruby-mode:
35 ;; Method/class completion is also available.
37 ;; (add-hook 'ruby-mode-hook (lambda ()
38 ;; (local-set-key 'f1 'ri)
39 ;; (local-set-key "\M-\C-i" 'ri-ruby-complete-symbol)
40 ;; (local-set-key 'f4 'ri-ruby-show-args)
41 ;; ))
44 ;; Usage:
45 ;; ======
46 ;; M-x ri
48 ;; M-Tab for completion
49 ;;
50 ;; Bugs:
51 ;; ====
53 ;; * The first time you give the ri command on xemacs, it may give
54 ;; strange behaviour in XEmacs. This is probably due to a
55 ;; bug in the way XEmacs handles processes under linux.
57 ;; * It is reported that the ruby-script doesn't work with XEmacs under
58 ;; MS-Windows. This is probably a bug in processes in XEmacs.
60 ;; Contributors:
61 ;; =============
63 ;; rubikitch (http://www.rubyist.net/~rubikitch/):
64 ;; fixed highlighting under Emacs
66 (require 'ansi-color)
68 (defvar ri-ruby-program "ruby"
69 "The ruby program name.")
71 (defvar ri-ruby-script "/cygdrive/c/cygwin/usr/share/emacs/site-lisp/ri/ri-emacs.rb"
72 "the ruby script to communicate with")
74 (defvar ri-ruby-process nil
75 "The current ri process where emacs is interacting with")
77 (defvar ri-ruby-history nil
78 "The history for ri")
80 (defvar ri-ruby-process-buffer nil)
82 (defun ri-ruby-get-process ()
83 (cond ((or (null ri-ruby-process)
84 (not (equal (process-status ri-ruby-process) 'run)))
85 (setq ri-ruby-process
86 (start-process "ri-ruby-process"
87 nil
88 ri-ruby-program ri-ruby-script))
89 (process-kill-without-query ri-ruby-process) ;kill when ending emacs
90 (ri-ruby-process-check-ready)))
91 ri-ruby-process)
93 (defun ri-ruby-process-filter-expr (proc str)
94 (let ((ansi-color-context nil))
95 (save-excursion
96 (set-buffer ri-ruby-process-buffer)
97 (goto-char (point-max))
98 (insert-string (ansi-color-filter-apply str)))))
100 (defun ri-ruby-process-filter-lines (proc str)
101 (save-excursion
102 (set-buffer ri-ruby-process-buffer)
103 (goto-char (point-max))
104 (insert-string (ansi-color-apply str))))
106 (defvar ri-startup-timeout 60)
107 (defun ri-ruby-process-check-ready ()
108 (let ((ri-ruby-process-buffer (generate-new-buffer " ri-ruby-output")))
109 (unwind-protect
110 (save-excursion
111 (set-buffer ri-ruby-process-buffer)
112 (set-process-filter ri-ruby-process 'ri-ruby-process-filter-expr)
113 (ri-ruby-check-process ri-ruby-process-buffer)
114 (accept-process-output ri-ruby-process ri-startup-timeout)
115 (goto-char (point-min))
116 (cond ((not (looking-at "READY.*\n"))
117 (delete-process ri-ruby-process)
118 (error "Couldn't start ruby script"))))
119 (set-process-filter ri-ruby-process t)
120 (kill-buffer ri-ruby-process-buffer))))
122 (defun ri-ruby-check-process (buffer)
123 (or (equal (process-status ri-ruby-process) 'run)
124 (let ((output (with-current-buffer buffer
125 (buffer-substring (point-min)
126 (point-max)))))
127 (error "Process is not running.\n" output))))
129 (defun ri-ruby-process-get-expr (cmd param)
130 (ri-ruby-get-process)
131 (let ((ri-ruby-process-buffer (generate-new-buffer " ri-ruby-output"))
132 (command (concat cmd " " param "\n")))
133 (unwind-protect
134 (save-excursion
135 (set-buffer ri-ruby-process-buffer)
136 (set-process-filter ri-ruby-process 'ri-ruby-process-filter-expr)
137 (process-send-string ri-ruby-process command)
138 (ri-ruby-check-process ri-ruby-process-buffer)
139 (while (progn (goto-char (point-min))
140 (not (looking-at ".*\n"))) ;we didn't read a whole line
141 (ri-ruby-check-process ri-ruby-process-buffer)
142 (accept-process-output ri-ruby-process))
143 (goto-char (point-min))
144 (read (buffer-substring (point)
145 (point-at-eol))))
146 (set-process-filter ri-ruby-process t)
147 (kill-buffer ri-ruby-process-buffer))))
149 (defun ri-ruby-process-get-lines (cmd param)
150 (ri-ruby-get-process)
151 (if (equal param "") nil
152 (let ((ri-ruby-process-buffer (generate-new-buffer " ri-ruby-output"))
153 (command (concat cmd " " param "\n")))
154 (unwind-protect
155 (save-excursion
156 (set-buffer ri-ruby-process-buffer)
157 (set-process-filter ri-ruby-process 'ri-ruby-process-filter-lines)
158 (process-send-string ri-ruby-process command)
159 (ri-ruby-check-process ri-ruby-process-buffer)
160 (while (progn (goto-char (point-max))
161 (goto-char (point-at-bol 0))
162 (not (looking-at "RI_EMACS_END_OF_INFO$")))
163 (ri-ruby-check-process ri-ruby-process-buffer)
164 (accept-process-output ri-ruby-process))
165 (if (bobp) nil
166 (backward-char)
167 (buffer-substring (point-min) (point))))
168 (set-process-filter ri-ruby-process t)
169 (kill-buffer ri-ruby-process-buffer)))))
171 (defun ri-ruby-complete-method (str pred type)
172 (let* ((cmd (cdr (assoc type '((nil . "TRY_COMPLETION")
173 (t . "COMPLETE_ALL")
174 (lambda . "LAMBDA")))))
175 (result (ri-ruby-process-get-expr cmd str)))
176 (if (and pred (listp result))
177 (setq result (mapcar pred result)))
178 result))
180 (defun ri-ruby-read-keyw ()
181 (let* ((curr (current-word))
182 (match (ri-ruby-process-get-expr "LAMBDA" curr))
183 (default (if match curr nil))
184 (prompt (concat "method- or classname"
185 (if default (concat " (default " default ")") "")
186 ": "))
187 (keyw (completing-read prompt 'ri-ruby-complete-method
188 nil t "" 'ri-ruby-history default))
189 (classes (ri-ruby-process-get-expr "CLASS_LIST" keyw))
190 (class (cond ((null classes) nil)
191 ((null (cdr classes)) (caar classes))
192 (t (completing-read (concat prompt keyw
193 " classname: ")
194 classes nil t)))))
195 (list keyw class)))
197 (defun ri-ruby-method-with-class (meth classes)
198 (if (null classes)
199 meth
200 (concat meth " [" (mapconcat 'car classes ", ") "]")))
202 (defun ri-ruby-complete-symbol ()
203 "Completion on ruby-mode."
204 (interactive)
205 (let* ((curr (current-word))
206 (keyw curr)
207 (classes (ri-ruby-process-get-expr "CLASS_LIST_WITH_FLAG" keyw))
208 (completion (try-completion curr 'ri-ruby-complete-method nil)))
209 (cond ((eq completion t)
210 (message "%s" (ri-ruby-method-with-class curr classes)))
211 ((null completion)
212 (message "Can't find completion for \"%s\"" curr)
213 (ding))
214 ((not (string= curr completion))
215 (delete-region (save-excursion (search-backward curr) (point))
216 (point))
217 (insert completion)
218 (setq classes (ri-ruby-process-get-expr "CLASS_LIST_WITH_FLAG" completion))
219 (message "%s" (ri-ruby-method-with-class completion classes)))
221 (message "Making completion list...")
222 (with-output-to-temp-buffer "*Completions*"
223 (display-completion-list
224 (all-completions curr 'ri-ruby-complete-method)))
225 (message "%s" (ri-ruby-method-with-class completion classes))))))
227 (defun test-ri-ruby-complete-symbol ()
228 "Test of ri-ruby-complete-symbol."
229 (interactive)
230 (pop-to-buffer "*ruby completion test*")
231 (ruby-mode)
232 (erase-buffer)
233 (goto-char (point-min))
234 (insert "prin
235 object_id
236 intern
237 printf
238 # (kill-process \"ri-ruby-process\")
241 (defun ri-ruby-show-args ()
242 (interactive)
243 (let* ((method (current-word))
244 (info (ri-ruby-process-get-lines "DISPLAY_ARGS" method)))
245 (when info
246 (message "%s" info))))
248 (defun ri (keyw &optional class)
249 "Execute `ri'."
250 (interactive (ri-ruby-read-keyw))
251 (let* ((method (if class (concat class "#" keyw) keyw))
252 (info (ri-ruby-process-get-lines "DISPLAY_INFO" method)))
253 (cond (info (ri-ruby-show-info method info))
254 ((null class))
255 (t (setq method (concat class "::" keyw))
256 (setq info (ri-ruby-process-get-lines "DISPLAY_INFO" method))
257 (if info (ri-ruby-show-info method info))))))
259 (cond ((fboundp 'with-displaying-help-buffer) ; for XEmacs
260 (defun ri-ruby-show-info (method info)
261 (with-displaying-help-buffer
262 (lambda () (princ info))
263 (format "ri `%s'" method))))
264 (t ; for Emacs
265 (defun ri-ruby-show-info (method info)
266 (let ((b (get-buffer-create (format "ri `%s'" method))))
267 (display-buffer b)
268 (with-current-buffer b
269 (buffer-disable-undo)
270 (erase-buffer)
271 (insert info)
272 (goto-char 1)))
273 info)))